File: //snap/google-cloud-cli/current/lib/googlecloudsdk/core/http.py
# -*- coding: utf-8 -*- #
# Copyright 2013 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A module to get an unauthenticated http object."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import enum
import google_auth_httplib2
from googlecloudsdk.core import context_aware
from googlecloudsdk.core import http_proxy
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import transport
from googlecloudsdk.core.util import encoding
import httplib2
import six
def Http(timeout='unset', response_encoding=None, ca_certs=None):
"""Get an httplib2.Http client that is properly configured for use by gcloud.
This method does not add credentials to the client. For an Http client that
has been authenticated, use core.credentials.http.Http().
Args:
timeout: double, The timeout in seconds to pass to httplib2. This is the
socket level timeout. If timeout is None, timeout is infinite. If
default argument 'unset' is given, a sensible default is selected using
transport.GetDefaultTimeout().
response_encoding: str, the encoding to use to decode the response.
ca_certs: str, absolute filename of a ca_certs file that overrides the
default. The gcloud config property for ca_certs, in turn, overrides
this argument.
Returns:
An httplib2.Http client object configured with all the required settings
for gcloud.
"""
http_client = _CreateRawHttpClient(timeout, ca_certs)
http_client = RequestWrapper().WrapWithDefaults(http_client,
response_encoding)
return http_client
def HttpClient(
timeout=None,
proxy_info=httplib2.proxy_info_from_environment,
ca_certs=httplib2.CA_CERTS,
disable_ssl_certificate_validation=False):
"""Returns a httplib2.Http subclass.
Args:
timeout: float, Request timeout, in seconds.
proxy_info: httplib2.ProxyInfo object or callable
ca_certs: str, absolute filename of a ca_certs file
disable_ssl_certificate_validation: bool, If true, disable ssl certificate
validation.
Returns: A httplib2.Http subclass
"""
if properties.VALUES.proxy.use_urllib3_via_shim.GetBool():
import httplib2shim # pylint:disable=g-import-not-at-top
http_class = httplib2shim.Http
else:
http_class = httplib2.Http
result = http_class(
timeout=timeout,
proxy_info=proxy_info,
ca_certs=ca_certs,
disable_ssl_certificate_validation=disable_ssl_certificate_validation)
ca_config = context_aware.Config()
if ca_config and ca_config.config_type == context_aware.ConfigType.ON_DISK_CERTIFICATE:
log.debug('Using client certificate %s',
ca_config.encrypted_client_cert_path)
result.add_certificate(ca_config.encrypted_client_cert_path,
ca_config.encrypted_client_cert_path, '',
password=ca_config.encrypted_client_cert_password)
return result
def _CreateRawHttpClient(timeout='unset', ca_certs=None):
"""Create an HTTP client matching the appropriate gcloud properties."""
# Compared with setting the default timeout in the function signature (i.e.
# timeout=300), this lets you test with short default timeouts by mocking
# GetDefaultTimeout.
if timeout != 'unset':
effective_timeout = timeout
else:
effective_timeout = transport.GetDefaultTimeout()
no_validate = properties.VALUES.auth.disable_ssl_validation.GetBool() or False
ca_certs_property = properties.VALUES.core.custom_ca_certs_file.Get()
# Believe an explicitly-set ca_certs property over anything we added.
if ca_certs_property:
ca_certs = ca_certs_property
if no_validate:
ca_certs = None
return HttpClient(timeout=effective_timeout,
proxy_info=http_proxy.GetHttpProxyInfo(),
ca_certs=ca_certs,
disable_ssl_certificate_validation=no_validate)
class Request(transport.Request):
"""Encapsulates parameters for making a general HTTP request.
This implementation does additional manipulation to ensure that the request
parameters are specified in the same way as they were specified by the
caller. That is, if the user calls:
request('URI', 'GET', None, {'header': '1'})
After modifying the request, we will call request using positional
parameters, instead of transforming the request into:
request('URI', method='GET', body=None, headers={'header': '1'})
"""
@classmethod
def FromRequestArgs(cls, *args, **kwargs):
return cls(*args, **kwargs)
def __init__(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs
uri = RequestParam.URI.Get(args, kwargs)
if not six.PY2:
# httplib2 needs text under Python 3.
uri = encoding.Decode(uri)
method = RequestParam.METHOD.Get(args, kwargs)
headers = RequestParam.HEADERS.Get(args, kwargs) or {}
body = RequestParam.BODY.Get(args, kwargs)
super(Request, self).__init__(uri, method, headers, body)
def ToRequestArgs(self):
args, kwargs = list(self._args), dict(self._kwargs)
RequestParam.URI.Set(args, kwargs, self.uri)
if self.method:
RequestParam.METHOD.Set(args, kwargs, self.method)
if self.headers:
RequestParam.HEADERS.Set(args, kwargs, self.headers)
if self.body:
RequestParam.BODY.Set(args, kwargs, self.body)
return args, kwargs
class Response(transport.Response):
"""Encapsulates responses from making a general HTTP request."""
@classmethod
def FromResponse(cls, response):
resp, content = response
headers = {h: v for h, v in six.iteritems(resp) if h != 'status'}
return cls(resp.get('status'), headers, content)
class RequestWrapper(transport.RequestWrapper):
"""Class for wrapping httplib.Httplib2 requests."""
request_class = Request
response_class = Response
def DecodeResponse(self, response, response_encoding):
response, content = response
content = content.decode(response_encoding)
return response, content
class RequestParam(enum.Enum):
"""Encapsulates parameters to a request() call and how to extract them.
http.request has the following signature:
request(self, uri, method="GET", body=None, headers=None, ...)
"""
URI = ('uri', 0)
METHOD = ('method', 1)
BODY = ('body', 2)
HEADERS = ('headers', 3)
def __init__(self, arg_name, index):
self.arg_name = arg_name
self.index = index
def Get(self, args, kwargs):
if len(args) > self.index:
return args[self.index]
if self.arg_name in kwargs:
return kwargs[self.arg_name]
return None
def Set(self, args, kwargs, value):
if len(args) > self.index:
args[self.index] = value
else:
kwargs[self.arg_name] = value
def GoogleAuthRequest():
"""A Request object for google-auth library.
Returns:
A http request which implements google.auth.transport.Request and uses
gcloud's http object in the core.
"""
return google_auth_httplib2.Request(Http())