MEDIUM: mux-h1: Handle H1C states via its state field instead of H1C_F_ST_*

The H1 connection state is now handled in a dedicated state. H1C_F_ST_*
flags are removed. All states are now exclusives. It is easier to know the
H1 connection states. It is alive, or usable, if it is not CLOSING or
CLOSED. It is CLOSING if it should be closed ASAP but a stream is still
attached and/or the output buffer is not empty. CLOSED is used when the H1
connection is ready to be closed. Other states are quite easy to understand.

There is no special changes in the H1 connection behavior. Except in
h1_send(). When a CLOSING connection is CLOSED, the function now reports an
activity. In addition, when an embryonic H1 stream is aborted, it is
destroyed. This way, the H1 connection can be switched to CLOSED state.
This commit is contained in:
Christopher Faulet 2022-10-04 17:35:19 +02:00
parent ef93be2a7b
commit 4e72b172d7
2 changed files with 101 additions and 87 deletions

View File

@ -38,16 +38,9 @@
#define H1C_F_IN_FULL 0x00000020 /* mux is blocked on input buffer full */
#define H1C_F_IN_SALLOC 0x00000040 /* mux is blocked on lack of stream's request buffer */
/* Flags indicating the connection state */
#define H1C_F_ST_EMBRYONIC 0x00000100 /* Set when a H1 stream with no stream connector is attached to the connection */
#define H1C_F_ST_ATTACHED 0x00000200 /* Set when a H1 stream with a stream connector is attached to the connection (may be not READY) */
#define H1C_F_ST_IDLE 0x00000400 /* connection is idle and may be reused
* (exclusive to all H1C_F_ST flags and never set when an h1s is attached) */
#define H1C_F_ERROR 0x00000800 /* connection must be closed ASAP because an error occurred (stream connector may still be attached) */
#define H1C_F_ST_SHUTDOWN 0x00001000 /* connection must be shut down ASAP flushing output first (stream connector may still be attached) */
#define H1C_F_ST_READY 0x00002000 /* Set in ATTACHED state with a READY stream connector. A stream connector is not ready when
* a TCP>H1 upgrade is in progress Thus this flag is only set if ATTACHED is also set */
#define H1C_F_ST_ALIVE (H1C_F_ST_IDLE|H1C_F_ST_EMBRYONIC|H1C_F_ST_ATTACHED)
/* 0x00000100 - 0x00000400 unused */
#define H1C_F_ERROR 0x00000800 /* connection must be closed ASAP because an error occurred (stream connector may still be attached) */
/* 0x00001000 - 0x00002000 unused */
#define H1C_F_SILENT_SHUT 0x00004000 /* if H1C is closed closed, silent (or dirty) shutdown must be performed */
/* 0x00008000 unused */
@ -74,11 +67,9 @@ static forceinline char *h1c_show_flags(char *buf, size_t len, const char *delim
/* flags */
_(H1C_F_OUT_ALLOC, _(H1C_F_OUT_FULL,
_(H1C_F_IN_ALLOC, _(H1C_F_IN_FULL, _(H1C_F_IN_SALLOC,
_(H1C_F_ST_EMBRYONIC, _(H1C_F_ST_ATTACHED, _(H1C_F_ST_IDLE,
_(H1C_F_ERROR, _(H1C_F_ST_SHUTDOWN, _(H1C_F_ST_READY,
_(H1C_F_SILENT_SHUT, _(H1C_F_WANT_SPLICE, _(H1C_F_ERR_PENDING,
_(H1C_F_ERROR, _(H1C_F_SILENT_SHUT, _(H1C_F_WANT_SPLICE, _(H1C_F_ERR_PENDING,
_(H1C_F_WAIT_NEXT_REQ, _(H1C_F_UPG_H2C, _(H1C_F_CO_MSG_MORE,
_(H1C_F_CO_STREAMER, _(H1C_F_IS_BACK)))))))))))))))))));
_(H1C_F_CO_STREAMER, _(H1C_F_IS_BACK))))))))))))));
/* epilogue */
_(~0U);
return buf;

