Skip to content
This repository has been archived by the owner on Jan 18, 2025. It is now read-only.

Commit

Permalink
Using transport helper for calling http.request().
Browse files Browse the repository at this point in the history
This assumes (for now) that http is an instance of
httplib.Http.
  • Loading branch information
dhermes committed Aug 9, 2016
1 parent 248dc6c commit 26cdf2d
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 32 deletions.
19 changes: 10 additions & 9 deletions oauth2client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,8 +792,9 @@ def _do_refresh_request(self, http_request):
headers = self._generate_refresh_request_headers()

logger.info('Refreshing access_token')
resp, content = http_request(
self.token_uri, method='POST', body=body, headers=headers)
resp, content = transport.request(
http_request, self.token_uri, method='POST',
body=body, headers=headers)
content = _helpers._from_bytes(content)
if resp.status == http_client.OK:
d = json.loads(content)
Expand Down Expand Up @@ -859,7 +860,7 @@ def _do_revoke(self, http_request, token):
logger.info('Revoking token')
query_params = {'token': token}
token_revoke_uri = _update_query_params(self.revoke_uri, query_params)
resp, content = http_request(token_revoke_uri)
resp, content = transport.request(http_request, token_revoke_uri)
if resp.status == http_client.OK:
self.invalid = True
else:
Expand Down Expand Up @@ -903,7 +904,7 @@ def _do_retrieve_scopes(self, http_request, token):
query_params = {'access_token': token, 'fields': 'scope'}
token_info_uri = _update_query_params(self.token_info_uri,
query_params)
resp, content = http_request(token_info_uri)
resp, content = transport.request(http_request, token_info_uri)
content = _helpers._from_bytes(content)
if resp.status == http_client.OK:
d = json.loads(content)
Expand Down Expand Up @@ -1571,7 +1572,7 @@ def verify_id_token(id_token, audience, http=None,
if http is None:
http = transport.get_cached_http()

resp, content = http.request(cert_uri)
resp, content = transport.request(http, cert_uri)
if resp.status == http_client.OK:
certs = json.loads(_helpers._from_bytes(content))
return crypt.verify_signed_jwt_with_certs(id_token, certs, audience)
Expand Down Expand Up @@ -1939,8 +1940,8 @@ def step1_get_device_and_user_codes(self, http=None):
if http is None:
http = transport.get_http_object()

resp, content = http.request(self.device_uri, method='POST', body=body,
headers=headers)
resp, content = transport.request(
http, self.device_uri, method='POST', body=body, headers=headers)
content = _helpers._from_bytes(content)
if resp.status == http_client.OK:
try:
Expand Down Expand Up @@ -2022,8 +2023,8 @@ def step2_exchange(self, code=None, http=None, device_flow_info=None):
if http is None:
http = transport.get_http_object()

resp, content = http.request(self.token_uri, method='POST', body=body,
headers=headers)
resp, content = transport.request(
http, self.token_uri, method='POST', body=body, headers=headers)
d = _parse_exchange_token_response(content)
if resp.status == http_client.OK and 'access_token' in d:
access_token = d['access_token']
Expand Down
7 changes: 3 additions & 4 deletions oauth2client/contrib/_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from oauth2client import _helpers
from oauth2client import client
from oauth2client import transport


METADATA_ROOT = 'http://metadata.google.internal/computeMetadata/v1/'
Expand Down Expand Up @@ -55,10 +56,8 @@ def get(http_request, path, root=METADATA_ROOT, recursive=None):
url = urlparse.urljoin(root, path)
url = _helpers._add_query_parameter(url, 'recursive', recursive)

response, content = http_request(
url,
headers=METADATA_HEADERS
)
response, content = transport.request(
http_request, url, headers=METADATA_HEADERS)

if response.status == http_client.OK:
decoded = _helpers._from_bytes(content)
Expand Down
59 changes: 46 additions & 13 deletions oauth2client/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ def new_request(uri, method='GET', body=None, headers=None,
_STREAM_PROPERTIES):
body_stream_position = body.tell()

resp, content = orig_request_method(uri, method, body,
clean_headers(headers),
redirections, connection_type)
resp, content = request(orig_request_method, uri, method, body,
clean_headers(headers),
redirections, connection_type)

# A stored token may expire between the time it is retrieved and
# the time the request is made, so we may need to try twice.
Expand All @@ -188,17 +188,17 @@ def new_request(uri, method='GET', body=None, headers=None,
if body_stream_position is not None:
body.seek(body_stream_position)

resp, content = orig_request_method(uri, method, body,
clean_headers(headers),
redirections, connection_type)
resp, content = request(orig_request_method, uri, method, body,
clean_headers(headers),
redirections, connection_type)

return resp, content

# Replace the request method with our own closure.
http.request = new_request

# Set credentials as a property of the request method.
setattr(http.request, 'credentials', credentials)
http.request.credentials = credentials


def wrap_http_for_jwt_access(credentials, http):
Expand Down Expand Up @@ -228,9 +228,9 @@ def new_request(uri, method='GET', body=None, headers=None,
if (credentials.access_token is None or
credentials.access_token_expired):
credentials.refresh(None)
return authenticated_request_method(uri, method, body,
headers, redirections,
connection_type)
return request(authenticated_request_method, uri,
method, body, headers, redirections,
connection_type)
else:
# If we don't have an 'aud' (audience) claim,
# create a 1-time token with the uri root as the audience
Expand All @@ -240,12 +240,45 @@ def new_request(uri, method='GET', body=None, headers=None,
token, unused_expiry = credentials._create_token({'aud': uri_root})

headers['Authorization'] = 'Bearer ' + token
return orig_request_method(uri, method, body,
clean_headers(headers),
redirections, connection_type)
return request(orig_request_method, uri, method, body,
clean_headers(headers),
redirections, connection_type)

# Replace the request method with our own closure.
http.request = new_request

# Set credentials as a property of the request method.
http.request.credentials = credentials


def request(http, uri, method='GET', body=None, headers=None,
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
connection_type=None):
"""Make an HTTP request with an HTTP object and arguments.
Args:
http: httplib2.Http, an http object to be used to make requests.
uri: string, The URI to be requested.
method: string, The HTTP method to use for the request. Defaults
to 'GET'.
body: string, The payload / body in HTTP request. By default
there is no payload.
headers: dict, Key-value pairs of request headers. By default
there are no headers.
redirections: int, The number of allowed 203 redirects for
the request. Defaults to 5.
connection_type: httplib.HTTPConnection, a subclass to be used for
establishing connection. If not set, the type
will be determined from the ``uri``.
Returns:
tuple, a pair of a httplib2.Response with the status code and other
headers and the bytes of the content returned.
"""
http_callable = getattr(http, 'request', http)
return http_callable(uri, method=method, body=body, headers=headers,
redirections=redirections,
connection_type=connection_type)


_CACHED_HTTP = httplib2.Http(MemoryCache())
14 changes: 12 additions & 2 deletions tests/contrib/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,23 @@
EXPECTED_URL = (
'http://metadata.google.internal/computeMetadata/v1/instance'
'/service-accounts/default')
EXPECTED_KWARGS = dict(headers=_metadata.METADATA_HEADERS)
EXPECTED_KWARGS = {
'headers': _metadata.METADATA_HEADERS,
'body': None,
'connection_type': None,
'method': 'GET',
'redirections': 5,
}


def request_mock(status, content_type, content):
response = http_mock.ResponseMock(
{'status': status, 'content-type': content_type})
return mock.Mock(return_value=(response, content.encode('utf-8')))
request_method = mock.Mock(
return_value=(response, content.encode('utf-8')))
# Make sure the mock doesn't have a request attr.
del request_method.request
return request_method


class TestMetadata(unittest2.TestCase):
Expand Down
17 changes: 13 additions & 4 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,8 @@ def _do_refresh_request_test_helper(self, response, content,
None, None, None)
credentials.store = store
http_request = mock.Mock()
# Make sure the mock doesn't have a request attr.
del http_request.request
http_request.return_value = response, content

with self.assertRaises(
Expand All @@ -1228,9 +1230,10 @@ def _do_refresh_request_test_helper(self, response, content,

self.assertEqual(exc_manager.exception.args, (error_msg,))
self.assertEqual(exc_manager.exception.status, response.status)
http_request.assert_called_once_with(None, body=gen_body.return_value,
headers=gen_headers.return_value,
method='POST')
http_request.assert_called_once_with(
None, method='POST', body=gen_body.return_value,
headers=gen_headers.return_value, redirections=5,
connection_type=None)

call1 = mock.call('Refreshing access_token')
failure_template = 'Failed to retrieve access token: %s'
Expand Down Expand Up @@ -1286,6 +1289,8 @@ def _do_revoke_test_helper(self, response, content,
revoke_uri=oauth2client.GOOGLE_REVOKE_URI)
credentials.store = store
http_request = mock.Mock()
# Make sure the mock doesn't have a request attr.
del http_request.request
http_request.return_value = response, content
token = u's3kr3tz'

Expand All @@ -1306,7 +1311,9 @@ def _do_revoke_test_helper(self, response, content,
store.delete.assert_not_called()

revoke_uri = oauth2client.GOOGLE_REVOKE_URI + '?token=' + token
http_request.assert_called_once_with(revoke_uri)
http_request.assert_called_once_with(
revoke_uri, method='GET', body=None, headers=None,
redirections=5, connection_type=None)

logger.info.assert_called_once_with('Revoking token')

Expand Down Expand Up @@ -1353,6 +1360,8 @@ def _do_retrieve_scopes_test_helper(self, response, content,
None, None, None, None, None, None, None,
token_info_uri=oauth2client.GOOGLE_TOKEN_INFO_URI)
http_request = mock.Mock()
# Make sure the mock doesn't have a request attr.
del http_request.request
http_request.return_value = response, content
token = u's3kr3tz'

Expand Down
40 changes: 40 additions & 0 deletions tests/test_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,43 @@ def test_wrap(self):
self.assertIsNone(result)
self.assertNotEqual(http.request, orig_req_method)
self.assertIs(http.request.credentials, credentials)


class Test_request(unittest2.TestCase):

uri = 'http://localhost'
method = 'POST'
body = 'abc'
redirections = 3

def test_with_request_attr(self):
http = mock.Mock()
mock_result = object()
mock_request = mock.Mock(return_value=mock_result)
http.request = mock_request

result = transport.request(http, self.uri, method=self.method,
body=self.body,
redirections=self.redirections)
self.assertIs(result, mock_result)
# Verify mock.
mock_request.assert_called_once_with(self.uri, method=self.method,
body=self.body,
redirections=self.redirections,
headers=None,
connection_type=None)

def test_with_callable_http(self):
mock_result = object()
http = mock.Mock(return_value=mock_result)
del http.request # Make sure the mock doesn't have a request attr.

result = transport.request(http, self.uri, method=self.method,
body=self.body,
redirections=self.redirections)
self.assertIs(result, mock_result)
# Verify mock.
http.assert_called_once_with(self.uri, method=self.method,
body=self.body,
redirections=self.redirections,
headers=None, connection_type=None)

0 comments on commit 26cdf2d

Please sign in to comment.