2828from plain .utils .regex_helper import _lazy_re_compile
2929
3030if TYPE_CHECKING :
31- from plain .internal . files . uploadhandler import FileUploadHandler
31+ from plain .http . request import Request
3232
3333__all__ = ("MultiPartParser" , "MultiPartParserError" , "InputStreamExhausted" )
3434
@@ -61,28 +61,15 @@ class MultiPartParser:
6161
6262 boundary_re = _lazy_re_compile (r"[ -~]{0,200}[!-~]" )
6363
64- def __init__ (
65- self ,
66- environ : dict [str , Any ],
67- input_data : Any ,
68- upload_handlers : list [FileUploadHandler ],
69- encoding : str | None = None ,
70- ):
64+ def __init__ (self , request : Request ):
7165 """
7266 Initialize the MultiPartParser object.
7367
74- :environ:
75- The WSGI environ dictionary from the request.
76- :input_data:
77- The raw post data, as a file-like object.
78- :upload_handlers:
79- A list of UploadHandler instances that perform operations on the
80- uploaded data.
81- :encoding:
82- The encoding with which to treat the incoming data.
68+ :request:
69+ The HTTP request object (used for headers and as the input stream).
8370 """
8471 # Content-Type should contain multipart and the boundary information.
85- content_type = environ . get ( "CONTENT_TYPE" , "" )
72+ content_type = request . content_type or ""
8673 if not content_type .startswith ("multipart/" ):
8774 raise MultiPartParserError (f"Invalid Content-Type: { content_type } " )
8875
@@ -93,37 +80,30 @@ def __init__(
9380 f"Invalid non-ASCII Content-Type in multipart: { force_str (content_type )} "
9481 )
9582
96- # Parse the header to get the boundary to split the parts .
97- _ , opts = parse_header_parameters ( content_type )
98- boundary = opts .get ("boundary" )
83+ # Get the boundary from parsed content type parameters .
84+ content_params = request . content_params or {}
85+ boundary = content_params .get ("boundary" )
9986 if not boundary or not self .boundary_re .fullmatch (boundary ):
10087 raise MultiPartParserError (
10188 f"Invalid boundary in multipart: { force_str (boundary )} "
10289 )
10390
104- # Content-Length should contain the length of the body we are about
105- # to receive.
106- try :
107- content_length = int (environ .get ("CONTENT_LENGTH" , 0 ))
108- except (ValueError , TypeError ):
109- content_length = 0
110-
91+ content_length = request .content_length
11192 if content_length < 0 :
11293 # This means we shouldn't continue...raise an error.
11394 raise MultiPartParserError (f"Invalid content length: { content_length !r} " )
11495
11596 self ._boundary = boundary .encode ("ascii" )
116- self ._input_data = input_data
97+ self ._request = request
11798
11899 # For compatibility with low-level network APIs (with 32-bit integers),
119100 # the chunk size should be < 2^31, but still divisible by 4.
120- possible_sizes = [x .chunk_size for x in upload_handlers if x .chunk_size ]
101+ possible_sizes = [x .chunk_size for x in request . upload_handlers if x .chunk_size ]
121102 self ._chunk_size = min ([2 ** 31 - 4 ] + possible_sizes )
122103
123- self ._environ = environ
124- self ._encoding = encoding or settings .DEFAULT_CHARSET
104+ self ._encoding = request .encoding or settings .DEFAULT_CHARSET
125105 self ._content_length = content_length
126- self ._upload_handlers = upload_handlers
106+ self ._upload_handlers = request . upload_handlers
127107
128108 def parse (self ) -> tuple [Any , MultiValueDict ]:
129109 # Call the actual parse routine and close all open files in case of
@@ -162,9 +142,7 @@ def _parse(self) -> tuple[Any, MultiValueDict]:
162142 # This allows overriding everything if need be.
163143 for handler in handlers :
164144 result = handler .handle_raw_input (
165- self ._input_data ,
166- self ._environ ,
167- self ._content_length ,
145+ self ._request ,
168146 self ._boundary ,
169147 encoding ,
170148 )
@@ -177,7 +155,7 @@ def _parse(self) -> tuple[Any, MultiValueDict]:
177155 self ._files = MultiValueDict ()
178156
179157 # Instantiate the parser and stream:
180- stream = LazyStream (ChunkIter (self ._input_data , self ._chunk_size ))
158+ stream = LazyStream (ChunkIter (self ._request , self ._chunk_size ))
181159
182160 # Whether or not to signal a file-completion at the beginning of the loop.
183161 old_field_name = None
@@ -366,13 +344,13 @@ def _parse(self) -> tuple[Any, MultiValueDict]:
366344 except StopUpload as e :
367345 self ._close_files ()
368346 if not e .connection_reset :
369- exhaust (self ._input_data )
347+ exhaust (self ._request )
370348 else :
371349 if not uploaded_file :
372350 for handler in handlers :
373351 handler .upload_interrupted ()
374352 # Make sure that the request data is all fed
375- exhaust (self ._input_data )
353+ exhaust (self ._request )
376354
377355 # Signal that the upload has completed.
378356 # any() shortcircuits if a handler's upload_complete() returns a value.
0 commit comments