Skip to content

Commit a983a6f

Browse files
Replace urllib3 with httpx
1 parent 7377408 commit a983a6f

File tree

3 files changed

+46
-47
lines changed

3 files changed

+46
-47
lines changed

gabbi/case.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
1515
The test case encapsulates the request headers and body and expected
1616
response headers and body. When the test is run an HTTP request is
17-
made using urllib3. Assertions are made against the response.
17+
made using httpx. Assertions are made against the response.
1818
"""
1919

2020
from collections import OrderedDict
@@ -500,7 +500,7 @@ def _run_request(
500500
"""
501501

502502
if 'user-agent' not in (key.lower() for key in headers):
503-
headers['user-agent'] = "gabbi/%s (Python urllib3)" % __version__
503+
headers['user-agent'] = "gabbi/%s (Python httpx)" % __version__
504504

505505
try:
506506
response, content = self.http.request(

gabbi/httpclient.py

+43-44
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,58 @@
1111
# License for the specific language governing permissions and limitations
1212
# under the License.
1313

14+
import logging
1415
import os
1516
import sys
1617

17-
import certifi
18-
import urllib3
18+
import httpx
1919

2020
from gabbi.handlers import jsonhandler
2121
from gabbi import utils
2222

23-
# Disable SSL warnings otherwise tests which process stderr will get
24-
# extra information.
25-
urllib3.disable_warnings()
23+
logging.getLogger('httpx').setLevel(logging.WARNING)
2624

2725

28-
class Http(urllib3.PoolManager):
29-
"""A subclass of the ``urllib3.PoolManager`` to munge the data.
26+
class Http:
27+
"""A class to munge the HTTP response.
3028
3129
This transforms the response to look more like what httplib2
3230
provided when it was used as the HTTP client.
3331
"""
3432

33+
def __init__(self, **kwargs):
34+
self.extensions = {}
35+
if 'server_hostname' in kwargs:
36+
self.extensions['sni_hostname'] = kwargs['server_hostname']
37+
self.client = httpx.Client(verify=kwargs.get('cert_validate', True))
38+
3539
def request(self, absolute_uri, method, body, headers, redirect, timeout):
36-
if redirect:
37-
retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
38-
else:
39-
retry = urllib3.util.Retry(total=False, redirect=False)
40-
response = super(Http, self).request(
41-
method,
42-
absolute_uri,
43-
body=body,
44-
headers=headers,
45-
retries=retry,
46-
timeout=timeout,
47-
)
40+
try:
41+
response = self.client.request(
42+
method=method,
43+
url=absolute_uri,
44+
headers=headers,
45+
content=body,
46+
timeout=timeout,
47+
follow_redirects=redirect,
48+
extensions=self.extensions,
49+
)
50+
except httpx.ConnectError as error:
51+
raise RuntimeError(
52+
f'{error}.\n\nMethod: {method}\nURL: {absolute_uri}'
53+
) from error
4854

4955
# Transform response into something akin to httplib2
5056
# response object.
51-
content = response.data
52-
status = response.status
53-
reason = response.reason
57+
content = response.content
58+
status = response.status_code
59+
reason = response.reason_phrase
60+
http_version = response.http_version
5461
headers = response.headers
5562
headers['status'] = str(status)
56-
headers['reason'] = reason
63+
headers['reason'] = str(reason)
64+
headers['http_protocol_version'] = str(http_version)
5765

58-
# Shut down open PoolManagers whose connections have completed to
59-
# save on socket file descriptors.
60-
self.clear()
6166
return headers, content
6267

6368

@@ -87,6 +92,7 @@ class VerboseHttp(Http):
8792
HEADER_BLACKLIST = [
8893
'status',
8994
'reason',
95+
'http_protocol_version',
9096
]
9197

9298
REQUEST_PREFIX = '>'
@@ -106,27 +112,26 @@ def __init__(self, **kwargs):
106112
self._stream = kwargs.pop('stream')
107113
if self._use_color:
108114
self.colorize = utils.get_colorizer(self._stream)
109-
super(VerboseHttp, self).__init__(**kwargs)
115+
super().__init__(**kwargs)
110116

111117
def request(self, absolute_uri, method, body, headers, redirect, timeout):
112118
"""Display request parameters before requesting."""
113119

114-
self._verbose_output('#### %s ####' % self.caption,
120+
self._verbose_output(f'#### {self.caption} ####',
115121
color=self.COLORMAP['caption'])
116-
self._verbose_output('%s %s' % (method, absolute_uri),
122+
self._verbose_output(f'{method} {absolute_uri}',
117123
prefix=self.REQUEST_PREFIX,
118124
color=self.COLORMAP['request'])
119125

120126
self._print_headers(headers, prefix=self.REQUEST_PREFIX)
121127
self._print_body(headers, body)
122128

123-
response, content = super(VerboseHttp, self).request(
129+
response, content = super().request(
124130
absolute_uri, method, body, headers, redirect, timeout)
125131

126132
# Blank line for division
127133
self._verbose_output('')
128-
self._verbose_output('%s %s' % (response['status'],
129-
response['reason']),
134+
self._verbose_output(f'{response["status"]} {response["reason"]}',
130135
prefix=self.RESPONSE_PREFIX,
131136
color=self.COLORMAP['status'])
132137
self._print_headers(response, prefix=self.RESPONSE_PREFIX)
@@ -172,8 +177,8 @@ def _print_body(self, headers, content):
172177

173178
def _print_header(self, name, value, prefix='', stream=None):
174179
"""Output one single header."""
175-
header = self.colorize(self.COLORMAP['header'], "%s:" % name)
176-
self._verbose_output("%s %s" % (header, value), prefix=prefix,
180+
header = self.colorize(self.COLORMAP['header'], f'{name}:')
181+
self._verbose_output(f'{header} {value}', prefix=prefix,
177182
stream=stream)
178183

179184
def _verbose_output(self, message, prefix='', color=None, stream=None):
@@ -194,29 +199,23 @@ def get_http(
194199
timeout=30,
195200
):
196201
"""Return an ``Http`` class for making requests."""
197-
cert_validation = {'cert_reqs': 'CERT_NONE'} if not cert_validate else {}
198-
199202
if not verbose:
200203
return Http(
201-
strict=True,
202-
ca_certs=certifi.where(),
203204
server_hostname=hostname,
204205
timeout=timeout,
205-
**cert_validation
206+
cert_validate=cert_validate,
206207
)
207208

208-
headers = False if verbose == 'body' else True
209-
body = False if verbose == 'headers' else True
209+
headers = verbose != 'body'
210+
body = verbose != 'headers'
210211

211212
return VerboseHttp(
212213
headers=headers,
213214
body=body,
214215
stream=sys.stdout,
215216
caption=caption,
216217
colorize=True,
217-
strict=True,
218-
ca_certs=certifi.where(),
219218
server_hostname=hostname,
220219
timeout=timeout,
221-
**cert_validation
220+
cert_validate=cert_validate,
222221
)

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pbr
22
pytest
33
PyYAML
4-
urllib3>=1.26.9,<2.0.0
4+
httpx
55
certifi
66
jsonpath-rw-ext>=1.0.0
77
wsgi-intercept>=1.13.0

0 commit comments

Comments
 (0)