mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-02 11:33:21 +00:00
[MEDIUM] http: rework the buffer alignment logic
There were still issues with the buffer alignment. Now we ensure that we always align it before a request or response is completely parsed if there is less than maxrewrite bytes free at the end. In practice, it's not called that often and ensures we can always work as expected.
This commit is contained in:
parent
864e8256ec
commit
83e3af0c86
147
src/proto_http.c
147
src/proto_http.c
@ -1979,6 +1979,57 @@ int http_skip_chunk_crlf(struct buffer *buf, struct http_msg *msg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void http_buffer_heavy_realign(struct buffer *buf, struct http_msg *msg)
|
||||
{
|
||||
char *end = buf->data + buf->size;
|
||||
int off = buf->data + buf->size - buf->w;
|
||||
|
||||
/* two possible cases :
|
||||
* - the buffer is in one contiguous block, we move it in-place
|
||||
* - the buffer is in two blocks, we move it via the trash
|
||||
*/
|
||||
if (buf->l) {
|
||||
int block1 = buf->l;
|
||||
int block2 = 0;
|
||||
if (buf->r <= buf->w) {
|
||||
/* non-contiguous block */
|
||||
block1 = buf->data + buf->size - buf->w;
|
||||
block2 = buf->r - buf->data;
|
||||
}
|
||||
if (block2)
|
||||
memcpy(trash, buf->data, block2);
|
||||
memmove(buf->data, buf->w, block1);
|
||||
if (block2)
|
||||
memcpy(buf->data + block1, trash, block2);
|
||||
}
|
||||
|
||||
/* adjust all known pointers */
|
||||
buf->w = buf->data;
|
||||
buf->lr += off; if (buf->lr >= end) buf->lr -= buf->size;
|
||||
buf->r += off; if (buf->r >= end) buf->r -= buf->size;
|
||||
msg->sol += off; if (msg->sol >= end) msg->sol -= buf->size;
|
||||
msg->eol += off; if (msg->eol >= end) msg->eol -= buf->size;
|
||||
|
||||
/* adjust relative pointers */
|
||||
msg->som = 0;
|
||||
msg->eoh += off; if (msg->eoh >= buf->size) msg->eoh -= buf->size;
|
||||
msg->col += off; if (msg->col >= buf->size) msg->col -= buf->size;
|
||||
msg->sov += off; if (msg->sov >= buf->size) msg->sov -= buf->size;
|
||||
|
||||
msg->sl.rq.u += off; if (msg->sl.rq.u >= buf->size) msg->sl.rq.u -= buf->size;
|
||||
msg->sl.rq.v += off; if (msg->sl.rq.v >= buf->size) msg->sl.rq.v -= buf->size;
|
||||
|
||||
if (msg->err_pos >= 0) {
|
||||
msg->err_pos += off;
|
||||
if (msg->err_pos >= buf->size)
|
||||
msg->err_pos -= buf->size;
|
||||
}
|
||||
|
||||
buf->flags &= ~BF_FULL;
|
||||
if (buf->l >= buffer_max_len(buf))
|
||||
buf->flags |= BF_FULL;
|
||||
}
|
||||
|
||||
/* This stream analyser waits for a complete HTTP request. It returns 1 if the
|
||||
* processing can continue on next analysers, or zero if it either needs more
|
||||
* data or wants to immediately abort the request (eg: timeout, error, ...). It
|
||||
@ -1998,8 +2049,8 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
|
||||
*
|
||||
* Here is the information we currently have :
|
||||
* req->data + msg->som = beginning of request
|
||||
* req->data + req->eoh = end of processed headers / start of current one
|
||||
* req->data + req->eol = end of current header or line (LF or CRLF)
|
||||
* req->data + msg->eoh = end of processed headers / start of current one
|
||||
* msg->eol = end of current header or line (LF or CRLF)
|
||||
* req->lr = first non-visited byte
|
||||
* req->r = end of data
|
||||
*
|
||||
@ -2027,7 +2078,23 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
|
||||
/* we're speaking HTTP here, so let's speak HTTP to the client */
|
||||
s->srv_error = http_return_srv_error;
|
||||
|
||||
http_parse_again:
|
||||
/* There's a protected area at the end of the buffer for rewriting
|
||||
* purposes. We don't want to start to parse the request if the
|
||||
* protected area is affected, because we may have to move processed
|
||||
* data later, which is much more complicated.
|
||||
*/
|
||||
if (req->l &&
|
||||
(req->r <= req->lr || req->r > req->data + req->size - global.tune.maxrewrite)) {
|
||||
if (req->send_max) {
|
||||
/* some data has still not left the buffer, wake us once that's done */
|
||||
buffer_dont_connect(req);
|
||||
req->flags |= BF_READ_DONTWAIT; /* try to get back here ASAP */
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_buffer_heavy_realign(req, msg);
|
||||
}
|
||||
|
||||
if (likely(req->lr < req->r))
|
||||
http_msg_analyzer(req, msg, &txn->hdr_idx);
|
||||
|
||||
@ -2075,58 +2142,6 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit)
|
||||
* must terminate it now.
|
||||
*/
|
||||
if (unlikely(req->flags & BF_FULL)) {
|
||||
/* It is possible the analyser has stopped because of
|
||||
* lack of space at the end of the buffer, while the
|
||||
* beginning is free (skipped blanks or previous
|
||||
* requests vanished). In this case, we try to re-aling
|
||||
* the buffer, and update the few relevant pointers.
|
||||
* Since we're copying via the trash, it's important that
|
||||
* it is always as large as a buffer.
|
||||
*/
|
||||
if (req->l < buffer_max_len(req)) {
|
||||
/* let's move what remains */
|
||||
int max, done;
|
||||
unsigned offset = req->w - req->data;
|
||||
|
||||
done = 0;
|
||||
while (done < req->l) {
|
||||
if (req->r > req->w)
|
||||
max = req->r - req->w;
|
||||
else
|
||||
max = req->data + req->size - req->w;
|
||||
memcpy(trash + done, req->w, max);
|
||||
done += max;
|
||||
req->w += max;
|
||||
if (req->w >= req->data + req->size)
|
||||
req->w -= req->size;
|
||||
}
|
||||
if (done)
|
||||
memcpy(req->data, trash, done);
|
||||
msg->eoh = msg->som = 0;
|
||||
|
||||
/* move all known pointers */
|
||||
req->w = req->data;
|
||||
req->lr -= offset; if (req->lr < req->data) req->lr += req->size;
|
||||
req->r -= offset; if (req->r < req->data) req->r += req->size;
|
||||
msg->sol -= offset; if (msg->sol < req->data) msg->sol += req->size;
|
||||
msg->eol -= offset; if (msg->eol < req->data) msg->eol += req->size;
|
||||
|
||||
/* adjust relative pointers */
|
||||
msg->col += req->size - offset; if (msg->col >= req->size) msg->col -= req->size;
|
||||
msg->sov += req->size - offset; if (msg->sov >= req->size) msg->sov -= req->size;
|
||||
msg->sl.rq.u += req->size - offset; if (msg->sl.rq.u >= req->size) msg->sl.rq.u -= req->size;
|
||||
msg->sl.rq.v += req->size - offset; if (msg->sl.rq.v >= req->size) msg->sl.rq.v -= req->size;
|
||||
|
||||
if (msg->err_pos >= 0) {
|
||||
msg->err_pos += req->size - offset;
|
||||
if (msg->err_pos >= req->size)
|
||||
msg->err_pos -= req->size;
|
||||
}
|
||||
|
||||
req->flags &= ~BF_FULL;
|
||||
goto http_parse_again;
|
||||
}
|
||||
|
||||
/* FIXME: check if URI is set and return Status
|
||||
* 414 Request URI too long instead.
|
||||
*/
|
||||
@ -3374,13 +3389,29 @@ int http_wait_for_response(struct session *s, struct buffer *rep, int an_bit)
|
||||
* For the parsing, we use a 28 states FSM.
|
||||
*
|
||||
* Here is the information we currently have :
|
||||
* rep->data + rep->som = beginning of response
|
||||
* rep->data + rep->eoh = end of processed headers / start of current one
|
||||
* rep->data + rep->eol = end of current header or line (LF or CRLF)
|
||||
* rep->data + msg->som = beginning of response
|
||||
* rep->data + msg->eoh = end of processed headers / start of current one
|
||||
* msg->eol = end of current header or line (LF or CRLF)
|
||||
* rep->lr = first non-visited byte
|
||||
* rep->r = end of data
|
||||
*/
|
||||
|
||||
/* There's a protected area at the end of the buffer for rewriting
|
||||
* purposes. We don't want to start to parse the request if the
|
||||
* protected area is affected, because we may have to move processed
|
||||
* data later, which is much more complicated.
|
||||
*/
|
||||
if (rep->l &&
|
||||
(rep->r <= rep->lr || rep->r > rep->data + rep->size - global.tune.maxrewrite)) {
|
||||
if (rep->send_max) {
|
||||
/* some data has still not left the buffer, wake us once that's done */
|
||||
buffer_dont_close(rep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_buffer_heavy_realign(rep, msg);
|
||||
}
|
||||
|
||||
if (likely(rep->lr < rep->r))
|
||||
http_msg_analyzer(rep, msg, &txn->hdr_idx);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user