geventhttpclient 2.3.9__tar.gz → 2.4.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.
Files changed (46) hide show
  1. {geventhttpclient-2.3.9/src/geventhttpclient.egg-info → geventhttpclient-2.4.0}/PKG-INFO +72 -2
  2. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/README.md +71 -1
  3. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/ext/_parser.c +8 -9
  4. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/llhttp/include/llhttp.h +371 -23
  5. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/llhttp/src/api.c +153 -13
  6. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/llhttp/src/http.c +23 -3
  7. geventhttpclient-2.4.0/llhttp/src/llhttp.c +10279 -0
  8. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/pyproject.toml +4 -27
  9. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/setup.py +1 -2
  10. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/__init__.py +1 -1
  11. geventhttpclient-2.4.0/src/geventhttpclient/client.py +568 -0
  12. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/connectionpool.py +47 -22
  13. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/header.py +2 -8
  14. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/requests.py +3 -5
  15. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/response.py +34 -13
  16. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/url.py +1 -1
  17. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/useragent.py +26 -25
  18. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0/src/geventhttpclient.egg-info}/PKG-INFO +72 -2
  19. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient.egg-info/SOURCES.txt +2 -0
  20. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_client.py +131 -7
  21. geventhttpclient-2.4.0/tests/test_continue.py +135 -0
  22. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_headers.py +2 -2
  23. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_keep_alive.py +4 -1
  24. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_no_module_ssl.py +4 -4
  25. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_parser.py +25 -2
  26. geventhttpclient-2.4.0/tests/test_proxy.py +172 -0
  27. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_ssl.py +9 -3
  28. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_useragent.py +25 -3
  29. geventhttpclient-2.3.9/llhttp/src/llhttp.c +0 -15954
  30. geventhttpclient-2.3.9/src/geventhttpclient/client.py +0 -318
  31. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/LICENSE-MIT +0 -0
  32. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/MANIFEST.in +0 -0
  33. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/ext/Python_compat.h +0 -0
  34. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/llhttp/LICENSE-MIT +0 -0
  35. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/setup.cfg +0 -0
  36. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/api.py +0 -0
  37. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/httplib.py +0 -0
  38. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient/httplib2.py +0 -0
  39. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient.egg-info/dependency_links.txt +0 -0
  40. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient.egg-info/requires.txt +0 -0
  41. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/src/geventhttpclient.egg-info/top_level.txt +0 -0
  42. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_httplib.py +0 -0
  43. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_httplib2.py +0 -0
  44. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_network_failures.py +0 -0
  45. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_requests.py +0 -0
  46. {geventhttpclient-2.3.9 → geventhttpclient-2.4.0}/tests/test_url.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geventhttpclient
3
- Version: 2.3.9
3
+ Version: 2.4.0
4
4
  Summary: HTTP client library for gevent
5
5
  Author-email: Antonin Amand <antonin.amand@gmail.com>
6
6
  License-Expression: MIT
@@ -76,10 +76,12 @@ simple use-cases, it can serve as a drop-in replacement.
76
76
 
77
77
  ```python
78
78
  import geventhttpclient as requests
79
+
79
80
  requests.get("https://github.com").text
80
81
  requests.post("http://httpbingo.org/post", data="asdfasd").json()
81
82
 
82
83
  from geventhttpclient import Session
84
+
83
85
  s = Session()
84
86
  s.get("http://httpbingo.org/headers").json()
85
87
  s.get("https://github.com").content
@@ -117,6 +119,7 @@ patch before you import or the `super()` calls will fail.
117
119
 
118
120
  ```python
119
121
  import geventhttpclient.httplib
122
+
120
123
  geventhttpclient.httplib.patch()
121
124
 
122
125
  import httplib2
@@ -152,6 +155,7 @@ assert response.status_code == 200
152
155
  # the json parser as it's being read.
153
156
  data = json.load(response)["data"]
154
157
 
158
+
155
159
  def print_friend_username(client, friend_id):
156
160
  friend_url = URL(f"/{friend_id}", params={"access_token": TOKEN})
157
161
  # the greenlet will block until a connection is available
@@ -163,6 +167,7 @@ def print_friend_username(client, friend_id):
163
167
  else:
164
168
  print(f"{friend['name']} has no username.")
165
169
 
170
+
166
171
  # allow to run 20 greenlet at a time, this is more than concurrency
