Skip to content

Commit 1348489

Browse files
Add method to disable keepalive if downstream is unfinished
This change adds a method for H1 servers to disable downstream keepalive upon sending the response if the downstream is unfinished e.g. to avoid needing to drain the body.
1 parent 337f878 commit 1348489

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

.bleep

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
bb2739d21ee0ff8c345a0c03c39f888cb087f311
1+
9c4fb3cdd246164ff27d0d1b8c4f5775cef8f1fc

pingora-core/src/protocols/http/server.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,17 @@ impl Session {
273273
}
274274
}
275275

276+
/// Sets whether keepalive should be disabled if response is written prior to
277+
/// downstream body finishing.
278+
///
279+
/// This is a noop for h2.
280+
pub fn set_close_on_response_before_downstream_finish(&mut self, close: bool) {
281+
match self {
282+
Self::H1(s) => s.set_close_on_response_before_downstream_finish(close),
283+
Self::H2(_) => {} // always ignored
284+
}
285+
}
286+
276287
/// Return a digest of the request including the method, path and Host header
277288
// TODO: make this use a `Formatter`
278289
pub fn request_summary(&self) -> String {

pingora-core/src/protocols/http/v1/server.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ pub struct HttpSession {
7979
min_send_rate: Option<usize>,
8080
/// When this is enabled informational response headers will not be proxied downstream
8181
ignore_info_resp: bool,
82+
/// Disable keepalive if response is sent before downstream body is finished
83+
close_on_response_before_downstream_finish: bool,
8284
}
8385

8486
impl HttpSession {
@@ -116,6 +118,7 @@ impl HttpSession {
116118
digest,
117119
min_send_rate: None,
118120
ignore_info_resp: false,
121+
close_on_response_before_downstream_finish: false,
119122
}
120123
}
121124

@@ -469,6 +472,11 @@ impl HttpSession {
469472
}
470473
}
471474

475+
if self.close_on_response_before_downstream_finish && !self.is_body_done() {
476+
debug!("set connection close before downstream finish");
477+
self.set_keepalive(None);
478+
}
479+
472480
// no need to add these headers to 1xx responses
473481
if !header.status.is_informational() && self.update_resp_headers {
474482
/* update headers */
@@ -946,6 +954,14 @@ impl HttpSession {
946954
self.ignore_info_resp = ignore;
947955
}
948956

957+
/// Sets whether keepalive should be disabled if response is written prior to
958+
/// downstream body finishing.
959+
///
960+
/// This may be set to avoid draining downstream if the body is no longer necessary.
961+
pub fn set_close_on_response_before_downstream_finish(&mut self, close: bool) {
962+
self.close_on_response_before_downstream_finish = close;
963+
}
964+
949965
/// Return the [Digest] of the connection.
950966
pub fn digest(&self) -> &Digest {
951967
&self.digest

pingora-proxy/tests/test_upstream.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ async fn test_duplex() {
4747
.send()
4848
.await
4949
.unwrap();
50+
let headers = res.headers();
51+
assert_eq!(headers["Connection"], "keep-alive");
5052
assert_eq!(res.status(), StatusCode::OK);
5153
let body = res.text().await.unwrap();
5254
assert_eq!(body.len(), 64 * 5);
@@ -110,6 +112,25 @@ async fn test_upload() {
110112
assert_eq!(body.len(), 64 * 5);
111113
}
112114

115+
#[tokio::test]
116+
async fn test_close_on_response_before_downstream_finish() {
117+
init();
118+
let client = reqwest::Client::new();
119+
let res = client
120+
.post("http://127.0.0.1:6147/test2")
121+
.header("x-close-on-response-before-downstream-finish", "1")
122+
.body("b".repeat(15 * 1024 * 1024)) // 15 MB upload
123+
.timeout(Duration::from_secs(5))
124+
.send()
125+
.await
126+
.unwrap();
127+
assert_eq!(res.status(), StatusCode::OK);
128+
let headers = res.headers();
129+
assert_eq!(headers["Connection"], "close");
130+
let body = res.text().await.unwrap();
131+
assert_eq!(body.len(), 11);
132+
}
133+
113134
#[tokio::test]
114135
async fn test_ws_server_ends_conn() {
115136
init();

pingora-proxy/tests/utils/server_utils.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ impl ProxyHttp for ExampleProxyHttp {
249249
.get("x-min-rate")
250250
.and_then(|v| v.to_str().ok().and_then(|v| v.parse().ok()));
251251

252+
let close_on_response_before_downstream_finish = req
253+
.headers
254+
.get("x-close-on-response-before-downstream-finish")
255+
.is_some();
256+
252257
let downstream_compression = req.headers.get("x-downstream-compression").is_some();
253258
if !downstream_compression {
254259
// enable upstream compression for all requests by default
@@ -263,6 +268,9 @@ impl ProxyHttp for ExampleProxyHttp {
263268

264269
session.set_min_send_rate(min_rate);
265270
session.set_write_timeout(write_timeout.map(Duration::from_secs));
271+
session.set_close_on_response_before_downstream_finish(
272+
close_on_response_before_downstream_finish,
273+
);
266274

267275
Ok(false)
268276
}

0 commit comments

Comments
 (0)