From 4ff3b89643d710750d5e7eec17479685e905dd80 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 16 Oct 2017 15:17:17 +0200 Subject: [PATCH] MINOR: connection: make conn_stream users also check for per-stream error flag In a 1:1 connection:stream there's no problem relying on the connection flags alone to check for errors. But in a mux, it will be possible to mark certain streams in error without having to mark all of them. An example is an H2 client sending RST_STREAM frames to abort a long download, or a parse error requiring to abort only this specific stream. This commit ensures that stream-interface and checks properly check for CS_FL_ERROR in cs->flags wherever CO_FL_ERROR was in use. Most likely over the long term, any check for CO_FL_ERROR will have to disappear. --- src/checks.c | 29 +++++++++++++++-------------- src/mux_pt.c | 8 ++++++++ src/stream_interface.c | 22 ++++++++++++---------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/checks.c b/src/checks.c index aff5ff3ea6..57468e0b33 100644 --- a/src/checks.c +++ b/src/checks.c @@ -596,7 +596,8 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired) if (conn && (!errno || errno == EAGAIN)) retrieve_errno_from_socket(conn); - if (conn && !(conn->flags & CO_FL_ERROR) && !expired) + if (conn && !(conn->flags & CO_FL_ERROR) && + !(cs->flags & CS_FL_ERROR) && !expired) return; /* we'll try to build a meaningful error message depending on the @@ -665,7 +666,7 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired) } else if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L4_CONN)) == CO_FL_WAIT_L4_CONN) { /* L4 not established (yet) */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) set_server_check_status(check, HCHK_STATUS_L4CON, err_msg); else if (expired) set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg); @@ -679,12 +680,12 @@ static void chk_report_conn_err(struct check *check, int errno_bck, int expired) } else if ((conn->flags & (CO_FL_CONNECTED|CO_FL_WAIT_L6_CONN)) == CO_FL_WAIT_L6_CONN) { /* L6 not established (yet) */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg); else if (expired) set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg); } - else if (conn->flags & CO_FL_ERROR) { + else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) { /* I/O error after connection was established and before we could diagnose */ set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg); } @@ -744,7 +745,7 @@ static void event_srv_chk_w(struct conn_stream *cs) if (check->bo->o) { conn->mux->snd_buf(cs, check->bo, 0); - if (conn->flags & CO_FL_ERROR) { + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) { chk_report_conn_err(check, errno, 0); __cs_stop_both(cs); goto out_wakeup; @@ -819,9 +820,9 @@ static void event_srv_chk_r(struct conn_stream *cs) done = 0; conn->mux->rcv_buf(cs, check->bi, check->bi->size); - if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) { + if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH) || cs->flags & CS_FL_ERROR) { done = 1; - if ((conn->flags & CO_FL_ERROR) && !check->bi->i) { + if ((conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) && !check->bi->i) { /* Report network errors only if we got no other data. Otherwise * we'll let the upper layers decide whether the response is OK * or not. It is very common that an RST sent by the server is @@ -1328,7 +1329,7 @@ static void event_srv_chk_r(struct conn_stream *cs) SPIN_UNLOCK(SERVER_LOCK, &check->server->lock); out_wakeup: /* collect possible new errors */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) chk_report_conn_err(check, 0, 0); /* Reset the check buffer... */ @@ -1375,7 +1376,7 @@ static int wake_srv_chk(struct conn_stream *cs) conn = cs_conn(cs); } - if (unlikely(conn->flags & CO_FL_ERROR)) { + if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) { /* We may get error reports bypassing the I/O handlers, typically * the case when sending a pure TCP check which fails, then the I/O * handlers above are not called. This is completely handled by the @@ -2191,7 +2192,7 @@ static struct task *process_chk_conn(struct task *t) else set_server_check_status(check, HCHK_STATUS_L4OK, NULL); } - else if ((conn->flags & CO_FL_ERROR) || expired) { + else if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) { chk_report_conn_err(check, 0, expired); } else @@ -2646,7 +2647,7 @@ static int tcpcheck_main(struct check *check) __cs_want_send(cs); if (conn->mux->snd_buf(cs, check->bo, 0) <= 0) { - if (conn->flags & CO_FL_ERROR) { + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) { chk_report_conn_err(check, errno, 0); __cs_stop_both(cs); goto out_end_tcpcheck; @@ -2878,9 +2879,9 @@ static int tcpcheck_main(struct check *check) __cs_want_recv(cs); if (conn->mux->rcv_buf(cs, check->bi, check->bi->size) <= 0) { - if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) { + if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH) || cs->flags & CS_FL_ERROR) { done = 1; - if ((conn->flags & CO_FL_ERROR) && !check->bi->i) { + if ((conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) && !check->bi->i) { /* Report network errors only if we got no other data. Otherwise * we'll let the upper layers decide whether the response is OK * or not. It is very common that an RST sent by the server is @@ -3037,7 +3038,7 @@ static int tcpcheck_main(struct check *check) out_end_tcpcheck: /* collect possible new errors */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) chk_report_conn_err(check, 0, 0); /* cleanup before leaving */ diff --git a/src/mux_pt.c b/src/mux_pt.c index 2a83eb4c7a..9438d641ad 100644 --- a/src/mux_pt.c +++ b/src/mux_pt.c @@ -82,6 +82,8 @@ static void mux_pt_recv(struct connection *conn) { struct conn_stream *cs = conn->mux_ctx; + if (conn->flags & CO_FL_ERROR) + cs->flags |= CS_FL_ERROR; if (conn_xprt_read0_pending(conn)) cs->flags |= CS_FL_EOS; cs->data_cb->recv(cs); @@ -95,6 +97,8 @@ static void mux_pt_send(struct connection *conn) { struct conn_stream *cs = conn->mux_ctx; + if (conn->flags & CO_FL_ERROR) + cs->flags |= CS_FL_ERROR; cs->data_cb->send(cs); cs_update_mux_polling(cs); } @@ -138,6 +142,8 @@ static int mux_pt_rcv_buf(struct conn_stream *cs, struct buffer *buf, int count) ret = cs->conn->xprt->rcv_buf(cs->conn, buf, count); if (conn_xprt_read0_pending(cs->conn)) cs->flags |= CS_FL_EOS; + if (cs->conn->flags & CO_FL_ERROR) + cs->flags |= CS_FL_ERROR; return (ret); } @@ -155,6 +161,8 @@ static int mux_pt_rcv_pipe(struct conn_stream *cs, struct pipe *pipe, unsigned i ret = cs->conn->xprt->rcv_pipe(cs->conn, pipe, count); if (conn_xprt_read0_pending(cs->conn)) cs->flags |= CS_FL_EOS; + if (cs->conn->flags & CO_FL_ERROR) + cs->flags |= CS_FL_ERROR; return (ret); } diff --git a/src/stream_interface.c b/src/stream_interface.c index e9bc831570..5b04b8ea98 100644 --- a/src/stream_interface.c +++ b/src/stream_interface.c @@ -435,7 +435,7 @@ static int si_idle_conn_wake_cb(struct conn_stream *cs) if (!conn_ctrl_ready(conn)) return 0; - if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH)) { + if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH) || cs->flags & CS_FL_ERROR) { /* warning, we can't do anything on after this call ! */ si_release_endpoint(si); return -1; @@ -574,7 +574,7 @@ static int si_cs_wake_cb(struct conn_stream *cs) /* First step, report to the stream-int what was detected at the * connection layer : errors and connection establishment. */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) si->flags |= SI_FL_ERR; /* If we had early data, and the handshake ended, then @@ -643,7 +643,7 @@ static void si_cs_send(struct conn_stream *cs) oc->pipe = NULL; } - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) return; } @@ -656,7 +656,8 @@ static void si_cs_send(struct conn_stream *cs) /* when we're here, we already know that there is no spliced * data left, and that there are sendable buffered data. */ - if (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_HANDSHAKE)) && !(oc->flags & CF_SHUTW)) { + if (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_HANDSHAKE)) && + !(cs->flags & CS_FL_ERROR) && !(oc->flags & CF_SHUTW)) { /* check if we want to inform the kernel that we're interested in * sending more data after this call. We want this if : * - we're about to close after this last send and want to merge @@ -991,7 +992,7 @@ static void stream_int_chk_snd_conn(struct stream_interface *si) __cs_want_send(cs); si_cs_send(cs); - if (cs->conn->flags & CO_FL_ERROR) { + if (cs->flags & CS_FL_ERROR || cs->conn->flags & CO_FL_ERROR) { /* Write error on the file descriptor */ __cs_stop_both(cs); si->flags |= SI_FL_ERR; @@ -1085,7 +1086,7 @@ static void si_cs_recv_cb(struct conn_stream *cs) * happens when we send too large a request to a backend server * which rejects it before reading it all. */ - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) return; /* maybe we were called immediately after an asynchronous shutr */ @@ -1150,7 +1151,7 @@ static void si_cs_recv_cb(struct conn_stream *cs) if (cs->flags & CS_FL_EOS) goto out_shutdown_r; - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) return; if (conn->flags & CO_FL_WAIT_ROOM) { @@ -1181,7 +1182,8 @@ static void si_cs_recv_cb(struct conn_stream *cs) * that if such an event is not handled above in splice, it will be handled here by * recv(). */ - while (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_WAIT_ROOM | CO_FL_HANDSHAKE)) && !(ic->flags & CF_SHUTR)) { + while (!(conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_WAIT_ROOM | CO_FL_HANDSHAKE)) && + !(cs->flags & CS_FL_ERROR) && !(ic->flags & CF_SHUTR)) { max = channel_recv_max(ic); if (!max) { @@ -1283,7 +1285,7 @@ static void si_cs_recv_cb(struct conn_stream *cs) } end_recv: - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) return; if (cs->flags & CS_FL_EOS) @@ -1311,7 +1313,7 @@ static void si_cs_send_cb(struct conn_stream *cs) struct connection *conn = cs->conn; struct stream_interface *si = cs->data; - if (conn->flags & CO_FL_ERROR) + if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) return; if (conn->flags & CO_FL_HANDSHAKE) -- 2.39.5