View File

@ -538,15 +538,15 @@ static inline void h1_close(struct h1c *h1c)
}
/* returns the number of streams in use on a connection to figure if it's idle
* or not. We rely on H1C_F_ST_IDLE to know if the connection is in-use or
* not. This flag is only set when no H1S is attached and when the previous
* stream, if any, was fully terminated without any error and in K/A mode.
* or not. We rely on H1C state to know if the connection is in-use or not. It
* is IDLE only when no H1 stream is attached and when the previous stream, if
* any, was fully terminated without any error and in K/A mode.
*/
static int h1_used_streams(struct connection *conn)
{
struct h1c *h1c = conn->ctx;
return ((h1c->flags & H1C_F_ST_IDLE) ? 0 : 1);
return ((h1c->state == H1_CS_IDLE) ? 0 : 1);
}
/* returns the number of streams still available on a connection */
@ -561,7 +561,7 @@ static void h1_refresh_timeout(struct h1c *h1c)
int is_idle_conn = 0;
if (h1c->task) {
if (!(h1c->flags & H1C_F_ST_ALIVE) || (h1c->flags & H1C_F_ST_SHUTDOWN)) {
if (!h1_is_alive(h1c)) {
/* half-closed or dead connections : switch to clientfin/serverfin
* timeouts so that we don't hang too long on clients that have
* gone away (especially in tunnel mode).
@ -571,12 +571,12 @@ static void h1_refresh_timeout(struct h1c *h1c)
is_idle_conn = 1;
}
else if (b_data(&h1c->obuf)) {
/* connection with pending outgoing data, need a timeout (server or client). */
/* alive connection with pending outgoing data, need a timeout (server or client). */
h1c->task->expire = tick_add(now_ms, h1c->timeout);
TRACE_DEVEL("refreshing connection's timeout (pending outgoing data)", H1_EV_H1C_SEND|H1_EV_H1C_RECV, h1c->conn);
}
else if (!(h1c->flags & (H1C_F_IS_BACK|H1C_F_ST_READY))) {
/* front connections waiting for a fully usable stream need a timeout. */
else if (!(h1c->flags & H1C_F_IS_BACK) && (h1c->state != H1_CS_RUNNING)) {
/* alive front connections waiting for a fully usable stream need a timeout. */
h1c->task->expire = tick_add(now_ms, h1c->timeout);
TRACE_DEVEL("refreshing connection's timeout (alive front h1c but not ready)", H1_EV_H1C_SEND|H1_EV_H1C_RECV, h1c->conn);
/* A frontend connection not yet ready could be treated the same way as an idle
@ -634,8 +634,7 @@ static void h1_set_idle_expiration(struct h1c *h1c)
h1c->idle_exp = TICK_ETERNITY;
return;
}
if (h1c->flags & H1C_F_ST_IDLE) {
if (h1c->state == H1_CS_IDLE) {
if (!tick_isset(h1c->idle_exp)) {
if ((h1c->flags & H1C_F_WAIT_NEXT_REQ) && /* Not the first request */
!b_data(&h1c->ibuf) && /* No input data */
@ -649,15 +648,15 @@ static void h1_set_idle_expiration(struct h1c *h1c)
}
}
}
else if ((h1c->flags & H1C_F_ST_ALIVE) && !(h1c->flags & H1C_F_ST_READY)) {
else if (h1c->state < H1_CS_RUNNING) {
if (!tick_isset(h1c->idle_exp)) {
h1c->idle_exp = tick_add_ifset(now_ms, h1c->px->timeout.httpreq);
TRACE_DEVEL("set idle expiration (http-request timeout)", H1_EV_H1C_RECV, h1c->conn);
}
}
else { // ST_ATTACHED or SHUTDOWN
else {
h1c->idle_exp = TICK_ETERNITY;
TRACE_DEVEL("unset idle expiration (attached || shutdown)", H1_EV_H1C_RECV, h1c->conn);
TRACE_DEVEL("unset idle expiration (running or closing)", H1_EV_H1C_RECV, h1c->conn);
}
}
/*****************************************************************/
@ -696,7 +695,7 @@ static struct stconn *h1s_new_sc(struct h1s *h1s, struct buffer *input)
goto err;
}
h1c->flags = (h1c->flags & ~H1C_F_ST_EMBRYONIC) | H1C_F_ST_ATTACHED | H1C_F_ST_READY;
h1c->state = H1_CS_RUNNING;
TRACE_LEAVE(H1_EV_STRM_NEW, h1c->conn, h1s);
return h1s_sc(h1s);
@ -714,7 +713,7 @@ static struct stconn *h1s_upgrade_sc(struct h1s *h1s, struct buffer *input)
goto err;
}
h1s->h1c->flags |= H1C_F_ST_READY;
h1s->h1c->state = H1_CS_RUNNING;
TRACE_LEAVE(H1_EV_STRM_NEW, h1s->h1c->conn, h1s);
return h1s_sc(h1s);
@ -754,7 +753,7 @@ static struct h1s *h1s_new(struct h1c *h1c)
if (h1c->flags & H1C_F_WAIT_NEXT_REQ)
h1s->flags |= H1S_F_NOT_FIRST;
h1c->flags = (h1c->flags & ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ)) | H1C_F_ST_EMBRYONIC;
h1s->h1c->state = H1_CS_EMBRYONIC;
TRACE_LEAVE(H1_EV_H1S_NEW, h1c->conn, h1s);
return h1s;
@ -824,7 +823,7 @@ static struct h1s *h1c_bck_stream_new(struct h1c *h1c, struct stconn *sc, struct
h1s->sd = sc->sedesc;
h1s->sess = sess;
h1c->flags = (h1c->flags & ~H1C_F_ST_EMBRYONIC) | H1C_F_ST_ATTACHED | H1C_F_ST_READY;
h1c->state = H1_CS_RUNNING;
if (h1c->px->options2 & PR_O2_RSPBUG_OK)
h1s->res.err_pos = -1;
@ -855,7 +854,6 @@ static void h1s_destroy(struct h1s *h1s)
h1_release_buf(h1c, &h1s->rxbuf);
h1c->flags &= ~(H1C_F_WANT_SPLICE|
H1C_F_ST_EMBRYONIC|H1C_F_ST_ATTACHED|H1C_F_ST_READY|
H1C_F_OUT_FULL|H1C_F_OUT_ALLOC|H1C_F_IN_SALLOC|
H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
if (h1s->flags & H1S_F_ERROR_MASK) {
@ -863,16 +861,18 @@ static void h1s_destroy(struct h1s *h1s)
TRACE_ERROR("h1s on error, set error on h1c", H1_EV_H1S_END|H1_EV_H1C_ERR, h1c->conn, h1s);
}
if (!(h1c->flags & (H1C_F_ERROR|H1C_F_ST_SHUTDOWN)) && /* No error/shutdown on h1c */
if (!(h1c->flags & H1C_F_ERROR) && /* No error */
h1_is_alive(h1c) && /* still alive */
!(h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH)) && /* No error/shutdown on conn */
(h1s->flags & H1S_F_WANT_KAL) && /* K/A possible */
h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) { /* req/res in DONE state */
h1c->flags |= (H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ);
h1c->state = H1_CS_IDLE;
h1c->flags |= H1C_F_WAIT_NEXT_REQ;
TRACE_STATE("set idle mode on h1c, waiting for the next request", H1_EV_H1C_ERR, h1c->conn, h1s);
}
else {
TRACE_STATE("set shutdown on h1c", H1_EV_H1S_END, h1c->conn, h1s);
h1c->flags |= H1C_F_ST_SHUTDOWN;
h1_close(h1c);
TRACE_STATE("close h1c", H1_EV_H1S_END, h1c->conn, h1s);
}
HA_ATOMIC_DEC(&h1c->px_counters->open_streams);
@ -908,7 +908,7 @@ static int h1_init(struct connection *conn, struct proxy *proxy, struct session
h1c->px = proxy;
h1c->state = H1_CS_IDLE;
h1c->flags = H1C_F_ST_IDLE;
h1c->flags = H1C_F_NONE;
h1c->errcode = 0;
h1c->ibuf = *input;
h1c->obuf = BUF_NULL;
@ -970,7 +970,7 @@ static int h1_init(struct connection *conn, struct proxy *proxy, struct session
goto fail;
/* Attach the SC but Not ready yet */
h1c->flags = (h1c->flags & ~H1C_F_ST_EMBRYONIC) | H1C_F_ST_ATTACHED;
h1c->state = H1_CS_UPGRADING;
TRACE_DEVEL("Inherit the SC from TCP connection to perform an upgrade",
H1_EV_H1C_NEW|H1_EV_STRM_NEW, h1c->conn, h1c->h1s);
}
@ -1378,7 +1378,7 @@ static void h1_capture_bad_message(struct h1c *h1c, struct h1s *h1s,
struct proxy *other_end;
union error_snapshot_ctx ctx;
if ((h1c->flags & H1C_F_ST_ATTACHED) && sc_strm(h1s_sc(h1s))) {
if (h1c->state == H1_CS_UPGRADING || h1c->state == H1_CS_RUNNING) {
if (sess == NULL)
sess = __sc_strm(h1s_sc(h1s))->sess;
if (!(h1m->flags & H1_MF_RESP))
@ -1836,7 +1836,7 @@ static size_t h1_process_demux(struct h1c *h1c, struct buffer *buf, size_t count
if (!b_data(&h1c->ibuf))
h1_release_buf(h1c, &h1c->ibuf);
if (!(h1c->flags & H1C_F_ST_READY)) {
if (h1c->state < H1_CS_RUNNING) {
/* The H1 connection is not ready. Most of time, there is no SC
* attached, except for TCP>H1 upgrade, from a TCP frontend. In both
* cases, it is only possible on the client side.
@ -1849,7 +1849,7 @@ static size_t h1_process_demux(struct h1c *h1c, struct buffer *buf, size_t count
goto end;
}
if (!(h1c->flags & H1C_F_ST_ATTACHED)) {
if (h1c->state == H1_CS_EMBRYONIC) {
TRACE_DEVEL("request headers fully parsed, create and attach the SC", H1_EV_RX_DATA, h1c->conn, h1s);
BUG_ON(h1s_sc(h1s));
if (!h1s_new_sc(h1s, buf)) {
@ -2636,7 +2636,14 @@ static int h1_send_error(struct h1c *h1c)
ret = 1;
}
}
if (h1c->h1s) {
TRACE_DEVEL("Abort embryonic H1S", H1_EV_H1C_ERR, h1c->conn, h1c->h1s);
h1s_destroy(h1c->h1s);
}
h1c->flags &= ~H1C_F_ERR_PENDING;
h1c->state = H1_CS_CLOSING;
out:
TRACE_LEAVE(H1_EV_H1C_ERR, h1c->conn);
return ret;
@ -2673,8 +2680,10 @@ static int h1_handle_parsing_error(struct h1c *h1c)
struct session *sess = h1c->conn->owner;
int ret = 1;
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB)))
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB))) {
h1c->state = H1_CS_CLOSING;
goto end;
}
session_inc_http_req_ctr(sess);
session_inc_http_err_ctr(sess);
@ -2703,8 +2712,10 @@ static int h1_handle_not_impl_err(struct h1c *h1c)
struct session *sess = h1c->conn->owner;
int ret = 1;
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB)))
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB))) {
h1c->state = H1_CS_CLOSING;
goto end;
}
session_inc_http_req_ctr(sess);
proxy_inc_fe_req_ctr(sess->listener, sess->fe);
@ -2730,8 +2741,10 @@ static int h1_handle_req_tout(struct h1c *h1c)
struct session *sess = h1c->conn->owner;
int ret = 1;
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB)))
if (!b_data(&h1c->ibuf) && ((h1c->flags & H1C_F_WAIT_NEXT_REQ) || (sess->fe->options & PR_O_IGNORE_PRB))) {
h1c->state = H1_CS_CLOSING;
goto end;
}
session_inc_http_req_ctr(sess);
proxy_inc_fe_req_ctr(sess->listener, sess->fe);
@ -2878,7 +2891,7 @@ static int h1_send(struct h1c *h1c)
if (!b_data(&h1c->obuf)) {
TRACE_DEVEL("leaving with everything sent", H1_EV_H1C_SEND, h1c->conn);
h1_release_buf(h1c, &h1c->obuf);
if (h1c->flags & H1C_F_ST_SHUTDOWN) {
if (h1c->state == H1_CS_CLOSING) {
TRACE_STATE("process pending shutdown for writes", H1_EV_H1C_SEND, h1c->conn);
h1_shutw_conn(conn);
}
@ -2889,7 +2902,7 @@ static int h1_send(struct h1c *h1c)
}
TRACE_LEAVE(H1_EV_H1C_SEND, h1c->conn);
return sent;
return sent || (h1c->state == H1_CS_CLOSED);
}
/* callback called on any event by the connection handler.
@ -2899,14 +2912,14 @@ static int h1_send(struct h1c *h1c)
static int h1_process(struct h1c * h1c)
{
struct connection *conn = h1c->conn;
struct h1s *h1s = h1c->h1s;
TRACE_ENTER(H1_EV_H1C_WAKE, conn);
/* Try to parse now the first block of a request, creating the H1 stream if necessary */
if (b_data(&h1c->ibuf) && /* Input data to be processed */
(h1c->flags & H1C_F_ST_ALIVE) && !(h1c->flags & H1C_F_ST_READY) && /* ST_IDLE/ST_EMBRYONIC or ST_ATTACH but not ST_READY */
!(h1c->flags & (H1C_F_IN_SALLOC|H1C_F_ERROR))) { /* No allocation failure on the stream rxbuf and no ERROR on the H1C */
(h1c->state < H1_CS_RUNNING) && /* IDLE, EMBRYONIC or UPGRADING */
!(h1c->flags & (H1C_F_IN_SALLOC|H1C_F_ERROR))) { /* No allocation failure on the stream rxbuf and no ERROR on the H1C */
struct h1s *h1s = h1c->h1s;
struct buffer *buf;
size_t count;
@ -2921,11 +2934,11 @@ static int h1_process(struct h1c * h1c)
/* Try to match H2 preface before parsing the request headers. */
if (b_isteq(&h1c->ibuf, 0, b_data(&h1c->ibuf), ist(H2_CONN_PREFACE)) > 0) {
h1c->flags |= H1C_F_UPG_H2C;
if (h1c->flags & H1C_F_ST_ATTACHED) {
if (h1c->state == H1_CS_UPGRADING) {
/* Force the REOS here to be sure to release the SC.
Here ATTACHED implies !READY, and h1s defined
*/
BUG_ON(!h1s || (h1c->flags & H1C_F_ST_READY));
BUG_ON(!h1s);
h1s->flags |= H1S_F_REOS;
}
TRACE_STATE("release h1c to perform H2 upgrade ", H1_EV_RX_DATA|H1_EV_H1C_WAKE);
@ -2938,7 +2951,7 @@ static int h1_process(struct h1c * h1c)
h1s = h1c_frt_stream_new(h1c, NULL, h1c->conn->owner);
if (!h1s) {
b_reset(&h1c->ibuf);
h1c->flags = (h1c->flags & ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ)) | H1C_F_ERROR;
h1c->flags |= H1C_F_ERROR;
goto no_parsing;
}
}
@ -2962,17 +2975,17 @@ static int h1_process(struct h1c * h1c)
no_parsing:
if (h1c->flags & H1C_F_ERROR) {
h1_handle_internal_err(h1c);
h1c->flags &= ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ);
h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
TRACE_ERROR("internal error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
}
else if (h1s->flags & H1S_F_NOT_IMPL_ERROR) {
h1_handle_not_impl_err(h1c);
h1c->flags = (h1c->flags & ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ)) | H1C_F_ERROR;
h1c->flags = (h1c->flags & ~H1C_F_WAIT_NEXT_REQ) | H1C_F_ERROR;
TRACE_ERROR("not-implemented error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
}
else if (h1s->flags & H1S_F_PARSING_ERROR || se_fl_test(h1s->sd, SE_FL_ERROR)) {
h1_handle_parsing_error(h1c);
h1c->flags = (h1c->flags & ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ)) | H1C_F_ERROR;
h1c->flags = (h1c->flags & ~H1C_F_WAIT_NEXT_REQ) | H1C_F_ERROR;
TRACE_ERROR("parsing error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
}
}
@ -2986,34 +2999,41 @@ static int h1_process(struct h1c * h1c)
if ((conn->flags & CO_FL_ERROR) ||
conn_xprt_read0_pending(conn) ||
(h1c->flags & H1C_F_ERROR) ||
((h1c->flags & H1C_F_SILENT_SHUT) && !b_data(&h1c->obuf))) {
if (!(h1c->flags & H1C_F_ST_READY)) {
(h1c->state >= H1_CS_CLOSING && (h1c->flags & H1C_F_SILENT_SHUT) && !b_data(&h1c->obuf))) {
if (h1c->state != H1_CS_RUNNING) {
/* No stream connector or not ready */
/* shutdown for reads and error on the frontend connection: Send an error */
if (!(h1c->flags & (H1C_F_IS_BACK|H1C_F_ERROR|H1C_F_ST_SHUTDOWN))) {
if (h1c->state < H1_CS_RUNNING && !(h1c->flags & (H1C_F_IS_BACK|H1C_F_ERROR))) {
/* shutdown for reads and error on the frontend connection: Send an error */
if (h1_handle_parsing_error(h1c))
h1_send(h1c);
h1c->flags = (h1c->flags & ~(H1C_F_ST_IDLE|H1C_F_WAIT_NEXT_REQ)) | H1C_F_ERROR;
h1c->flags = (h1c->flags & ~H1C_F_WAIT_NEXT_REQ) | H1C_F_ERROR;
}
/* Handle pending error, if any (only possible on frontend connection) */
if (h1c->flags & H1C_F_ERR_PENDING) {
else if (h1c->flags & H1C_F_ERR_PENDING) {
/* Handle pending error, if any (only possible on frontend connection) */
BUG_ON(h1c->flags & H1C_F_IS_BACK);
if (h1_send_error(h1c))
h1_send(h1c);
}
else {
h1_close(h1c);
TRACE_STATE("close h1c", H1_EV_H1S_END, h1c->conn);
}
/* If there is some pending outgoing data or error, just wait */
if (b_data(&h1c->obuf) || (h1c->flags & H1C_F_ERR_PENDING))
if (h1c->state == H1_CS_CLOSING || (h1c->flags & H1C_F_ERR_PENDING))
goto end;
/* Otherwise we can release the H1 connection */
goto release;
}
else {
struct h1s *h1s = h1c->h1s;
/* Here there is still a H1 stream with a stream connector.
* Report the connection state at the stream level
*/
BUG_ON(!h1s);
if (conn_xprt_read0_pending(conn)) {
h1s->flags |= H1S_F_REOS;
TRACE_STATE("read0 on connection", H1_EV_H1C_RECV, conn, h1s);
@ -3059,9 +3079,9 @@ static int h1_process(struct h1c * h1c)
}
}
if ((h1c->flags & H1C_F_WANT_SPLICE) && !h1s_data_pending(h1s)) {
if (h1c->state == H1_CS_RUNNING && (h1c->flags & H1C_F_WANT_SPLICE) && !h1s_data_pending(h1c->h1s)) {
TRACE_DEVEL("xprt rcv_buf blocked (want_splice), notify h1s for recv", H1_EV_H1C_RECV, h1c->conn);
h1_wake_stream_for_recv(h1s);
h1_wake_stream_for_recv(h1c->h1s);
}
end:
@ -3070,10 +3090,12 @@ static int h1_process(struct h1c * h1c)
return 0;
release:
if (h1c->flags & H1C_F_ST_ATTACHED) {
/* Don't release the H1 connection right now, we must destroy the
* attached SC first. Here, the H1C must not be READY */
BUG_ON(!h1s || h1c->flags & H1C_F_ST_READY);
if (h1c->state == H1_CS_UPGRADING) {
struct h1s *h1s = h1c->h1s;
/* Don't release the H1 connection right now, we must destroy
* the attached SC first */
BUG_ON(!h1s);
if (conn_xprt_read0_pending(conn) || (h1s->flags & H1S_F_REOS))
se_fl_set(h1s->sd, SE_FL_EOS);
@ -3169,7 +3191,7 @@ static int h1_wake(struct connection *conn)
if (ret == 0) {
struct h1s *h1s = h1c->h1s;
if (h1c->flags & H1C_F_ST_ATTACHED)
if (h1c->state == H1_CS_UPGRADING || h1c->state == H1_CS_RUNNING)
h1_alert(h1s);
}
return ret;
@ -3207,7 +3229,7 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state)
/* If a stream connector is still attached and ready to the mux, wait for the
* stream's timeout
*/
if (h1c->flags & H1C_F_ST_READY) {
if (h1c->state == H1_CS_RUNNING) {
HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
t->expire = TICK_ETERNITY;
TRACE_DEVEL("leaving (SC still attached)", H1_EV_H1C_WAKE, h1c->conn, h1c->h1s);
@ -3215,8 +3237,8 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state)
}
/* Try to send an error to the client */
if (!(h1c->flags & (H1C_F_IS_BACK|H1C_F_ERROR|H1C_F_ERR_PENDING|H1C_F_ST_SHUTDOWN))) {
h1c->flags = (h1c->flags & ~H1C_F_ST_IDLE) | H1C_F_ERROR;
if (h1c->state != H1_CS_CLOSING && !(h1c->flags & (H1C_F_IS_BACK|H1C_F_ERROR|H1C_F_ERR_PENDING))) {
h1c->flags |= H1C_F_ERROR;
TRACE_DEVEL("timeout error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR, h1c->conn, h1c->h1s);
if (h1_handle_req_tout(h1c))
h1_send(h1c);
@ -3227,9 +3249,9 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state)
}
}
if (h1c->flags & H1C_F_ST_ATTACHED) {
if (h1c->state == H1_CS_UPGRADING) {
/* Don't release the H1 connection right now, we must destroy the
* attached SC first. Here, the H1C must not be READY */
* attached SC first. */
se_fl_set(h1c->h1s->sd, SE_FL_EOS | SE_FL_ERROR);
h1_alert(h1c->h1s);
h1_refresh_timeout(h1c);
@ -3352,7 +3374,7 @@ static void h1_detach(struct sedesc *sd)
is_not_first = h1s->flags & H1S_F_NOT_FIRST;
h1s_destroy(h1s);
if ((h1c->flags & (H1C_F_IS_BACK|H1C_F_ST_IDLE)) == (H1C_F_IS_BACK|H1C_F_ST_IDLE)) {
if (h1c->state == H1_CS_IDLE && (h1c->flags & H1C_F_IS_BACK)) {
/* this connection may be killed at any moment, we want it to
* die "cleanly" (i.e. only an RST).
*/
@ -3366,7 +3388,7 @@ static void h1_detach(struct sedesc *sd)
*/
if (b_data(&h1c->ibuf)) {
h1_release_buf(h1c, &h1c->ibuf);
h1c->flags = (h1c->flags & ~H1C_F_ST_IDLE) | H1C_F_ST_SHUTDOWN;
h1c->state = H1_CS_CLOSING;
TRACE_DEVEL("remaining data on detach, kill connection", H1_EV_STRM_END|H1_EV_H1C_END);
goto release;
}
@ -3413,14 +3435,15 @@ static void h1_detach(struct sedesc *sd)
release:
/* We don't want to close right now unless the connection is in error or shut down for writes */
if ((h1c->flags & H1C_F_ERROR) ||
(h1c->state == H1_CS_CLOSED) ||
(h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_WR_SH)) ||
((h1c->flags & H1C_F_ST_SHUTDOWN) && !b_data(&h1c->obuf)) ||
(h1c->state == H1_CS_CLOSING && !b_data(&h1c->obuf)) ||
!h1c->conn->owner) {
TRACE_DEVEL("killing dead connection", H1_EV_STRM_END, h1c->conn);
h1_release(h1c);
}
else {
if (h1c->flags & H1C_F_ST_IDLE) {
if (h1c->state == H1_CS_IDLE) {
/* If we have a new request, process it immediately or
* subscribe for reads waiting for new data
*/
@ -3473,9 +3496,8 @@ static void h1_shutw(struct stconn *sc, enum co_shw_mode mode)
goto do_shutw;
}
if (!(h1c->flags & (H1C_F_ST_READY|H1C_F_ERROR))) {
/* Here attached is implicit because there is SC */
TRACE_STATE("keep connection alive (ALIVE but not READY nor ERROR)", H1_EV_STRM_SHUT, h1c->conn, h1s);
if (h1c->state == H1_CS_UPGRADING && !(h1c->flags & H1C_F_ERROR)) {
TRACE_STATE("keep connection alive (UPGRADING)", H1_EV_STRM_SHUT, h1c->conn, h1s);
goto end;
}
if (((h1s->flags & H1S_F_WANT_KAL) && h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE)) {
@ -3484,7 +3506,7 @@ static void h1_shutw(struct stconn *sc, enum co_shw_mode mode)
}
do_shutw:
h1c->flags |= H1C_F_ST_SHUTDOWN;
h1c->state = H1_CS_CLOSING;
if (mode != CO_SHW_NORMAL)
h1c->flags |= H1C_F_SILENT_SHUT;
@ -3498,10 +3520,11 @@ static void h1_shutw_conn(struct connection *conn)
{
struct h1c *h1c = conn->ctx;
TRACE_ENTER(H1_EV_H1C_END, conn);
h1_close(h1c);
if (conn->flags & CO_FL_SOCK_WR_SH)
return;
TRACE_ENTER(H1_EV_H1C_END, conn);
conn_xprt_shutw(conn);
conn_sock_shutw(conn, (h1c && !(h1c->flags & H1C_F_SILENT_SHUT)));
TRACE_LEAVE(H1_EV_H1C_END, conn);
@ -3598,8 +3621,8 @@ static size_t h1_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, in
TRACE_ENTER(H1_EV_STRM_RECV, h1c->conn, h1s, 0, (size_t[]){count});
/* Do nothing for now if not READY */
if (!(h1c->flags & H1C_F_ST_READY)) {
/* Do nothing for now if not RUNNING (implies UPGRADING) */
if (h1c->state < H1_CS_RUNNING) {
TRACE_DEVEL("h1c not ready yet", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
goto end;
}