(self)
| 355 | return headers |
| 356 | |
| 357 | def set_body_reader(self): |
| 358 | chunked = False |
| 359 | content_length = None |
| 360 | |
| 361 | for (name, value) in self.headers: |
| 362 | if name == "CONTENT-LENGTH": |
| 363 | if content_length is not None: |
| 364 | raise InvalidHeader("CONTENT-LENGTH", req=self) |
| 365 | content_length = value |
| 366 | elif name == "TRANSFER-ENCODING": |
| 367 | # T-E can be a list |
| 368 | # https://datatracker.ietf.org/doc/html/rfc9112#name-transfer-encoding |
| 369 | vals = [v.strip() for v in value.split(',')] |
| 370 | for val in vals: |
| 371 | if val.lower() == "chunked": |
| 372 | # DANGER: transfer codings stack, and stacked chunking is never intended |
| 373 | if chunked: |
| 374 | raise InvalidHeader("TRANSFER-ENCODING", req=self) |
| 375 | chunked = True |
| 376 | elif val.lower() == "identity": |
| 377 | # does not do much, could still plausibly desync from what the proxy does |
| 378 | # safe option: nuke it, its never needed |
| 379 | if chunked: |
| 380 | raise InvalidHeader("TRANSFER-ENCODING", req=self) |
| 381 | elif val.lower() in ('compress', 'deflate', 'gzip'): |
| 382 | # chunked should be the last one |
| 383 | if chunked: |
| 384 | raise InvalidHeader("TRANSFER-ENCODING", req=self) |
| 385 | self.force_close() |
| 386 | else: |
| 387 | raise UnsupportedTransferCoding(value) |
| 388 | |
| 389 | if chunked: |
| 390 | # two potentially dangerous cases: |
| 391 | # a) CL + TE (TE overrides CL.. only safe if the recipient sees it that way too) |
| 392 | # b) chunked HTTP/1.0 (always faulty) |
| 393 | if self.version < (1, 1): |
| 394 | # framing wonky, see RFC 9112 Section 6.1 |
| 395 | raise InvalidHeader("TRANSFER-ENCODING", req=self) |
| 396 | if content_length is not None: |
| 397 | # we cannot be certain the message framing we understood matches proxy intent |
| 398 | # -> whatever happens next, remaining input must not be trusted |
| 399 | raise InvalidHeader("CONTENT-LENGTH", req=self) |
| 400 | self.body = Body(ChunkedReader(self, self.unreader)) |
| 401 | elif content_length is not None: |
| 402 | try: |
| 403 | if str(content_length).isnumeric(): |
| 404 | content_length = int(content_length) |
| 405 | else: |
| 406 | raise InvalidHeader("CONTENT-LENGTH", req=self) |
| 407 | except ValueError: |
| 408 | raise InvalidHeader("CONTENT-LENGTH", req=self) |
| 409 | |
| 410 | if content_length < 0: |
| 411 | raise InvalidHeader("CONTENT-LENGTH", req=self) |
| 412 | |
| 413 | self.body = Body(LengthReader(self.unreader, content_length)) |
| 414 | else: |
no test coverage detected