python / python/cpython

`BaseHTTPRequestHandler` hides `TimeoutError` from `handle_error()`

Abierto
#99,777 5 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

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

Descripción

Bug report

If anything in your method handlers (e.g. do_GET()) ends up throwing a TimeoutError, then that unhandled exception is never forwarded to handle_error(), and hence you do not get a chance to do whatever reporting you have set up there.

The cause of this is an undocumented timeout handling that was added in bpo-6267. It blindly assumes that any TimeoutError must come from the request socket and that it was requested and hence expected. That is not the case for any mildly complex request handler that might communicate with other services.

The timeline for this issue is:

?.? to 2.6: No exceptions are hidden from handle_error()
2.7 to 3.2: recv() and send() timeouts are hidden. Other timeouts, e.g. connect(), are not.
3.3 to 3.9: Same situation, but by pure luck since socket.timeout was not converted to TimeoutError yet
3.10: All timeouts are hidden, not just for recv() and send()

Your environment

  • CPython versions tested on: 2.7.18, 3.2.3, 3.5.10, 3.10.7
  • Operating system and architecture: Debian 7 x86_64 and Fedora 35 x86_64

Test case

This test case should give the output:

Got error!
Got error!

But instead gives:

127.0.0.1 - - [25/Nov/2022 13:48:49] Request timed out: TimeoutError(110, 'Connection timed out')
127.0.0.1 - - [25/Nov/2022 13:48:50] Request timed out: TimeoutError('timed out')

For 3.9 or older you get a mix.

Code: (change simulate if you want to have the actual socket code throw the exceptions)

#!/usr/bin/python3

import os
import errno
import socket
import threading
from time import sleep
try:
	from http.server import HTTPServer, BaseHTTPRequestHandler
except ImportError:
	from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

simulate = True

class ConnectRequestHandler(BaseHTTPRequestHandler):
	def do_GET(self):
		if simulate:
			raise socket.error(errno.ETIMEDOUT, "Timeout")
		else:
			socket.create_connection(("1.2.3.4", 9000))

class RecvRequestHandler(BaseHTTPRequestHandler):
	def do_GET(self):
		if simulate:
			raise socket.timeout(errno.ETIMEDOUT, "Timeout")
		else:
			(sock, dummy) = socket.socketpair()
			sock.settimeout(1)
			while sock.recv(1024):
				pass

class Server(HTTPServer):
	def handle_error(self, request, client_address):
		print("Got error!")

def run_connect_server():
	s = Server(("127.0.0.1", 8998),
	           ConnectRequestHandler)
	s.handle_request()
	
def run_recv_server():
	s = Server(("127.0.0.1", 8999),
	           RecvRequestHandler)
	s.handle_request()

threading.Thread(target=run_connect_server).start()
threading.Thread(target=run_recv_server).start()
sleep(0.5)

s = socket.create_connection(("127.0.0.1", 8998))
s.send(b'GET / HTTP/1.0\r\n\r\n')
while s.recv(1024):
	pass
s.close()

s = socket.create_connection(("127.0.0.1", 8999))
s.send(b'GET / HTTP/1.0\r\n\r\n')
while s.recv(1024):
	pass
s.close()

sleep(0.5)
Linked PRs
  • gh-99806

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 con BaseHTTPRequestHandler y la ruta de gestión de solicitudes descrita en el issue; después, reproduce el caso de prueba proporcionado para observar qué excepciones TimeoutError llegan a handle_error(). Se considera terminado cuando los timeouts provocados por el handler se notifican mediante handle_error() sin perder el comportamiento de timeout existente del socket de la solicitud.

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

Evaluación

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

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.