python / python/cpython

xmlrpc.client does not properly handle severed HTTPS connections

Abierto
#121,624 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

type-bug
Lenguaje dominante
Python
Estrellas
77.2k
Forks
35.9k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza leyendo Lib/http/client.py alrededor de la ruta de envío y Lib/xmlrpc/client.py alrededor del manejo de reintentos de solicitudes. Reproduce en Linux, con Python 3.11, el escenario de tiempo de espera de HTTPS keep-alive descrito y verifica después que una desconexión SSL abrupta reciba el manejo previsto y no haga que la solicitud XML-RPC falle inmediatamente.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
networking
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
45/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.