]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: connection: make conn_stream users also check for per-stream error flag
authorWilly Tarreau <w@1wt.eu>
Mon, 16 Oct 2017 13:17:17 +0000 (15:17 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 31 Oct 2017 17:03:23 +0000 (18:03 +0100)
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
src/mux_pt.c
src/stream_interface.c

index aff5ff3ea6b0ed9dbdc217d15f382a712001264b..57468e0b330fe1c371ceba0b1e44860a6a2d4d4f 100644 (file)
@@ -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 */
index 2a83eb4c7ae6660d66aa7d4dd19850d350643790..9438d641add7b0d49d62379f5944a14dea3f8557 100644 (file)
@@ -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);
 }
 
index e9bc8315705965815130307b0994f0b28da25e80..5b04b8ea9801629d5392660e43fa90311d0db1c2 100644 (file)
@@ -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 <conn> 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)