Fix TLS accept loop stalls
This commit is contained in:
1 parent
80d6514c9d
commit
8fdbb23bc9
2 files changed
+66
-2
No files matched your search
@@ -8,6 +8,7 @@ import os
|
||||
import re
|
||||
import signal
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
@@ -157,6 +158,7 @@ class QuietHTTPServer(ThreadingHTTPServer):
|
||||
server_address = args[0] if args else kwargs.get('server_address', None)
|
||||
if server_address and ':' in server_address[0]:
|
||||
self.address_family = socket.AF_INET6
|
||||
self.ssl_context: object | None = None
|
||||
super().__init__(*args, **kwargs)
|
||||
self.accept_loop_requests_total = 0
|
||||
self.accept_loop_last_request_at = 0.0
|
||||
@@ -185,6 +187,31 @@ class QuietHTTPServer(ThreadingHTTPServer):
|
||||
else:
|
||||
super().server_bind()
|
||||
|
||||
def get_request(self):
|
||||
"""Accept a connection without letting TLS handshakes block the loop.
|
||||
|
||||
``ssl.wrap_socket(listening_socket)`` performs the TLS handshake inside
|
||||
``accept()``. A browser/network probe that opens TCP but never sends a
|
||||
ClientHello can then freeze the one accept loop, leaving the process
|
||||
alive but unable to serve any later clients. Accept the raw socket here
|
||||
and wrap the accepted connection with ``do_handshake_on_connect=False``
|
||||
so any slow/broken handshake times out in its own request thread.
|
||||
"""
|
||||
request, client_address = self.socket.accept()
|
||||
ssl_context = getattr(self, "ssl_context", None)
|
||||
if ssl_context is None:
|
||||
return request, client_address
|
||||
try:
|
||||
tls_request = ssl_context.wrap_socket(
|
||||
request,
|
||||
server_side=True,
|
||||
do_handshake_on_connect=False,
|
||||
)
|
||||
except Exception:
|
||||
request.close()
|
||||
raise
|
||||
return tls_request, client_address
|
||||
|
||||
def _handle_request_noblock(self):
|
||||
"""Record accept-loop progress before dispatching a request handler.
|
||||
|
||||
@@ -207,7 +234,10 @@ class QuietHTTPServer(ThreadingHTTPServer):
|
||||
exc_type, exc_value, _ = sys.exc_info()
|
||||
|
||||
# Silently ignore common connection errors caused by client disconnects
|
||||
if exc_type in (ConnectionResetError, BrokenPipeError, ConnectionAbortedError, TimeoutError):
|
||||
if exc_type in (
|
||||
ConnectionResetError, BrokenPipeError, ConnectionAbortedError,
|
||||
TimeoutError, ssl.SSLError, ssl.SSLEOFError,
|
||||
):
|
||||
return
|
||||
|
||||
# Also handle socket errors that indicate client disconnect
|
||||
@@ -648,7 +678,7 @@ def main() -> None:
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
ctx.load_cert_chain(TLS_CERT, TLS_KEY)
|
||||
httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
|
||||
httpd.ssl_context = ctx
|
||||
print(f' TLS enabled: cert={TLS_CERT}, key={TLS_KEY}', flush=True)
|
||||
except Exception as e:
|
||||
print(f'[!!] WARNING: TLS setup failed ({e}), falling back to HTTP', flush=True)
|
||||
|
||||
Reference in new issue
Block a user