def stream_multipart(data, files=None, callback=None): from itertools import chain if files: fields = chain(iter_fields(data), iter_fields(files)) else: fields = data body, content_type = encode_multipart_formdata_stream(fields) data = MultiPartIO(body, callback=callback) headers = {'Content-Type':content_type} return data, headers
def stream_multipart(data, files=None, callback=None): from itertools import chain if files: fields = chain(iter_fields(data), iter_fields(files)) else: fields = data body, content_type = encode_multipart_formdata_stream(fields) data = MultiPartIO(body, callback=callback) headers = {'Content-Type': content_type} return data, headers
def __create_request_parts(self, files): request_list = [] boundary = _make_boundary() content_length = 0 boundary_string = b'--%s\r\n' % (boundary) for fieldname, value in iter_fields(files): content_length += len(boundary_string) if isinstance(value, tuple): filename, data = value content_disposition_string = ( ('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) + ('Content-Type: %s\r\n\r\n' % (guess_content_type(filename)))) else: data = value content_disposition_string = ( ('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) + 'Content-Type: text/plain\r\n\r\n') request_list.append( BytesIO(str(boundary_string + content_disposition_string))) content_length += len(content_disposition_string) if hasattr(data, 'read'): data_stream = data else: data_stream = BytesIO(str(data)) data_stream.seek(0, 2) data_size = data_stream.tell() data_stream.seek(0) request_list.append(data_stream) content_length += data_size end_string = b'\r\n' request_list.append(BytesIO(end_string)) content_length += len(end_string) request_list.append(BytesIO(b'--%s--\r\n' % (boundary))) content_length += len(boundary_string) # There's a bug in httplib.py that generates a UnicodeDecodeError on binary uploads if # there are *any* unicode strings passed into headers as part of the requests call. # For this reason all strings are explicitly converted to non-unicode at this point. self.content_type_header = { b'Content-Type': b'multipart/form-data; boundary=%s' % boundary } self.content_length_header = {b'Content-Length': str(content_length)} self._body_parts = request_list self._content_length = content_length + 2
def __create_request_parts(self, files): request_list = [] boundary = choose_boundary() content_length = 0 boundary_string = b'--%s\r\n' % (boundary) for fieldname, value in iter_fields(files): content_length += len(boundary_string) if isinstance(value, tuple): filename, data = value content_disposition_string = (('Content-Disposition: form-data; name="%s"; ''filename="%s"\r\n' % (fieldname, filename)) + ('Content-Type: %s\r\n\r\n' % (guess_content_type(filename)))) else: data = value content_disposition_string = (('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) + 'Content-Type: text/plain\r\n\r\n') request_list.append(BytesIO(str(boundary_string + content_disposition_string))) content_length += len(content_disposition_string) if hasattr(data, 'read'): data_stream = data else: data_stream = BytesIO(str(data)) data_stream.seek(0,2) data_size = data_stream.tell() data_stream.seek(0) request_list.append(data_stream) content_length += data_size end_string = b'\r\n' request_list.append(BytesIO(end_string)) content_length += len(end_string) request_list.append(BytesIO(b'--%s--\r\n' % (boundary))) content_length += len(boundary_string) # There's a bug in httplib.py that generates a UnicodeDecodeError on binary uploads if # there are *any* unicode strings passed into headers as part of the requests call. # For this reason all strings are explicitly converted to non-unicode at this point. self.content_type_header = {b'Content-Type': b'multipart/form-data; boundary=%s' % boundary} self.content_length_header = {b'Content-Length': str(content_length)} self._body_parts = request_list self._content_length = content_length + 2
def encode_multipart_formdata(fields, boundary=None): """Fix bug in multipart/form-data POST request handling. For some reason, the specific combination of Rack + Ruby + Rails versions that we are using in production has trouble handling multipart/form-data POST requests where the non-binary parts have a Content-Type header. To get around this, we just monkey patch the ```encode_multipart_formdata``` function in ```urllib3``` and modify it to *not* set the Content-Type header on non-binary parts. """ body = BytesIO() if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body.write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): filename, data = value writer(body).write('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body.write(b('Content-Type: %s\r\n\r\n' % (get_content_type(filename)))) else: data = value writer(body).write( 'Content-Disposition: form-data; name="%s"\r\n\r\n' % ( fieldname)) if isinstance(data, int): data = str(int) # Backwards compatibility if isinstance(data, six.text_type): writer(body).write(data) else: body.write(data) body.write(b'\r\n') body.write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body.getvalue(), content_type
def encode_multipart_formdata(fields, boundary=None): """Fix bug in multipart/form-data POST request handling. For some reason, the specific combination of Rack + Ruby + Rails versions that we are using in production has trouble handling multipart/form-data POST requests where the non-binary parts have a Content-Type header. To get around this, we just monkey patch the ```encode_multipart_formdata``` function in ```urllib3``` and modify it to *not* set the Content-Type header on non-binary parts. """ body = BytesIO() if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body.write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): filename, data = value writer(body).write('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body.write( b('Content-Type: %s\r\n\r\n' % (get_content_type(filename)))) else: data = value writer(body).write( 'Content-Disposition: form-data; name="%s"\r\n\r\n' % (fieldname)) if isinstance(data, int): data = str(int) # Backwards compatibility if isinstance(data, six.text_type): writer(body).write(data) else: body.write(data) body.write(b'\r\n') body.write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body.getvalue(), content_type
def encode_multipart_formdata_stream(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data MIME format. :param fields: Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ body = [] def body_write(item): if isinstance(item, basestring): item = BytesIO(item) body.append(item) body_write_encode = lambda item: body.append(BytesIO(item.encode('utf-8'))) if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body_write(b('--%s\r\n' % (boundary))) if isinstance(value, tuple): if len(value) == 3: filename, data, content_type = value else: filename, data = value content_type = get_content_type(filename) body_write_encode('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body_write(b('Content-Type: %s\r\n\r\n' % (content_type,))) else: data = value body_write_encode('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body_write(b'\r\n') if isinstance(data, int): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): body_write_encode(data) else: body_write(data) body_write(b'\r\n') body_write(b('--%s--\r\n' % (boundary))) content_type = b('multipart/form-data; boundary=%s' % boundary) return body, content_type
def encode_multipart_formdata_stream(fields, boundary=None): """ Encode a dictionary of ``fields`` using the multipart/form-data MIME format. :param fields: Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section. Field names and filenames must be unicode. :param boundary: If not specified, then a random boundary will be generated using :func:`mimetools.choose_boundary`. """ body = [] def body_write(item): if isinstance(item, bytes): item = BytesIO(item) elif isinstance(item, (str, unicode)): item = StringIO(item) body.append(item) body_write_encode = lambda item: body.append(BytesIO(item.encode('utf-8'))) if boundary is None: boundary = choose_boundary() for fieldname, value in iter_fields(fields): body_write_encode('--%s\r\n' % (boundary)) if isinstance(value, tuple): if len(value) == 3: filename, data, content_type = value else: filename, data = value from mimetypes import guess_type content_type, _ = guess_type(filename) if content_type is None: content_type = 'application/octet-stream' body_write_encode('Content-Disposition: form-data; name="%s"; ' 'filename="%s"\r\n' % (fieldname, filename)) body_write_encode('Content-Type: %s\r\n\r\n' % (content_type, )) else: data = value body_write_encode('Content-Disposition: form-data; name="%s"\r\n' % (fieldname)) body_write(b'\r\n') if isinstance(data, (int, long)): data = str(data) # Backwards compatibility if isinstance(data, six.text_type): body_write_encode(data) else: body_write(data) body_write(b'\r\n') body_write_encode('--%s--\r\n' % (boundary)) content_type = 'multipart/form-data; boundary=%s' % (boundary) return body, content_type