Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for keepalive #63

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions src/zinolib/ritz.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
import select

from .config.tcl import parse_tcl_config # noqa: F401 (used to be in this file)
from .utils import windows_codepage_cp1252, generate_authtoken
from .utils import windows_codepage_cp1252, generate_authtoken, enable_socket_keepalive


codecs.register_error("windows_codepage_cp1252", windows_codepage_cp1252)
Expand Down Expand Up @@ -328,7 +328,7 @@
"""
DELIMITER = "\r\n"

def __init__(self, server, port=8001, timeout=10, username=None, password=None):
def __init__(self, server, port=8001, timeout=10, username=None, password=None, keepalive=True):
"""Initialize"""
global logger

Expand All @@ -340,6 +340,7 @@
self.timeout = timeout
self.username = username
self.password = password
self.keepalive = keepalive
self._buff = ""

def __enter__(self):
Expand Down Expand Up @@ -448,6 +449,10 @@
else:
raise NotConnectedError("Did not get a status code 200")

if self.keepalive:
enable_socket_keepalive(self._sock)
logger.info("Set keepalive on protocol socket")

# Automaticly authenticate if username and password is supplied
if self.username and self.password:
self.authenticate(self.username, self.password)
Expand Down Expand Up @@ -1090,13 +1095,14 @@
"""
DELIMITER = "\r\n"

def __init__(self, zino_session, port=8002, timeout=30):
def __init__(self, zino_session, port=8002, timeout=30, keepalive=True):
self._sock = None
self.connStatus = False
self._buff = ""
self.zino_session = zino_session
self.port = port
self.timeout = timeout
self.keepalive = keepalive

Check warning on line 1105 in src/zinolib/ritz.py

View check run for this annotation

Codecov / codecov/patch

src/zinolib/ritz.py#L1105

Added line #L1105 was not covered by tests

def __enter__(self):
self.connect()
Expand Down Expand Up @@ -1133,6 +1139,10 @@
else:
raise NotConnectedError("Key not found")

if self.keepalive:
enable_socket_keepalive(self._sock)
logger.info("Set keepalive on notifier socket")

Check warning on line 1144 in src/zinolib/ritz.py

View check run for this annotation

Codecov / codecov/patch

src/zinolib/ritz.py#L1142-L1144

Added lines #L1142 - L1144 were not covered by tests

def poll(self, timeout=0):
"""Poll the notifier socket for new data

Expand Down
42 changes: 42 additions & 0 deletions src/zinolib/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import functools
import hashlib
import platform
import socket


__all__ = [
"windows_codepage_cp1252",
"generate_authtoken",
"enable_socket_keepalive",
]


Expand Down Expand Up @@ -80,3 +83,42 @@
return return_value
return wrapper
return inner


def _enable_keepalive_linux(sock, after_idle_sec, interval_sec, max_fails):
"""Set TCP keepalive on an open socket.

It activates after 1 second (after_idle_sec) of idleness,
then sends a keepalive ping once every 3 seconds (interval_sec),
and closes the connection after 5 failed ping (max_fails), or 15 seconds
"""
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)


def _enable_keepalive_osx(sock, after_idle_sec, interval_sec, max_fails):
"""Set TCP keepalive on an open socket.

sends a keepalive ping once every 3 seconds (interval_sec)
"""
# scraped from /usr/include, not exported by python's socket module
TCP_KEEPALIVE = 0x10
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, TCP_KEEPALIVE, interval_sec)

Check warning on line 109 in src/zinolib/utils.py

View check run for this annotation

Codecov / codecov/patch

src/zinolib/utils.py#L107-L109

Added lines #L107 - L109 were not covered by tests


def _enable_keepalive_win(sock, after_idle_sec, interval_sec, max_fails):
sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, after_idle_sec * 1000, interval_sec * 1000))

Check warning on line 113 in src/zinolib/utils.py

View check run for this annotation

Codecov / codecov/patch

src/zinolib/utils.py#L113

Added line #L113 was not covered by tests


def enable_socket_keepalive(sock, after_idle_sec=60, interval_sec=60, max_fails=5):
platforms = {
"Linux": _enable_keepalive_linux,
"Darwin": _enable_keepalive_osx,
"Windows": _enable_keepalive_win,
}
if (plat := platform.system()) in platforms:
return platforms[plat](sock, after_idle_sec, interval_sec, max_fails)
raise RuntimeError('Unsupported platform: {}'.format(plat))

Check warning on line 124 in src/zinolib/utils.py

View check run for this annotation

Codecov / codecov/patch

src/zinolib/utils.py#L124

Added line #L124 was not covered by tests
Loading