Skip to content

Commit 11482ad

Browse files
authored
Merge pull request googleapis#559 from dhermes/begin-transport-module
Begin transport module for httplib2 specific pieces.
2 parents 3e9b857 + 213050d commit 11482ad

File tree

8 files changed

+353
-145
lines changed

8 files changed

+353
-145
lines changed

docs/source/oauth2client.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Submodules
1919
oauth2client.file
2020
oauth2client.service_account
2121
oauth2client.tools
22+
oauth2client.transport
2223
oauth2client.util
2324

2425
Module contents
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
oauth2client.transport module
2+
=============================
3+
4+
.. automodule:: oauth2client.transport
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

oauth2client/client.py

Lines changed: 12 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import sys
2929
import tempfile
3030

31-
import httplib2
3231
import six
3332
from six.moves import http_client
3433
from six.moves import urllib
@@ -39,9 +38,9 @@
3938
from oauth2client import GOOGLE_REVOKE_URI
4039
from oauth2client import GOOGLE_TOKEN_INFO_URI
4140
from oauth2client import GOOGLE_TOKEN_URI
41+
from oauth2client import transport
4242
from oauth2client import util
4343
from oauth2client._helpers import _from_bytes
44-
from oauth2client._helpers import _to_bytes
4544
from oauth2client._helpers import _urlsafe_b64decode
4645

4746

@@ -71,9 +70,6 @@
7170
# Constant to use for the out of band OAuth 2.0 flow.
7271
OOB_CALLBACK_URN = 'urn:ietf:wg:oauth:2.0:oob'
7372

74-
# Google Data client libraries may need to set this to [401, 403].
75-
REFRESH_STATUS_CODES = (http_client.UNAUTHORIZED,)
76-
7773
# The value representing user credentials.
7874
AUTHORIZED_USER = 'authorized_user'
7975

@@ -120,6 +116,12 @@
120116
# easier testing (by replacing with a stub).
121117
_UTCNOW = datetime.datetime.utcnow
122118

119+
# NOTE: These names were previously defined in this module but have been
120+
# moved into `oauth2client.transport`,
121+
clean_headers = transport.clean_headers
122+
MemoryCache = transport.MemoryCache
123+
REFRESH_STATUS_CODES = transport.REFRESH_STATUS_CODES
124+
123125

124126
class SETTINGS(object):
125127
"""Settings namespace for globally defined values."""
@@ -177,22 +179,6 @@ class CryptoUnavailableError(Error, NotImplementedError):
177179
"""Raised when a crypto library is required, but none is available."""
178180

179181

180-
class MemoryCache(object):
181-
"""httplib2 Cache implementation which only caches locally."""
182-
183-
def __init__(self):
184-
self.cache = {}
185-
186-
def get(self, key):
187-
return self.cache.get(key)
188-
189-
def set(self, key, value):
190-
self.cache[key] = value
191-
192-
def delete(self, key):
193-
self.cache.pop(key, None)
194-
195-
196182
def _parse_expiry(expiry):
197183
if expiry and isinstance(expiry, datetime.datetime):
198184
return expiry.strftime(EXPIRY_FORMAT)
@@ -451,32 +437,6 @@ def delete(self):
451437
self.release_lock()
452438

453439

