Skip to content

Commit 0d4824e

Browse files
authored
Do multiple reads for WSGI even with content length (#37)
Signed-off-by: Anuraag Agrawal <anuraaga@gmail.com>
1 parent 386bddc commit 0d4824e

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

src/connectrpc/_server_sync.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,34 @@ def prepare_response_headers(
115115
return headers
116116

117117

118+
def _read_body_with_content_length(
119+
environ: WSGIEnvironment, content_length: int
120+
) -> bytes:
121+
input_stream: BytesIO = environ["wsgi.input"]
122+
123+
# Many app servers buffer the entire request before executing the app
124+
# so do an optimistic read before looping.
125+
chunk = input_stream.read(content_length)
126+
if len(chunk) == content_length:
127+
return chunk
128+
129+
bytes_read = len(chunk)
130+
chunks = [chunk]
131+
while bytes_read < content_length:
132+
to_read = content_length - bytes_read
133+
chunk = input_stream.read(to_read)
134+
if not chunk:
135+
break
136+
chunks.append(chunk)
137+
bytes_read += len(chunk)
138+
if bytes_read < content_length:
139+
raise ConnectError(
140+
Code.INVALID_ARGUMENT,
141+
f"request truncated, expected {content_length} bytes but only received {bytes_read} bytes",
142+
)
143+
return b"".join(chunks)
144+
145+
118146
def _read_body(environ: WSGIEnvironment) -> Iterator[bytes]:
119147
input_stream: BytesIO = environ["wsgi.input"]
120148
while True:
@@ -257,7 +285,7 @@ def _handle_post_request(
257285
content_length = environ.get("CONTENT_LENGTH")
258286
content_length = 0 if not content_length else int(content_length)
259287
if content_length > 0:
260-
req_body = environ["wsgi.input"].read(content_length)
288+
req_body = _read_body_with_content_length(environ, content_length)
261289
else:
262290
req_body = b"".join(_read_body(environ))
263291

0 commit comments

Comments
 (0)