Exemple #1
0
 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 []
Exemple #2
0
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)
Exemple #3
0
 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()
Exemple #4
0
 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
Exemple #7
0
    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
Exemple #8
0
 def get_content_type(self):
     content_type = self.request.headers.get('Content-Type', 'text/plain')
     # noinspection PyProtectedMember
     return httputil._parse_header(content_type)