python / python/cpython

xmlrpc.client does not properly handle severed HTTPS connections

Aberta
#121,624 0 comentários 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

type-bug
Linguagem predominante
Python
Estrelas
77.2k
Forks
35.9k
Métricas de merge de PRs
Métricas de PR pendentes

Descrição

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

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Comece lendo Lib/http/client.py em torno do caminho de envio e Lib/xmlrpc/client.py em torno do tratamento de novas tentativas de requisições. Reproduza no Linux, com Python 3.11, o cenário de tempo limite de HTTPS keep-alive descrito e, em seguida, verifique se uma desconexão SSL abrupta recebe o tratamento pretendido e não faz a requisição XML-RPC falhar imediatamente.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
python
Domínio
networking
Tipo de issue
Bug
Dificuldade
4/5
Tempo estimado
3-5 dias
Status de atividade
Estagnada
Clareza
Razoavelmente clara
Facilidade para iniciantes
45/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.