http_client_request 0.0.7__tar.gz → 0.0.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.
- {http_client_request-0.0.7 → http_client_request-0.0.9}/PKG-INFO +5 -5
- {http_client_request-0.0.7 → http_client_request-0.0.9}/http_client_request/__init__.py +91 -14
- {http_client_request-0.0.7 → http_client_request-0.0.9}/pyproject.toml +5 -5
- {http_client_request-0.0.7 → http_client_request-0.0.9}/http_client_request/py.typed +0 -0
- {http_client_request-0.0.7 → http_client_request-0.0.9}/readme.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: http_client_request
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.0.9
|
|
4
|
+
Summary: http.client request extension.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/python-modules/tree/main/http_client_request
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: http.client,request
|
|
@@ -22,10 +22,10 @@ Classifier: Topic :: Software Development :: Libraries
|
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Dist: http_response (>=0.0.9)
|
|
24
24
|
Requires-Dist: python-argtools (>=0.0.2)
|
|
25
|
-
Requires-Dist: python-cookietools (>=0.1.
|
|
25
|
+
Requires-Dist: python-cookietools (>=0.1.4)
|
|
26
26
|
Requires-Dist: python-dicttools (>=0.0.4)
|
|
27
|
-
Requires-Dist: python-filewrap (>=0.2.
|
|
28
|
-
Requires-Dist: python-http_request (>=0.1.
|
|
27
|
+
Requires-Dist: python-filewrap (>=0.2.9)
|
|
28
|
+
Requires-Dist: python-http_request (>=0.1.6)
|
|
29
29
|
Requires-Dist: python-property (>=0.0.3)
|
|
30
30
|
Requires-Dist: python-undefined (>=0.0.3)
|
|
31
31
|
Requires-Dist: urllib3
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
7
|
-
__version__ = (0, 0,
|
|
7
|
+
__version__ = (0, 0, 9)
|
|
8
8
|
__all__ = [
|
|
9
9
|
"CONNECTION_POOL", "HTTPConnection", "HTTPSConnection", "HTTPResponse",
|
|
10
10
|
"ConnectionPool", "request",
|
|
@@ -24,8 +24,10 @@ from http.cookies import BaseCookie
|
|
|
24
24
|
from io import BufferedReader
|
|
25
25
|
from inspect import signature
|
|
26
26
|
from os import PathLike
|
|
27
|
+
from platform import system
|
|
27
28
|
from select import select
|
|
28
29
|
from socket import socket as Socket
|
|
30
|
+
from ssl import SSLWantReadError
|
|
29
31
|
from types import EllipsisType
|
|
30
32
|
from typing import cast, overload, Any, Final, Literal
|
|
31
33
|
from urllib.error import HTTPError
|
|
@@ -73,6 +75,68 @@ def sock_buf_readable(sock: Socket, /) -> bool:
|
|
|
73
75
|
return bool(rlist)
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
def set_keepalive_linux(
|
|
79
|
+
sock: Socket,
|
|
80
|
+
after_idle_sec: int = 1,
|
|
81
|
+
interval_sec: int = 5,
|
|
82
|
+
max_fails: int = 5,
|
|
83
|
+
):
|
|
84
|
+
"""Set TCP keepalive on an open socket on Linux.
|
|
85
|
+
"""
|
|
86
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
87
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec) # type: ignore
|
|
88
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
|
|
89
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def set_keepalive_osx(
|
|
93
|
+
sock: Socket,
|
|
94
|
+
after_idle_sec: int = 1,
|
|
95
|
+
interval_sec: int = 5,
|
|
96
|
+
max_fails: int = 5,
|
|
97
|
+
):
|
|
98
|
+
"""Set TCP keepalive on an open socket on MaxOSX.
|
|
99
|
+
"""
|
|
100
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
101
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, after_idle_sec) # type: ignore
|
|
102
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
|
|
103
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def set_keepalive_win(
|
|
107
|
+
sock: Socket,
|
|
108
|
+
after_idle_sec: int = 1,
|
|
109
|
+
interval_sec: int = 5,
|
|
110
|
+
max_fails: int = 5,
|
|
111
|
+
):
|
|
112
|
+
"""Set TCP keepalive on an open socket on Windows.
|
|
113
|
+
"""
|
|
114
|
+
sock.ioctl(socket.SIO_KEEPALIVE_VALS, (1, after_idle_sec * 1000, interval_sec * 1000)) # type: ignore
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def set_keepalive(
|
|
118
|
+
sock: Socket,
|
|
119
|
+
after_idle_sec: int = 1,
|
|
120
|
+
interval_sec: int = 3,
|
|
121
|
+
max_fails: int = 5,
|
|
122
|
+
):
|
|
123
|
+
"""Set TCP keepalive on an open socket on multiple platforms.
|
|
124
|
+
|
|
125
|
+
It activates after `after_idle_sec` second of idleness,
|
|
126
|
+
then sends a keepalive ping once every `interval_sec` seconds,
|
|
127
|
+
and closes the connection after `max_fails` failed ping (max_fails), or 15 seconds.
|
|
128
|
+
"""
|
|
129
|
+
platform = system()
|
|
130
|
+
match platform:
|
|
131
|
+
case "Darwin":
|
|
132
|
+
return set_keepalive_osx(sock, after_idle_sec, interval_sec, max_fails)
|
|
133
|
+
case "Windows":
|
|
134
|
+
return set_keepalive_win(sock, after_idle_sec, interval_sec, max_fails)
|
|
135
|
+
case "Linux":
|
|
136
|
+
return set_keepalive_linux(sock, after_idle_sec, interval_sec, max_fails)
|
|
137
|
+
raise RuntimeError(f"unsupport platform {platform!r}")
|
|
138
|
+
|
|
139
|
+
|
|
76
140
|
try:
|
|
77
141
|
from fcntl import ioctl
|
|
78
142
|
from termios import FIONREAD
|
|
@@ -108,11 +172,17 @@ class HTTPResponse(BaseHTTPResponse):
|
|
|
108
172
|
def buffer_size(self, /) -> int:
|
|
109
173
|
fp = self._fp
|
|
110
174
|
sock = fp.raw._sock
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
175
|
+
sock.setblocking(False)
|
|
176
|
+
try:
|
|
177
|
+
cache_size = len(fp.peek() or b"")
|
|
178
|
+
while True:
|
|
179
|
+
buffer_size = cache_size + sock_bufsize(sock)
|
|
180
|
+
if cache_size == (cache_size := len(fp.peek() or b"")):
|
|
181
|
+
return buffer_size
|
|
182
|
+
except (BlockingIOError, SSLWantReadError):
|
|
183
|
+
return 0
|
|
184
|
+
finally:
|
|
185
|
+
sock.setblocking(True)
|
|
116
186
|
|
|
117
187
|
@property
|
|
118
188
|
def unbuffer_size(self, /) -> int:
|
|
@@ -211,7 +281,11 @@ class HTTPConnection(BaseHTTPConnection):
|
|
|
211
281
|
|
|
212
282
|
@property
|
|
213
283
|
def response(self, /) -> None | HTTPResponse:
|
|
214
|
-
return self.
|
|
284
|
+
return self._HTTPConnection__response # type: ignore
|
|
285
|
+
|
|
286
|
+
def connect(self, /):
|
|
287
|
+
super().connect()
|
|
288
|
+
set_keepalive(self.sock)
|
|
215
289
|
|
|
216
290
|
def getresponse(self, /) -> HTTPResponse:
|
|
217
291
|
return cast(HTTPResponse, super().getresponse())
|
|
@@ -232,7 +306,11 @@ class HTTPSConnection(BaseHTTPSConnection):
|
|
|
232
306
|
|
|
233
307
|
@property
|
|
234
308
|
def response(self, /) -> None | HTTPResponse:
|
|
235
|
-
return self.
|
|
309
|
+
return self._HTTPConnection__response # type: ignore
|
|
310
|
+
|
|
311
|
+
def connect(self, /):
|
|
312
|
+
super().connect()
|
|
313
|
+
set_keepalive(self.sock)
|
|
236
314
|
|
|
237
315
|
def getresponse(self, /) -> HTTPResponse:
|
|
238
316
|
return cast(HTTPResponse, super().getresponse())
|
|
@@ -290,12 +368,11 @@ class ConnectionPool:
|
|
|
290
368
|
resp = con.response
|
|
291
369
|
if not sock or getattr(sock, "_closed"):
|
|
292
370
|
con.connect()
|
|
293
|
-
elif resp and 0
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
con.connect()
|
|
371
|
+
elif resp and 0 < resp.unbuffer_size <= 1024 * 1024:
|
|
372
|
+
try:
|
|
373
|
+
resp.read()
|
|
374
|
+
except (ConnectionResetError, BrokenPipeError):
|
|
375
|
+
con.connect()
|
|
299
376
|
else:
|
|
300
377
|
sock.setblocking(False)
|
|
301
378
|
try:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "http_client_request"
|
|
3
|
-
version = "0.0.
|
|
4
|
-
description = "
|
|
3
|
+
version = "0.0.9"
|
|
4
|
+
description = "http.client request extension."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
readme = "readme.md"
|
|
@@ -29,10 +29,10 @@ include = [
|
|
|
29
29
|
python = "^3.12"
|
|
30
30
|
http_response = ">=0.0.9"
|
|
31
31
|
python-argtools = ">=0.0.2"
|
|
32
|
-
python-cookietools = ">=0.1.
|
|
32
|
+
python-cookietools = ">=0.1.4"
|
|
33
33
|
python-dicttools = ">=0.0.4"
|
|
34
|
-
python-filewrap = ">=0.2.
|
|
35
|
-
python-http_request = ">=0.1.
|
|
34
|
+
python-filewrap = ">=0.2.9"
|
|
35
|
+
python-http_request = ">=0.1.6"
|
|
36
36
|
python-property = ">=0.0.3"
|
|
37
37
|
python-undefined = ">=0.0.3"
|
|
38
38
|
urllib3 = "*"
|
|
File without changes
|
|
File without changes
|