geventhttpclient 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geventhttpclient-2.2.1/LICENSE-MIT +22 -0
- {geventhttpclient-2.2.0/src/geventhttpclient.egg-info → geventhttpclient-2.2.1}/PKG-INFO +29 -25
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/README.md +8 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/pyproject.toml +3 -2
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/__init__.py +1 -1
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/client.py +4 -8
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/connectionpool.py +4 -28
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/httplib.py +33 -9
- geventhttpclient-2.2.1/src/geventhttpclient/httplib2.py +69 -0
- geventhttpclient-2.2.1/src/geventhttpclient/tests/conftest.py +57 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_client.py +17 -51
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_httplib.py +19 -21
- geventhttpclient-2.2.1/src/geventhttpclient/tests/test_httplib2.py +31 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_network_failures.py +2 -15
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_ssl.py +77 -23
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_url.py +49 -26
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_useragent.py +1 -15
- geventhttpclient-2.2.1/src/geventhttpclient/url.py +160 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/useragent.py +5 -5
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1/src/geventhttpclient.egg-info}/PKG-INFO +29 -25
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient.egg-info/SOURCES.txt +1 -2
- geventhttpclient-2.2.0/LICENSE.txt +0 -27
- geventhttpclient-2.2.0/src/geventhttpclient/httplib2.py +0 -37
- geventhttpclient-2.2.0/src/geventhttpclient/tests/conftest.py +0 -7
- geventhttpclient-2.2.0/src/geventhttpclient/tests/test_http_host.py +0 -14
- geventhttpclient-2.2.0/src/geventhttpclient/tests/test_httplib2.py +0 -20
- geventhttpclient-2.2.0/src/geventhttpclient/url.py +0 -246
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/MANIFEST.in +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/ext/Python_compat.h +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/ext/_parser.c +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/llhttp/LICENSE-MIT +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/llhttp/include/llhttp.h +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/llhttp/src/api.c +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/llhttp/src/http.c +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/llhttp/src/llhttp.c +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/setup.cfg +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/setup.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/header.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/response.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/oncert.pem +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/server.crt +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/server.key +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_headers.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_keep_alive.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_no_module_ssl.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_parser.py +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient.egg-info/dependency_links.txt +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient.egg-info/requires.txt +0 -0
- {geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
This software is licensed under the MIT License.
|
|
2
|
+
|
|
3
|
+
Copyright Antonin Amand <antonin.amand@gmail.com>, 2018.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
10
|
+
persons to whom the Software is furnished to do so, subject to the
|
|
11
|
+
following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included
|
|
14
|
+
in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
17
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
19
|
+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
20
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
21
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
22
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: geventhttpclient
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: HTTP client library for gevent
|
|
5
5
|
Author-email: Antonin Amand <antonin.amand@gmail.com>
|
|
6
|
-
License:
|
|
6
|
+
License: This software is licensed under the MIT License.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
licensed under the same terms.
|
|
8
|
+
Copyright Antonin Amand <antonin.amand@gmail.com>, 2018.
|
|
10
9
|
|
|
11
|
-
Permission is hereby granted, free of charge, to any person obtaining a
|
|
12
|
-
of this software and associated documentation files (the
|
|
13
|
-
deal in the Software without restriction, including
|
|
14
|
-
rights to use, copy, modify, merge, publish,
|
|
15
|
-
sell copies of the Software, and to permit
|
|
16
|
-
furnished to do so, subject to the
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
|
11
|
+
copy of this software and associated documentation files (the
|
|
12
|
+
"Software"), to deal in the Software without restriction, including
|
|
13
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
14
|
+
distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
15
|
+
persons to whom the Software is furnished to do so, subject to the
|
|
16
|
+
following conditions:
|
|
17
17
|
|
|
18
|
-
The above copyright notice and this permission notice shall be included
|
|
19
|
-
all copies or substantial portions of the Software.
|
|
18
|
+
The above copyright notice and this permission notice shall be included
|
|
19
|
+
in all copies or substantial portions of the Software.
|
|
20
20
|
|
|
21
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
22
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
23
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
24
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
25
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
26
|
-
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
27
|
-
IN THE SOFTWARE.
|
|
28
|
-
|
|
29
|
-
Note: Previous versions of gevenhttpclient used http_parser.c, which in turn
|
|
30
|
-
was based on src/http/ngx_http_parse.c from NGINX, copyright Igor Sysoev,
|
|
31
|
-
Joyent, Inc. and other Node contributors. See http://github.com/joyent/http-parser
|
|
32
|
-
for more information
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
22
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
23
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
24
|
+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
25
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
26
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
27
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
33
28
|
|
|
34
29
|
Project-URL: homepage, http://github.com/geventhttpclient/geventhttpclient
|
|
35
30
|
Project-URL: issues, http://github.com/geventhttpclient/geventhttpclient/issues
|
|
@@ -38,6 +33,7 @@ Keywords: http,gevent,client
|
|
|
38
33
|
Classifier: Development Status :: 5 - Production/Stable
|
|
39
34
|
Classifier: Intended Audience :: Developers
|
|
40
35
|
Classifier: Intended Audience :: Information Technology
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
41
37
|
Classifier: Operating System :: OS Independent
|
|
42
38
|
Classifier: Programming Language :: Python :: 3
|
|
43
39
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -48,7 +44,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
48
44
|
Classifier: Topic :: Software Development :: Libraries
|
|
49
45
|
Requires-Python: >=3.9
|
|
50
46
|
Description-Content-Type: text/markdown
|
|
51
|
-
License-File: LICENSE
|
|
47
|
+
License-File: LICENSE-MIT
|
|
52
48
|
Requires-Dist: gevent
|
|
53
49
|
Requires-Dist: certifi
|
|
54
50
|
Requires-Dist: brotli
|
|
@@ -241,3 +237,11 @@ for mor details. Also note, HTTPX is better be used with asyncio, not gevent.
|
|
|
241
237
|
| Httpx | 770.3 |
|
|
242
238
|
|
|
243
239
|
*Linux(x86_64), Python 3.11.6 @ Intel i7-7560U*
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
This package is distributed under the [MIT license](https://github.com/geventhttpclient/geventhttpclient/blob/master/LICENSE-MIT).
|
|
244
|
+
Previous versions of geventhttpclient used http_parser.c, which in turn
|
|
245
|
+
was based on `src/http/ngx_http_parse.c` from NGINX, copyright Igor Sysoev,
|
|
246
|
+
Joyent, Inc., and other Node contributors. For more information, see
|
|
247
|
+
http://github.com/joyent/http-parser
|
|
@@ -174,3 +174,11 @@ for mor details. Also note, HTTPX is better be used with asyncio, not gevent.
|
|
|
174
174
|
| Httpx | 770.3 |
|
|
175
175
|
|
|
176
176
|
*Linux(x86_64), Python 3.11.6 @ Intel i7-7560U*
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
This package is distributed under the [MIT license](https://github.com/geventhttpclient/geventhttpclient/blob/master/LICENSE-MIT).
|
|
181
|
+
Previous versions of geventhttpclient used http_parser.c, which in turn
|
|
182
|
+
was based on `src/http/ngx_http_parse.c` from NGINX, copyright Igor Sysoev,
|
|
183
|
+
Joyent, Inc., and other Node contributors. For more information, see
|
|
184
|
+
http://github.com/joyent/http-parser
|
|
@@ -5,17 +5,18 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "geventhttpclient"
|
|
8
|
-
version = "2.2.
|
|
8
|
+
version = "2.2.1" # dont forget to update version __init__.py as well
|
|
9
9
|
description = "HTTP client library for gevent"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.9"
|
|
12
|
-
license = {file = "LICENSE
|
|
12
|
+
license = {file = "LICENSE-MIT"}
|
|
13
13
|
keywords = ["http", "gevent", "client"]
|
|
14
14
|
authors = [{name = "Antonin Amand", email = "antonin.amand@gmail.com"}]
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 5 - Production/Stable",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"Intended Audience :: Information Technology",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
19
20
|
"Operating System :: OS Independent",
|
|
20
21
|
"Programming Language :: Python :: 3",
|
|
21
22
|
"Programming Language :: Python :: 3.9",
|
|
@@ -146,14 +146,10 @@ class HTTPClient:
|
|
|
146
146
|
self.default_headers.update(self.DEFAULT_HEADERS)
|
|
147
147
|
self.default_headers.update(headers)
|
|
148
148
|
self.block_size = block_size
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
url.host = self.host
|
|
154
|
-
url.port = self.port
|
|
155
|
-
url.scheme = self.ssl and PROTO_HTTPS or PROTO_HTTP
|
|
156
|
-
return url
|
|
149
|
+
|
|
150
|
+
scheme = PROTO_HTTPS if self.ssl else PROTO_HTTP
|
|
151
|
+
port_str = f":{port}" if port else ""
|
|
152
|
+
self._base_url_string = f"{scheme}://{self.host}{port_str}/"
|
|
157
153
|
|
|
158
154
|
def close(self):
|
|
159
155
|
self._connection_pool.close()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from contextlib import contextmanager
|
|
3
2
|
|
|
4
3
|
import gevent.queue
|
|
5
4
|
import gevent.socket
|
|
@@ -198,12 +197,15 @@ def init_ssl_context(ssl_context_factory, ca_certs, check_hostname=True):
|
|
|
198
197
|
|
|
199
198
|
|
|
200
199
|
try:
|
|
200
|
+
from ssl import PROTOCOL_TLS_CLIENT
|
|
201
|
+
|
|
201
202
|
import gevent.ssl
|
|
202
203
|
|
|
203
204
|
try:
|
|
204
205
|
from gevent.ssl import create_default_context
|
|
205
206
|
except ImportError:
|
|
206
207
|
create_default_context = None
|
|
208
|
+
|
|
207
209
|
except ImportError:
|
|
208
210
|
pass
|
|
209
211
|
else:
|
|
@@ -222,6 +224,7 @@ else:
|
|
|
222
224
|
"ciphers": _DEFAULT_CIPHERS,
|
|
223
225
|
"ca_certs": _CA_CERTS,
|
|
224
226
|
"cert_reqs": gevent.ssl.CERT_REQUIRED,
|
|
227
|
+
"ssl_version": PROTOCOL_TLS_CLIENT,
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
def __init__(
|
|
@@ -260,30 +263,3 @@ else:
|
|
|
260
263
|
else:
|
|
261
264
|
server_hostname = self.ssl_options.get("server_hostname", self._request_host)
|
|
262
265
|
return self.ssl_context.wrap_socket(sock, server_hostname=server_hostname)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
class ClientPool:
|
|
266
|
-
"""
|
|
267
|
-
Pool implementation for HTTP clients, that weren't designed with concurrency in mind.
|
|
268
|
-
Usage example:
|
|
269
|
-
|
|
270
|
-
pool = ClientPool(MyHttpClient)
|
|
271
|
-
with pool.get() as client:
|
|
272
|
-
response = client.request("127.0.0.1")
|
|
273
|
-
"""
|
|
274
|
-
|
|
275
|
-
def __init__(self, factory, concurrency=5):
|
|
276
|
-
self.factory = factory
|
|
277
|
-
self.queue = gevent.queue.Queue(concurrency)
|
|
278
|
-
for i in range(concurrency):
|
|
279
|
-
self.queue.put(factory())
|
|
280
|
-
|
|
281
|
-
@contextmanager
|
|
282
|
-
def get(self):
|
|
283
|
-
client = self.queue.get()
|
|
284
|
-
yield client
|
|
285
|
-
self.queue.put(client)
|
|
286
|
-
|
|
287
|
-
def close(self):
|
|
288
|
-
for client in self.queue:
|
|
289
|
-
client.close()
|
|
@@ -3,14 +3,15 @@ Provide HTTPConnection, HTTPSConnection and HTTPResponse implementations ready
|
|
|
3
3
|
to use as drop-in replacements for their counterparts in http.client.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
import http.client
|
|
6
|
+
import http.client
|
|
7
|
+
from contextlib import contextmanager
|
|
7
8
|
|
|
8
9
|
import gevent.socket
|
|
9
10
|
import gevent.ssl
|
|
10
11
|
|
|
11
12
|
from geventhttpclient import connectionpool, header, response
|
|
12
13
|
|
|
13
|
-
_UNKNOWN = getattr(
|
|
14
|
+
_UNKNOWN = getattr(http.client, "_UNKNOWN", "UNKNOWN")
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class HTTPLibHeaders(header.Headers):
|
|
@@ -120,10 +121,7 @@ class HTTPResponse(response.HTTPSocketResponse):
|
|
|
120
121
|
return self.status_code
|
|
121
122
|
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class HTTPConnection(httplib.HTTPConnection):
|
|
124
|
+
class HTTPConnection(http.client.HTTPConnection):
|
|
127
125
|
response_class = HTTPResponse
|
|
128
126
|
source_address = None
|
|
129
127
|
_hidden_socket = None
|
|
@@ -220,9 +218,35 @@ else:
|
|
|
220
218
|
|
|
221
219
|
|
|
222
220
|
def patch():
|
|
223
|
-
|
|
224
|
-
|
|
221
|
+
http.client.HTTPConnection = HTTPConnection
|
|
222
|
+
http.client.HTTPResponse = HTTPResponse
|
|
223
|
+
try:
|
|
224
|
+
http.client.HTTPSConnection = HTTPSConnection
|
|
225
|
+
except NameError:
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@contextmanager
|
|
230
|
+
def patched():
|
|
231
|
+
"""Temporarily patch http.client."""
|
|
232
|
+
http_client_HTTPConnection = http.client.HTTPConnection
|
|
233
|
+
http_client_HTTPResponse = http.client.HTTPResponse
|
|
225
234
|
try:
|
|
226
|
-
|
|
235
|
+
http_client_HTTPSConnection = http.client.HTTPSConnection
|
|
227
236
|
except NameError:
|
|
228
237
|
pass
|
|
238
|
+
try:
|
|
239
|
+
http.client.HTTPConnection = HTTPConnection
|
|
240
|
+
http.client.HTTPResponse = HTTPResponse
|
|
241
|
+
try:
|
|
242
|
+
http.client.HTTPSConnection = HTTPSConnection
|
|
243
|
+
except NameError:
|
|
244
|
+
pass
|
|
245
|
+
yield
|
|
246
|
+
finally:
|
|
247
|
+
http.client.HTTPConnection = http_client_HTTPConnection
|
|
248
|
+
http.client.HTTPResponse = http_client_HTTPResponse
|
|
249
|
+
try:
|
|
250
|
+
http.client.HTTPSConnection = http_client_HTTPSConnection
|
|
251
|
+
except NameError:
|
|
252
|
+
pass
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Drop-in replacement for httplib2. Make sure to monkey patch everything first!
|
|
3
|
+
|
|
4
|
+
import gevent.monkey
|
|
5
|
+
gevent.monkey.patch_all()
|
|
6
|
+
|
|
7
|
+
from geventhttpclient import httplib
|
|
8
|
+
httplib.patch()
|
|
9
|
+
|
|
10
|
+
from geventhttpclient import httplib2
|
|
11
|
+
|
|
12
|
+
http = httplib2.Http(concurrency=5)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from contextlib import contextmanager
|
|
16
|
+
|
|
17
|
+
import gevent.queue
|
|
18
|
+
|
|
19
|
+
import geventhttpclient.httplib
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ClientPool:
|
|
23
|
+
"""
|
|
24
|
+
Pool implementation for HTTP clients, that weren't designed with concurrency in mind.
|
|
25
|
+
Usage example:
|
|
26
|
+
|
|
27
|
+
pool = ClientPool(MyHttpClient)
|
|
28
|
+
with pool.get() as client:
|
|
29
|
+
response = client.request("127.0.0.1")
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, factory, concurrency=5):
|
|
33
|
+
self.factory = factory
|
|
34
|
+
self.queue = gevent.queue.Queue(concurrency)
|
|
35
|
+
for i in range(concurrency):
|
|
36
|
+
self.queue.put(factory())
|
|
37
|
+
|
|
38
|
+
@contextmanager
|
|
39
|
+
def get(self):
|
|
40
|
+
client = self.queue.get()
|
|
41
|
+
yield client
|
|
42
|
+
self.queue.put(client)
|
|
43
|
+
|
|
44
|
+
def close(self):
|
|
45
|
+
for client in self.queue:
|
|
46
|
+
client.close()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
with geventhttpclient.httplib.patched():
|
|
50
|
+
import httplib2
|
|
51
|
+
|
|
52
|
+
class Http:
|
|
53
|
+
"""Imitate a httplib2.Http client. Except that it is now run concurrently."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, *args, **kw):
|
|
56
|
+
self.concurrency = max(kw.pop("concurrency", 0), 2) # never smaller than 2
|
|
57
|
+
self.args = args
|
|
58
|
+
self.kw = kw
|
|
59
|
+
self.pool = ClientPool(self._http_factory, concurrency=self.concurrency)
|
|
60
|
+
|
|
61
|
+
def _http_factory(self):
|
|
62
|
+
return httplib2.Http(*self.args, **self.kw)
|
|
63
|
+
|
|
64
|
+
def request(self, *args, **kw):
|
|
65
|
+
with self.pool.get() as client:
|
|
66
|
+
return client.request(*args, **kw)
|
|
67
|
+
|
|
68
|
+
def close(self):
|
|
69
|
+
self.pool.close()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import gevent.monkey
|
|
2
|
+
|
|
3
|
+
gevent.monkey.patch_all() # make sure all tests run monkey patched
|
|
4
|
+
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
|
|
7
|
+
import gevent.pywsgi
|
|
8
|
+
import gevent.queue
|
|
9
|
+
import gevent.server
|
|
10
|
+
|
|
11
|
+
TEST_HOST = "127.0.0.1"
|
|
12
|
+
TEST_PORT = 54323
|
|
13
|
+
LISTENER = TEST_HOST, TEST_PORT
|
|
14
|
+
LISTENER_URL = f"http://{TEST_HOST}:{TEST_PORT}/"
|
|
15
|
+
HTTPBIN_HOST = "httpbingo.org" # this might be exchanged with a self-hosted version
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@contextmanager
|
|
19
|
+
def server(handler):
|
|
20
|
+
exception_queue = gevent.queue.Queue()
|
|
21
|
+
|
|
22
|
+
def wrapped_handler(env, start_response):
|
|
23
|
+
try:
|
|
24
|
+
return handler(env, start_response)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
exception_queue.put(e)
|
|
27
|
+
raise
|
|
28
|
+
|
|
29
|
+
server = gevent.server.StreamServer(LISTENER, handle=wrapped_handler)
|
|
30
|
+
server.start()
|
|
31
|
+
try:
|
|
32
|
+
yield
|
|
33
|
+
if not exception_queue.empty():
|
|
34
|
+
raise exception_queue.get()
|
|
35
|
+
finally:
|
|
36
|
+
server.stop()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@contextmanager
|
|
40
|
+
def wsgiserver(handler):
|
|
41
|
+
exception_queue = gevent.queue.Queue()
|
|
42
|
+
|
|
43
|
+
def wrapped_handler(env, start_response):
|
|
44
|
+
try:
|
|
45
|
+
return handler(env, start_response)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
exception_queue.put(e)
|
|
48
|
+
raise
|
|
49
|
+
|
|
50
|
+
server = gevent.pywsgi.WSGIServer(LISTENER, wrapped_handler)
|
|
51
|
+
server.start()
|
|
52
|
+
try:
|
|
53
|
+
yield
|
|
54
|
+
if not exception_queue.empty():
|
|
55
|
+
raise exception_queue.get()
|
|
56
|
+
finally:
|
|
57
|
+
server.stop()
|
|
@@ -1,37 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import os
|
|
3
|
-
from contextlib import contextmanager
|
|
4
2
|
|
|
5
3
|
import gevent.pool
|
|
6
|
-
import gevent.
|
|
4
|
+
import gevent.queue
|
|
7
5
|
import gevent.server
|
|
8
6
|
import pytest
|
|
9
|
-
from gevent.ssl import SSLError
|
|
10
7
|
|
|
11
|
-
from geventhttpclient import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
HTTPBIN_HOST = "httpbingo.org" # this might be exchanged with a self-hosted version
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@contextmanager
|
|
18
|
-
def server(handler):
|
|
19
|
-
server = gevent.server.StreamServer(LISTENER, handle=handler)
|
|
20
|
-
server.start()
|
|
21
|
-
try:
|
|
22
|
-
yield
|
|
23
|
-
finally:
|
|
24
|
-
server.stop()
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@contextmanager
|
|
28
|
-
def wsgiserver(handler):
|
|
29
|
-
server = gevent.pywsgi.WSGIServer(LISTENER, handler)
|
|
30
|
-
server.start()
|
|
31
|
-
try:
|
|
32
|
-
yield
|
|
33
|
-
finally:
|
|
34
|
-
server.stop()
|
|
8
|
+
from geventhttpclient import __version__
|
|
9
|
+
from geventhttpclient.client import METHOD_GET, HTTPClient
|
|
10
|
+
from geventhttpclient.tests.conftest import HTTPBIN_HOST, LISTENER, server, wsgiserver
|
|
35
11
|
|
|
36
12
|
|
|
37
13
|
def httpbin_client(
|
|
@@ -80,12 +56,22 @@ def httpbin():
|
|
|
80
56
|
@pytest.mark.parametrize("request_uri", ["/tp/", "tp/", f"http://{HTTPBIN_HOST}/tp/"])
|
|
81
57
|
def test_build_request(httpbin, request_uri):
|
|
82
58
|
request_ref = f"GET /tp/ HTTP/1.1\r\nUser-Agent: python/gevent-http-client-{__version__}\r\nHost: {HTTPBIN_HOST}\r\n\r\n"
|
|
83
|
-
assert httpbin._build_request(
|
|
59
|
+
assert httpbin._build_request(METHOD_GET, request_uri) == request_ref
|
|
84
60
|
|
|
85
61
|
|
|
86
62
|
def test_build_request_invalid_host(httpbin):
|
|
87
63
|
with pytest.raises(ValueError):
|
|
88
|
-
httpbin._build_request(
|
|
64
|
+
httpbin._build_request(METHOD_GET, "http://someunrelatedhost.com/")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.mark.parametrize("port", [None, 1234])
|
|
68
|
+
@pytest.mark.parametrize("host", ["localhost", "127.0.0.1", "::1", "[::1]"])
|
|
69
|
+
def test_build_request_host(host, port):
|
|
70
|
+
client = HTTPClient(host, port)
|
|
71
|
+
host_ref = (
|
|
72
|
+
f"host: {f'[{host}]' if host.startswith(':') else host}{f':{port}' if port else ''}\r\n"
|
|
73
|
+
)
|
|
74
|
+
assert host_ref in client._build_request(METHOD_GET, "").lower()
|
|
89
75
|
|
|
90
76
|
|
|
91
77
|
test_url_client_args = [
|
|
@@ -255,7 +241,7 @@ def test_bytes_post():
|
|
|
255
241
|
|
|
256
242
|
|
|
257
243
|
def test_string_post():
|
|
258
|
-
with wsgiserver(check_upload("12345", 5)):
|
|
244
|
+
with wsgiserver(check_upload(b"12345", 5)):
|
|
259
245
|
client = HTTPClient(*LISTENER)
|
|
260
246
|
client.post("/", "12345")
|
|
261
247
|
|
|
@@ -334,26 +320,6 @@ def test_response_context_manager(httpbin):
|
|
|
334
320
|
assert r._sock is None # released
|
|
335
321
|
|
|
336
322
|
|
|
337
|
-
@pytest.mark.network
|
|
338
|
-
def test_client_ssl():
|
|
339
|
-
client = HTTPClient("github.com", ssl=True)
|
|
340
|
-
assert client.port == 443
|
|
341
|
-
response = client.get("/")
|
|
342
|
-
assert response.status_code == 200
|
|
343
|
-
body = response.read()
|
|
344
|
-
assert len(body)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
@pytest.mark.network
|
|
348
|
-
def test_ssl_fail_invalid_certificate():
|
|
349
|
-
certs = os.path.join(os.path.dirname(os.path.abspath(__file__)), "oncert.pem")
|
|
350
|
-
client = HTTPClient("github.com", ssl_options={"ca_certs": certs})
|
|
351
|
-
assert client.port == 443
|
|
352
|
-
with pytest.raises(SSLError) as e_info:
|
|
353
|
-
client.get("/")
|
|
354
|
-
assert e_info.value.reason == "CERTIFICATE_VERIFY_FAILED"
|
|
355
|
-
|
|
356
|
-
|
|
357
323
|
@pytest.mark.network
|
|
358
324
|
def test_multi_queries_greenlet_safe():
|
|
359
325
|
httpbin = httpbin_client(concurrency=3)
|
{geventhttpclient-2.2.0 → geventhttpclient-2.2.1}/src/geventhttpclient/tests/test_httplib.py
RENAMED
|
@@ -1,23 +1,10 @@
|
|
|
1
|
+
import http.client
|
|
1
2
|
import urllib.request
|
|
2
|
-
from contextlib import contextmanager
|
|
3
|
-
from http.client import HTTPException
|
|
4
3
|
|
|
5
|
-
import gevent.server
|
|
6
4
|
import pytest
|
|
7
5
|
|
|
8
|
-
from geventhttpclient.httplib import HTTPConnection
|
|
9
|
-
|
|
10
|
-
LISTENER = "127.0.0.1", 54323
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@contextmanager
|
|
14
|
-
def server(handler):
|
|
15
|
-
server = gevent.server.StreamServer(LISTENER, handle=handler)
|
|
16
|
-
server.start()
|
|
17
|
-
try:
|
|
18
|
-
yield
|
|
19
|
-
finally:
|
|
20
|
-
server.stop()
|
|
6
|
+
from geventhttpclient.httplib import HTTPConnection, patched
|
|
7
|
+
from geventhttpclient.tests.conftest import HTTPBIN_HOST, LISTENER, server
|
|
21
8
|
|
|
22
9
|
|
|
23
10
|
def wrong_response_status_line(sock, addr):
|
|
@@ -29,7 +16,7 @@ def test_httplib_exception():
|
|
|
29
16
|
with server(wrong_response_status_line):
|
|
30
17
|
connection = HTTPConnection(*LISTENER)
|
|
31
18
|
connection.request("GET", "/")
|
|
32
|
-
with pytest.raises(HTTPException):
|
|
19
|
+
with pytest.raises(http.client.HTTPException):
|
|
33
20
|
connection.getresponse()
|
|
34
21
|
|
|
35
22
|
|
|
@@ -67,9 +54,20 @@ def test_msg():
|
|
|
67
54
|
assert response.msg["Content-Type"] == "text/plain"
|
|
68
55
|
|
|
69
56
|
|
|
57
|
+
def test_patched():
|
|
58
|
+
assert http.client.HTTPResponse.__module__ == "http.client"
|
|
59
|
+
assert http.client.HTTPConnection.__module__ == "http.client"
|
|
60
|
+
with patched():
|
|
61
|
+
assert http.client.HTTPResponse.__module__ == "geventhttpclient.httplib"
|
|
62
|
+
assert http.client.HTTPConnection.__module__ == "geventhttpclient.httplib"
|
|
63
|
+
assert http.client.HTTPResponse.__module__ == "http.client"
|
|
64
|
+
assert http.client.HTTPConnection.__module__ == "http.client"
|
|
65
|
+
|
|
66
|
+
|
|
70
67
|
@pytest.mark.network
|
|
71
|
-
@pytest.mark.parametrize("url", ["http://
|
|
68
|
+
@pytest.mark.parametrize("url", [f"http://{HTTPBIN_HOST}", "https://github.com"])
|
|
72
69
|
def test_urllib_request(url):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
with patched():
|
|
71
|
+
content = urllib.request.urlopen(url).read()
|
|
72
|
+
assert content
|
|
73
|
+
assert b"body" in content
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import gevent.pool
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
import geventhttpclient.httplib
|
|
5
|
+
|
|
6
|
+
httplib2 = pytest.importorskip("geventhttpclient.httplib2")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def job(client, url):
|
|
10
|
+
response, content = client.request(url)
|
|
11
|
+
assert content
|
|
12
|
+
assert b"body" in content
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.mark.network
|
|
16
|
+
def test_request_parallel():
|
|
17
|
+
with geventhttpclient.httplib.patched():
|
|
18
|
+
errors = []
|
|
19
|
+
|
|
20
|
+
client = httplib2.Http(concurrency=5)
|
|
21
|
+
group = gevent.pool.Pool(size=5)
|
|
22
|
+
|
|
23
|
+
urls = ["https://google.com", "http://gevent.org", "https://github.com"]
|
|
24
|
+
for url in urls:
|
|
25
|
+
g = group.spawn(job, client, url)
|
|
26
|
+
g.link_exception(lambda g: errors.append(g.exception))
|
|
27
|
+
group.join()
|
|
28
|
+
if errors:
|
|
29
|
+
for e in errors:
|
|
30
|
+
print(e)
|
|
31
|
+
raise errors[0]
|
|
@@ -1,24 +1,11 @@
|
|
|
1
|
-
from contextlib import contextmanager
|
|
2
1
|
from http.client import HTTPException
|
|
3
2
|
|
|
4
3
|
import gevent.server
|
|
5
4
|
import gevent.socket
|
|
6
5
|
import pytest
|
|
7
6
|
|
|
8
|
-
from geventhttpclient import HTTPClient
|
|
9
|
-
|
|
10
|
-
CRLF = "\r\n"
|
|
11
|
-
LISTENER = "127.0.0.1", 54323
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@contextmanager
|
|
15
|
-
def server(handler):
|
|
16
|
-
server = gevent.server.StreamServer(LISTENER, handle=handler)
|
|
17
|
-
server.start()
|
|
18
|
-
try:
|
|
19
|
-
yield
|
|
20
|
-
finally:
|
|
21
|
-
server.stop()
|
|
7
|
+
from geventhttpclient.client import CRLF, HTTPClient
|
|
8
|
+
from geventhttpclient.tests.conftest import LISTENER, server
|
|
22
9
|
|
|
23
10
|
|
|
24
11
|
def wrong_response_status_line(sock, addr):
|