gunicorn/gunicorn/http/parser.py
Benoit Chesneau 47b9a18619 fix: handle SSLWantReadError in finish_body() (#3448)
The finish_body() function can raise ssl.SSLWantReadError when
discarding unread request body data on SSL connections. This causes
TLS requests to fail intermittently with "Invalid request" errors.

Handle SSLWantReadError by treating it as "no more data to read".
This is safe because finish_body() only discards leftover data before
keepalive - if SSL says "need to wait for more data", there's nothing
left to discard.

Fixes #3448
2026-01-23 09:38:41 +01:00

67 lines
1.8 KiB
Python

#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
import ssl
from gunicorn.http.message import Request
from gunicorn.http.unreader import SocketUnreader, IterUnreader
class Parser:
mesg_class = None
def __init__(self, cfg, source, source_addr):
self.cfg = cfg
if hasattr(source, "recv"):
self.unreader = SocketUnreader(source)
else:
self.unreader = IterUnreader(source)
self.mesg = None
self.source_addr = source_addr
# request counter (for keepalive connetions)
self.req_count = 0
def __iter__(self):
return self
def finish_body(self):
"""Discard any unread body of the current message.
This should be called before returning a keepalive connection to
the poller to ensure the socket doesn't appear readable due to
leftover body bytes.
"""
if self.mesg:
try:
data = self.mesg.body.read(8192)
while data:
data = self.mesg.body.read(8192)
except ssl.SSLWantReadError:
# SSL socket has no more application data available
pass
def __next__(self):
# Stop if HTTP dictates a stop.
if self.mesg and self.mesg.should_close():
raise StopIteration()
# Discard any unread body of the previous message
self.finish_body()
# Parse the next request
self.req_count += 1
self.mesg = self.mesg_class(self.cfg, self.unreader, self.source_addr, self.req_count)
if not self.mesg:
raise StopIteration()
return self.mesg
next = __next__
class RequestParser(Parser):
mesg_class = Request