1# Copyright 2016 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Transport adapter for http.client, for internal use only."""
16
17import http.client as http_client
18import logging
19import socket
20import urllib
21
22from google.auth import _helpers
23from google.auth import exceptions
24from google.auth import transport
25
26_LOGGER = logging.getLogger(__name__)
27
28
29class Response(transport.Response):
30 """http.client transport response adapter.
31
32 Args:
33 response (http.client.HTTPResponse): The raw http client response.
34 """
35
36 def __init__(self, response):
37 self._status = response.status
38 self._headers = {key.lower(): value for key, value in response.getheaders()}
39 self._data = response.read()
40
41 @property
42 def status(self):
43 return self._status
44
45 @property
46 def headers(self):
47 return self._headers
48
49 @property
50 def data(self):
51 return self._data
52
53
54class Request(transport.Request):
55 """http.client transport request adapter."""
56
57 def __call__(
58 self, url, method="GET", body=None, headers=None, timeout=None, **kwargs
59 ):
60 """Make an HTTP request using http.client.
61
62 Args:
63 url (str): The URI to be requested.
64 method (str): The HTTP method to use for the request. Defaults
65 to 'GET'.
66 body (bytes): The payload / body in HTTP request.
67 headers (Mapping): Request headers.
68 timeout (Optional(int)): The number of seconds to wait for a
69 response from the server. If not specified or if None, the
70 socket global default timeout will be used.
71 kwargs: Additional arguments passed throught to the underlying
72 :meth:`~http.client.HTTPConnection.request` method.
73
74 Returns:
75 Response: The HTTP response.
76
77 Raises:
78 google.auth.exceptions.TransportError: If any exception occurred.
79 """
80 # socket._GLOBAL_DEFAULT_TIMEOUT is the default in http.client.
81 if timeout is None:
82 timeout = socket._GLOBAL_DEFAULT_TIMEOUT
83
84 # http.client doesn't allow None as the headers argument.
85 if headers is None:
86 headers = {}
87
88 # http.client needs the host and path parts specified separately.
89 parts = urllib.parse.urlsplit(url)
90 path = urllib.parse.urlunsplit(
91 ("", "", parts.path, parts.query, parts.fragment)
92 )
93
94 if parts.scheme != "http":
95 raise exceptions.TransportError(
96 "http.client transport only supports the http scheme, {}"
97 "was specified".format(parts.scheme)
98 )
99
100 connection = http_client.HTTPConnection(parts.netloc, timeout=timeout)
101
102 try:
103
104 _helpers.request_log(_LOGGER, method, url, body, headers)
105 connection.request(method, path, body=body, headers=headers, **kwargs)
106 response = connection.getresponse()
107 _helpers.response_log(_LOGGER, response)
108 return Response(response)
109
110 except (http_client.HTTPException, socket.error) as caught_exc:
111 new_exc = exceptions.TransportError(caught_exc)
112 raise new_exc from caught_exc
113
114 finally:
115 connection.close()