[MINOR] http: capture incorrectly chunked message bodies

It is possible to block on incorrectly chunked requests or responses,
but this becomes very hard to debug when it happens once in a while.
This patch adds the ability to also capture incorrectly chunked requests
and responses. The chunk will appear in the error buffer and will be
verifiable with the usual "show errors". The incorrect byte will match
the error location.
diff --git a/src/proto_http.c b/src/proto_http.c
index 2c33a84..8ca39c1 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2085,13 +2085,13 @@
 		if (++ptr >= end)
 			ptr = buf->data;
 		if (chunk & 0xF000000) /* overflow will occur */
-			return -1;
+			goto error;
 		chunk = (chunk << 4) + c;
 	}
 
 	/* empty size not allowed */
 	if (ptr == buf->lr)
-		return -1;
+		goto error;
 
 	while (http_is_spht[(unsigned char)*ptr]) {
 		if (++ptr >= end)
@@ -2114,7 +2114,7 @@
 			}
 
 			if (*ptr != '\n')
-				return -1;
+				goto error;
 			if (++ptr >= end)
 				ptr = buf->data;
 			/* done */
@@ -2137,7 +2137,7 @@
 			continue;
 		}
 		else
-			return -1;
+			goto error;
 	}
 
 	/* OK we found our CRLF and now <ptr> points to the next byte,
@@ -2149,6 +2149,9 @@
 	msg->hdr_content_len = chunk;
 	msg->msg_state = chunk ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
 	return 1;
+ error:
+	msg->err_pos = ptr - buf->data;
+	return -1;
 }
 
 /* This function skips trailers in the buffer <buf> associated with HTTP
@@ -2187,8 +2190,10 @@
 			}
 
 			if (*ptr == '\r') {
-				if (p1)
+				if (p1) {
+					msg->err_pos = ptr - buf->data;
 					return -1;
+				}
 				p1 = ptr;
 			}
 
@@ -2255,8 +2260,10 @@
 	if (bytes > buf->l - buf->send_max)
 		return 0;
 
-	if (*ptr != '\n')
+	if (*ptr != '\n') {
+		msg->err_pos = ptr - buf->data;
 		return -1;
+	}
 
 	ptr++;
 	if (ptr >= buf->data + buf->size)
@@ -4361,6 +4368,8 @@
 				goto missing_data;
 			else if (ret < 0) {
 				session_inc_http_err_ctr(s);
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->fe->invalid_req, s, req, msg, HTTP_MSG_CHUNK_SIZE, s->be);
 				goto return_bad_req;
 			}
 			/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
@@ -4382,6 +4391,8 @@
 				goto missing_data;
 			else if (ret < 0) {
 				session_inc_http_err_ctr(s);
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->fe->invalid_req, s, req, msg, HTTP_MSG_DATA_CRLF, s->be);
 				goto return_bad_req;
 			}
 			/* we're in MSG_CHUNK_SIZE now */
@@ -4393,11 +4404,15 @@
 				goto missing_data;
 			else if (ret < 0) {
 				session_inc_http_err_ctr(s);
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->fe->invalid_req, s, req, msg, HTTP_MSG_TRAILERS, s->be);
 				goto return_bad_req;
 			}
 			/* we're in HTTP_MSG_DONE now */
 		}
 		else {
+			int old_state = msg->msg_state;
+
 			/* other states, DONE...TUNNEL */
 			/* for keep-alive we don't want to forward closes on DONE */
 			if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
@@ -4417,6 +4432,8 @@
 						if (!(s->flags & SN_FINST_MASK))
 							s->flags |= SN_FINST_D;
 					}
+					if (msg->err_pos >= 0)
+						http_capture_bad_message(&s->fe->invalid_req, s, req, msg, old_state, s->be);
 					goto return_bad_req;
 				}
 				return 1;
@@ -5352,8 +5369,11 @@
 
 			if (!ret)
 				goto missing_data;
-			else if (ret < 0)
+			else if (ret < 0) {
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->be->invalid_rep, s, res, msg, HTTP_MSG_CHUNK_SIZE, s->fe);
 				goto return_bad_res;
+			}
 			/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
 			/* Don't set a PUSH at the end of that chunk if it's not the last one */
 			if (msg->msg_state == HTTP_MSG_DATA)
@@ -5371,8 +5391,11 @@
 
 			if (!ret)
 				goto missing_data;
-			else if (ret < 0)
+			else if (ret < 0) {
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->be->invalid_rep, s, res, msg, HTTP_MSG_DATA_CRLF, s->fe);
 				goto return_bad_res;
+			}
 			/* we're in MSG_CHUNK_SIZE now */
 		}
 		else if (msg->msg_state == HTTP_MSG_TRAILERS) {
@@ -5380,11 +5403,16 @@
 
 			if (ret == 0)
 				goto missing_data;
-			else if (ret < 0)
+			else if (ret < 0) {
+				if (msg->err_pos >= 0)
+					http_capture_bad_message(&s->be->invalid_rep, s, res, msg, HTTP_MSG_TRAILERS, s->fe);
 				goto return_bad_res;
+			}
 			/* we're in HTTP_MSG_DONE now */
 		}
 		else {
+			int old_state = msg->msg_state;
+
 			/* other states, DONE...TUNNEL */
 			/* for keep-alive we don't want to forward closes on DONE */
 			if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
@@ -5405,6 +5433,8 @@
 						if (!(s->flags & SN_FINST_MASK))
 							s->flags |= SN_FINST_D;
 					}
+					if (msg->err_pos >= 0)
+						http_capture_bad_message(&s->be->invalid_rep, s, res, msg, old_state, s->fe);
 					goto return_bad_res;
 				}
 				return 1;