geventhttpclient 2.3.7__tar.gz → 2.3.9__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.9}/PKG-INFO +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/pyproject.toml +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/__init__.py +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/connectionpool.py +48 -8
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9/src/geventhttpclient.egg-info}/PKG-INFO +1 -1
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_client.py +31 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/LICENSE-MIT +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/MANIFEST.in +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/README.md +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/ext/Python_compat.h +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/ext/_parser.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/llhttp/LICENSE-MIT +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/llhttp/include/llhttp.h +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/llhttp/src/api.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/llhttp/src/http.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/llhttp/src/llhttp.c +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/setup.cfg +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/setup.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/api.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/client.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/header.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/httplib.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/httplib2.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/requests.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/response.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/url.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient/useragent.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient.egg-info/SOURCES.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient.egg-info/dependency_links.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient.egg-info/requires.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient.egg-info/top_level.txt +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_headers.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_httplib.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_httplib2.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_keep_alive.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_network_failures.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_no_module_ssl.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_parser.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_requests.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_ssl.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/tests/test_url.py +0 -0
- {geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/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.9" # 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.9" # 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,6 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import socket
|
|
3
|
+
import select
|
|
2
4
|
|
|
3
5
|
import gevent.queue
|
|
4
6
|
import gevent.socket
|
|
@@ -125,7 +127,7 @@ class ConnectionPool:
|
|
|
125
127
|
if first_error:
|
|
126
128
|
raise first_error
|
|
127
129
|
else:
|
|
128
|
-
raise RuntimeError(f"Cannot resolve {self.
|
|
130
|
+
raise RuntimeError(f"Cannot resolve {self._connection_host}:{self._connection_port}")
|
|
129
131
|
|
|
130
132
|
def after_connect(self, sock):
|
|
131
133
|
pass
|
|
@@ -149,19 +151,57 @@ class ConnectionPool:
|
|
|
149
151
|
if not parts or parts[1] != b"200":
|
|
150
152
|
raise RuntimeError(f"Error response from Proxy server : {resp}")
|
|
151
153
|
|
|
154
|
+
def _is_socket_alive(self, sock):
|
|
155
|
+
"""Check if a socket is still connected and alive.
|
|
156
|
+
|
|
157
|
+
Uses select() to check if socket is readable. An idle keep-alive socket
|
|
158
|
+
should NOT be readable.
|
|
159
|
+
|
|
160
|
+
Returns False if connection is closed or broken.
|
|
161
|
+
"""
|
|
162
|
+
if sock is None:
|
|
163
|
+
return False
|
|
164
|
+
try:
|
|
165
|
+
# Proactive check: A closed socket has a fileno of -1.
|
|
166
|
+
if sock.fileno() < 0:
|
|
167
|
+
return False
|
|
168
|
+
# If the socket is readable while idle, it's either a FIN or dirty.
|
|
169
|
+
ready_to_read, _, _ = select.select([sock], [], [], 0.0)
|
|
170
|
+
if ready_to_read:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
return True
|
|
174
|
+
except (OSError, ValueError, socket.error):
|
|
175
|
+
return False
|
|
176
|
+
|
|
152
177
|
def get_socket(self):
|
|
153
178
|
"""get a socket from the pool. This blocks until one is available."""
|
|
154
179
|
self._semaphore.acquire()
|
|
155
180
|
if self._closed:
|
|
156
181
|
raise RuntimeError("connection pool closed")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
182
|
+
|
|
183
|
+
# Try to get a valid connection from the pool
|
|
184
|
+
while not self._socket_queue.empty():
|
|
160
185
|
try:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
sock = self._socket_queue.get(block=False)
|
|
187
|
+
if self._is_socket_alive(sock):
|
|
188
|
+
# Connection is still alive, return it
|
|
189
|
+
return sock
|
|
190
|
+
else:
|
|
191
|
+
# Connection is dead, close it and try next
|
|
192
|
+
try:
|
|
193
|
+
sock.close()
|
|
194
|
+
except: # noqa
|
|
195
|
+
pass
|
|
196
|
+
except gevent.queue.Empty:
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
# No valid connections in pool, create a new one
|
|
200
|
+
try:
|
|
201
|
+
return self._create_socket()
|
|
202
|
+
except: # noqa
|
|
203
|
+
self._semaphore.release()
|
|
204
|
+
raise
|
|
165
205
|
|
|
166
206
|
def return_socket(self, sock):
|
|
167
207
|
"""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.9}/src/geventhttpclient.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/src/geventhttpclient.egg-info/requires.txt
RENAMED
|
File without changes
|
{geventhttpclient-2.3.7 → geventhttpclient-2.3.9}/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
|