python / python/cpython

xmlrpc.client does not properly handle severed HTTPS connections

Aperta
#121,624 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

type-bug
Lingua principale
Python
Stelle
77.2k
Fork
35.9k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Bug report

Bug description:

I have an xmlrpc.client based script running Python 3.11.2 which has the following setup and problem :

  • it is solliciting an HTTPS endpoint with HTTP/1.1
  • then endpoint is an Apache with a KeepAliveTimeout 5 (seconds)
  • the script makes about 10k XML-RPC requests in a loop for 20 minutes, everything is fine
  • the script works elsewhere for 5min
  • the script then resume to make XML-RPC requests to the same endpoint (same xmlrpc.client object)
  • it fails with this stack trace :
...
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1122, in __call__
    return self.__send(self.__name, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1464, in __request
    response = self.__transport.request(
               ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1166, in request
    return self.single_request(host, handler, request_body, verbose)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1178, in single_request
    http_conn = self.send_request(host, handler, request_body, verbose)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1291, in send_request
    self.send_content(connection, request_body)
  File "/usr/lib/python3.11/xmlrpc/client.py", line 1321, in send_content
    connection.endheaders(request_body)
  File "/usr/lib/python3.11/http/client.py", line 1277, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.11/http/client.py", line 1076, in _send_output
    self.send(chunk)
  File "/usr/lib/python3.11/http/client.py", line 998, in send
    self.sock.sendall(data)
  File "/usr/lib/python3.11/ssl.py", line 1274, in sendall
    v = self.send(byte_view[count:])
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/ssl.py", line 1243, in send
    return self._sslobj.write(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^
ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2393)

My analysis : the code in xmlrpc.client:request() handles the fact that the TCP connection from a previous HTTP keepalive request might be closed by the server, but only if http.client explicitly raises http.client.RemoteDisconnected. However in my case the remote disconnection was raised as a ssl.SSLEOFError which confused xmlrpc.client and made it fail right away instead of retrying as it is designed to do.

I'm not sure about the fix, but I thought that it would be correct for http.client to assimilate ssl.SSLEOFError to http.client.RemoteDisconnected :

  • semantics seem to match according to the documentation (https://docs.python.org/3/library/ssl.html#ssl.SSLEOFError) : "A subclass of SSLError raised when the SSL connection has been terminated abruptly. Generally, you shouldn’t try to reuse the underlying transport when this error is encountered."
  • it is easier for http.client users to handle, they don't care if the plain socket or the SSL layer reports the remote disconnection

This patch solved the bug described above in my case :

--- /usr/lib/python3.11/http/client.py.orig	2024-05-02 13:59:08.000000000 +0200
+++ /usr/lib/python3.11/http/client.py	2024-07-11 12:22:57.211454581 +0200
@@ -994,10 +994,13 @@
                 self.sock.sendall(datablock)
             return
         sys.audit("http.client.send", self, data)
         try:
             self.sock.sendall(data)
+        except Exception as e:
+            if type(e).__name__ == 'SSLEOFError':
+                raise RemoteDisconnected("SSL layer disconnected")
         except TypeError:
             if isinstance(data, collections.abc.Iterable):
                 for d in data:
                     self.sock.sendall(d)
             else:

It is a bit convoluted because ssl is conditionnaly imported.

CPython versions tested on:

3.11

Operating systems tested on:

Linux

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia leggendo Lib/http/client.py intorno al percorso di invio e Lib/xmlrpc/client.py intorno alla gestione dei tentativi aggiuntivi delle richieste. Riproduci su Linux, con Python 3.11, lo scenario descritto di timeout HTTPS keep-alive, quindi verifica che una disconnessione SSL improvvisa riceva la gestione prevista e non faccia fallire immediatamente la richiesta XML-RPC.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
networking
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Abbastanza chiara
Idoneità per principianti
45/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.