1# -*- coding: utf-8 -*- # 2# Copyright 2015 Google LLC. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16"""Helper function to open a url using a proxy using httlib2 connections.""" 17 18from __future__ import absolute_import 19from __future__ import division 20from __future__ import unicode_literals 21 22from googlecloudsdk.core import http_proxy 23from googlecloudsdk.core import properties 24 25import httplib2 26from six.moves import urllib 27 28 29class HttplibConnectionHandler(urllib.request.HTTPHandler, 30 urllib.request.HTTPSHandler): 31 """urllib2 Handler Class to use httplib2 connections. 32 33 This handler makes urllib2 use httplib2.HTTPSConnectionWithTimeout. The 34 httplib2 connections can handle both HTTP and SOCKS proxies, passed via the 35 ProxyInfo object. It also has CA_CERTS files and validates SSL certificates. 36 37 The handler also IDNA encodes the host it's connecting to. socks library with 38 socks5 proxy throws an odd encode exception even for ANSII hostnames if encode 39 is not called. 40 """ 41 42 def http_open(self, req): 43 def build(host, **kwargs): 44 proxy_info = http_proxy.GetHttpProxyInfo() 45 if callable(proxy_info): 46 proxy_info = proxy_info('http') 47 return httplib2.HTTPConnectionWithTimeout( 48 host.encode('idna').decode(), 49 proxy_info=proxy_info, 50 **kwargs) 51 return self.do_open(build, req) 52 53 def https_open(self, req): 54 def build(host, **kwargs): 55 proxy_info = http_proxy.GetHttpProxyInfo() 56 if callable(proxy_info): 57 proxy_info = proxy_info('https') 58 ca_certs = properties.VALUES.core.custom_ca_certs_file.Get() 59 return httplib2.HTTPSConnectionWithTimeout( 60 host.encode('idna').decode(), 61 proxy_info=proxy_info, 62 ca_certs=ca_certs, 63 **kwargs) 64 return self.do_open(build, req) 65 66 67# TODO(b/120992538) Use urllib3 when PROXY/USE_URLLIB3_VIA_SHIM 68def urlopen(req, data=None, timeout=60): 69 """Helper function that mimics urllib2.urlopen, but adds proxy information.""" 70 71 # We need to pass urllib2.ProxyHandler({}) to disable the proxy handling in 72 # urllib2 open. If we don't, then urllib will substitute the host with the 73 # proxy address and the HttplibConnectionHandler won't get the original host. 74 # (the default urllib2.HTTPSHandler needs this substitution trickery) 75 # We do the proxy detection in http_proxy.GetHttpProxyInfo and pass it to 76 # httplib2.HTTPSConnectionWithTimeout via proxy info object. 77 # httplib2.HTTPSConnectionWithTimeout takes care of handling proxies. 78 opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), 79 HttplibConnectionHandler()) 80 return opener.open(req, data, timeout) 81