BUG/MEDIUM: tcpcheck: Properly catch early HTTP parsing errors
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 20 Oct 2021 11:53:38 +0000 (13:53 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 3 Nov 2021 10:09:08 +0000 (11:09 +0100)
When an HTTP response is parsed, early parsing errors are not properly
handled. When this error is reported by the multiplexer, nothing is copied
into the input buffer. The HTX message remains empty but the
HTX_FL_PARSING_ERROR flag is set. In addition CS_FL_EOI is set on the
conn-stream. This last flag must be handled to prevent subscription for
receive events. Otherwise, in the best case, a L7 timeout error is
reported. But a transient loop is also possible if a shutdown is received
because the multiplexer notifies the check of the event while the check
never handles it and waits for more data.

Now, if CS_FL_EOI flag is set on the conn-stream, expect rules are
evaluated. Any error must be handled there.

Thanks to @kazeburo for his valuable report.

This patch should fix the issue #1420. It must be backported at least to
2.4. On 2.3 and 2.2, there is no loop but the wrong error is reported (empty
response instead of invalid one). Thus it may also be backported as far as
2.2.

(cherry picked from commit d16e7dd0e4cf07c9731a1a1c288d003a9f84d1b5)
Signed-off-by: Willy Tarreau <w@1wt.eu>
(cherry picked from commit bd0174758fed578d685c52271b030b10c475fb2b)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>

src/tcpcheck.c

index 09c1ac6..973a705 100644 (file)
@@ -1508,6 +1508,10 @@ enum tcpcheck_eval_ret tcpcheck_eval_recv(struct check *check, struct tcpcheck_r
                goto stop;
        }
        if (!cur_read) {
+               if (cs->flags & CS_FL_EOI) {
+                       /* If EOI is set, it means there is a response or an error */
+                       goto out;
+               }
                if (!(cs->flags & (CS_FL_WANT_ROOM|CS_FL_ERROR|CS_FL_EOS))) {
                        conn->mux->subscribe(cs, SUB_RETRY_RECV, &check->wait_list);
                        goto wait_more_data;