def http_request(cls, url, method='GET', body=None, headers=None): """Make an HTTP request with the given method to the given URL, returning the resulting `http_client.HTTPResponse` instance. If the `body` argument is a `Resource` instance, it is serialized to XML by calling its `to_element()` method before submitting it. Requests are authenticated per the Recurly API specification using the ``recurly.API_KEY`` value for the API key. Requests and responses are logged at the ``DEBUG`` level to the ``recurly.http.request`` and ``recurly.http.response`` loggers respectively. """ if recurly.API_KEY is None: raise recurly.UnauthorizedError('recurly.API_KEY not set') headers = {} if headers is None else dict(headers) headers.setdefault('Accept', 'application/xml') headers.update({'User-Agent': recurly.USER_AGENT}) headers['X-Api-Version'] = recurly.api_version() headers['Authorization'] = 'Basic %s' % base64.b64encode( six.b('%s:' % recurly.API_KEY)).decode() if isinstance(body, Resource): body = ElementTree.tostring(body.to_element(), encoding='UTF-8') headers['Content-Type'] = 'application/xml; charset=utf-8' if method in ('POST', 'PUT') and body is None: headers['Content-Length'] = '0' print(body) resp = requests.get(url, headers=headers) print(resp) resp_headers = resp.headers recurly.cache_rate_limit_headers(resp_headers) return resp
def http_request(cls, url, method='GET', body=None, headers=None): """Make an HTTP request with the given method to the given URL, returning the resulting `http_client.HTTPResponse` instance. If the `body` argument is a `Resource` instance, it is serialized to XML by calling its `to_element()` method before submitting it. Requests are authenticated per the Recurly API specification using the ``recurly.API_KEY`` value for the API key. Requests and responses are logged at the ``DEBUG`` level to the ``recurly.http.request`` and ``recurly.http.response`` loggers respectively. """ if recurly.API_KEY is None: raise recurly.UnauthorizedError('recurly.API_KEY not set') is_non_ascii = lambda s: any(ord(c) >= 128 for c in s) if is_non_ascii(recurly.API_KEY) or is_non_ascii(recurly.SUBDOMAIN): raise recurly.ConfigurationError("""Setting API_KEY or SUBDOMAIN to unicode strings may cause problems. Please use strings. Issue described here: https://gist.github.com/maximehardy/d3a0a6427d2b6791b3dc""" ) urlparts = urlsplit(url) connection_options = {} if recurly.SOCKET_TIMEOUT_SECONDS: connection_options['timeout'] = recurly.SOCKET_TIMEOUT_SECONDS if urlparts.scheme != 'https': connection = http_client.HTTPConnection(urlparts.netloc, **connection_options) elif recurly.CA_CERTS_FILE is None: connection = http_client.HTTPSConnection(urlparts.netloc, **connection_options) else: connection_options['context'] = ssl.create_default_context( cafile=recurly.CA_CERTS_FILE) connection = http_client.HTTPSConnection(urlparts.netloc, **connection_options) headers = {} if headers is None else dict(headers) headers.setdefault('Accept', 'application/xml') headers.update({'User-Agent': recurly.USER_AGENT}) headers['X-Api-Version'] = recurly.api_version() headers['Authorization'] = 'Basic %s' % base64.b64encode( six.b('%s:' % recurly.API_KEY)).decode() log = logging.getLogger('recurly.http.request') if log.isEnabledFor(logging.DEBUG): log.debug("%s %s HTTP/1.1", method, url) for header, value in six.iteritems(headers): if header == 'Authorization': value = '<redacted>' log.debug("%s: %s", header, value) log.debug('') if method in ('POST', 'PUT') and body is not None: if isinstance(body, Resource): log.debug(body.as_log_output()) else: log.debug(body) if isinstance(body, Resource): body = ElementTree.tostring(body.to_element(), encoding='UTF-8') headers['Content-Type'] = 'application/xml; charset=utf-8' if method in ('POST', 'PUT') and body is None: headers['Content-Length'] = '0' connection.request(method, url, body, headers) resp = connection.getresponse() resp_headers = cls.headers_as_dict(resp) log = logging.getLogger('recurly.http.response') if log.isEnabledFor(logging.DEBUG): log.debug("HTTP/1.1 %d %s", resp.status, resp.reason) log.debug(resp_headers) log.debug('') recurly.cache_rate_limit_headers(resp_headers) return resp
def __enter__(self): self.request_context = mock.patch.object(http_client.HTTPConnection, 'request') self.request_context.return_value = None self.request_mock = self.request_context.__enter__() self.fixture_file = open(join(dirname(__file__), 'fixtures', self.fixture), 'rb') # Read through the request. preamble_line = self.fixture_file.readline().strip() try: self.method, self.uri, http_version = preamble_line.split(None, 2) self.method = self.method.decode() self.uri = self.uri.decode() except ValueError: raise ValueError("Couldn't parse preamble line from fixture file %r; does it have a fixture in it?" % self.fixture) # Read request headers def read_headers(fp): while True: try: line = fp.readline() except EOFError: return if not line or line == six.b('\n'): return yield line if six.PY2: msg = http_client.HTTPMessage(self.fixture_file, 0) self.headers = dict((k, v.strip()) for k, v in (header.split(':', 1) for header in msg.headers)) else: # http.client.HTTPMessage doesn't have importing headers from file msg = http_client.HTTPMessage() headers = email.message_from_bytes(six.b('').join(read_headers(self.fixture_file))) self.headers = dict((k, v.strip()) for k, v in headers._headers) # self.headers = {k: v for k, v in headers._headers} msg.fp = None # Read through to the vertical space. def nextline(fp): while True: try: line = fp.readline() except EOFError: return if not line or line.startswith(six.b('\x16')): return yield line body = six.b('').join(nextline(self.fixture_file)) # exhaust the request either way self.body = None if self.method in ('PUT', 'POST'): if 'Content-Type' in self.headers: if 'application/xml' in self.headers['Content-Type']: self.body = xml(body) else: self.body = body # Set up the response returner. sock = mock.Mock() sock.makefile = mock.Mock(return_value=self.fixture_file) response = http_client.HTTPResponse(sock, method=self.method) response.begin() self.response_context = mock.patch.object(http_client.HTTPConnection, 'getresponse', lambda self: response) self.response_mock = self.response_context.__enter__() recurly.cache_rate_limit_headers(self.headers) return self