geventhttpclient 2.0.12__tar.gz → 2.1.0__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.0.12 → geventhttpclient-2.1.0}/MANIFEST.in +3 -4
- geventhttpclient-2.1.0/PKG-INFO +238 -0
- geventhttpclient-2.0.12/README.mdown → geventhttpclient-2.1.0/README.md +16 -8
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/llhttp/include/llhttp.h +1 -1
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/llhttp/src/llhttp.c +495 -421
- geventhttpclient-2.1.0/pyproject.toml +103 -0
- geventhttpclient-2.1.0/setup.py +20 -0
- geventhttpclient-2.1.0/src/geventhttpclient/__init__.py +7 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/client.py +65 -59
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/connectionpool.py +63 -84
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/header.py +11 -23
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/httplib.py +18 -26
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/response.py +29 -53
- geventhttpclient-2.1.0/src/geventhttpclient/tests/conftest.py +3 -0
- geventhttpclient-2.1.0/src/geventhttpclient/tests/test_client.py +368 -0
- geventhttpclient-2.1.0/src/geventhttpclient/tests/test_headers.py +197 -0
- geventhttpclient-2.1.0/src/geventhttpclient/tests/test_http_host.py +14 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_httplib.py +28 -25
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_keep_alive.py +7 -8
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_network_failures.py +60 -43
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_no_module_ssl.py +11 -17
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_parser.py +46 -32
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/test_ssl.py +68 -78
- geventhttpclient-2.1.0/src/geventhttpclient/tests/test_url.py +141 -0
- geventhttpclient-2.1.0/src/geventhttpclient/tests/test_useragent.py +249 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/url.py +72 -61
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/useragent.py +155 -175
- geventhttpclient-2.1.0/src/geventhttpclient.egg-info/PKG-INFO +238 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient.egg-info/SOURCES.txt +3 -2
- geventhttpclient-2.1.0/src/geventhttpclient.egg-info/requires.txt +15 -0
- geventhttpclient-2.0.12/PKG-INFO +0 -36
- geventhttpclient-2.0.12/pytest.ini +0 -3
- geventhttpclient-2.0.12/setup.py +0 -68
- geventhttpclient-2.0.12/src/geventhttpclient/__init__.py +0 -6
- geventhttpclient-2.0.12/src/geventhttpclient/tests/test_client.py +0 -350
- geventhttpclient-2.0.12/src/geventhttpclient/tests/test_headers.py +0 -191
- geventhttpclient-2.0.12/src/geventhttpclient/tests/test_http_host.py +0 -30
- geventhttpclient-2.0.12/src/geventhttpclient/tests/test_url.py +0 -130
- geventhttpclient-2.0.12/src/geventhttpclient/tests/test_useragent.py +0 -219
- geventhttpclient-2.0.12/src/geventhttpclient.egg-info/PKG-INFO +0 -36
- geventhttpclient-2.0.12/src/geventhttpclient.egg-info/requires.txt +0 -4
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/LICENSE.txt +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/ext/Python_compat.h +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/ext/_parser.c +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/llhttp/LICENSE-MIT +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/llhttp/src/api.c +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/llhttp/src/http.c +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/setup.cfg +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/oncert.pem +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/server.crt +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient/tests/server.key +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient.egg-info/dependency_links.txt +0 -0
- {geventhttpclient-2.0.12 → geventhttpclient-2.1.0}/src/geventhttpclient.egg-info/top_level.txt +0 -0
|
@@ -5,7 +5,6 @@ recursive-include llhttp *.h *.c LICENSE-MIT
|
|
|
5
5
|
recursive-include src/geventhttpclient/tests *
|
|
6
6
|
global-exclude __pycache__
|
|
7
7
|
global-exclude *.py[co]
|
|
8
|
-
include
|
|
9
|
-
include
|
|
10
|
-
include
|
|
11
|
-
include pytest.ini
|
|
8
|
+
include pyproject.toml
|
|
9
|
+
include LICENSE.txt
|
|
10
|
+
include README.md
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: geventhttpclient
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: HTTP client library for gevent
|
|
5
|
+
Author-email: Antonin Amand <antonin.amand@gmail.com>
|
|
6
|
+
License: Based on llhttp, copyright Fedor Indutny, 2018.
|
|
7
|
+
|
|
8
|
+
Python extension is copyright Antonin Amand <antonin.amand@gmail.com>,
|
|
9
|
+
licensed under the same terms.
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to
|
|
13
|
+
deal in the Software without restriction, including without limitation the
|
|
14
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
15
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in
|
|
19
|
+
all copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
26
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
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
|
|
33
|
+
|
|
34
|
+
Project-URL: homepage, http://github.com/geventhttpclient/geventhttpclient
|
|
35
|
+
Project-URL: issues, http://github.com/geventhttpclient/geventhttpclient/issues
|
|
36
|
+
Project-URL: download, https://pypi.org/project/geventhttpclient/#files
|
|
37
|
+
Keywords: http,gevent,client
|
|
38
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
39
|
+
Classifier: Intended Audience :: Developers
|
|
40
|
+
Classifier: Intended Audience :: Information Technology
|
|
41
|
+
Classifier: Operating System :: OS Independent
|
|
42
|
+
Classifier: Programming Language :: Python :: 3
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
47
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
48
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
49
|
+
Requires-Python: >=3.9
|
|
50
|
+
Description-Content-Type: text/markdown
|
|
51
|
+
License-File: LICENSE.txt
|
|
52
|
+
Requires-Dist: gevent
|
|
53
|
+
Requires-Dist: certifi
|
|
54
|
+
Requires-Dist: brotli
|
|
55
|
+
Requires-Dist: urllib3
|
|
56
|
+
Provides-Extra: dev
|
|
57
|
+
Requires-Dist: pytest; extra == "dev"
|
|
58
|
+
Requires-Dist: dpkt; extra == "dev"
|
|
59
|
+
Requires-Dist: requests; extra == "dev"
|
|
60
|
+
Provides-Extra: benchmarks
|
|
61
|
+
Requires-Dist: requests; extra == "benchmarks"
|
|
62
|
+
Requires-Dist: httpx; extra == "benchmarks"
|
|
63
|
+
Requires-Dist: urllib3; extra == "benchmarks"
|
|
64
|
+
Requires-Dist: httplib2; extra == "benchmarks"
|
|
65
|
+
|
|
66
|
+
[](https://github.com/geventhttpclient/geventhttpclient/actions)
|
|
67
|
+
[](https://pypi.org/project/geventhttpclient/)
|
|
68
|
+

|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
# geventhttpclient
|
|
72
|
+
|
|
73
|
+
A high performance, concurrent HTTP client library for python using
|
|
74
|
+
[gevent](http://gevent.org).
|
|
75
|
+
|
|
76
|
+
`gevent.httplib` support was removed in [gevent 1.0](https://github.com/surfly/gevent/commit/b45b83b1bc4de14e3c4859362825044b8e3df7d6
|
|
77
|
+
), **geventhttpclient** now provides that missing functionality.
|
|
78
|
+
|
|
79
|
+
**geventhttpclient** uses a fast [http parser](https://github.com/nodejs/llhttp),
|
|
80
|
+
written in C.
|
|
81
|
+
|
|
82
|
+
**geventhttpclient** has been specifically designed for high concurrency,
|
|
83
|
+
streaming and support HTTP 1.1 persistent connections. More generally it is
|
|
84
|
+
designed for efficiently pulling from REST APIs and streaming APIs
|
|
85
|
+
like Twitter's.
|
|
86
|
+
|
|
87
|
+
Safe SSL support is provided by default. **geventhttpclient** depends on
|
|
88
|
+
the certifi CA Bundle. This is the same CA Bundle which ships with the
|
|
89
|
+
Requests codebase, and is derived from Mozilla Firefox's canonical set.
|
|
90
|
+
|
|
91
|
+
As of version 2.1, only Python 3.9+ is fully supported (with prebuilt wheels).
|
|
92
|
+
|
|
93
|
+
A simple example:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
#!/usr/bin/python
|
|
97
|
+
|
|
98
|
+
from geventhttpclient import HTTPClient
|
|
99
|
+
from geventhttpclient.url import URL
|
|
100
|
+
|
|
101
|
+
url = URL('http://gevent.org/')
|
|
102
|
+
|
|
103
|
+
http = HTTPClient(url.host)
|
|
104
|
+
|
|
105
|
+
# issue a get request
|
|
106
|
+
response = http.get(url.request_uri)
|
|
107
|
+
|
|
108
|
+
# read status_code
|
|
109
|
+
response.status_code
|
|
110
|
+
|
|
111
|
+
# read response body
|
|
112
|
+
body = response.read()
|
|
113
|
+
|
|
114
|
+
# close connections
|
|
115
|
+
http.close()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## httplib compatibility and monkey patch
|
|
119
|
+
|
|
120
|
+
**geventhttpclient.httplib** module contains classes for drop in
|
|
121
|
+
replacement of httplib connection and response objects.
|
|
122
|
+
If you use httplib directly you can replace the **httplib** imports
|
|
123
|
+
by **geventhttpclient.httplib**.
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
# from httplib import HTTPConnection
|
|
127
|
+
from geventhttpclient.httplib import HTTPConnection
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If you use **httplib2**, **urllib** or **urllib2**; you can patch **httplib** to
|
|
131
|
+
use the wrappers from **geventhttpclient**.
|
|
132
|
+
For **httplib2**, make sure you patch before you import or the *super*
|
|
133
|
+
calls will fail.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
import geventhttpclient.httplib
|
|
137
|
+
geventhttpclient.httplib.patch()
|
|
138
|
+
|
|
139
|
+
import httplib2
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## High Concurrency
|
|
143
|
+
|
|
144
|
+
HTTPClient has connection pool built in and is greenlet safe by design.
|
|
145
|
+
You can use the same instance among several greenlets.
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
#!/usr/bin/env python
|
|
149
|
+
|
|
150
|
+
import gevent.pool
|
|
151
|
+
import json
|
|
152
|
+
|
|
153
|
+
from geventhttpclient import HTTPClient
|
|
154
|
+
from geventhttpclient.url import URL
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# go to http://developers.facebook.com/tools/explorer and copy the access token
|
|
158
|
+
TOKEN = '<go to http://developers.facebook.com/tools/explorer and copy the access token>'
|
|
159
|
+
|
|
160
|
+
url = URL('https://graph.facebook.com/me/friends')
|
|
161
|
+
url['access_token'] = TOKEN
|
|
162
|
+
|
|
163
|
+
# setting the concurrency to 10 allow to create 10 connections and
|
|
164
|
+
# reuse them.
|
|
165
|
+
http = HTTPClient.from_url(url, concurrency=10)
|
|
166
|
+
|
|
167
|
+
response = http.get(url.request_uri)
|
|
168
|
+
assert response.status_code == 200
|
|
169
|
+
|
|
170
|
+
# response comply to the read protocol. It passes the stream to
|
|
171
|
+
# the json parser as it's being read.
|
|
172
|
+
data = json.load(response)['data']
|
|
173
|
+
|
|
174
|
+
def print_friend_username(http, friend_id):
|
|
175
|
+
friend_url = URL('/' + str(friend_id))
|
|
176
|
+
friend_url['access_token'] = TOKEN
|
|
177
|
+
# the greenlet will block until a connection is available
|
|
178
|
+
response = http.get(friend_url.request_uri)
|
|
179
|
+
assert response.status_code == 200
|
|
180
|
+
friend = json.load(response)
|
|
181
|
+
if 'username' in friend:
|
|
182
|
+
print('%s: %s' % (friend['username'], friend['name']))
|
|
183
|
+
else:
|
|
184
|
+
print('%s has no username.' % friend['name'])
|
|
185
|
+
|
|
186
|
+
# allow to run 20 greenlet at a time, this is more than concurrency
|
|
187
|
+
# of the http client but isn't a problem since the client has its own
|
|
188
|
+
# connection pool.
|
|
189
|
+
pool = gevent.pool.Pool(20)
|
|
190
|
+
for item in data:
|
|
191
|
+
friend_id = item['id']
|
|
192
|
+
pool.spawn(print_friend_username, http, friend_id)
|
|
193
|
+
|
|
194
|
+
pool.join()
|
|
195
|
+
http.close()
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Streaming
|
|
199
|
+
|
|
200
|
+
**geventhttpclient** supports streaming.
|
|
201
|
+
Response objects have a read(N) and readline() method that read the stream
|
|
202
|
+
incrementally.
|
|
203
|
+
See *src/examples/twitter_streaming.py* for pulling twitter stream API.
|
|
204
|
+
|
|
205
|
+
Here is an example on how to download a big file chunk by chunk to save memory:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
#!/usr/bin/env python
|
|
209
|
+
|
|
210
|
+
from geventhttpclient import HTTPClient, URL
|
|
211
|
+
|
|
212
|
+
url = URL('http://127.0.0.1:80/100.dat')
|
|
213
|
+
http = HTTPClient.from_url(url)
|
|
214
|
+
response = http.get(url.query_string)
|
|
215
|
+
assert response.status_code == 200
|
|
216
|
+
|
|
217
|
+
CHUNK_SIZE = 1024 * 16 # 16KB
|
|
218
|
+
with open('/tmp/100.dat', 'w') as f:
|
|
219
|
+
data = response.read(CHUNK_SIZE)
|
|
220
|
+
while data:
|
|
221
|
+
f.write(data)
|
|
222
|
+
data = response.read(CHUNK_SIZE)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Benchmarks
|
|
226
|
+
|
|
227
|
+
The benchmark does 10000 get requests against a local nginx server in the default
|
|
228
|
+
configuration with a concurrency of 10. See `benchmarks` folder. The requests per
|
|
229
|
+
second for a couple of popular clients is given in the table below.
|
|
230
|
+
|
|
231
|
+
| HTTP Client | RPS |
|
|
232
|
+
|----------------------|--------|
|
|
233
|
+
| GeventHTTPClient | 7181.2 |
|
|
234
|
+
| Urllib3 | 2847.5 |
|
|
235
|
+
| Httpx | 1682.9 |
|
|
236
|
+
| Requests | 1186.4 |
|
|
237
|
+
|
|
238
|
+
*Linux(x86_64), Python 3.11.6 @ Intel i7-7560U*
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
[](https://github.com/geventhttpclient/geventhttpclient/actions)
|
|
2
|
+
[](https://pypi.org/project/geventhttpclient/)
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
1
6
|
# geventhttpclient
|
|
2
7
|
|
|
3
8
|
A high performance, concurrent HTTP client library for python using
|
|
@@ -18,10 +23,7 @@ Safe SSL support is provided by default. **geventhttpclient** depends on
|
|
|
18
23
|
the certifi CA Bundle. This is the same CA Bundle which ships with the
|
|
19
24
|
Requests codebase, and is derived from Mozilla Firefox's canonical set.
|
|
20
25
|
|
|
21
|
-
As of version 1
|
|
22
|
-
but Python 2.7 and 3.5 *should* work too.
|
|
23
|
-
|
|
24
|
-
Use of SSL/TLS with python 2.7.9 is not recommended and may be broken.
|
|
26
|
+
As of version 2.1, only Python 3.9+ is fully supported (with prebuilt wheels).
|
|
25
27
|
|
|
26
28
|
A simple example:
|
|
27
29
|
|
|
@@ -157,9 +159,15 @@ with open('/tmp/100.dat', 'w') as f:
|
|
|
157
159
|
|
|
158
160
|
## Benchmarks
|
|
159
161
|
|
|
160
|
-
The benchmark does
|
|
161
|
-
a concurrency of 10. See
|
|
162
|
+
The benchmark does 10000 get requests against a local nginx server in the default
|
|
163
|
+
configuration with a concurrency of 10. See `benchmarks` folder. The requests per
|
|
164
|
+
second for a couple of popular clients is given in the table below.
|
|
162
165
|
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
| HTTP Client | RPS |
|
|
167
|
+
|----------------------|--------|
|
|
168
|
+
| GeventHTTPClient | 7181.2 |
|
|
169
|
+
| Urllib3 | 2847.5 |
|
|
170
|
+
| Httpx | 1682.9 |
|
|
171
|
+
| Requests | 1186.4 |
|
|
165
172
|
|
|
173
|
+
*Linux(x86_64), Python 3.11.6 @ Intel i7-7560U*
|