Commit ac8609f6 authored by Dag Haavi Finstad's avatar Dag Haavi Finstad

Add H/2 stream timeouts

Avoid keeping H/2 streams alive indefinitely.

This adds timeouts for:
  - How long we allow a stream to wait for a WINDOW_UPDATE, subject
    to idle_send_timeout
  - Total duration after we started transmitting a response,
    subject to send_timeout

We currently do not make any attempt at sending a GOAWAY or a PING for
client inactivity. This may be something to reconsider at a later point.
parent bf1ea70e
......@@ -121,6 +121,8 @@ struct h2_req {
enum h2_stream_e state;
struct h2_sess *h2sess;
struct req *req;
double t_send;
double t_winupd;
pthread_cond_t *cond;
VTAILQ_ENTRY(h2_req) list;
int64_t t_window;
......
......@@ -254,6 +254,8 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody)
if (sendbody && req->resp_len == 0)
sendbody = 0;
r2->t_send = req->t_prev;
H2_Send_Get(req->wrk, r2->h2sess, r2);
H2_Send(req->wrk, r2, H2_F_HEADERS,
(sendbody ? 0 : H2FF_HEADERS_END_STREAM) | H2FF_HEADERS_END_HEADERS,
......
......@@ -889,6 +889,37 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2,
return (h2_tx_rst(wrk, h2, h2e));
}
static int
h2_stream_tmo(struct h2_sess *h2, const struct h2_req *r2)
{
int r = 0;
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
if (r2->t_winupd != 0 || r2->t_send != 0) {
Lck_Lock(&h2->sess->mtx);
if (r2->t_winupd != 0 &&
h2->sess->t_idle - r2->t_winupd >
cache_param->idle_send_timeout) {
VSLb(h2->vsl, SLT_Debug,
"H2: stream %u: Hit idle_send_timeout waiting "
"for WINDOW_UPDATE", r2->stream);
r = 1;
}
if (r == 0 && r2->t_send != 0 &&
h2->sess->t_idle - r2->t_send > cache_param->send_timeout) {
VSLb(h2->vsl, SLT_Debug,
"H2: stream %u: Hit send_timeout", r2->stream);
r = 1;
}
Lck_Unlock(&h2->sess->mtx);
}
return (r);
}
/***********************************************************************
* Called in loop from h2_new_session()
*/
......@@ -912,6 +943,7 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
h2_error h2e;
struct h2_req *r2, *r22;
char b[8];
int abort = 0;
ASSERT_RXTHR(h2);
(void)VTCP_blocking(*h2->htc->rfd);
......@@ -925,6 +957,8 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
break;
case HTC_S_TIMEOUT:
VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
if (abort)
break;
switch (r2->state) {
case H2_S_CLOSED:
if (!r2->scheduled)
......@@ -933,6 +967,10 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
case H2_S_OPEN:
case H2_S_CLOS_REM:
case H2_S_CLOS_LOC:
if (h2_stream_tmo(h2, r2)) {
abort = 1;
continue;
}
return (1);
default:
break;
......
......@@ -35,6 +35,7 @@
#include "http2/cache_http2.h"
#include "vend.h"
#include "vtim.h"
static void
h2_send_get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
......@@ -204,15 +205,14 @@ h2_do_window(struct worker *wrk, struct h2_req *r2,
Lck_Lock(&h2->sess->mtx);
if (r2->t_window <= 0 || h2->req0->t_window <= 0) {
r2->t_winupd = VTIM_real();
h2_send_rel(h2, r2);
while (r2->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
r2->cond = &wrk->cond;
// XXX: timeout handling (subject to send_timeout?)
AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
r2->cond = NULL;
}
while (h2->req0->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
// XXX: timeout handling
AZ(Lck_CondWait(h2->cond, &h2->sess->mtx, 0));
}
......@@ -235,6 +235,7 @@ h2_do_window(struct worker *wrk, struct h2_req *r2,
h2_win_charge(r2, h2, w);
assert (w > 0);
}
r2->t_winupd = 0;
Lck_Unlock(&h2->sess->mtx);
return (w);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment