geventhttpclient 2.3.7__tar.gz → 2.3.8__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.3.7/src/geventhttpclient.egg-info → geventhttpclient-2.3.8}/PKG-INFO +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/pyproject.toml +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/__init__.py +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/connectionpool.py +45 -8
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8/src/geventhttpclient.egg-info}/PKG-INFO +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_client.py +31 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/LICENSE-MIT +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/MANIFEST.in +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/README.md +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/ext/Python_compat.h +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/ext/_parser.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/llhttp/LICENSE-MIT +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/llhttp/include/llhttp.h +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/llhttp/src/api.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/llhttp/src/http.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/llhttp/src/llhttp.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/setup.cfg +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/setup.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/api.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/client.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/header.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/httplib.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/httplib2.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/requests.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/response.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/url.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient/useragent.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/SOURCES.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/dependency_links.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/requires.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/top_level.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_headers.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_httplib.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_httplib2.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_keep_alive.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_network_failures.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_no_module_ssl.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_parser.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_requests.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_ssl.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_url.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/tests/test_useragent.py +0 -0
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "geventhttpclient"
|
|
8
|
-
version = "2.3.
|
|
8
|
+
version = "2.3.8" # don't 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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# package
|
|
2
2
|
|
|
3
|
-
__version__ = "2.3.
|
|
3
|
+
__version__ = "2.3.8" # don't forget to update version in pyproject.toml as well
|
|
4
4
|
|
|
5
5
|
from geventhttpclient.api import delete, get, head, options, patch, post, put, request
|
|
6
6
|
from geventhttpclient.client import HTTPClient
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import socket
|
|
2
3
|
|
|
3
4
|
import gevent.queue
|
|
4
5
|
import gevent.socket
|
|
@@ -125,7 +126,7 @@ class ConnectionPool:
|
|
|
125
126
|
if first_error:
|
|
126
127
|
raise first_error
|
|
127
128
|
else:
|
|
128
|
-
raise RuntimeError(f"Cannot resolve {self.
|
|
129
|
+
raise RuntimeError(f"Cannot resolve {self._connection_host}:{self._connection_port}")
|
|
129
130
|
|
|
130
131
|
def after_connect(self, sock):
|
|
131
132
|
pass
|
|
@@ -149,19 +150,55 @@ class ConnectionPool:
|
|
|
149
150
|
if not parts or parts[1] != b"200":
|
|
150
151
|
raise RuntimeError(f"Error response from Proxy server : {resp}")
|
|
151
152
|
|
|
153
|
+
def _is_socket_alive(self, sock):
|
|
154
|
+
"""check if a socket is still connected and alive.
|
|
155
|
+
|
|
156
|
+
uses MSG_PEEK | MSG_DONTWAIT to check for eof without consuming data,
|
|
157
|
+
and without blocking.
|
|
158
|
+
|
|
159
|
+
returns false if connection is closed or broken.
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
raw_sock = getattr(sock, "_sock", sock)
|
|
163
|
+
if raw_sock.fileno() < 0:
|
|
164
|
+
return False
|
|
165
|
+
data = raw_sock.recv(1, socket.MSG_PEEK | socket.MSG_DONTWAIT)
|
|
166
|
+
if data == b"":
|
|
167
|
+
return False
|
|
168
|
+
return True
|
|
169
|
+
except BlockingIOError:
|
|
170
|
+
return True
|
|
171
|
+
except OSError:
|
|
172
|
+
return False
|
|
173
|
+
|
|
152
174
|
def get_socket(self):
|
|
153
175
|
"""get a socket from the pool. This blocks until one is available."""
|
|
154
176
|
self._semaphore.acquire()
|
|
155
177
|
if self._closed:
|
|
156
178
|
raise RuntimeError("connection pool closed")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
179
|
+
|
|
180
|
+
# Try to get a valid connection from the pool
|
|
181
|
+
while not self._socket_queue.empty():
|
|
160
182
|
try:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
sock = self._socket_queue.get(block=False)
|
|
184
|
+
if self._is_socket_alive(sock):
|
|
185
|
+
# Connection is still alive, return it
|
|
186
|
+
return sock
|
|
187
|
+
else:
|
|
188
|
+
# Connection is dead, close it and try next
|
|
189
|
+
try:
|
|
190
|
+
sock.close()
|
|
191
|
+
except: # noqa
|
|
192
|
+
pass
|
|
193
|
+
except gevent.queue.Empty:
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
# No valid connections in pool, create a new one
|
|
197
|
+
try:
|
|
198
|
+
return self._create_socket()
|
|
199
|
+
except: # noqa
|
|
200
|
+
self._semaphore.release()
|
|
201
|
+
raise
|
|
165
202
|
|
|
166
203
|
def return_socket(self, sock):
|
|
167
204
|
"""return a socket to the pool."""
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import socket
|
|
2
3
|
|
|
3
4
|
import gevent.pool
|
|
4
5
|
import gevent.queue
|
|
@@ -7,6 +8,7 @@ import pytest
|
|
|
7
8
|
|
|
8
9
|
from geventhttpclient import __version__
|
|
9
10
|
from geventhttpclient.client import METHOD_GET, HTTPClient
|
|
11
|
+
from geventhttpclient.connectionpool import ConnectionPool
|
|
10
12
|
from tests.common import HTTPBIN_HOST, LISTENER, check_upload, server, wsgiserver
|
|
11
13
|
|
|
12
14
|
|
|
@@ -350,3 +352,32 @@ def test_multi_queries_greenlet_safe():
|
|
|
350
352
|
assert count == 5
|
|
351
353
|
# ensure at least 3 of requests got 200
|
|
352
354
|
assert ok_count >= 3
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
class TestIsSocketAlive:
|
|
358
|
+
"""Unit tests for ConnectionPool._is_socket_alive()"""
|
|
359
|
+
|
|
360
|
+
def test_closed_socket_returns_false(self):
|
|
361
|
+
sock = socket.socket()
|
|
362
|
+
sock.close()
|
|
363
|
+
pool = ConnectionPool("127.0.0.1", 80, "127.0.0.1", 80)
|
|
364
|
+
assert sock.fileno() < 0
|
|
365
|
+
assert pool._is_socket_alive(sock) is False
|
|
366
|
+
|
|
367
|
+
def test_healthy_socket_returns_true(self):
|
|
368
|
+
s1, s2 = socket.socketpair()
|
|
369
|
+
try:
|
|
370
|
+
pool = ConnectionPool("127.0.0.1", 80, "127.0.0.1", 80)
|
|
371
|
+
assert pool._is_socket_alive(s1) is True
|
|
372
|
+
finally:
|
|
373
|
+
s1.close()
|
|
374
|
+
s2.close()
|
|
375
|
+
|
|
376
|
+
def test_peer_orderly_close_returns_false(self):
|
|
377
|
+
s1, s2 = socket.socketpair()
|
|
378
|
+
try:
|
|
379
|
+
s2.close()
|
|
380
|
+
pool = ConnectionPool("127.0.0.1", 80, "127.0.0.1", 80)
|
|
381
|
+
assert pool._is_socket_alive(s1) is False
|
|
382
|
+
finally:
|
|
383
|
+
s1.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/requires.txt
RENAMED
|
File without changes
|
{geventhttpclient-2.3.7 → geventhttpclient-2.3.8}/src/geventhttpclient.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|