def _parse_extensions_header(self, headers): extensions = headers.get("Sec-WebSocket-Extensions", '') if extensions: return [ httputil._parse_header(e.strip()) for e in extensions.split(',') ] return []
def my_parse_multipart_form_data(boundary, data, arguments, files): """Parses a ``multipart/form-data`` body. The ``boundary`` and ``data`` parameters are both byte strings. The dictionaries given in the arguments and files parameters will be updated with the contents of the body. """ # The standard allows for the boundary to be quoted in the header, # although it's rare (it happens at least for google app engine # xmpp). I think we're also supposed to handle backslash-escapes # here but I'll save that until we see a client that uses them # in the wild. if boundary.startswith(b'"') and boundary.endswith(b'"'): boundary = boundary[1:-1] final_boundary_index = data.rfind(b"--" + boundary + b"--") if final_boundary_index == -1: warning("Invalid multipart/form-data: no final boundary") return parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n") for part in parts: if not part: continue eoh = part.find(b"\r\n\r\n") if eoh == -1: warning("multipart/form-data missing headers") continue headers = HTTPHeaders.parse(part[:eoh].decode("utf-8")) disp_header = headers.get("Content-Disposition", "") disposition, disp_params = _parse_header(disp_header) if len(disp_header)==0: # This is the part before first delimiter and in the original tornado.httputil it's validated for proper Content-Disposition, # which is wrong pass else: if len(disp_header) and disposition != "form-data" or not part.endswith(b"\r\n"): warning("Invalid multipart/form-data") continue value = part[eoh + 4:-2] if not disp_params.get("name"): warning("multipart/form-data value missing name") continue name = disp_params["name"] if disp_params.get("filename"): ctype = headers.get("Content-Type", "application/unknown") files.setdefault(name, []).append(HTTPFile( # type: ignore filename=disp_params["filename"], body=value, content_type=ctype)) else: arguments.setdefault(name, []).append(value)
def data_received(self, chunk): b = 0 if chunk.startswith(self.boundary): i = chunk.find(b'\r\n\r\n') if i != -1: b = i + 4 headers = HTTPHeaders.parse( chunk[len(self.boundary):i].decode("utf-8")) disp_header = headers.get("Content-Disposition", "") _, disp_params = _parse_header(disp_header) filename = disp_params["filename"] ext = filename.split('.')[-1] self.filename = filename self.temp_file_path = os.path.join( self.tmp_path, 'uploading_file_%s.%s' % (str(uuid.uuid4()), ext)) self.file = open(self.temp_file_path, 'wb') e = chunk.rfind(self.final_boundary_index) if e == -1: e = len(chunk) if e > (self.len_final - 1): temp = self.last + chunk[:self.len_final - 1] else: temp = self.last + chunk[:e] last_index = temp.find(self.final_boundary_index) if last_index != -1: e = last_index - self.len_final + 1 if len(chunk) > self.len_final: self.last = chunk[-self.len_final + 1:] else: self.last = chunk if self.file: self.file.write(chunk[b:e]) if e < len(chunk): self.file.close() self.uploaded_done()
def _parse_extensions_header(self, headers): extensions = headers.get("Sec-WebSocket-Extensions", '') if extensions: return [httputil._parse_header(e.strip()) for e in extensions.split(',')] return []
def data_received(self, data): if self._buffer is not None: data = self._buffer + data self._buffer = None boundary = data.find(self._boundary) if boundary != 0 and self._disp_buffer is None: self._boundary_length += boundary - self._boundary_padding self._boundary_padding = boundary self._sep = b''.join([data[:boundary], data[:boundary]]) elif boundary != 0: # boundary not at the begining value = data if boundary == -1 else data[:boundary - self._boundary_padding] if not self.execute_handle(self.HANDLE_DATA_SUFFIX, value): self._disp_buffer += value if boundary == -1: # boundary not found, streaming in progress return # boundary found, terminate current disposition self.execute_handle(self.HANDLE_END_SUFFIX) # process all disposition found in current stream while boundary != -1: app_log.debug('processing boundary') data = data[boundary:] # find next boundary boundary = data.find(self._boundary, self._boundary_length) eoh = data.find(self._sep) if eoh == -1: if boundary == -1: # header and boundary not found, stream probably cut in the midle of header self._buffer = data break # disposition not found because header not found app_log.debug('invalid disposition header') continue # process header data_header = data[self._boundary_length:eoh] app_log.debug('header data: %r', data_header) self._disp_header = HTTPHeaders.parse(data_header.decode('utf-8')) disp_header = self._disp_header.get('Content-Disposition', '') disposition, self._disp_params = _parse_header(disp_header) if disposition != 'form-data': app_log.warning('invalid multipart/form-data') continue self._disp_name = self._disp_params.get('name') if self._disp_name is None: app_log.warning('multipart/form-data value missing name') continue app_log.debug('disposition name %s', self._disp_name) # get disposition value and execute begin handler bod = eoh + len(self._sep) eod = boundary - self._boundary_padding if boundary == -1: value = data[bod:] else: value = data[bod:eod] self._disp_buffer = value self.execute_handle(self.HANDLE_BEGIN_SUFFIX, value) if boundary != -1: # next boundary found, execute end handler self.execute_handle(self.HANDLE_END_SUFFIX)
def data_received(self, chunk): """ Receive chunk of multipart/form-data :arg chunk: chunk of data """ if not self._buffer: self._buffer = chunk else: self._buffer += chunk while True: if self.current_phase == PHASE_BOUNDARY: if len(self._buffer) > len(self._boundary_delimiter): if self._buffer.startswith(self._boundary_delimiter): self.current_phase = PHASE_HEADERS self._buffer = self._buffer[len(self. _boundary_delimiter):] elif self._buffer.startswith(self._end_boundary): result = self.parser_delegate.finish_file() if is_future(result): yield result return else: gen_log.warning("Invalid multipart/form-data") return else: # wait for next chunk return if self.current_phase == PHASE_HEADERS: if b"\r\n\r\n" in self._buffer: headers, remaining_part = self._buffer.split( b"\r\n\r\n", 1) if headers: headers = HTTPHeaders.parse(headers.decode("utf-8")) else: gen_log.warning("multipart/form-data missing headers") return disp_header = headers.get("Content-Disposition", "") disposition, disp_params = _parse_header(disp_header) if disposition != "form-data": gen_log.warning("Invalid multipart/form-data") return self._buffer = remaining_part self.current_phase = PHASE_BODY result = self.parser_delegate.start_file( headers, disp_params) if is_future(result): yield result else: # wait for all headers for current file return if self.current_phase == PHASE_BODY: if self._boundary_delimiter in self._buffer: data, remaining_data = self._buffer.split( self._boundary_delimiter, 1) self._buffer = remaining_data result = self.parser_delegate.file_data_received(data[:-2]) if is_future(result): yield result self.current_phase = PHASE_HEADERS result = self.parser_delegate.finish_file() if is_future(result): yield result continue elif self._end_boundary in self._buffer: result = self.parser_delegate.file_data_received( self._buffer.split(self._end_boundary)[0]) if is_future(result): yield result result = self.parser_delegate.finish_file() if is_future(result): yield result return else: if self._buffer: result = self.parser_delegate.file_data_received( self._buffer) if is_future(result): yield result self._buffer = b"" return
async def data_received(self, chunk): """Receive chunk of multipart/form-data.""" self._buffer += chunk while True: if self.current_phase == PHASE_BOUNDARY: if len(self._buffer) > len(self._boundary_delimiter): if self._buffer.startswith(self._boundary_delimiter): self.current_phase = PHASE_HEADERS self._buffer = self._buffer[len(self._boundary_delimiter):] elif self._buffer.startswith(self._end_boundary): # TODO: Is it possible? return else: gen_log.warning('Invalid multipart/form-data') return else: # Wait for next chunk return if self.current_phase == PHASE_HEADERS: if b"\r\n\r\n" in self._buffer: headers, remaining_part = self._buffer.split(b"\r\n\r\n", 1) if headers: headers = HTTPHeaders.parse(headers.decode(self.encoding)) else: gen_log.warning('multipart/form-data missing headers') return if 'Content-Disposition' in headers: self.current_field_type = FIELD disposition_header = headers.get('Content-Disposition', '') disposition, disposition_params = _parse_header(disposition_header) if disposition != 'form-data': gen_log.warning('Invalid multipart/form-data') return self.current_phase = PHASE_BODY self._buffer = remaining_part self._data_size = 0 # Reset data size counter before enter PHASE_BODY phase try: field_name = disposition_params['name'].strip() except (KeyError, IndexError, AttributeError): return field_name = force_text(field_name, self.encoding, errors='replace') self._field_name = field_name self._transfer_encoding = headers.get('Content-Transfer-Encoding', '') if 'filename' in disposition_params: self.current_field_type = FILE file_name = disposition_params.get('filename') if file_name: file_name = force_text(file_name, self.encoding, errors='replace') self._file_name = file_name if file_name: content_type = headers.get('Content-Type', '') content_type, content_type_extra = _parse_header(content_type) charset = content_type_extra.get('charset') try: content_length = int(headers.get('Content-Length', 0)) except (TypeError, ValueError): content_length = None await self.new_file( field_name, file_name, content_type, content_length, charset, content_type_extra) else: # Wait for all headers for current file return if self.current_phase == PHASE_BODY: if self._boundary_delimiter in self._buffer: data, remaining_data = self._buffer.split(self._boundary_delimiter, 1) self._buffer = remaining_data await self.receive_data_chunk(data[:-2]) await self.complete_part() self.current_phase = PHASE_HEADERS continue elif self._end_boundary in self._buffer: remaining_data = self._buffer.split(self._end_boundary)[0] await self.receive_data_chunk(remaining_data) await self.complete_part() return else: if self._buffer: await self.receive_data_chunk(self._buffer) self._buffer = b"" return
def get_content_type(self): content_type = self.request.headers.get('Content-Type', 'text/plain') # noinspection PyProtectedMember return httputil._parse_header(content_type)