@@ -185,14 +185,14 @@ class Response:
185
185
status_code: Response code.
186
186
reason_phrase: Response reason.
187
187
headers: Response headers.
188
- body: Response body, if any .
188
+ body: Response body.
189
189
190
190
"""
191
191
192
192
status_code : int
193
193
reason_phrase : str
194
194
headers : Headers
195
- body : bytes | None = None
195
+ body : bytes = b""
196
196
197
197
_exception : Exception | None = None
198
198
@@ -266,36 +266,9 @@ def parse(
266
266
267
267
headers = yield from parse_headers (read_line )
268
268
269
- # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
270
-
271
- if "Transfer-Encoding" in headers :
272
- raise NotImplementedError ("transfer codings aren't supported" )
273
-
274
- # Since websockets only does GET requests (no HEAD, no CONNECT), all
275
- # responses except 1xx, 204, and 304 include a message body.
276
- if 100 <= status_code < 200 or status_code == 204 or status_code == 304 :
277
- body = None
278
- else :
279
- content_length : int | None
280
- try :
281
- # MultipleValuesError is sufficiently unlikely that we don't
282
- # attempt to handle it. Instead we document that its parent
283
- # class, LookupError, may be raised.
284
- raw_content_length = headers ["Content-Length" ]
285
- except KeyError :
286
- content_length = None
287
- else :
288
- content_length = int (raw_content_length )
289
-
290
- if content_length is None :
291
- try :
292
- body = yield from read_to_eof (MAX_BODY_SIZE )
293
- except RuntimeError :
294
- raise SecurityError (f"body too large: over { MAX_BODY_SIZE } bytes" )
295
- elif content_length > MAX_BODY_SIZE :
296
- raise SecurityError (f"body too large: { content_length } bytes" )
297
- else :
298
- body = yield from read_exact (content_length )
269
+ body = yield from read_body (
270
+ status_code , headers , read_line , read_exact , read_to_eof
271
+ )
299
272
300
273
return cls (status_code , reason , headers , body )
301
274
@@ -308,11 +281,37 @@ def serialize(self) -> bytes:
308
281
# we can keep this simple.
309
282
response = f"HTTP/1.1 { self .status_code } { self .reason_phrase } \r \n " .encode ()
310
283
response += self .headers .serialize ()
311
- if self .body is not None :
312
- response += self .body
284
+ response += self .body
313
285
return response
314
286
315
287
288
+ def parse_line (
289
+ read_line : Callable [[int ], Generator [None , None , bytes ]],
290
+ ) -> Generator [None , None , bytes ]:
291
+ """
292
+ Parse a single line.
293
+
294
+ CRLF is stripped from the return value.
295
+
296
+ Args:
297
+ read_line: Generator-based coroutine that reads a LF-terminated line
298
+ or raises an exception if there isn't enough data.
299
+
300
+ Raises:
301
+ EOFError: If the connection is closed without a CRLF.
302
+ SecurityError: If the response exceeds a security limit.
303
+
304
+ """
305
+ try :
306
+ line = yield from read_line (MAX_LINE_LENGTH )
307
+ except RuntimeError :
308
+ raise SecurityError ("line too long" )
309
+ # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5
310
+ if not line .endswith (b"\r \n " ):
311
+ raise EOFError ("line without CRLF" )
312
+ return line [:- 2 ]
313
+
314
+
316
315
def parse_headers (
317
316
read_line : Callable [[int ], Generator [None , None , bytes ]],
318
317
) -> Generator [None , None , Headers ]:
@@ -364,28 +363,62 @@ def parse_headers(
364
363
return headers
365
364
366
365
367
- def parse_line (
366
+ def read_body (
367
+ status_code : int ,
368
+ headers : Headers ,
368
369
read_line : Callable [[int ], Generator [None , None , bytes ]],
370
+ read_exact : Callable [[int ], Generator [None , None , bytes ]],
371
+ read_to_eof : Callable [[int ], Generator [None , None , bytes ]],
369
372
) -> Generator [None , None , bytes ]:
370
- """
371
- Parse a single line.
372
-
373
- CRLF is stripped from the return value.
374
-
375
- Args:
376
- read_line: Generator-based coroutine that reads a LF-terminated line
377
- or raises an exception if there isn't enough data.
378
-
379
- Raises:
380
- EOFError: If the connection is closed without a CRLF.
381
- SecurityError: If the response exceeds a security limit.
373
+ # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
374
+
375
+ # Since websockets only does GET requests (no HEAD, no CONNECT), all
376
+ # responses except 1xx, 204, and 304 include a message body.
377
+ if 100 <= status_code < 200 or status_code == 204 or status_code == 304 :
378
+ return b""
379
+
380
+ # MultipleValuesError is sufficiently unlikely that we don't attempt to
381
+ # handle it when accessing headers. Instead we document that its parent
382
+ # class, LookupError, may be raised.
383
+ # Conversions from str to int are protected by sys.set_int_max_str_digits..
384
+
385
+ elif (coding := headers .get ("Transfer-Encoding" )) is not None :
386
+ if coding != "chunked" :
387
+ raise NotImplementedError (f"transfer coding { coding } isn't supported" )
388
+
389
+ body = b""
390
+ while True :
391
+ chunk_size_line = yield from parse_line (read_line )
392
+ raw_chunk_size = chunk_size_line .split (b";" , 1 )[0 ]
393
+ # Set a lower limit than default_max_str_digits; 1 EB is plenty.
394
+ if len (raw_chunk_size ) > 15 :
395
+ str_chunk_size = raw_chunk_size .decode (errors = "backslashreplace" )
396
+ raise SecurityError (f"chunk too large: 0x{ str_chunk_size } bytes" )
397
+ chunk_size = int (raw_chunk_size , 16 )
398
+ if chunk_size == 0 :
399
+ break
400
+ if len (body ) + chunk_size > MAX_BODY_SIZE :
401
+ raise SecurityError (
402
+ f"chunk too large: { chunk_size } bytes after { len (body )} bytes"
403
+ )
404
+ body += yield from read_exact (chunk_size )
405
+ if (yield from read_exact (2 )) != b"\r \n " :
406
+ raise ValueError ("chunk without CRLF" )
407
+ # Read the trailer.
408
+ yield from parse_headers (read_line )
409
+ return body
410
+
411
+ elif (raw_content_length := headers .get ("Content-Length" )) is not None :
412
+ # Set a lower limit than default_max_str_digits; 1 EiB is plenty.
413
+ if len (raw_content_length ) > 18 :
414
+ raise SecurityError (f"body too large: { raw_content_length } bytes" )
415
+ content_length = int (raw_content_length )
416
+ if content_length > MAX_BODY_SIZE :
417
+ raise SecurityError (f"body too large: { content_length } bytes" )
418
+ return (yield from read_exact (content_length ))
382
419
383
- """
384
- try :
385
- line = yield from read_line (MAX_LINE_LENGTH )
386
- except RuntimeError :
387
- raise SecurityError ("line too long" )
388
- # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5
389
- if not line .endswith (b"\r \n " ):
390
- raise EOFError ("line without CRLF" )
391
- return line [:- 2 ]
420
+ else :
421
+ try :
422
+ return (yield from read_to_eof (MAX_BODY_SIZE ))
423
+ except RuntimeError :
424
+ raise SecurityError (f"body too large: over { MAX_BODY_SIZE } bytes" )
0 commit comments