def putrequest(self, method, url, skip_host=False, skip_accept_encoding=False): '''Send a request to the server. This is mostly a regurgitation of CPython's HTTPConnection.putrequest, but fixed up so we can still send arbitrary bytes in the request line on py3. See also: https://bugs.python.org/issue36274 To use, swap out a HTTP(S)Connection's putrequest with something like:: conn.putrequest = putrequest.__get__(conn) :param method: specifies an HTTP request method, e.g. 'GET'. :param url: specifies the object being requested, e.g. '/index.html'. :param skip_host: if True does not add automatically a 'Host:' header :param skip_accept_encoding: if True does not add automatically an 'Accept-Encoding:' header ''' # (Mostly) inline the HTTPConnection implementation; just fix it # so we can send non-ascii request lines. For comparison, see # https://github.com/python/cpython/blob/v2.7.16/Lib/httplib.py#L888-L1003 # and https://github.com/python/cpython/blob/v3.7.2/ # Lib/http/client.py#L1061-L1183 if self._HTTPConnection__response \ and self._HTTPConnection__response.isclosed(): self._HTTPConnection__response = None if self._HTTPConnection__state == http_client._CS_IDLE: self._HTTPConnection__state = http_client._CS_REQ_STARTED else: raise http_client.CannotSendRequest(self._HTTPConnection__state) self._method = method if not url: url = '/' self._path = url request = '%s %s %s' % (method, url, self._http_vsn_str) if not isinstance(request, bytes): # This choice of encoding is the whole reason we copy/paste from # cpython. When making backend requests, it should never be # necessary; however, we have some functional tests that want # to send non-ascii bytes. # TODO: when https://bugs.python.org/issue36274 is resolved, make # sure we fix up our API to match whatever upstream chooses to do self._output(request.encode('latin1')) else: self._output(request) if self._http_vsn == 11: if not skip_host: netloc = '' if url.startswith('http'): nil, netloc, nil, nil, nil = urllib.parse.urlsplit(url) if netloc: try: netloc_enc = netloc.encode("ascii") except UnicodeEncodeError: netloc_enc = netloc.encode("idna") self.putheader('Host', netloc_enc) else: if self._tunnel_host: host = self._tunnel_host port = self._tunnel_port else: host = self.host port = self.port try: host_enc = host.encode("ascii") except UnicodeEncodeError: host_enc = host.encode("idna") if host.find(':') >= 0: host_enc = b'[' + host_enc + b']' if port == self.default_port: self.putheader('Host', host_enc) else: host_enc = host_enc.decode("ascii") self.putheader('Host', "%s:%s" % (host_enc, port)) if not skip_accept_encoding: self.putheader('Accept-Encoding', 'identity')
def side_effect(mo, **kwargs): self.assertEqual(managed_object, mo._type) self.assertEqual(managed_object, mo.value) raise httplib.CannotSendRequest()
def _do_request(self, method, path, query_params, body, maximum_redirects, extra_headers=None): full_uri = path if query_params is not None: full_uri = "%s?%s" % (path, urlencode(query_params)) headers = self._headers.copy() if extra_headers is not None: headers.update(extra_headers) local_connection = False try: if logger.is_debugging(): logger.debug("Request: %s %s" % (method, full_uri)) logger.debug("Headers:") for key, value in headers.items(): logger.debug("--> %s: %s" % (key, value)) # if we weren't called under a `with` statement, we'll need to manage the # connection here. if self._conn is None: self.__enter__() local_connection = True try: self._conn.request(method, full_uri, body, headers) response = self._conn.getresponse() response_body = response.read().decode("utf-8").strip() if logger.is_debugging(): logger.debug("Response: %s %s" % (response.status, response.reason)) logger.debug("Headers:") for key, value in response.getheaders(): logger.debug("--> %s: %s" % (key, value)) logger.debug("--> %s" % response_body) finally: if local_connection: self.__exit__() # Handle any redirects. if 300 <= response.status < 400: if maximum_redirects == 0: raise http.CannotSendRequest("Too many redirects") location = response.getheader("Location") next_url = urljoin(self._url.geturl(), location) logger.debug("--> Redirected to: %s" % next_url) return self._do_request( method, next_url, query_params, body, maximum_redirects - 1, extra_headers, ) self._handle_set_cookie(response) return self._tweak_response( HTTPResponse(full_uri, response=response, body=response_body)) except ( http.HTTPException, ssl.CertificateError, IOError, OSError, socket.error, socket.herror, socket.gaierror, socket.timeout, ) as exception: logger.debug("An exception occurred processing the HTTP request.", exc_info=True) return HTTPResponse(full_uri, exception=exception)