]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h1: Rely on the H1C to deal with shutdown for reads
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 5 Oct 2022 06:22:33 +0000 (08:22 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 17 Nov 2022 13:33:15 +0000 (14:33 +0100)
read0 is now handled with a H1 connection flag (H1C_F_EOS). Corresponding
flag was removed on the H1 stream and we fully rely on the SE descriptor at
the stream level.

Concretly, it means we rely on the H1 connection flags instead of the
connection one. H1C_F_EOS is only set in h1_recv() or h1_rcv_pipe() after a
read if a read0 was detected.

include/haproxy/mux_h1-t.h
src/mux_h1.c

index 24579e677152ebcdfc9ec39ebc24178a9bd54a99..56b5d52aebcb23499e2e7da5a48cec7f86337d6d 100644 (file)
 #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 */
 
-/* 0x00000100 - 0x00000400 unused */
-#define H1C_F_ERROR       0x00000800 /* connection must be closed ASAP because an error occurred (stream connector may still be attached) */
+/* 0x00000200 - 0x00000400 unused */
+#define H1C_F_EOS            0x00000100 /* End-of-stream seen on the H1 connection (read0 detected) */
+#define H1C_F_ERR_PENDING    0x00000200 /* A write error was detected (block sends but not reads) */
+#define H1C_F_ERROR          0x00000400 /* A read error was detected (handled has an abort) */
+
 /* 0x00001000 - 0x00002000 unused */
 #define H1C_F_SILENT_SHUT    0x00004000 /* if H1C is closed closed, silent (or dirty) shutdown must be performed */
 #define H1C_F_ABRT_PENDING   0x00008000 /* An error must be sent (previous attempt failed) and H1 connection must be closed ASAP */
@@ -67,9 +70,10 @@ 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_ERROR, _(H1C_F_SILENT_SHUT, _(H1C_F_ABRT_PENDING, _(H1C_F_WANT_SPLICE,
+       _(H1C_F_EOS, _(H1C_F_ERR_PENDING, _(H1C_F_ERROR,
+       _(H1C_F_SILENT_SHUT, _(H1C_F_ABRT_PENDING, _(H1C_F_WANT_SPLICE,
        _(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;
@@ -84,7 +88,7 @@ static forceinline char *h1c_show_flags(char *buf, size_t len, const char *delim
 #define H1S_F_TX_BLK         0x00200000 /* Don't process more output data, waiting sync with input side */
 #define H1S_F_RX_CONGESTED   0x00000004 /* Cannot process input data RX path is congested (waiting for more space in channel's buffer) */
 
-#define H1S_F_REOS           0x00000008 /* End of input stream seen even if not delivered yet */
+/* 0x00000008 unused */
 #define H1S_F_WANT_KAL       0x00000010
 #define H1S_F_WANT_TUN       0x00000020
 #define H1S_F_WANT_CLO       0x00000040
@@ -112,10 +116,10 @@ static forceinline char *h1s_show_flags(char *buf, size_t len, const char *delim
        _(0);
        /* flags */
        _(H1S_F_RX_BLK, _(H1S_F_TX_BLK, _(H1S_F_RX_CONGESTED,
-       _(H1S_F_REOS, _(H1S_F_WANT_KAL, _(H1S_F_WANT_TUN, _(H1S_F_WANT_CLO,
+       _(H1S_F_WANT_KAL, _(H1S_F_WANT_TUN, _(H1S_F_WANT_CLO,
        _(H1S_F_NOT_FIRST, _(H1S_F_BODYLESS_RESP,
        _(H1S_F_INTERNAL_ERROR, _(H1S_F_NOT_IMPL_ERROR, _(H1S_F_PARSING_ERROR, _(H1S_F_PROCESSING_ERROR,
-       _(H1S_F_HAVE_SRV_NAME, _(H1S_F_HAVE_O_CONN)))))))))))))));
+       _(H1S_F_HAVE_SRV_NAME, _(H1S_F_HAVE_O_CONN))))))))))))));
        /* epilogue */
        _(~0U);
        return buf;
index 3f7e102ca187650a83c7d69b5db305340d4b7f98..5f3f121e28c785da5fbe1b4602f120432d4bbd66 100644 (file)
@@ -421,39 +421,35 @@ static void h1_trace(enum trace_level level, uint64_t mask, const struct trace_s
 /*****************************************************/
 /*
  * Indicates whether or not we may receive data. The rules are the following :
- *   - if an error or a shutdown for reads was detected on the connection we
+ *   - if an error or a shutdown for reads was detected on the H1 connection we
  *      must not attempt to receive
  *   - if we are waiting for the connection establishment, we must not attempt
  *      to receive
- *   - if an error was detected on the stream we must not attempt to receive
  *   - if reads are explicitly disabled, we must not attempt to receive
  *   - if the input buffer failed to be allocated or is full , we must not try
  *     to receive
- *   - if the mux is not blocked on an input condition, we may attempt to receive
- *   - otherwise must may not attempt to receive
+ *   - if the mux is blocked on an input condition, we must may not attempt to
+ *     receive
+ *   - otherwise we may attempt to receive
  */
 static inline int h1_recv_allowed(const struct h1c *h1c)
 {
-       if (h1c->flags & H1C_F_ERROR) {
-               TRACE_DEVEL("recv not allowed because of error on h1c", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
+       if (h1c->flags & (H1C_F_EOS|H1C_F_ERROR)) {
+               TRACE_DEVEL("recv not allowed because of (eos|error) on h1c", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
                return 0;
        }
 
-       if (h1c->conn->flags & (CO_FL_ERROR|CO_FL_SOCK_RD_SH|CO_FL_WAIT_L4_CONN|CO_FL_WAIT_L6_CONN)) {
-               TRACE_DEVEL("recv not allowed because of (error|read0|waitl4|waitl6) on connection", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
+       if (h1c->conn->flags & (CO_FL_WAIT_L4_CONN|CO_FL_WAIT_L6_CONN)) {
+               TRACE_DEVEL("recv not allowed because of (waitl4|waitl6) on connection", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
                return 0;
        }
 
-       if (h1c->h1s && (h1c->h1s->flags & H1S_F_ERROR_MASK)) {
-               TRACE_DEVEL("recv not allowed because of error on h1s", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
+       if ((h1c->flags & (H1C_F_IN_ALLOC|H1C_F_IN_FULL|H1C_F_IN_SALLOC))) {
+               TRACE_DEVEL("recv not allowed because input is blocked", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
                return 0;
        }
 
-       if (!(h1c->flags & (H1C_F_IN_ALLOC|H1C_F_IN_FULL|H1C_F_IN_SALLOC)))
-               return 1;
-
-       TRACE_DEVEL("recv not allowed because input is blocked", H1_EV_H1C_RECV|H1_EV_H1C_BLK, h1c->conn);
-       return 0;
+       return 1;
 }
 
 /*
@@ -861,9 +857,8 @@ 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) &&                                           /* No error */
+               if (!(h1c->flags & (H1C_F_EOS|H1C_F_ERR_PENDING|H1C_F_ERROR)) &&             /* No error/read0 */
                    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->state = H1_CS_IDLE;
@@ -1899,12 +1894,14 @@ static size_t h1_process_demux(struct h1c *h1c, struct buffer *buf, size_t count
        }
        else {
                se_fl_clr(h1s->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM);
-               if (h1s->flags & H1S_F_REOS) {
+               if (h1c->flags & H1C_F_EOS) {
                        se_fl_set(h1s->sd, SE_FL_EOS);
+                       TRACE_STATE("report EOS to SE", H1_EV_RX_DATA, h1c->conn, h1s);
                        if (h1m->state >= H1_MSG_DONE || (h1m->state > H1_MSG_LAST_LF && !(h1m->flags & H1_MF_XFER_LEN))) {
                                /* DONE or TUNNEL or SHUTR without XFER_LEN, set
                                 * EOI on the stream connector */
                                se_fl_set(h1s->sd, SE_FL_EOI);
+                               TRACE_STATE("report EOI to SE", H1_EV_RX_DATA, h1c->conn, h1s);
                        }
                        else if (h1m->state < H1_MSG_DONE) {
                                se_fl_set(h1s->sd, SE_FL_ERROR);
@@ -2474,7 +2471,6 @@ static size_t h1_process_mux(struct h1c *h1c, struct buffer *buf, size_t count)
                                         * to the client. Switch the response to tunnel mode.
                                         */
                                        h1_set_tunnel_mode(h1s);
-                                       TRACE_STATE("switch H1 response in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s);
                                }
 
                                if (h1s->flags & H1S_F_RX_BLK) {
@@ -2492,9 +2488,9 @@ static size_t h1_process_mux(struct h1c *h1c, struct buffer *buf, size_t count)
                                /* Unexpected error during output processing */
                                chn_htx->flags |= HTX_FL_PROCESSING_ERROR;
                                h1s->flags |= H1S_F_PROCESSING_ERROR;
-                               h1c->flags |= H1C_F_ERROR;
-                               TRACE_ERROR("processing output error, set error on h1c/h1s",
-                                           H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
+                               se_fl_set(h1s->sd, SE_FL_ERROR);
+                               TRACE_ERROR("processing output error, set error on h1s",
+                                           H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
                                goto end;
                }
 
@@ -2531,11 +2527,14 @@ static size_t h1_process_mux(struct h1c *h1c, struct buffer *buf, size_t count)
         * in the output buffer.
         */
        if (h1s->req.state == H1_MSG_DONE && h1s->res.state == H1_MSG_DONE) {
+               se_fl_set(h1s->sd, SE_FL_EOI);
                if (!htx_is_empty(chn_htx)) {
-                       h1c->flags |= H1C_F_ERROR;
-                       TRACE_ERROR("txn done but data waiting to be sent, set error on h1c", H1_EV_H1C_ERR, h1c->conn, h1s);
+                       chn_htx->flags |= HTX_FL_PROCESSING_ERROR;
+                       h1s->flags |= H1S_F_PROCESSING_ERROR;
+                       se_fl_set(h1s->sd, SE_FL_ERROR);
+                       TRACE_ERROR("txn done but data waiting to be sent, set error on h1s",
+                                   H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
                }
-               se_fl_set(h1s->sd, SE_FL_EOI);
        }
 
        TRACE_LEAVE(H1_EV_TX_DATA, h1c->conn, h1s, chn_htx, (size_t[]){total});
@@ -2815,13 +2814,23 @@ static int h1_recv(struct h1c *h1c)
                ret = conn->xprt->rcv_buf(conn, conn->xprt_ctx, &h1c->ibuf, max, flags);
                HA_ATOMIC_ADD(&h1c->px_counters->bytes_in, ret);
        }
+
+       if (conn_xprt_read0_pending(conn)) {
+               TRACE_DEVEL("read0 on connection", H1_EV_H1C_RECV, h1c->conn);
+               h1c->flags |= H1C_F_EOS;
+       }
+       if (h1c->conn->flags & CO_FL_ERROR) {
+               TRACE_DEVEL("connection error", H1_EV_H1C_RECV, h1c->conn);
+               h1c->flags |= H1C_F_ERROR;
+       }
+
        if (max && !ret && h1_recv_allowed(h1c)) {
                TRACE_STATE("failed to receive data, subscribing", H1_EV_H1C_RECV, h1c->conn);
                conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
        }
        else {
+               TRACE_DATA("data received or pending or connection error", H1_EV_H1C_RECV, h1c->conn, 0, 0, (size_t[]){ret});
                h1_wake_stream_for_recv(h1c->h1s);
-               TRACE_DATA("data received", H1_EV_H1C_RECV, h1c->conn, 0, 0, (size_t[]){ret});
        }
 
        if (!b_data(&h1c->ibuf))
@@ -2832,7 +2841,7 @@ static int h1_recv(struct h1c *h1c)
        }
 
        TRACE_LEAVE(H1_EV_H1C_RECV, h1c->conn);
-       return !!ret || (conn->flags & CO_FL_ERROR) || conn_xprt_read0_pending(conn);
+       return !!ret || (h1c->flags & (H1C_F_EOS|H1C_F_ERROR));
 }
 
 
@@ -2848,9 +2857,11 @@ static int h1_send(struct h1c *h1c)
 
        TRACE_ENTER(H1_EV_H1C_SEND, h1c->conn);
 
-       if (conn->flags & CO_FL_ERROR) {
-               TRACE_DEVEL("leaving on connection error", H1_EV_H1C_SEND, h1c->conn);
+       if (h1c->flags & (H1C_F_ERROR|H1C_F_ERR_PENDING)) {
+               TRACE_DEVEL("leaving on H1C error|err_pending", H1_EV_H1C_SEND, h1c->conn);
                b_reset(&h1c->obuf);
+               if (h1c->flags & H1C_F_EOS)
+                       h1c->flags |= H1C_F_ERROR;
                return 1;
        }
 
@@ -2874,9 +2885,12 @@ static int h1_send(struct h1c *h1c)
                sent = 1;
        }
 
-       if (conn->flags & (CO_FL_ERROR|CO_FL_SOCK_WR_SH)) {
-               TRACE_DEVEL("connection error or output closed", H1_EV_H1C_SEND, h1c->conn);
-               /* error or output closed, nothing to send, clear the buffer to release it */
+       if (conn->flags & CO_FL_ERROR) {
+               /* connection error, nothing to send, clear the buffer to release it */
+               TRACE_DEVEL("connection error", H1_EV_H1C_SEND, h1c->conn);
+               h1c->flags |= H1C_F_ERR_PENDING;
+               if (h1c->flags & H1C_F_EOS)
+                       h1c->flags |= H1C_F_ERROR;
                b_reset(&h1c->obuf);
        }
 
@@ -2899,7 +2913,7 @@ static int h1_send(struct h1c *h1c)
        }
 
        TRACE_LEAVE(H1_EV_H1C_SEND, h1c->conn);
-       return sent || (h1c->state == H1_CS_CLOSED);
+       return sent || (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) || (h1c->state == H1_CS_CLOSED);
 }
 
 /* callback called on any event by the connection handler.
@@ -2915,7 +2929,7 @@ static int h1_process(struct h1c * h1c)
        /* 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->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 */
+           !(h1c->flags & (H1C_F_IN_SALLOC|H1C_F_ABRT_PENDING))) {              /* No allocation failure on the stream rxbuf and no ERROR on the H1C */
                struct h1s *h1s = h1c->h1s;
                struct buffer *buf;
                size_t count;
@@ -2932,11 +2946,8 @@ static int h1_process(struct h1c * h1c)
                        if (b_isteq(&h1c->ibuf, 0, b_data(&h1c->ibuf), ist(H2_CONN_PREFACE)) > 0) {
                                h1c->flags |= H1C_F_UPG_H2C;
                                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);
-                                       h1s->flags |= H1S_F_REOS;
+                                       se_fl_set(h1s->sd, SE_FL_EOS); /* Set EOS here to release the SC */
                                }
                                TRACE_STATE("release h1c to perform H2 upgrade ", H1_EV_RX_DATA|H1_EV_H1C_WAKE);
                                goto release;
@@ -2948,7 +2959,9 @@ 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_F_ERROR;
+                               h1_handle_internal_err(h1c);
+                               h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
+                               TRACE_ERROR("alloc error", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
                                goto no_parsing;
                        }
                }
@@ -2972,17 +2985,17 @@ static int h1_process(struct h1c * h1c)
          no_parsing:
                if (h1s->flags & H1S_F_INTERNAL_ERROR) {
                        h1_handle_internal_err(h1c);
-                       h1c->flags = (h1c->flags & ~H1C_F_WAIT_NEXT_REQ) | H1C_F_ERROR;
+                       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_WAIT_NEXT_REQ) | H1C_F_ERROR;
+                       h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
                        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_WAIT_NEXT_REQ) | H1C_F_ERROR;
+                       h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
                        TRACE_ERROR("parsing error detected", H1_EV_H1C_WAKE|H1_EV_H1C_ERR);
                }
                else if (h1c->state < H1_CS_RUNNING) {
@@ -2990,24 +3003,23 @@ static int h1_process(struct h1c * h1c)
                        h1c->conn->xprt->subscribe(h1c->conn, h1c->conn->xprt_ctx, SUB_RETRY_RECV, &h1c->wait_event);
                }
        }
+
        h1_send(h1c);
 
        /* H1 connection must be released ASAP if:
-        *  - an error occurred on the connection or the H1C or
+        *  - an error occurred on the H1C or
         *  - a read0 was received or
         *  - a silent shutdown was emitted and all outgoing data sent
         */
-       if ((conn->flags & CO_FL_ERROR) ||
-           conn_xprt_read0_pending(conn) ||
-           (h1c->flags & H1C_F_ERROR) ||
+       if ((h1c->flags & (H1C_F_EOS|H1C_F_ERROR|H1C_F_ABRT_PENDING)) ||
            (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 */
+                       /* No stream connector or upgrading */
                        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 */
+                               /* shutdown for reads and no error on the frontend connection: Send an error */
                                if (h1_handle_parsing_error(h1c))
                                        h1_send(h1c);
-                               h1c->flags = (h1c->flags & ~H1C_F_WAIT_NEXT_REQ) | H1C_F_ERROR;
+                               h1c->flags &= ~H1C_F_WAIT_NEXT_REQ;
                        }
                        else if (h1c->flags & H1C_F_ABRT_PENDING) {
                                /* Handle pending error, if any (only possible on frontend connection) */
@@ -3031,17 +3043,14 @@ static int h1_process(struct h1c * h1c)
                        struct h1s *h1s = h1c->h1s;
 
                        /* Here there is still a H1 stream with a stream connector.
-                        * Report the connection state at the stream level
+                        * Report an error at the stream level and wake up the stream
                         */
                        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);
+                       if (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) {
+                               se_fl_set_error(h1s->sd);
+                               TRACE_STATE("report (ERR_PENDING|ERROR) to SE", H1_EV_H1C_RECV, conn, h1s);
                        }
-                       if ((h1c->flags & H1C_F_ERROR) || ((conn->flags & CO_FL_ERROR) &&
-                             (se_fl_test(h1s->sd, SE_FL_EOI | SE_FL_EOS) || !b_data(&h1c->ibuf))))
-                               se_fl_set(h1s->sd, SE_FL_ERROR);
                        TRACE_POINT(H1_EV_STRM_WAKE, h1c->conn, h1s);
                        h1_alert(h1s);
                }
@@ -3098,10 +3107,14 @@ static int h1_process(struct h1c * h1c)
                 * the attached SC first */
                BUG_ON(!h1s);
 
-               if (conn_xprt_read0_pending(conn) || (h1s->flags & H1S_F_REOS))
+               if (h1c->flags & H1C_F_EOS) {
                        se_fl_set(h1s->sd, SE_FL_EOS);
-               if ((h1c->flags & H1C_F_ERROR) || (conn->flags & CO_FL_ERROR))
-                       se_fl_set(h1s->sd, SE_FL_ERROR);
+                       TRACE_STATE("report EOS to SE", H1_EV_H1C_RECV, conn, h1s);
+               }
+               if (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) {
+                       se_fl_set_error(h1s->sd);
+                       TRACE_STATE("report (ERR_PENDING|ERROR) to SE", H1_EV_H1C_RECV, conn, h1s);
+               }
                h1_alert(h1s);
                TRACE_DEVEL("waiting to release the SC before releasing the connection", H1_EV_H1C_WAKE);
        }
@@ -3302,7 +3315,7 @@ static int h1_attach(struct connection *conn, struct sedesc *sd, struct session
        h1c->flags &= ~H1C_F_SILENT_SHUT;
 
        TRACE_ENTER(H1_EV_STRM_NEW, conn);
-       if (h1c->flags & H1C_F_ERROR) {
+       if (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) {
                TRACE_ERROR("h1c on error", H1_EV_STRM_NEW|H1_EV_STRM_END|H1_EV_STRM_ERR, conn);
                goto err;
        }
@@ -3437,7 +3450,6 @@ static void h1_detach(struct sedesc *sd)
        /* 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->state == H1_CS_CLOSING && !b_data(&h1c->obuf)) ||
            !h1c->conn->owner) {
                TRACE_DEVEL("killing dead connection", H1_EV_STRM_END, h1c->conn);
@@ -3492,12 +3504,12 @@ static void h1_shutw(struct stconn *sc, enum co_shw_mode mode)
                TRACE_STATE("stream wants to kill the connection", H1_EV_STRM_SHUT, h1c->conn, h1s);
                goto do_shutw;
        }
-       if (h1c->conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH)) {
-               TRACE_STATE("shutdown on connection (error|rd_sh|wr_sh)", H1_EV_STRM_SHUT, h1c->conn, h1s);
+       if (h1c->state == H1_CS_CLOSING || (h1c->flags & (H1C_F_EOS|H1C_F_ERR_PENDING|H1C_F_ERROR))) {
+               TRACE_STATE("shutdown on connection (EOS || CLOSING || ERROR)", H1_EV_STRM_SHUT, h1c->conn, h1s);
                goto do_shutw;
        }
 
-       if (h1c->state == H1_CS_UPGRADING && !(h1c->flags & H1C_F_ERROR)) {
+       if (h1c->state == H1_CS_UPGRADING) {
                TRACE_STATE("keep connection alive (UPGRADING)", H1_EV_STRM_SHUT, h1c->conn, h1s);
                goto end;
        }
@@ -3670,8 +3682,8 @@ static size_t h1_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, in
                return 0;
        }
 
-       if (h1c->flags & H1C_F_ERROR) {
-               se_fl_set(h1s->sd, SE_FL_ERROR);
+       if (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) {
+               se_fl_set_error(h1s->sd);
                TRACE_ERROR("H1C on error, leaving in error", H1_EV_STRM_SEND|H1_EV_H1C_ERR|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s);
                return 0;
        }
@@ -3707,9 +3719,10 @@ static size_t h1_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, in
                        break;
        }
 
-       if ((h1c->flags & H1C_F_ERROR) || ((h1c->conn->flags & CO_FL_ERROR) &&
-            (se_fl_test(h1s->sd, SE_FL_EOI | SE_FL_EOS) || !b_data(&h1c->ibuf)))) {
-               se_fl_set(h1s->sd, SE_FL_ERROR);
+       if (h1c->flags & (H1C_F_ERR_PENDING|H1C_F_ERROR)) {
+               // FIXME: following test was removed :
+               // ((h1c->conn->flags & CO_FL_ERROR) && (se_fl_test(h1s->sd, SE_FL_EOI | SE_FL_EOS) || !b_data(&h1c->ibuf)))) {
+               se_fl_set_error(h1s->sd);
                TRACE_ERROR("reporting error to the app-layer stream", H1_EV_STRM_SEND|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s);
        }
 
@@ -3753,7 +3766,6 @@ static int h1_rcv_pipe(struct stconn *sc, struct pipe *pipe, unsigned int count)
                if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_CLEN)) {
                        if (ret > h1m->curr_len) {
                                h1s->flags |= H1S_F_PARSING_ERROR;
-                               h1c->flags |= H1C_F_ERROR;
                                se_fl_set(h1s->sd, SE_FL_ERROR);
                                TRACE_ERROR("too much payload, more than announced",
                                            H1_EV_RX_DATA|H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
@@ -3772,8 +3784,8 @@ static int h1_rcv_pipe(struct stconn *sc, struct pipe *pipe, unsigned int count)
 
   end:
        if (conn_xprt_read0_pending(h1c->conn)) {
-               h1s->flags |= H1S_F_REOS;
-               h1c->flags &= ~H1C_F_WANT_SPLICE;
+               se_fl_set(h1s->sd, SE_FL_EOS);
+               h1c->flags = (h1c->flags & ~H1C_F_WANT_SPLICE) | H1C_F_EOS;
                TRACE_STATE("Allow xprt rcv_buf on read0", H1_EV_STRM_RECV, h1c->conn, h1s);
        }
        if (h1c->conn->flags & CO_FL_ERROR) {
@@ -3816,7 +3828,6 @@ static int h1_snd_pipe(struct stconn *sc, struct pipe *pipe)
        if (h1m->state == H1_MSG_DATA && (h1m->flags & H1_MF_CLEN)) {
                if (ret > h1m->curr_len) {
                        h1s->flags |= H1S_F_PROCESSING_ERROR;
-                       h1c->flags |= H1C_F_ERROR;
                        se_fl_set(h1s->sd, SE_FL_ERROR);
                        TRACE_ERROR("too much payload, more than announced",
                                    H1_EV_TX_DATA|H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
@@ -3833,8 +3844,10 @@ static int h1_snd_pipe(struct stconn *sc, struct pipe *pipe)
 
   end:
        if (h1c->conn->flags & CO_FL_ERROR) {
-               se_fl_set(h1s->sd, SE_FL_ERROR);
-               h1c->flags = (h1c->flags & ~H1C_F_WANT_SPLICE) | H1C_F_ERROR;
+               h1c->flags = (h1c->flags & ~H1C_F_WANT_SPLICE) | H1C_F_ERR_PENDING;
+               if (h1c->flags & H1C_F_EOS)
+                       h1c->flags |= H1C_F_ERROR;
+               se_fl_set_error(h1s->sd);
                TRACE_DEVEL("connection error", H1_EV_STRM_ERR|H1_EV_H1C_ERR|H1_EV_H1S_ERR, h1c->conn, h1s);
        }