codekingpro/portable-devtools
114k
1from __future__ import absolute_import2 3import sys4 5from .filepost import encode_multipart_formdata6from .packages import six7from .packages.six.moves.urllib.parse import urlencode8 9__all__ = ["RequestMethods"]10 11 12class RequestMethods(object):13 """14 Convenience mixin for classes who implement a :meth:`urlopen` method, such15 as :class:`urllib3.HTTPConnectionPool` and16 :class:`urllib3.PoolManager`.17 18 Provides behavior for making common types of HTTP request methods and19 decides which type of request field encoding to use.20 21 Specifically,22 23 :meth:`.request_encode_url` is for sending requests whose fields are24 encoded in the URL (such as GET, HEAD, DELETE).25 26 :meth:`.request_encode_body` is for sending requests whose fields are27 encoded in the *body* of the request using multipart or www-form-urlencoded28 (such as for POST, PUT, PATCH).29 30 :meth:`.request` is for making any kind of request, it will look up the31 appropriate encoding format and use one of the above two methods to make32 the request.33 34 Initializer parameters:35 36 :param headers:37 Headers to include with all requests, unless other headers are given38 explicitly.39 """40 41 _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"}42 43 def __init__(self, headers=None):44 self.headers = headers or {}45 46 def urlopen(47 self,48 method,49 url,50 body=None,51 headers=None,52 encode_multipart=True,53 multipart_boundary=None,54 **kw55 ): # Abstract56 raise NotImplementedError(57 "Classes extending RequestMethods must implement "58 "their own ``urlopen`` method."59 )60 61 def request(self, method, url, fields=None, headers=None, **urlopen_kw):62 """63 Make a request using :meth:`urlopen` with the appropriate encoding of64 ``fields`` based on the ``method`` used.65 66 This is a convenience method that requires the least amount of manual67 effort. It can be used in most situations, while still having the68 option to drop down to more specific methods when necessary, such as69 :meth:`request_encode_url`, :meth:`request_encode_body`,70 or even the lowest level :meth:`urlopen`.71 """72 method = method.upper()73 74 urlopen_kw["request_url"] = url75 76 if method in self._encode_url_methods:77 return self.request_encode_url(78 method, url, fields=fields, headers=headers, **urlopen_kw79 )80 else:81 return self.request_encode_body(82 method, url, fields=fields, headers=headers, **urlopen_kw83 )84 85 def request_encode_url(self, method, url, fields=None, headers=None, **urlopen_kw):86 """87 Make a request using :meth:`urlopen` with the ``fields`` encoded in88 the url. This is useful for request methods like GET, HEAD, DELETE, etc.89 """90 if headers is None:91 headers = self.headers92 93 extra_kw = {"headers": headers}94 extra_kw.update(urlopen_kw)95 96 if fields:97 url += "?" + urlencode(fields)98 99 return self.urlopen(method, url, **extra_kw)100 101 def request_encode_body(102 self,103 method,104 url,105 fields=None,106 headers=None,107 encode_multipart=True,108 multipart_boundary=None,109 **urlopen_kw110 ):111 """112 Make a request using :meth:`urlopen` with the ``fields`` encoded in113 the body. This is useful for request methods like POST, PUT, PATCH, etc.114 115 When ``encode_multipart=True`` (default), then116 :func:`urllib3.encode_multipart_formdata` is used to encode117 the payload with the appropriate content type. Otherwise118 :func:`urllib.parse.urlencode` is used with the119 'application/x-www-form-urlencoded' content type.120 121 Multipart encoding must be used when posting files, and it's reasonably122 safe to use it in other times too. However, it may break request123 signing, such as with OAuth.124 125 Supports an optional ``fields`` parameter of key/value strings AND126 key/filetuple. A filetuple is a (filename, data, MIME type) tuple where127 the MIME type is optional. For example::128 129 fields = {130 'foo': 'bar',131 'fakefile': ('foofile.txt', 'contents of foofile'),132 'realfile': ('barfile.txt', open('realfile').read()),133 'typedfile': ('bazfile.bin', open('bazfile').read(),134 'image/jpeg'),135 'nonamefile': 'contents of nonamefile field',136 }137 138 When uploading a file, providing a filename (the first parameter of the139 tuple) is optional but recommended to best mimic behavior of browsers.140 141 Note that if ``headers`` are supplied, the 'Content-Type' header will142 be overwritten because it depends on the dynamic random boundary string143 which is used to compose the body of the request. The random boundary144 string can be explicitly set with the ``multipart_boundary`` parameter.145 """146 if headers is None:147 headers = self.headers148 149 extra_kw = {"headers": {}}150 151 if fields:152 if "body" in urlopen_kw:153 raise TypeError(154 "request got values for both 'fields' and 'body', can only specify one."155 )156 157 if encode_multipart:158 body, content_type = encode_multipart_formdata(159 fields, boundary=multipart_boundary160 )161 else:162 body, content_type = (163 urlencode(fields),164 "application/x-www-form-urlencoded",165 )166 167 extra_kw["body"] = body168 extra_kw["headers"] = {"Content-Type": content_type}169 170 extra_kw["headers"].update(headers)171 extra_kw.update(urlopen_kw)172 173 return self.urlopen(method, url, **extra_kw)174 175 176if not six.PY2:177 178 class RequestModule(sys.modules[__name__].__class__):179 def __call__(self, *args, **kwargs):180 """181 If user tries to call this module directly urllib3 v2.x style raise an error to the user182 suggesting they may need urllib3 v2183 """184 raise TypeError(185 "'module' object is not callable\n"186 "urllib3.request() method is not supported in this release, "187 "upgrade to urllib3 v2 to use it\n"188 "see https://urllib3.readthedocs.io/en/stable/v2-migration-guide.html"189 )190 191 sys.modules[__name__].__class__ = RequestModule192 