167
172
  # of the http client but isn't a problem since the client has its own
168
173
  # connection pool.
@@ -192,7 +197,7 @@ client = HTTPClient.from_url(url)
192
197
  response = client.get(url.query_string)
193
198
  assert response.status_code == 200
194
199
 
195
- CHUNK_SIZE = 1024 * 16 # 16KB
200
+ CHUNK_SIZE = 1024 * 16 # 16KB
196
201
  with open("/tmp/100.dat", "w") as f:
197
202
  data = response.read(CHUNK_SIZE)
198
203
  while data:
@@ -200,6 +205,71 @@ with open("/tmp/100.dat", "w") as f:
200
205
  data = response.read(CHUNK_SIZE)
201
206
  ```
202
207
 
208
+ ## Chunked request bodies
209
+
210
+ Requests with `Transfer-Encoding: chunked` are supported. Add the header
211
+ explicitly to have the body chunk-encoded automatically:
212
+
213
+ ```python
214
+ client = HTTPClient.from_url(url)
215
+ response = client.post(
216
+ "/upload",
217
+ body=data,
218
+ headers={"Transfer-Encoding": "chunked"},
219
+ )
220
+ ```
221
+
222
+ Bodies of unknown length are chunk-encoded automatically, so generators and
223
+ iterables of bytes work as request bodies as well:
224
+
225
+ ```python
226
+ def generate_data():
227
+ for i in range(100):
228
+ yield b"some data block\n"
229
+
230
+
231
+ response = client.post("/upload", body=generate_data())
232
+ ```
233
+
234
+ Chunked transfer encoding requires HTTP/1.1; a `ValueError` is raised for
235
+ HTTP/1.0 requests. A user-provided `Content-Length` header is dropped when
236
+ chunked encoding is used.
237
+
238
+ ## Proxy support
239
+
240
+ `HTTPClient` can route requests through an HTTP proxy. Plain HTTP requests are
241
+ forwarded with an absolute request URI, HTTPS requests are tunneled with
242
+ `CONNECT`, optionally with `Basic` proxy authentication:
243
+
244
+ ```python
245
+ client = HTTPClient(
246
+ "target.example.com",
247
+ port=443,
248
+ ssl=True,
249
+ proxy_host="proxy.example.com",
250
+ proxy_port=3128,
251
+ proxy_user="user",
252
+ proxy_password="pass",
253
+ )
254
+ ```
255
+
256
+ The same keyword arguments are accepted by `UserAgent` and `HTTPClientPool`.
257
+
258
+ ## Expect: 100-continue
259
+
260
+ Add an `Expect: 100-continue` header to let the server reject a request with a
261
+ large body before the body is sent. The client sends the headers first, waits
262
+ for an interim `100 Continue` response before sending the body, and returns
263
+ the final response directly if the server rejects the request:
264
+
265
+ ```python
266
+ client = HTTPClient.from_url(url)
267
+ response = client.post("/upload", body=data, headers={"Expect": "100-continue"})
268
+ ```
269
+
270
+ Note that servers not answering an `Expect: 100-continue` request block until
271
+ the network timeout is reached.
272
+
203
273
  ## Benchmarks
204
274
 
205
275
  The benchmark runs 10000 `GET` requests against a local nginx server in the default
@@ -33,10 +33,12 @@ simple use-cases, it can serve as a drop-in replacement.
33
33
 
34
34
  ```python
35
35
  import geventhttpclient as requests
36
+
36
37
  requests.get("https://github.com").text
37
38
  requests.post("http://httpbingo.org/post", data="asdfasd").json()
38
39
 
39
40
  from geventhttpclient import Session
41
+
40
42
  s = Session()
41
43
  s.get("http://httpbingo.org/headers").json()
42
44
  s.get("https://github.com").content
@@ -74,6 +76,7 @@ patch before you import or the `super()` calls will fail.
74
76
 
