Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f6bb30367 | |||
| ef9bac80ff | |||
| 9c78701038 | |||
| 26fd9409a7 |
12
changelog.md
12
changelog.md
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-17 - 25.11.22 - fix(rustproxy-http)
|
||||
reuse healthy HTTP/2 upstream connections after requests with bodies
|
||||
|
||||
- Registers successful HTTP/2 connections in the pool regardless of whether the proxied request included a body
|
||||
- Continues to avoid pooling upstream connections that returned 502 Bad Gateway responses
|
||||
|
||||
## 2026-03-17 - 25.11.21 - fix(rustproxy-http)
|
||||
reuse pooled HTTP/2 connections for requests with and without bodies
|
||||
|
||||
- remove the bodyless-request restriction from HTTP/2 pool checkout
|
||||
- always return successful HTTP/2 senders to the connection pool after requests
|
||||
|
||||
## 2026-03-17 - 25.11.20 - fix(rustproxy-http)
|
||||
avoid downgrading cached backend protocol on H2 stream errors
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartproxy",
|
||||
"version": "25.11.20",
|
||||
"version": "25.11.22",
|
||||
"private": false,
|
||||
"description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
|
||||
"main": "dist_ts/index.js",
|
||||
|
||||
@@ -677,20 +677,10 @@ impl HttpProxyService {
|
||||
h2: use_h2,
|
||||
};
|
||||
|
||||
// H2 pool checkout — only for bodyless requests (GET/HEAD/DELETE).
|
||||
//
|
||||
// WORKAROUND: Requests with bodies (POST/PUT uploads) always get fresh H2
|
||||
// connections. Reusing a pooled H2 connection after a large upload can stall
|
||||
// forever due to depleted connection-level flow control windows. The h2 crate
|
||||
// has no stall/timeout detection (https://github.com/hyperium/hyper/issues/2899),
|
||||
// and Go/nginx HTTP/2 servers have known issues with connection-level window
|
||||
// replenishment after large transfers (https://github.com/golang/go/issues/16481,
|
||||
// https://github.com/golang/go/issues/56558). A fresh connection guarantees
|
||||
// clean flow control state. The overhead is ~3-5ms for TLS+H2 handshake.
|
||||
//
|
||||
// TODO: Revisit once h2 crate adds flow control stall detection, or once
|
||||
// Go/nginx H2 connection-level window handling is confirmed reliable.
|
||||
if use_h2 && body.is_end_stream() {
|
||||
// H2 pool checkout — reuse pooled connections for all requests.
|
||||
// The h2 crate properly replenishes connection-level flow control
|
||||
// windows via release_capacity() as data is consumed.
|
||||
if use_h2 {
|
||||
if let Some((mut sender, age)) = self.connection_pool.checkout_h2(&pool_key) {
|
||||
match tokio::time::timeout(
|
||||
std::time::Duration::from_millis(500),
|
||||
@@ -1048,12 +1038,9 @@ impl HttpProxyService {
|
||||
});
|
||||
}
|
||||
|
||||
// Only pool the H2 connection if the request had no body.
|
||||
// Requests with bodies (uploads) deplete connection-level flow control windows.
|
||||
let request_had_body = !body.is_end_stream();
|
||||
let sender_for_pool = sender.clone();
|
||||
let result = self.forward_h2_with_sender(sender, parts, body, upstream_headers, upstream_path, route, route_id, source_ip, Some(pool_key), domain, conn_activity).await;
|
||||
if !request_had_body && matches!(&result, Ok(ref resp) if resp.status() != StatusCode::BAD_GATEWAY) {
|
||||
if matches!(&result, Ok(ref resp) if resp.status() != StatusCode::BAD_GATEWAY) {
|
||||
let g = self.connection_pool.register_h2(pool_key.clone(), sender_for_pool);
|
||||
gen_holder.store(g, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
@@ -1379,12 +1366,8 @@ impl HttpProxyService {
|
||||
|
||||
match sender.send_request(upstream_req).await {
|
||||
Ok(upstream_response) => {
|
||||
// Only pool after bodyless requests — uploads deplete connection-level
|
||||
// flow control windows (see comment at pool checkout above).
|
||||
if retry_state.is_some() {
|
||||
let g = self.connection_pool.register_h2(pool_key.clone(), sender);
|
||||
gen_holder.store(g, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
let g = self.connection_pool.register_h2(pool_key.clone(), sender);
|
||||
gen_holder.store(g, std::sync::atomic::Ordering::Relaxed);
|
||||
self.build_streaming_response(upstream_response, route, route_id, source_ip, conn_activity).await
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartproxy',
|
||||
version: '25.11.20',
|
||||
version: '25.11.22',
|
||||
description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user