Skip to content

Commit f376919

Browse files
committed
Refresh internal https transport every 4 minutes
to avoid an `ssl.SSLEOFError: EOF occurred in violation of protocol` error on Python 3.10 and later
1 parent 1eb0d5e commit f376919

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

tcms_api/xmlrpc.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
# pylint: disable=protected-access,too-few-public-methods
1+
# pylint: disable=protected-access,too-few-public-methods,invalid-name,attribute-defined-outside-init
22

33
import sys
44
import urllib.parse
55

66
from base64 import b64encode
7+
from datetime import datetime, timedelta
78
from http import HTTPStatus
89
from http.client import HTTPSConnection
910
from xmlrpc.client import _Method, SafeTransport, Transport, ServerProxy
@@ -23,7 +24,26 @@
2324

2425

2526
class TCMSProxy(ServerProxy):
27+
_connected_since = datetime(2024, 1, 1, 0, 0)
28+
2629
def __request(self, methodname, params):
30+
# refresh the connection every 4 minutes to avoid an
31+
# `ssl.SSLEOFError: EOF occurred in violation of protocol` error with Python >= 3.10
32+
# In practice I've discovered that 5 minutes works as well, 6 minutes fails so
33+
# be more cautious and refresh the connection earlier!
34+
#
35+
# Side note: originally I thought this is related to calling
36+
# context.set_alpn_protocols(['http/1.1']) inside http/client.py, introduced in
37+
# https://github.com/python/cpython/commit/f97406be4c0a02c1501c7ab8bc8ef3850eddb962
38+
# but that doesn't seem to be the case (or is much harder for me to debug)!
39+
if getattr(
40+
self._ServerProxy__transport, # pylint: disable=access-member-before-definition
41+
"internal_refresh",
42+
False,
43+
) and datetime.now() - self._connected_since > timedelta(minutes=4):
44+
self._ServerProxy__transport = self._ServerProxy__transport.__class__()
45+
self._connected_since = datetime.now()
46+
2747
self._ServerProxy__transport._extra_headers = [("Referer", methodname)]
2848
return self._ServerProxy__request(methodname, params)
2949

@@ -56,12 +76,15 @@ def parse_response(self, response):
5676
class SafeCookieTransport(SafeTransport, CookieTransport):
5777
"""SafeTransport subclass that supports cookies."""
5878

79+
internal_refresh = True
5980
scheme = "https"
6081

6182

6283
class KerbTransport(SafeCookieTransport):
6384
"""Handles GSSAPI Negotiation (SPNEGO) authentication."""
6485

86+
internal_refresh = False
87+
6588
def get_host_info(self, host):
6689
host, extra_headers, x509 = Transport.get_host_info(self, host)
6790

0 commit comments

Comments
 (0)