75
77
  ```python
76
78
  import geventhttpclient.httplib
79
+
77
80
  geventhttpclient.httplib.patch()
78
81
 
79
82
  import httplib2
@@ -109,6 +112,7 @@ assert response.status_code == 200
109
112
  # the json parser as it's being read.
110
113
  data = json.load(response)["data"]
111
114
 
115
+
112
116
  def print_friend_username(client, friend_id):
113
117
  friend_url = URL(f"/{friend_id}", params={"access_token": TOKEN})
114
118
  # the greenlet will block until a connection is available
@@ -120,6 +124,7 @@ def print_friend_username(client, friend_id):
120
124
  else:
121
125
  print(f"{friend['name']} has no username.")
122
126
 
127
+
123
128
  # allow to run 20 greenlet at a time, this is more than concurrency
124
129
  # of the http client but isn't a problem since the client has its own
125
130
  # connection pool.
@@ -149,7 +154,7 @@ client = HTTPClient.from_url(url)
149
154
  response = client.get(url.query_string)
150
155
  assert response.status_code == 200
151
156
 
152
- CHUNK_SIZE = 1024 * 16 # 16KB
157
+ CHUNK_SIZE = 1024 * 16 # 16KB
153
158
  with open("/tmp/100.dat", "w") as f:
154
159
  data = response.read(CHUNK_SIZE)
155
160
  while data:
@@ -157,6 +162,71 @@ with open("/tmp/100.dat", "w") as f:
157
162
  data = response.read(CHUNK_SIZE)
158
163
  ```
159
164
 
165
+ ## Chunked request bodies
166
+
167
+ Requests with `Transfer-Encoding: chunked` are supported. Add the header
168
+ explicitly to have the body chunk-encoded automatically:
169
+
170
+ ```python
171
+ client = HTTPClient.from_url(url)
172
+ response = client.post(
173
+ "/upload",
174
+ body=data,
175
+ headers={"Transfer-Encoding": "chunked"},
176
+ )
177
+ ```
178
+
179
+ Bodies of unknown length are chunk-encoded automatically, so generators and
180
+ iterables of bytes work as request bodies as well:
181
+
182
+ ```python
183
+ def generate_data():
184
+ for i in range(100):
185
+ yield b"some data block\n"
186
+
187
+
188
+ response = client.post("/upload", body=generate_data())
189
+ ```
190
+
191
+ Chunked transfer encoding requires HTTP/1.1; a `ValueError` is raised for
192
+ HTTP/1.0 requests. A user-provided `Content-Length` header is dropped when
193
+ chunked encoding is used.
194
+
195
+ ## Proxy support
196
+
197
+ `HTTPClient` can route requests through an HTTP proxy. Plain HTTP requests are
198
+ forwarded with an absolute request URI, HTTPS requests are tunneled with
199
+ `CONNECT`, optionally with `Basic` proxy authentication:
200
+
201
+ ```python
202
+ client = HTTPClient(
203
+ "target.example.com",
204
+ port=443,
205
+ ssl=True,
206
+ proxy_host="proxy.example.com",
207
+ proxy_port=3128,
208
+ proxy_user="user",
209
+ proxy_password="pass",
210
+ )
211
+ ```
212
+
213
+ The same keyword arguments are accepted by `UserAgent` and `HTTPClientPool`.
214
+
215
+ ## Expect: 100-continue
216
+
217
+ Add an `Expect: 100-continue` header to let the server reject a request with a
218
+ large body before the body is sent. The client sends the headers first, waits
219
+ for an interim `100 Continue` response before sending the body, and returns
220
+ the final response directly if the server rejects the request:
221
+
222
+ ```python
223
+ client = HTTPClient.from_url(url)
224
+ response = client.post("/upload", body=data, headers={"Expect": "100-continue"})
225
+ ```
226
+
227
+ Note that servers not answering an `Expect: 100-continue` request block until
228
+ the network timeout is reached.
229
+
160
230
  ## Benchmarks
161
231
 
162
232
  The benchmark runs 10000 `GET` requests against a local nginx server in the default
@@ -141,15 +141,14 @@ static int on_body(llhttp_t* parser, const char *at, size_t length)
141
141
  return fail;
142
142
  }
143
143
 
144
- static llhttp_settings_t _parser_settings = {
145
- on_message_begin,
146
- NULL, // on_url
147
- on_status,
148
- on_header_field,
149
- on_header_value,
150
- on_headers_complete,
151
- on_body,
152
- on_message_complete
144
+ static const llhttp_settings_t _parser_settings = {
145
+ .on_message_begin = on_message_begin,
146
+ .on_status = on_status,
147
+ .on_header_field = on_header_field,
148
+ .on_header_value = on_header_value,
149
+ .on_headers_complete = on_headers_complete,
150
+ .on_body = on_body,
151
+ .on_message_complete = on_message_complete,
153
152
  };
154
153
 
155
154
  static PyObject*