Parse one and exactly one stream that encapsulates a boundary.
(stream, max_header_size)
| 687 | |
| 688 | |
| 689 | def parse_boundary_stream(stream, max_header_size): |
| 690 | """ |
| 691 | Parse one and exactly one stream that encapsulates a boundary. |
| 692 | """ |
| 693 | |
| 694 | # Look for the end of headers and if not found extend the search to double |
| 695 | # the size up to the MAX_TOTAL_HEADER_SIZE. |
| 696 | headers_chunk_size = 1024 |
| 697 | while True: |
| 698 | if headers_chunk_size > max_header_size: |
| 699 | raise MultiPartParserError("Request max total header size exceeded.") |
| 700 | |
| 701 | # Stream at beginning of header, look for end of header and parse it if |
| 702 | # found. The header must fit within one chunk. |
| 703 | chunk = stream.read(headers_chunk_size) |
| 704 | # 'find' returns the top of these four bytes, so munch them later to |
| 705 | # prevent them from polluting the payload. |
| 706 | header_end = chunk.find(b"\r\n\r\n") |
| 707 | if header_end != -1: |
| 708 | break |
| 709 | |
| 710 | # Find no header, mark this fact and pass on the stream verbatim. |
| 711 | stream.unget(chunk) |
| 712 | # No more data to read. |
| 713 | if len(chunk) < headers_chunk_size: |
| 714 | return (RAW, {}, stream) |
| 715 | # Double the chunk size. |
| 716 | headers_chunk_size *= 2 |
| 717 | |
| 718 | header = chunk[:header_end] |
| 719 | |
| 720 | # here we place any excess chunk back onto the stream, as |
| 721 | # well as throwing away the CRLFCRLF bytes from above. |
| 722 | stream.unget(chunk[header_end + 4 :]) |
| 723 | |
| 724 | TYPE = RAW |
| 725 | outdict = {} |
| 726 | |
| 727 | # Eliminate blank lines |
| 728 | for line in header.split(b"\r\n"): |
| 729 | try: |
| 730 | header_name, value_and_params = line.decode().split(":", 1) |
| 731 | name = header_name.lower().rstrip(" ") |
| 732 | value, params = parse_header_parameters(value_and_params.lstrip(" ")) |
| 733 | params = {k: v.encode() for k, v in params.items()} |
| 734 | except (ValueError, LookupError): # Invalid header. |
| 735 | continue |
| 736 | |
| 737 | if name == "content-disposition": |
| 738 | TYPE = FIELD |
| 739 | if params.get("filename"): |
| 740 | TYPE = FILE |
| 741 | |
| 742 | outdict[name] = value, params |
| 743 | |
| 744 | if TYPE == RAW: |
| 745 | stream.unget(chunk) |
| 746 |