Handle a request on a connection. Runs in a worker thread.
(self, conn)
| 444 | conn.close() |
| 445 | |
| 446 | def handle(self, conn): |
| 447 | """Handle a request on a connection. Runs in a worker thread.""" |
| 448 | req = None |
| 449 | try: |
| 450 | # For new connections (not yet initialized), wait for data with timeout |
| 451 | # to prevent slow clients from blocking thread pool slots indefinitely. |
| 452 | # Skip this for already-initialized connections (keepalive, deferred) |
| 453 | # since they're coming from the poller and data is already available. |
| 454 | if not conn.initialized and not conn.data_ready: |
| 455 | # Wait for data with timeout before committing this thread |
| 456 | if not conn.wait_for_data(DEFAULT_WORKER_DATA_TIMEOUT): |
| 457 | # No data within timeout - defer to poller |
| 458 | return _DEFER |
| 459 | |
| 460 | # Always ensure blocking mode in worker thread. |
| 461 | # Critical for keepalive connections: the socket is set to non-blocking |
| 462 | # for the selector in finish_request(), but must be blocking for |
| 463 | # request/body reading to avoid SSLWantReadError on SSL connections. |
| 464 | conn.sock.setblocking(True) |
| 465 | |
| 466 | # Initialize connection in worker thread to handle SSL errors gracefully |
| 467 | # (ENOTCONN from ssl_wrap_socket would crash main thread otherwise) |
| 468 | conn.init() |
| 469 | |
| 470 | # HTTP/2 connections require special handling |
| 471 | if conn.is_http2: |
| 472 | return self.handle_http2(conn) |
| 473 | |
| 474 | req = next(conn.parser) |
| 475 | if not req: |
| 476 | return False |
| 477 | |
| 478 | # Handle the request |
| 479 | keepalive = self.handle_request(req, conn) |
| 480 | if keepalive: |
| 481 | # Discard any unread request body before keepalive to prevent |
| 482 | # the socket from appearing readable due to leftover bytes. |
| 483 | # Bound the drain by the worker data timeout: a stalled client |
| 484 | # must not keep this thread blocked. |
| 485 | drain_deadline = time.monotonic() + DEFAULT_WORKER_DATA_TIMEOUT |
| 486 | if not conn.parser.finish_body(deadline=drain_deadline): |
| 487 | # Abandon keepalive when the body could not be fully drained. |
| 488 | return False |
| 489 | return True |
| 490 | except http.errors.NoMoreData as e: |
| 491 | self.log.debug("Ignored premature client disconnection. %s", e) |
| 492 | except StopIteration as e: |
| 493 | self.log.debug("Closing connection. %s", e) |
| 494 | except ssl.SSLError as e: |
| 495 | if e.args[0] == ssl.SSL_ERROR_EOF: |
| 496 | self.log.debug("ssl connection closed") |
| 497 | conn.sock.close() |
| 498 | else: |
| 499 | self.log.debug("Error processing SSL request.") |
| 500 | self.handle_error(req, conn.sock, conn.client, e) |
| 501 | except OSError as e: |
| 502 | if e.errno not in (errno.EPIPE, errno.ECONNRESET, errno.ENOTCONN): |
| 503 | self.log.exception("Socket error processing request.") |
nothing calls this directly
no test coverage detected