wip
diff --git a/googleapiclient/_auth.py b/googleapiclient/_auth.py index 065b2ec..6257aa7 100644 --- a/googleapiclient/_auth.py +++ b/googleapiclient/_auth.py
@@ -19,6 +19,7 @@ try: import google.auth import google.auth.credentials + import google.auth.transport.requests HAS_GOOGLE_AUTH = True except ImportError: # pragma: NO COVER @@ -112,14 +113,7 @@ from googleapiclient.http import build_http if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials): - if google_auth_httplib2 is None: - raise ValueError( - "Credentials from google.auth specified, but " - "google-api-python-client is unable to use these credentials " - "unless google-auth-httplib2 is installed. Please install " - "google-auth-httplib2." - ) - return google_auth_httplib2.AuthorizedHttp(credentials, http=build_http()) + return google.auth.transport.requests.AuthorizedSession(credentials) else: return credentials.authorize(build_http()) @@ -131,7 +125,7 @@ # and likely tear a hole in spacetime. refresh_http = httplib2.Http() if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials): - request = google_auth_httplib2.Request(refresh_http) + request = google.auth.transport.requests.Request(refresh_http) return credentials.refresh(request) else: return credentials.refresh(refresh_http)
diff --git a/googleapiclient/discovery.py b/googleapiclient/discovery.py index d137b2e..d63f6b7 100644 --- a/googleapiclient/discovery.py +++ b/googleapiclient/discovery.py
@@ -318,7 +318,8 @@ # If discovery_http was created by this function, we are done with it # and can safely close it if http is None: - discovery_http.close() + if hasattr(discovery_http, "close"): + discovery_http.close() if service is None: raise UnknownApiNameOrVersion("name: %s version: %s" % (serviceName, version)) @@ -633,18 +634,7 @@ adc_cert_path, adc_key_path ) if client_cert_to_use: - cert_path, key_path, passphrase = client_cert_to_use() - - # The http object we built could be google_auth_httplib2.AuthorizedHttp - # or httplib2.Http. In the first case we need to extract the wrapped - # httplib2.Http object from google_auth_httplib2.AuthorizedHttp. - http_channel = ( - http.http - if google_auth_httplib2 - and isinstance(http, google_auth_httplib2.AuthorizedHttp) - else http - ) - http_channel.add_certificate(key_path, cert_path, "", passphrase) + raise Exception("Not supported") # If user doesn't provide api endpoint via client options, decide which # api endpoint to use. @@ -1426,7 +1416,8 @@ # httplib2 leaves sockets open by default. # Cleanup using the `close` method. # https://github.com/httplib2/httplib2/issues/148 - self._http.close() + if hasattr(self._http, "close"): + self._http.close() def _set_service_methods(self): self._add_basic_methods(self._resourceDesc, self._rootDesc, self._schema)
diff --git a/googleapiclient/http.py b/googleapiclient/http.py index 187f6f5..2d2c1dc 100644 --- a/googleapiclient/http.py +++ b/googleapiclient/http.py
@@ -188,7 +188,11 @@ try: exception = None - resp, content = http.request(uri, method, *args, **kwargs) + response = http.request(url=uri, method=method, **kwargs) + headers = dict(response.headers) + headers['status'] = response.status_code + resp = httplib2.Response(headers) + content = response.content # Retry on SSL errors and socket timeout errors. except _ssl_SSLError as ssl_error: exception = ssl_error @@ -928,7 +932,7 @@ self._rand, str(self.uri), method=str(self.method), - body=self.body, + data=self.body, headers=self.headers, ) @@ -1929,6 +1933,77 @@ http.request = new_request return http +import requests + + +class _Http(object): + def __init__(self, timeout=None): + """Constructor. + + Args: + timeout(int): number of seconds to wait before a socket timeout. + If None is passed for timeout then a default timeout value will be used. + """ + + self.credentials = httplib2.Credentials() + + # Key/cert + self.certificates = httplib2.KeyCerts() + + # authorization objects + self.authorizations = [] + + # If set to False then no redirects are followed, even safe ones. + self.follow_redirects = True + + self.redirect_codes = httplib2.REDIRECT_CODES + + # Which HTTP methods do we apply optimistic concurrency to, i.e. + # which methods get an "if-match:" etag header added to them. + self.optimistic_concurrency_methods = ["PUT", "PATCH"] + + self.safe_methods = list(httplib2.SAFE_METHODS) + + # If 'follow_redirects' is True, and this is set to True then + # all redirecs are followed, including unsafe ones. + self.follow_all_redirects = False + + self.ignore_etag = False + + self.force_exception_to_status_code = False + + self.timeout = timeout + + # Keep Authorization: headers on a redirect. + self.forward_authorization_headers = False + + def request( # pylint: disable=invalid-name + self, + uri, + method='GET', + body=None, + headers=None, + redirections=None, + timeout=None): + """Makes an HTTP request using httplib2 semantics.""" + + if timeout is None: + if self.timeout is not None: + timeout = self.timeout + elif socket.getdefaulttimeout() is not None: + timeout = socket.getdefaulttimeout() + else: + timeout = DEFAULT_HTTP_TIMEOUT_SEC + + with requests.Session() as session: + session.max_redirects = redirections + response = session.request( + method, uri, data=body, headers=headers, timeout=timeout + ) + headers = dict(response.headers) + headers['status'] = response.status_code + content = response.content + return httplib2.Response(headers), content def build_http(): """Builds httplib2.Http object @@ -1941,11 +2016,7 @@ before interacting with this method. """ - if socket.getdefaulttimeout() is not None: - http_timeout = socket.getdefaulttimeout() - else: - http_timeout = DEFAULT_HTTP_TIMEOUT_SEC - http = httplib2.Http(timeout=http_timeout) + http = _Http() # 308's are used by several Google APIs (Drive, YouTube) # for Resumable Uploads rather than Permanent Redirects. # This asks httplib2 to exclude 308s from the status codes
diff --git a/tests/test__auth.py b/tests/test__auth.py index cb70ff3..b7513f7 100644 --- a/tests/test__auth.py +++ b/tests/test__auth.py
@@ -104,11 +104,8 @@ authorized_http = _auth.authorized_http(credentials) - self.assertIsInstance(authorized_http, google_auth_httplib2.AuthorizedHttp) + self.assertIsInstance(authorized_http, google.auth.transport.requests.AuthorizedSession) self.assertEqual(authorized_http.credentials, credentials) - self.assertIsInstance(authorized_http.http, httplib2.Http) - self.assertIsInstance(authorized_http.http.timeout, int) - self.assertGreater(authorized_http.http.timeout, 0) @unittest.skipIf(not HAS_OAUTH2CLIENT, "oauth2client unavailable.") @@ -186,15 +183,3 @@ with self.assertRaises(EnvironmentError): print(_auth.default_credentials()) - -class TestGoogleAuthWithoutHttplib2(unittest.TestCase): - def setUp(self): - _auth.google_auth_httplib2 = None - - def tearDown(self): - _auth.google_auth_httplib2 = google_auth_httplib2 - - def test_default_credentials(self): - credentials = mock.Mock(spec=google.auth.credentials.Credentials) - with self.assertRaises(ValueError): - _auth.authorized_http(credentials)
diff --git a/tests/test_discovery.py b/tests/test_discovery.py index 6c3dfe8..944fa6d 100644 --- a/tests/test_discovery.py +++ b/tests/test_discovery.py
@@ -85,6 +85,7 @@ UnknownFileType, ) from googleapiclient.http import ( + _Http, HttpMock, HttpMockSequence, MediaFileUpload, @@ -464,12 +465,6 @@ class DiscoveryErrors(unittest.TestCase): - def test_tests_should_be_run_with_strict_positional_enforcement(self): - try: - plus = build("plus", "v1", None, static_discovery=False) - self.fail("should have raised a TypeError exception over missing http=.") - except TypeError: - pass def test_failed_to_parse_discovery_json(self): self.http = HttpMock(datafile("malformed.json"), {"status": "200"}) @@ -590,7 +585,7 @@ discovery, base="https://www.googleapis.com/", credentials=None ) # plus service requires Authorization - self.assertIsInstance(plus._http, httplib2.Http) + self.assertIsInstance(plus._http, _Http) self.assertIsInstance(plus._http.timeout, int) self.assertGreater(plus._http.timeout, 0) @@ -607,7 +602,7 @@ plus = build_from_document( discovery, base="https://www.googleapis.com/", developerKey="123" ) - self.assertIsInstance(plus._http, httplib2.Http) + self.assertIsInstance(plus._http, _Http) # It should not be an AuthorizedHttp, because that would indicate that # application default credentials were used. self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp) @@ -845,6 +840,7 @@ ("always", "false", MTLS_ENDPOINT), ] ) + @unittest.skip("MTLS not supported in this build") def test_mtls_with_provided_client_cert( self, use_mtls_env, use_client_cert, base_url ): @@ -877,6 +873,7 @@ ("always", "false"), ] ) + @unittest.skip("MTLS not supported in this build") def test_endpoint_not_switch(self, use_mtls_env, use_client_cert): # Test endpoint is not switched if user provided api endpoint discovery = read_datafile("plus.json") @@ -915,6 +912,7 @@ @mock.patch( "google.auth.transport.mtls.default_client_encrypted_cert_source", autospec=True ) + @unittest.skip("MTLS not supported in this build") def test_mtls_with_default_client_cert( self, use_mtls_env, @@ -958,6 +956,7 @@ @mock.patch( "google.auth.transport.mtls.has_default_client_cert_source", autospec=True ) + @unittest.skip("MTLS not supported in this build") def test_mtls_with_no_client_cert( self, use_mtls_env, use_client_cert, base_url, has_default_client_cert_source ): @@ -1533,7 +1532,7 @@ discovery = read_datafile("plus.json") service = build_from_document(discovery, credentials=credentials) - self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp) + self.assertIsInstance(service._http, google.auth.transport.requests.AuthorizedSession) self.assertEqual(service._http.credentials, credentials) def test_no_scopes_no_credentials(self): @@ -1541,7 +1540,7 @@ discovery = read_datafile("zoo.json") service = build_from_document(discovery) # Should be an ordinary httplib2.Http instance and not AuthorizedHttp. - self.assertIsInstance(service._http, httplib2.Http) + self.assertIsInstance(service._http, _Http) def test_full_featured(self): # Zoo should exercise all discovery facets