454-
def clean_headers(headers):
455-
"""Forces header keys and values to be strings, i.e not unicode.
456-
457-
The httplib module just concats the header keys and values in a way that
458-
may make the message header a unicode string, which, if it then tries to
459-
contatenate to a binary request body may result in a unicode decode error.
460-
461-
Args:
462-
headers: dict, A dictionary of headers.
463-
464-
Returns:
465-
The same dictionary but with all the keys converted to strings.
466-
"""
467-
clean = {}
468-
try:
469-
for k, v in six.iteritems(headers):
470-
if not isinstance(k, six.binary_type):
471-
k = str(k)
472-
if not isinstance(v, six.binary_type):
473-
v = str(v)
474-
clean[_to_bytes(k)] = _to_bytes(v)
475-
except UnicodeEncodeError:
476-
raise NonAsciiHeaderError(k, ': ', v)
477-
return clean
478-
479-
480440
def _update_query_params(uri, params):
481441
"""Updates a URI with new query parameters.
482442
@@ -494,26 +454,6 @@ def _update_query_params(uri, params):
494454
return urllib.parse.urlunparse(new_parts)
495455

496456

497-
def _initialize_headers(headers):
498-
"""Creates a copy of the headers."""
499-
if headers is None:
500-
headers = {}
501-
else:
502-
headers = dict(headers)
503-
return headers
504-
505-
506-
def _apply_user_agent(headers, user_agent):
507-
"""Adds a user-agent to the headers."""
508-
if user_agent is not None:
509-
if 'user-agent' in headers:
510-
headers['user-agent'] = (user_agent + ' ' + headers['user-agent'])
511-
else:
512-
headers['user-agent'] = user_agent
513-
514-
return headers
515-
516-
517457
class OAuth2Credentials(Credentials):
518458
"""Credentials object for OAuth 2.0.
519459
@@ -604,58 +544,7 @@ def authorize(self, http):
604544
that adds in the Authorization header and then calls the original
605545
version of 'request()'.
606546
"""
607-
request_orig = http.request
608-
609-
# The closure that will replace 'httplib2.Http.request'.
610-
def new_request(uri, method='GET', body=None, headers=None,
611-
redirections=httplib2.DEFAULT_MAX_REDIRECTS,
612-
connection_type=None):
613-
if not self.access_token:
614-
logger.info('Attempting refresh to obtain '
615-
'initial access_token')
616-
self._refresh(request_orig)
617-
618-
# Clone and modify the request headers to add the appropriate
619-
# Authorization header.
620-
headers = _initialize_headers(headers)
621-
self.apply(headers)
622-
_apply_user_agent(headers, self.user_agent)
623-
624-
body_stream_position = None
625-
if all(getattr(body, stream_prop, None) for stream_prop in
626-
('read', 'seek', 'tell')):
627-
body_stream_position = body.tell()
628-
629-
resp, content = request_orig(uri, method, body,
630-
clean_headers(headers),
631-
redirections, connection_type)
632-
633-
# A stored token may expire between the time it is retrieved and
634-
# the time the request is made, so we may need to try twice.
635-
max_refresh_attempts = 2
636-
for refresh_attempt in range(max_refresh_attempts):
637-
if resp.status not in REFRESH_STATUS_CODES:
638-
break
639-
logger.info('Refreshing due to a %s (attempt %s/%s)',
640-
resp.status, refresh_attempt + 1,
641-
max_refresh_attempts)
642-
self._refresh(request_orig)
643-
self.apply(headers)
644-
if body_stream_position is not None:
645-
body.seek(body_stream_position)
646-
647-
resp, content = request_orig(uri, method, body,
648-
clean_headers(headers),
649-
redirections, connection_type)
650-
651-
return (resp, content)
652-
653-
# Replace the request method with our own closure.
654-
http.request = new_request
655-
656-
# Set credentials as a property of the request method.
657-
setattr(http.request, 'credentials', self)
658-
547+
transport.wrap_http_for_auth(self, http)
659548
return http
660549

661550
def refresh(self, http):
@@ -781,7 +670,7 @@ def get_access_token(self, http=None):
781670
"""
782671
if not self.access_token or self.access_token_expired:
783672
if not http:
784-
http = httplib2.Http()
673+
http = transport.get_http_object()
785674
self.refresh(http)
786675
return AccessTokenInfo(access_token=self.access_token,
787676
expires_in=self._expires_in())
@@ -1654,11 +1543,6 @@ def _require_crypto_or_die():
16541543
raise CryptoUnavailableError('No crypto library available')
16551544

16561545

1657-
# Only used in verify_id_token(), which is always calling to the same URI
1658-
# for the certs.
1659-
_cached_http = httplib2.Http(MemoryCache())
1660-
1661-
16621546
@util.positional(2)
16631547
def verify_id_token(id_token, audience, http=None,
16641548
cert_uri=ID_TOKEN_VERIFICATION_CERTS):
@@ -1684,7 +1568,7 @@ def verify_id_token(id_token, audience, http=None,
16841568
"""
16851569
_require_crypto_or_die()
16861570
if http is None:
1687-
http = _cached_http
1571+
http = transport.get_cached_http()
16881572

16891573
resp, content = http.request(cert_uri)
16901574
if resp.status == http_client.OK:
@@ -2027,7 +1911,7 @@ def step1_get_device_and_user_codes(self, http=None):
20271911
headers['user-agent'] = self.user_agent
20281912

20291913
if http is None:
2030-
http = httplib2.Http()
1914+
http = transport.get_http_object()
20311915

20321916
resp, content = http.request(self.device_uri, method='POST', body=body,
20331917
headers=headers)
@@ -2110,7 +1994,7 @@ def step2_exchange(self, code=None, http=None, device_flow_info=None):
21101994
headers['user-agent'] = self.user_agent
21111995

21121996
if http is None:
2113-
http = httplib2.Http()
1997+
http = transport.get_http_object()
21141998

21151999
resp, content = http.request(self.token_uri, method='POST', body=body,
21162000
headers=headers)

oauth2client/service_account.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@
2727
from oauth2client import GOOGLE_TOKEN_URI
2828
from oauth2client import util
2929
from oauth2client._helpers import _from_bytes
30-
from oauth2client.client import _apply_user_agent
31-
from oauth2client.client import _initialize_headers
3230
from oauth2client.client import _UTCNOW
3331
from oauth2client.client import AccessTokenInfo
3432
from oauth2client.client import AssertionCredentials
35-
from oauth2client.client import clean_headers
3633
from oauth2client.client import EXPIRY_FORMAT
3734
from oauth2client.client import SERVICE_ACCOUNT
35+
from oauth2client.transport import _apply_user_agent
36+
from oauth2client.transport import _initialize_headers
37+
from oauth2client.transport import clean_headers
3838

3939

4040
_PASSWORD_DEFAULT = 'notasecret'

0 commit comments

Comments
 (0)