[BUG] http: fix erroneous trailers size computation
We used to forward more trailers than required, causing a
desynchronization of the output. Now we schedule all for forwarding
as soon as we encounter them.
diff --git a/src/proto_http.c b/src/proto_http.c
index 6e2f4c6..d8dc711 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -1895,13 +1895,15 @@
* the trailers is found, it is automatically scheduled to be forwarded,
* msg->msg_state switches to HTTP_MSG_DONE, and the function returns >0.
* If not enough data are available, the function does not change anything
- * except maybe buf->lr if it could parse some lines, and returns zero. If
- * a parse error is encountered, the function returns < 0 and does not
- * change anything except maybe buf->lr. Note that the message must already
- * be in HTTP_MSG_TRAILERS state before calling this function, which implies
- * that all non-trailers data have already been scheduled for forwarding. It
- * is also important to note that this function is designed to be able to
- * parse wrapped headers at end of buffer.
+ * except maybe buf->lr and msg->sov if it could parse some lines, and returns
+ * zero. If a parse error is encountered, the function returns < 0 and does not
+ * change anything except maybe buf->lr and msg->sov. Note that the message
+ * must already be in HTTP_MSG_TRAILERS state before calling this function,
+ * which implies that all non-trailers data have already been scheduled for
+ * forwarding, and that the difference between msg->som and msg->sov exactly
+ * matches the length of trailers already parsed and not forwarded. It is also
+ * important to note that this function is designed to be able to parse wrapped
+ * headers at end of buffer.
*/
int http_forward_trailers(struct buffer *buf, struct http_msg *msg)
{
@@ -1909,6 +1911,7 @@
while (1) {
char *p1 = NULL, *p2 = NULL;
char *ptr = buf->lr;
+ int bytes;
/* scan current line and stop at LF or CRLF */
while (1) {
@@ -1938,18 +1941,21 @@
if (p2 >= buf->data + buf->size)
p2 = buf->data;
- if (p1 == buf->lr) {
- /* LF/CRLF at beginning of line => end of trailers at p2 */
- int bytes;
- buf->lr = p2;
+ bytes = p2 - buf->lr;
+ if (bytes < 0)
+ bytes += buf->size;
+
+ /* schedule this line for forwarding */
+ msg->sov += bytes;
+ if (msg->sov >= buf->size)
+ msg->sov -= buf->size;
- bytes = buf->lr - buf->data;
- /* Forward last remaining trailers. Note that since all the
- * bytes are included in the buffer and we've found the end,
- * we won't leave anything in to_forward.
+ if (p1 == buf->lr) {
+ /* LF/CRLF at beginning of line => end of trailers at p2.
+ * Everything was scheduled for forwarding, there's nothing
+ * left from this message.
*/
- buffer_forward(buf, bytes);
- msg->som = msg->sov = bytes;
+ buf->lr = p2;
msg->msg_state = HTTP_MSG_DONE;
return 1;
}
@@ -3272,16 +3278,16 @@
}
}
- /* we may already have some data pending */
- if (msg->hdr_content_len || msg->som != msg->sov) {
- buffer_forward(req, msg->sov - msg->som + msg->hdr_content_len);
- msg->hdr_content_len = 0; /* don't forward that again */
- msg->som = msg->sov;
- }
-
while (1) {
- // printf("req1: stq=%d, str=%d, l=%d, send_max=%d, fl=%08x, txf=%08x\n",
- // msg->msg_state, txn->rsp.msg_state, s->rep->l, s->rep->send_max, s->rep->flags, txn->flags);
+ /* we may have some data pending */
+ if (msg->hdr_content_len || msg->som != msg->sov) {
+ int bytes = msg->sov - msg->som;
+ if (bytes < 0) /* sov may have wrapped at the end */
+ bytes += req->size;
+ buffer_forward(req, bytes + msg->hdr_content_len);
+ msg->hdr_content_len = 0; /* don't forward that again */
+ msg->som = msg->sov;
+ }
if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->hdr_content_len, then
@@ -3295,13 +3301,6 @@
else if (ret < 0)
goto return_bad_req;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
-
- /* forward the chunk size as well as any pending data */
- if (msg->hdr_content_len || msg->som != msg->sov) {
- buffer_forward(req, msg->sov - msg->som + msg->hdr_content_len);
- msg->hdr_content_len = 0; /* don't forward that again */
- msg->som = msg->sov;
- }
}
else if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */
@@ -4380,16 +4379,17 @@
}
}
- /* we may already have some data pending */
- if (msg->hdr_content_len || msg->som != msg->sov) {
- buffer_forward(res, msg->sov - msg->som + msg->hdr_content_len);
- msg->hdr_content_len = 0; /* don't forward that again */
- msg->som = msg->sov;
- }
-
while (1) {
- // printf("res1: stq=%d, str=%d, l=%d, send_max=%d, fl=%08x, txf=%08x\n",
- // txn->req.msg_state, msg->msg_state, s->rep->l, s->rep->send_max, s->rep->flags, txn->flags);
+ /* we may have some data pending */
+ if (msg->hdr_content_len || msg->som != msg->sov) {
+ int bytes = msg->sov - msg->som;
+ if (bytes < 0) /* sov may have wrapped at the end */
+ bytes += res->size;
+ buffer_forward(res, bytes + msg->hdr_content_len);
+ msg->hdr_content_len = 0; /* don't forward that again */
+ msg->som = msg->sov;
+ }
+
if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->hdr_content_len, then
* set ->sov to point to the body and switch to DATA or TRAILERS state.
@@ -4401,13 +4401,6 @@
else if (ret < 0)
goto return_bad_res;
/* otherwise we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state */
-
- /* forward the chunk size as well as any pending data */
- if (msg->hdr_content_len || msg->som != msg->sov) {
- buffer_forward(res, msg->sov - msg->som + msg->hdr_content_len);
- msg->hdr_content_len = 0; /* don't forward that again */
- msg->som = msg->sov;
- }
}
else if (msg->msg_state == HTTP_MSG_DATA) {
/* must still forward */