From d5bae5e8e4bd42757f8948be76ac2f635f7bc941 Mon Sep 17 00:00:00 2001 From: CastagnaIT Date: Wed, 13 Sep 2023 15:23:05 +0200 Subject: [PATCH] Enabled TCP Keep Alive on httpcore (httpx) Currently there is no way to enable TCP KeepAlive directly on httpx module ref. PR:https://github.com/CastagnaIT/plugin.video.netflix/pull/1172 --- packages/httpcore/_backends/sync.py | 3 ++ packages/httpcore/_backends/tcp_keep_alive.py | 38 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 packages/httpcore/_backends/tcp_keep_alive.py diff --git a/packages/httpcore/_backends/sync.py b/packages/httpcore/_backends/sync.py index f2dbd32af..da36bb1ab 100644 --- a/packages/httpcore/_backends/sync.py +++ b/packages/httpcore/_backends/sync.py @@ -16,6 +16,7 @@ ) from .._utils import is_socket_readable from .base import SOCKET_OPTION, NetworkBackend, NetworkStream +from .tcp_keep_alive import enable_tcp_keep_alive class TLSinTLSStream(NetworkStream): # pragma: no cover @@ -217,6 +218,8 @@ def connect_tcp( for option in socket_options: sock.setsockopt(*option) # pragma: no cover sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + # Enable TCP Keep-Alive + enable_tcp_keep_alive(sock) return SyncStream(sock) def connect_unix_socket( diff --git a/packages/httpcore/_backends/tcp_keep_alive.py b/packages/httpcore/_backends/tcp_keep_alive.py new file mode 100644 index 000000000..90a08c7f6 --- /dev/null +++ b/packages/httpcore/_backends/tcp_keep_alive.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +""" + Copyright (C) 2021 Stefano Gottardo - @CastagnaIT + Helper to enable TCP Keep Alive + + SPDX-License-Identifier: MIT + See LICENSES/MIT.md for more information. +""" +import socket +import sys + +TCP_KEEP_IDLE = 45 +TCP_KEEPALIVE_INTERVAL = 10 +TCP_KEEP_CNT = 6 + + +def enable_tcp_keep_alive(sock): + """Enable TCP Keep-Alive (by default disabled)""" + # More info on PR: https://github.com/CastagnaIT/plugin.video.netflix/pull/1065 + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) + if sys.platform == 'linux': + # TCP Keep Alive Probes for Linux/Android + if hasattr(socket, 'TCP_KEEPIDLE'): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, TCP_KEEP_IDLE) + if hasattr(socket, 'TCP_KEEPINTVL'): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, TCP_KEEPALIVE_INTERVAL) + if hasattr(socket, 'TCP_KEEPCNT'): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, TCP_KEEP_CNT) + elif sys.platform == 'darwin': + # TCP Keep Alive Probes for MacOS + # NOTE: The socket constants from MacOS netinet/tcp.h are not exported by python's socket module + # The MacOS TCP_KEEPALIVE(0x10) constant should be the same thing of the linux TCP_KEEPIDLE constant + sock.setsockopt(socket.IPPROTO_TCP, getattr(socket, 'TCP_KEEPIDLE', 0x10), TCP_KEEP_IDLE) + sock.setsockopt(socket.IPPROTO_TCP, getattr(socket, 'TCP_KEEPINTVL', 0x101), TCP_KEEPALIVE_INTERVAL) + sock.setsockopt(socket.IPPROTO_TCP, getattr(socket, 'TCP_KEEPCNT', 0x102), TCP_KEEP_CNT) + elif sys.platform == 'win32': + # TCP Keep Alive Probes for Windows + sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, TCP_KEEP_IDLE * 1000, TCP_KEEPALIVE_INTERVAL * 1000))