Skip to content

Commit 3b6f3ce

Browse files
authored
Drain request body in all error cases in WSGI (#94)
Signed-off-by: Anuraag Agrawal <anuraaga@gmail.com>
1 parent 74a2727 commit 3b6f3ce

File tree

1 file changed

+16
-12
lines changed

1 file changed

+16
-12
lines changed

src/connectrpc/_server_sync.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ def __call__(
224224
)
225225

226226
except Exception as e:
227+
_drain_request_body(environ)
227228
return self._handle_error(e, ctx, start_response)
228229

229230
def _handle_unary(
@@ -468,12 +469,13 @@ def _handle_stream(
468469
# been called in time. So we return the response stream as a separate generator
469470
# function. This means some duplication of error handling.
470471
return _response_stream(
471-
first_response, response_stream, writer, send_trailers, ctx
472+
first_response, environ, response_stream, writer, send_trailers, ctx
472473
)
473474
except Exception as e:
474475
# Exception before any response message was returned. An error after the first
475476
# response message will be handled by _response_stream, so here we have a
476477
# full error-only response.
478+
_drain_request_body(environ)
477479
_send_stream_response_headers(
478480
start_response, protocol, codec, resp_compression.name(), ctx
479481
)
@@ -554,21 +556,13 @@ def _request_stream(
554556
read_max_bytes: int | None = None,
555557
) -> Iterator[_REQ]:
556558
reader = EnvelopeReader(request_class, codec, compression, read_max_bytes)
557-
try:
558-
for chunk in _read_body(environ):
559-
yield from reader.feed(chunk)
560-
except ConnectError:
561-
if environ.get("SERVER_PROTOCOL", "").startswith("HTTP/1"):
562-
# In HTTP/1, the request body should be drained before returning. Generally it's
563-
# best for the application server to handle this, but gunicorn is a famous
564-
# server that doesn't do so, so we go ahead and do it ourselves.
565-
for _ in _read_body(environ):
566-
pass
567-
raise
559+
for chunk in _read_body(environ):
560+
yield from reader.feed(chunk)
568561

569562

570563
def _response_stream(
571564
first_response: _RES,
565+
environ: WSGIEnvironment,
572566
response_stream: Iterator[_RES],
573567
writer: EnvelopeWriter,
574568
send_trailers: Callable[[list[tuple[str, str]]], None] | None,
@@ -583,6 +577,7 @@ def _response_stream(
583577
yield body
584578
except Exception as e:
585579
error = e
580+
_drain_request_body(environ)
586581

587582
yield _end_response(
588583
writer.end(
@@ -638,3 +633,12 @@ def _apply_interceptors(
638633
continue
639634
func = functools.partial(interceptor.intercept_bidi_stream_sync, func)
640635
return replace(endpoint, function=func)
636+
637+
638+
def _drain_request_body(environ: WSGIEnvironment) -> None:
639+
if environ.get("SERVER_PROTOCOL", "").startswith("HTTP/1"):
640+
# In HTTP/1, the request body should be drained before returning. Generally it's
641+
# best for the application server to handle this, but gunicorn is a famous
642+
# server that doesn't do so, so we go ahead and do it ourselves.
643+
for _ in _read_body(environ):
644+
pass

0 commit comments

Comments
 (0)