Path: blob/master/venv/Lib/site-packages/urllib3/fields.py
811 views
from __future__ import absolute_import1import email.utils2import mimetypes3import re45from .packages import six678def guess_content_type(filename, default="application/octet-stream"):9"""10Guess the "Content-Type" of a file.1112:param filename:13The filename to guess the "Content-Type" of using :mod:`mimetypes`.14:param default:15If no "Content-Type" can be guessed, default to `default`.16"""17if filename:18return mimetypes.guess_type(filename)[0] or default19return default202122def format_header_param_rfc2231(name, value):23"""24Helper function to format and quote a single header parameter using the25strategy defined in RFC 2231.2627Particularly useful for header parameters which might contain28non-ASCII values, like file names. This follows RFC 2388 Section 4.4.2930:param name:31The name of the parameter, a string expected to be ASCII only.32:param value:33The value of the parameter, provided as ``bytes`` or `str``.34:ret:35An RFC-2231-formatted unicode string.36"""37if isinstance(value, six.binary_type):38value = value.decode("utf-8")3940if not any(ch in value for ch in '"\\\r\n'):41result = u'%s="%s"' % (name, value)42try:43result.encode("ascii")44except (UnicodeEncodeError, UnicodeDecodeError):45pass46else:47return result4849if six.PY2: # Python 2:50value = value.encode("utf-8")5152# encode_rfc2231 accepts an encoded string and returns an ascii-encoded53# string in Python 2 but accepts and returns unicode strings in Python 354value = email.utils.encode_rfc2231(value, "utf-8")55value = "%s*=%s" % (name, value)5657if six.PY2: # Python 2:58value = value.decode("utf-8")5960return value616263_HTML5_REPLACEMENTS = {64u"\u0022": u"%22",65# Replace "\" with "\\".66u"\u005C": u"\u005C\u005C",67u"\u005C": u"\u005C\u005C",68}6970# All control characters from 0x00 to 0x1F *except* 0x1B.71_HTML5_REPLACEMENTS.update(72{73six.unichr(cc): u"%{:02X}".format(cc)74for cc in range(0x00, 0x1F + 1)75if cc not in (0x1B,)76}77)787980def _replace_multiple(value, needles_and_replacements):81def replacer(match):82return needles_and_replacements[match.group(0)]8384pattern = re.compile(85r"|".join([re.escape(needle) for needle in needles_and_replacements.keys()])86)8788result = pattern.sub(replacer, value)8990return result919293def format_header_param_html5(name, value):94"""95Helper function to format and quote a single header parameter using the96HTML5 strategy.9798Particularly useful for header parameters which might contain99non-ASCII values, like file names. This follows the `HTML5 Working Draft100Section 4.10.22.7`_ and matches the behavior of curl and modern browsers.101102.. _HTML5 Working Draft Section 4.10.22.7:103https://w3c.github.io/html/sec-forms.html#multipart-form-data104105:param name:106The name of the parameter, a string expected to be ASCII only.107:param value:108The value of the parameter, provided as ``bytes`` or `str``.109:ret:110A unicode string, stripped of troublesome characters.111"""112if isinstance(value, six.binary_type):113value = value.decode("utf-8")114115value = _replace_multiple(value, _HTML5_REPLACEMENTS)116117return u'%s="%s"' % (name, value)118119120# For backwards-compatibility.121format_header_param = format_header_param_html5122123124class RequestField(object):125"""126A data container for request body parameters.127128:param name:129The name of this request field. Must be unicode.130:param data:131The data/value body.132:param filename:133An optional filename of the request field. Must be unicode.134:param headers:135An optional dict-like object of headers to initially use for the field.136:param header_formatter:137An optional callable that is used to encode and format the headers. By138default, this is :func:`format_header_param_html5`.139"""140141def __init__(142self,143name,144data,145filename=None,146headers=None,147header_formatter=format_header_param_html5,148):149self._name = name150self._filename = filename151self.data = data152self.headers = {}153if headers:154self.headers = dict(headers)155self.header_formatter = header_formatter156157@classmethod158def from_tuples(cls, fieldname, value, header_formatter=format_header_param_html5):159"""160A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters.161162Supports constructing :class:`~urllib3.fields.RequestField` from163parameter of key/value strings AND key/filetuple. A filetuple is a164(filename, data, MIME type) tuple where the MIME type is optional.165For example::166167'foo': 'bar',168'fakefile': ('foofile.txt', 'contents of foofile'),169'realfile': ('barfile.txt', open('realfile').read()),170'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'),171'nonamefile': 'contents of nonamefile field',172173Field names and filenames must be unicode.174"""175if isinstance(value, tuple):176if len(value) == 3:177filename, data, content_type = value178else:179filename, data = value180content_type = guess_content_type(filename)181else:182filename = None183content_type = None184data = value185186request_param = cls(187fieldname, data, filename=filename, header_formatter=header_formatter188)189request_param.make_multipart(content_type=content_type)190191return request_param192193def _render_part(self, name, value):194"""195Overridable helper function to format a single header parameter. By196default, this calls ``self.header_formatter``.197198:param name:199The name of the parameter, a string expected to be ASCII only.200:param value:201The value of the parameter, provided as a unicode string.202"""203204return self.header_formatter(name, value)205206def _render_parts(self, header_parts):207"""208Helper function to format and quote a single header.209210Useful for single headers that are composed of multiple items. E.g.,211'Content-Disposition' fields.212213:param header_parts:214A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format215as `k1="v1"; k2="v2"; ...`.216"""217parts = []218iterable = header_parts219if isinstance(header_parts, dict):220iterable = header_parts.items()221222for name, value in iterable:223if value is not None:224parts.append(self._render_part(name, value))225226return u"; ".join(parts)227228def render_headers(self):229"""230Renders the headers for this request field.231"""232lines = []233234sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"]235for sort_key in sort_keys:236if self.headers.get(sort_key, False):237lines.append(u"%s: %s" % (sort_key, self.headers[sort_key]))238239for header_name, header_value in self.headers.items():240if header_name not in sort_keys:241if header_value:242lines.append(u"%s: %s" % (header_name, header_value))243244lines.append(u"\r\n")245return u"\r\n".join(lines)246247def make_multipart(248self, content_disposition=None, content_type=None, content_location=None249):250"""251Makes this request field into a multipart request field.252253This method overrides "Content-Disposition", "Content-Type" and254"Content-Location" headers to the request parameter.255256:param content_type:257The 'Content-Type' of the request body.258:param content_location:259The 'Content-Location' of the request body.260261"""262self.headers["Content-Disposition"] = content_disposition or u"form-data"263self.headers["Content-Disposition"] += u"; ".join(264[265u"",266self._render_parts(267((u"name", self._name), (u"filename", self._filename))268),269]270)271self.headers["Content-Type"] = content_type272self.headers["Content-Location"] = content_location273274275