Skip to content

Commit fc4c147

Browse files
purple4reinankzou
andauthored
Provide urllib3 implementation of HTTPClient. (#879)
* Provide urllib3 implementation of HTTPClient. * pin ubuntu version and urllib version * Fix linting error. --------- Co-authored-by: Kevin Zou <[email protected]>
1 parent d375ea0 commit fc4c147

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
strategy:
4848
fail-fast: false
4949
matrix:
50-
os: [ubuntu-latest]
50+
os: [ubuntu-22.04]
5151
python-version: ['pypy2.7', '3.7', 'pypy3.8']
5252
# os: [ubuntu-latest, windows-latest, macos-latest]
5353
# python-version: ['2.7', '3.7', '3.8', '3.9', '3.10', 'pypy-2.7', 'pypy-3.8']

datadog/api/http_client.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@
2727
except ImportError:
2828
urlfetch, urlfetch_errors = None, None
2929

30+
try:
31+
import urllib3 # type: ignore
32+
except ImportError:
33+
urllib3 = None
34+
3035
# datadog
3136
from datadog.api.exceptions import ProxyError, ClientError, HTTPError, HttpTimeout
3237

@@ -178,6 +183,58 @@ def raise_on_status(cls, result):
178183
raise HTTPError(status_code)
179184

180185

186+
class Urllib3Client(HTTPClient):
187+
"""
188+
HTTP client based on 3rd party `urllib3` module.
189+
"""
190+
191+
_pool = None
192+
_pool_lock = Lock()
193+
194+
@classmethod
195+
def request(cls, method, url, headers, params, data, timeout, proxies, verify, max_retries):
196+
"""
197+
Wrapper around `urllib3.PoolManager.request` method. This method will raise
198+
exceptions for HTTP status codes that are not 2xx.
199+
"""
200+
try:
201+
with cls._pool_lock:
202+
if cls._pool is None:
203+
cls._pool = urllib3.PoolManager(
204+
retries=max_retries,
205+
timeout=timeout,
206+
cert_reqs="CERT_REQUIRED" if verify else "CERT_NONE",
207+
)
208+
209+
newheaders = copy.deepcopy(headers)
210+
newheaders["User-Agent"] = _get_user_agent_header()
211+
response = cls._pool.request(
212+
method, url, body=data, fields=params, headers=newheaders
213+
)
214+
cls.raise_on_status(response)
215+
216+
except urllib3.exceptions.ProxyError as e:
217+
raise _remove_context(ProxyError(method, url, e))
218+
except urllib3.exceptions.MaxRetryError as e:
219+
raise _remove_context(ClientError(method, url, e))
220+
except urllib3.exceptions.TimeoutError as e:
221+
raise _remove_context(HttpTimeout(method, url, e))
222+
except urllib3.exceptions.HTTPError as e:
223+
raise _remove_context(HTTPError(e))
224+
225+
return response
226+
227+
@classmethod
228+
def raise_on_status(cls, response):
229+
"""
230+
Raise on HTTP status code errors.
231+
"""
232+
status_code = response.status
233+
if status_code < 200 or status_code >= 300:
234+
if status_code not in (400, 401, 403, 404, 409, 429):
235+
raise HTTPError(status_code, response.reason)
236+
237+
181238
def resolve_http_client():
182239
"""
183240
Resolve an appropriate HTTP client based the defined priority and user environment.
@@ -190,6 +247,10 @@ def resolve_http_client():
190247
log.debug(u"Use `urlfetch` based HTTP client.")
191248
return URLFetchClient
192249

250+
if urllib3:
251+
log.debug(u"Use `urllib3` based HTTP client.")
252+
return Urllib3Client
253+
193254
raise ImportError(
194255
u"Datadog API client was unable to resolve a HTTP client. " u" Please install `requests` library."
195256
)

0 commit comments

Comments
 (0)