Skip to content

Commit

Permalink
🔖 Release 3.2.1 (#41)
Browse files Browse the repository at this point in the history
**Fixed**
- Performance issues in HTTP/2, and HTTP/3, with or without multiplexed connections.

**Changed**
- Enforced a maximum in-flight request when using multiplexed connections. Default to 200 per connections
  so, actually 2000 per Session (_default is 10 connections_). This can be overriden in our `HTTPAdapter` for advanced users.
  • Loading branch information
Ousret authored Nov 6, 2023
1 parent 17678b8 commit 0e112b6
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 11 deletions.
10 changes: 10 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
Release History
===============

3.2.1 (2023-11-06)
------------------

**Fixed**
- Performance issues in HTTP/2, and HTTP/3, with or without multiplexed connections.

**Changed**
- Enforced a maximum in-flight request when using multiplexed connections. Default to 200 per connections
so, actually 2000 per Session (_default is 10 connections_). This can be overriden in our `HTTPAdapter` for advanced users.

3.2.0 (2023-11-05)
------------------

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
**Niquests** is a simple, yet elegant, HTTP library. It is a drop-in replacement for **Requests** that is no longer under
feature freeze.

Niquests, is the “**Safest**, **Fastest**, **Easiest**, and **Most advanced**” Python HTTP Client.
Niquests, is the “**Safest**, **Fastest<sup>*</sup>**, **Easiest**, and **Most advanced**” Python HTTP Client.

```python
>>> import niquests
Expand Down Expand Up @@ -73,4 +73,8 @@ We intend to keep it that way. As long as we can, long live Niquests!

---

<small><sup>(*)</sup> performance measured when leveraging a multiplexed connection with or without uses of any form of concurrency as of november 2023.</small>

---

[![Kenneth Reitz](https://raw.githubusercontent.com/jawah/niquests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf)
4 changes: 2 additions & 2 deletions src/niquests/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
__url__: str = "https://niquests.readthedocs.io"

__version__: str
__version__ = "3.2.0"
__version__ = "3.2.1"

__build__: int = 0x030200
__build__: int = 0x030201
__author__: str = "Kenneth Reitz"
__author_email__: str = "[email protected]"
__license__: str = "Apache-2.0"
Expand Down
35 changes: 27 additions & 8 deletions src/niquests/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def __init__(
quic_cache_layer: CacheLayerAltSvcType | None = None,
disable_http2: bool = False,
disable_http3: bool = False,
max_in_flight_multiplexed: int | None = None,
):
if isinstance(max_retries, bool):
self.max_retries: RetryType = False
Expand Down Expand Up @@ -217,7 +218,12 @@ def __init__(
self._disable_http3 = disable_http3

#: we keep a list of pending (lazy) response
self._promises: list[Response] = []
self._promises: dict[str, Response] = {}
self._max_in_flight_multiplexed = (
max_in_flight_multiplexed
if max_in_flight_multiplexed is not None
else self._pool_connections * 200
)

disabled_svn = set()

Expand Down Expand Up @@ -454,7 +460,7 @@ def build_response(
# Add new cookies from the server.
extract_cookies_to_jar(response.cookies, req, resp)
else:
self._promises.append(response)
self._promises[resp.uid] = response

# Give the Response some context.
response.request = req
Expand Down Expand Up @@ -605,6 +611,10 @@ def send(
and request.method is not None
), "Tried to send a non-initialized PreparedRequest"

# We enforce a limit to avoid burning out our connection pool.
if multiplexed and len(self._promises) >= self._max_in_flight_multiplexed:
self.gather()

try:
conn = self.get_connection(request.url, proxies)
except LocationValueError as e:
Expand Down Expand Up @@ -763,6 +773,8 @@ def _future_handler(self, response: Response, low_resp: BaseHTTPResponse) -> Non
for k, v in response._promise._parameters.items()
if k.startswith("niquests_")
}

response_promise = response._promise
del response._promise

if allow_redirects:
Expand Down Expand Up @@ -827,7 +839,7 @@ def on_post_connection(conn_info: ConnectionInfo) -> None:
for k, v in promise_ctx_backup.items():
next_promise._promise.set_parameter(k, v)

self._promises.remove(response)
del self._promises[response_promise.uid]

return
else:
Expand Down Expand Up @@ -889,7 +901,7 @@ def on_post_connection(conn_info: ConnectionInfo) -> None:
if not stream:
response.content

self._promises.remove(response)
del self._promises[response_promise.uid]

def gather(self, *responses: Response) -> None:
if not self._promises:
Expand All @@ -898,15 +910,22 @@ def gather(self, *responses: Response) -> None:
# Either we did not have a list of promises to fulfill...
if not responses:
while True:
response = None
low_resp = self.poolmanager.get_response()

if low_resp is None:
break

for response in self._promises:
if low_resp.is_from_promise(response._promise):
break
assert (
low_resp._fp is not None
and hasattr(low_resp._fp, "from_promise")
and low_resp._fp.from_promise is not None
)

response = (
self._promises[low_resp._fp.from_promise.uid]
if low_resp._fp.from_promise.uid in self._promises
else None
)

if response is None:
raise MultiplexingError(
Expand Down

0 comments on commit 0e112b6

Please sign in to comment.