forked from Azure/azure-linux-extensions
-
Notifications
You must be signed in to change notification settings - Fork 0
/
urllib2httpclient.py
228 lines (179 loc) · 8.17 KB
/
urllib2httpclient.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env python2
#
# Copyright (C) Microsoft Corporation, All rights reserved.
"""Urllib2 HttpClient."""
import http.client
import socket
import time
import traceback
import urllib.request, urllib.error, urllib.parse
from httpclient import *
PY_MAJOR_VERSION = 0
PY_MINOR_VERSION = 1
PY_MICRO_VERSION = 2
SSL_MODULE_NAME = "ssl"
# On some system the ssl module might be missing
try:
import ssl
except ImportError:
ssl = None
class HttpsClientHandler(urllib.request.HTTPSHandler):
"""Https handler to enable attaching cert/key to request. Also used to disable strict cert verification for
testing.
"""
def __init__(self, cert_path, key_path, insecure=False):
self.cert_path = cert_path
self.key_path = key_path
ssl_context = None
if insecure and SSL_MODULE_NAME in sys.modules and (sys.version_info[PY_MAJOR_VERSION] == 2 and
sys.version_info[PY_MINOR_VERSION] >= 7 and
sys.version_info[PY_MICRO_VERSION] >= 9):
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
urllib.request.HTTPSHandler.__init__(self, context=ssl_context) # Context can be None here
def https_open(self, req):
return self.do_open(self.get_https_connection, req, context=self._context)
def get_https_connection(self, host, context=None, timeout=180):
"""urllib2's AbstractHttpHandler will invoke this method with the host/timeout parameter. See urllib2's
AbstractHttpHandler for more details.
Args:
host : string , the host.
context : ssl_context , the ssl context.
timeout : int , the timeout value in seconds.
Returns:
An HttpsConnection
"""
socket.setdefaulttimeout(180)
if self.cert_path is None or self.key_path is None:
return http.client.HTTPSConnection(host, timeout=timeout, context=context)
else:
return http.client.HTTPSConnection(host, cert_file=self.cert_path, key_file=self.key_path, timeout=timeout,
context=context)
def request_retry_handler(func):
def decorated_func(*args, **kwargs):
max_retry_count = 3
for iteration in range(0, max_retry_count, 1):
try:
ret = func(*args, **kwargs)
return ret
except Exception as exception:
if iteration >= max_retry_count - 1:
raise RetryAttemptExceededException(traceback.format_exc())
elif SSL_MODULE_NAME in sys.modules:
if type(exception).__name__ == 'SSLError':
time.sleep(5 + iteration)
continue
raise exception
return decorated_func
class Urllib2HttpClient(HttpClient):
"""Urllib2 http client. Inherits from HttpClient.
Targets:
[2.7.9 - 2.7.9+] only due to the lack of strict certificate verification prior to this version.
Implements the following method common to all classes inheriting HttpClient.
get (url, headers)
post (url, headers, data)
"""
def __init__(self, cert_path, key_path, insecure=False, proxy_configuration=None):
HttpClient.__init__(self, cert_path, key_path, insecure, proxy_configuration)
@request_retry_handler
def issue_request(self, url, headers, method=None, data=None):
"""Issues a GET request to the provided url and using the provided headers.
Args:
url : string , the url.
headers : dictionary, contains the headers key value pair.
data : string , contains the serialized request body.
Returns:
A RequestResponse
:param method:
"""
https_handler = HttpsClientHandler(self.cert_path, self.key_path, self.insecure)
opener = urllib.request.build_opener(https_handler)
if self.proxy_configuration is not None:
proxy_handler = urllib.request.ProxyHandler({'http': self.proxy_configuration,
'https': self.proxy_configuration})
opener.add_handler(proxy_handler)
req = urllib.request.Request(url, data=data, headers=headers)
req.get_method = lambda: method
response = opener.open(req, timeout=30)
opener.close()
https_handler.close()
return response
def get(self, url, headers=None):
"""Issues a GET request to the provided url and using the provided headers.
Args:
url : string , the url.
headers : dictionary, contains the headers key value pair.
Returns:
An http_response
"""
headers = self.merge_headers(self.default_headers, headers)
try:
response = self.issue_request(url, headers=headers, method=self.GET)
except urllib.error.HTTPError:
exception_type, error = sys.exc_info()[:2]
return RequestResponse(error.code)
return RequestResponse(response.getcode(), response.read())
def post(self, url, headers=None, data=None):
"""Issues a POST request to the provided url and using the provided headers.
Args:
url : string , the url.
headers : dictionary, contains the headers key value pair.
data : dictionary, contains the non-serialized request body.
Returns:
A RequestResponse
"""
headers = self.merge_headers(self.default_headers, headers)
if data is None:
serial_data = ""
else:
serial_data = self.json.dumps(data)
headers.update({self.CONTENT_TYPE_HEADER_KEY: self.APP_JSON_HEADER_VALUE})
try:
response = self.issue_request(url, headers=headers, method=self.POST, data=serial_data)
except urllib.error.HTTPError:
exception_type, error = sys.exc_info()[:2]
return RequestResponse(error.code)
return RequestResponse(response.getcode(), response.read())
def put(self, url, headers=None, data=None):
"""Issues a PUT request to the provided url and using the provided headers.
Args:
url : string , the url.
headers : dictionary, contains the headers key value pair.
data : dictionary, contains the non-serialized request body.
Returns:
A RequestResponse
"""
headers = self.merge_headers(self.default_headers, headers)
if data is None:
serial_data = ""
else:
serial_data = self.json.dumps(data)
headers.update({self.CONTENT_TYPE_HEADER_KEY: self.APP_JSON_HEADER_VALUE})
try:
response = self.issue_request(url, headers=headers, method=self.PUT, data=serial_data)
except urllib.error.HTTPError:
exception_type, error = sys.exc_info()[:2]
return RequestResponse(error.code)
return RequestResponse(response.getcode(), response.read())
def delete(self, url, headers=None, data=None):
"""Issues a DELETE request to the provided url and using the provided headers.
Args:
url : string , the url.
headers : dictionary, contains the headers key value pair.
data : dictionary, contains the non-serialized request body.
Returns:
A RequestResponse
"""
headers = self.merge_headers(self.default_headers, headers)
if data is None:
serial_data = ""
else:
serial_data = self.json.dumps(data)
headers.update({self.CONTENT_TYPE_HEADER_KEY: self.APP_JSON_HEADER_VALUE})
try:
response = self.issue_request(url, headers=headers, method=self.DELETE, data=serial_data)
except urllib.error.HTTPError:
exception_type, error = sys.exc_info()[:2]
return RequestResponse(error.code)
return RequestResponse(response.getcode(), response.read())