You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
8.4 KiB

  1. From 5bebcd06287be9024f0fba25f350393f02e050c1 Mon Sep 17 00:00:00 2001
  2. From: Willy Tarreau <w@1wt.eu>
  3. Date: Thu, 10 Jul 2014 19:06:10 +0200
  4. Subject: [PATCH 24/25] BUG/MAJOR: http: correctly rewind the request body
  5. after start of forwarding
  6. Daniel Dubovik reported an interesting bug showing that the request body
  7. processing was still not 100% fixed. If a POST request contained short
  8. enough data to be forwarded at once before trying to establish the
  9. connection to the server, we had no way to correctly rewind the body.
  10. The first visible case is that balancing on a header does not always work
  11. on such POST requests since the header cannot be found. But there are even
  12. nastier implications which are that http-send-name-header would apply to
  13. the wrong location and possibly even affect part of the request's body
  14. due to an incorrect rewinding.
  15. There are two options to fix the problem :
  16. - first one is to force the HTTP_MSG_F_WAIT_CONN flag on all hash-based
  17. balancing algorithms and http-send-name-header, but there's always a
  18. risk that any new algorithm forgets to set it ;
  19. - the second option is to account for the amount of skipped data before
  20. the connection establishes so that we always know the position of the
  21. request's body relative to the buffer's origin.
  22. The second option is much more reliable and fits very well in the spirit
  23. of the past changes to fix forwarding. Indeed, at the moment we have
  24. msg->sov which points to the start of the body before headers are forwarded
  25. and which equals zero afterwards (so it still points to the start of the
  26. body before forwarding data). A minor change consists in always making it
  27. point to the start of the body even after data have been forwarded. It means
  28. that it can get a negative value (so we need to change its type to signed)..
  29. In order to avoid wrapping, we only do this as long as the other side of
  30. the buffer is not connected yet.
  31. Doing this definitely fixes the issues above for the requests. Since the
  32. response cannot be rewound we don't need to perform any change there.
  33. This bug was introduced/remained unfixed in 1.5-dev23 so the fix must be
  34. backported to 1.5.
  35. (cherry picked from commit bb2e669f9e73531ac9cc9277b40066b701eec918)
  36. ---
  37. doc/internals/body-parsing.txt | 20 +++++++++++++-------
  38. include/types/proto_http.h | 11 ++++++-----
  39. src/proto_http.c | 9 +++++++--
  40. 3 files changed, 26 insertions(+), 14 deletions(-)
  41. diff --git a/doc/internals/body-parsing.txt b/doc/internals/body-parsing.txt
  42. index e9c8b4b..5baa549 100644
  43. --- a/doc/internals/body-parsing.txt
  44. +++ b/doc/internals/body-parsing.txt
  45. @@ -67,12 +67,17 @@ msg.next : points to the next byte to inspect. This offset is automatically
  46. automatically adjusted to the number of bytes already inspected.
  47. msg.sov : start of value. First character of the header's value in the header
  48. - states, start of the body in the data states until headers are
  49. - forwarded. This offset is automatically adjusted when inserting or
  50. - removing some headers. In data states, it always constains the size
  51. - of the whole HTTP headers (including the trailing CRLF) that needs
  52. - to be forwarded before the first byte of body. Once the headers are
  53. - forwarded, this value drops to zero.
  54. + states, start of the body in the data states. Strictly positive
  55. + values indicate that headers were not forwarded yet (<buf.p> is
  56. + before the start of the body), and null or positive values are seen
  57. + after headers are forwarded (<buf.p> is at or past the start of the
  58. + body). The value stops changing when data start to leave the buffer
  59. + (in order to avoid integer overflows). So the maximum possible range
  60. + is -<buf.size> to +<buf.size>. This offset is automatically adjusted
  61. + when inserting or removing some headers. It is useful to rewind the
  62. + request buffer to the beginning of the body at any phase. The
  63. + response buffer does not really use it since it is immediately
  64. + forwarded to the client.
  65. msg.sol : start of line. Points to the beginning of the current header line
  66. while parsing headers. It is cleared to zero in the BODY state,
  67. @@ -97,7 +102,8 @@ msg.eol : end of line. Points to the CRLF or LF of the current header line
  68. states nor by forwarding.
  69. The beginning of the message headers can always be found this way even after
  70. -headers have been forwarded :
  71. +headers or data have been forwarded, provided that everything is still present
  72. +in the buffer :
  73. headers = buf.p + msg->sov - msg->eoh - msg->eol
  74. diff --git a/include/types/proto_http.h b/include/types/proto_http.h
  75. index 12e1141..c53c7fd 100644
  76. --- a/include/types/proto_http.h
  77. +++ b/include/types/proto_http.h
  78. @@ -329,7 +329,8 @@ enum {
  79. * message or a response message.
  80. *
  81. * The values there are a little bit obscure, because their meaning can change
  82. - * during the parsing :
  83. + * during the parsing. Please read carefully doc/internal/body-parsing.txt if
  84. + * you need to manipulate them. Quick reminder :
  85. *
  86. * - eoh (End of Headers) : relative offset in the buffer of first byte that
  87. * is not part of a completely processed header.
  88. @@ -344,9 +345,9 @@ enum {
  89. * - sov (start of value) : Before HTTP_MSG_BODY, points to the value of
  90. * the header being parsed. Starting from
  91. * HTTP_MSG_BODY, will point to the start of the
  92. - * body (relative to buffer's origin), or to data
  93. - * following a chunk size. Thus <sov> bytes of
  94. - * headers will have to be sent only once.
  95. + * body (relative to buffer's origin). It can be
  96. + * negative when forwarding data. It stops growing
  97. + * once data start to leave the buffer.
  98. *
  99. * - next (parse pointer) : next relative byte to be parsed. Always points
  100. * to a byte matching the current state.
  101. @@ -372,7 +373,7 @@ struct http_msg {
  102. /* 6 bytes unused here */
  103. struct channel *chn; /* pointer to the channel transporting the message */
  104. unsigned int next; /* pointer to next byte to parse, relative to buf->p */
  105. - unsigned int sov; /* current header: start of value */
  106. + int sov; /* current header: start of value ; data: start of body */
  107. unsigned int eoh; /* End Of Headers, relative to buffer */
  108. unsigned int sol; /* start of current line during parsing otherwise zero */
  109. unsigned int eol; /* end of line */
  110. diff --git a/src/proto_http.c b/src/proto_http.c
  111. index 4a862b0..94afed7 100644
  112. --- a/src/proto_http.c
  113. +++ b/src/proto_http.c
  114. @@ -5315,7 +5315,7 @@ int http_request_forward_body(struct session *s, struct channel *req, int an_bit
  115. * an "Expect: 100-continue" header.
  116. */
  117. - if (msg->sov) {
  118. + if (msg->sov > 0) {
  119. /* we have msg->sov which points to the first byte of message
  120. * body, and req->buf.p still points to the beginning of the
  121. * message. We forward the headers now, as we don't need them
  122. @@ -5429,6 +5429,8 @@ int http_request_forward_body(struct session *s, struct channel *req, int an_bit
  123. * such as last chunk of data or trailers.
  124. */
  125. b_adv(req->buf, msg->next);
  126. + if (unlikely(!(s->rep->flags & CF_READ_ATTACHED)))
  127. + msg->sov -= msg->next;
  128. msg->next = 0;
  129. /* for keep-alive we don't want to forward closes on DONE */
  130. @@ -5479,6 +5481,9 @@ int http_request_forward_body(struct session *s, struct channel *req, int an_bit
  131. missing_data:
  132. /* we may have some pending data starting at req->buf->p */
  133. b_adv(req->buf, msg->next);
  134. + if (unlikely(!(s->rep->flags & CF_READ_ATTACHED)))
  135. + msg->sov -= msg->next + MIN(msg->chunk_len, req->buf->i);
  136. +
  137. msg->next = 0;
  138. msg->chunk_len -= channel_forward(req, msg->chunk_len);
  139. @@ -6493,7 +6498,7 @@ int http_response_forward_body(struct session *s, struct channel *res, int an_bi
  140. /* in most states, we should abort in case of early close */
  141. channel_auto_close(res);
  142. - if (msg->sov) {
  143. + if (msg->sov > 0) {
  144. /* we have msg->sov which points to the first byte of message
  145. * body, and res->buf.p still points to the beginning of the
  146. * message. We forward the headers now, as we don't need them
  147. --
  148. 1.8.5.5