}
}
+/* Estimate the size of the HTX request after the parsing. */
+static size_t h1_eval_htx_req_size(struct h1m *h1m, union h1_sl *h1sl, struct http_hdr *hdrs)
+{
+ size_t sz;
+ int i;
+
+ /* size of the HTX start-line */
+ sz = sizeof(struct htx_sl) + h1sl->rq.m.len + h1sl->rq.u.len + h1sl->rq.v.len;
+
+ /* size of all HTX headers */
+ for (i = 0; hdrs[i].n.len; i++)
+ sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
+
+ /* size of the EOH */
+ sz += sizeof(struct htx_blk) + 1;
+
+ /* size of the EOM */
+ if (h1m->state == H1_MSG_DONE)
+ sz += sizeof(struct htx_blk) + 1;
+
+ return sz;
+}
+
+/* Estimate the size of the HTX response after the parsing. */
+static size_t h1_eval_htx_res_size(struct h1m *h1m, union h1_sl *h1sl, struct http_hdr *hdrs)
+{
+ size_t sz;
+ int i;
+
+ /* size of the HTX start-line */
+ sz = sizeof(struct htx_sl) + h1sl->st.v.len + h1sl->st.c.len + h1sl->st.r.len;
+
+ /* size of all HTX headers */
+ for (i = 0; hdrs[i].n.len; i++)
+ sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
+
+ /* size of the EOH */
+ sz += sizeof(struct htx_blk) + 1;
+
+ /* size of the EOM */
+ if (h1m->state == H1_MSG_DONE)
+ sz += sizeof(struct htx_blk) + 1;
+
+ return sz;
+}
+
/*
* Handle 100-Continue responses or any other informational 1xx responses which
* is non-final. In such case, this function reset the response parser. It is
unsigned int flags = HTX_SL_F_NONE;
int ret = 0;
- if (!max)
+ if (!max || !b_data(buf))
goto end;
/* Realing input buffer if necessary */
goto h2c_upgrade;
}
- ret = h1_headers_to_hdr_list(b_peek(buf, *ofs), b_peek(buf, *ofs) + max,
+ ret = h1_headers_to_hdr_list(b_peek(buf, *ofs), b_tail(buf),
hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, &h1sl);
if (ret <= 0) {
/* Incomplete or invalid message. If the buffer is full, it's an
* parsing.
*/
- /* Be sure to keep some space to do headers rewritting */
- if (ret > (b_size(buf) - global.tune.maxrewrite))
- goto error;
-
/* Save the request's method or the response's status, check if the body
* length is known and check the VSN validity */
if (!(h1m->flags & H1_MF_RESP)) {
if (!(h1m->flags & H1_MF_RESP)) {
struct htx_sl *sl;
+ if (h1_eval_htx_req_size(h1m, &h1sl, hdrs) > max)
+ goto error;
+
sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
if (!sl || !htx_add_all_headers(htx, hdrs))
goto error;
else {
struct htx_sl *sl;
+ if (h1_eval_htx_res_size(h1m, &h1sl, hdrs) > max)
+ goto error;
+
flags |= HTX_SL_F_IS_RESP;
sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
if (!sl || !htx_add_all_headers(htx, hdrs))
* ULLONG_MAX. This value is impossible in other cases.
*/
htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
-
- /* Recheck there is enough space to do headers rewritting */
- if (htx_used_space(htx) > b_size(buf) - global.tune.maxrewrite)
- goto error;
-
*ofs += ret;
end:
return ret;
*/
static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
struct buffer *buf, size_t *ofs, size_t max,
- struct buffer *htxbuf, size_t reserve)
+ struct buffer *htxbuf)
{
- uint32_t data_space;
size_t total = 0;
- int ret = 0;
-
- data_space = htx_free_data_space(htx);
- if (data_space <= reserve)
- goto end;
- data_space -= reserve;
+ int32_t ret = 0;
if (h1m->flags & H1_MF_XFER_LEN) {
if (h1m->flags & H1_MF_CLEN) {
/* content-length: read only h2m->body_len */
- ret = max;
- if (ret > data_space)
- ret = data_space;
+ ret = htx_get_max_blksz(htx, max);
if ((uint64_t)ret > h1m->curr_len)
ret = h1m->curr_len;
if (ret > b_contig_data(buf, *ofs))
ret = b_contig_data(buf, *ofs);
+
if (ret) {
/* very often with large files we'll face the following
* situation :
else if (!htx_add_data(htx, ist2(b_peek(buf, *ofs), ret)))
goto end;
h1m->curr_len -= ret;
+ max -= sizeof(struct htx_blk) + ret;
*ofs += ret;
total += ret;
}
if (!h1m->curr_len) {
- if (!htx_add_endof(htx, HTX_BLK_EOM))
+ if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
goto end;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
new_chunk:
/* te:chunked : parse chunks */
if (h1m->state == H1_MSG_CHUNK_CRLF) {
- ret = h1_skip_chunk_crlf(buf, *ofs, *ofs + max);
+ ret = h1_skip_chunk_crlf(buf, *ofs, b_data(buf));
if (ret <= 0)
goto end;
h1m->state = H1_MSG_CHUNK_SIZE;
- max -= ret;
*ofs += ret;
total += ret;
}
if (h1m->state == H1_MSG_CHUNK_SIZE) {
unsigned int chksz;
- ret = h1_parse_chunk_size(buf, *ofs, *ofs + max, &chksz);
+ ret = h1_parse_chunk_size(buf, *ofs, b_data(buf), &chksz);
if (ret <= 0)
goto end;
if (!chksz) {
- if (!htx_add_endof(htx, HTX_BLK_EOD))
+ if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOD))
goto end;
h1s->flags |= H1S_F_HAVE_I_EOD;
h1m->state = H1_MSG_TRAILERS;
+ max -= sizeof(struct htx_blk) + 1;
}
else
h1m->state = H1_MSG_DATA;
h1m->curr_len = chksz;
h1m->body_len += chksz;
- max -= ret;
*ofs += ret;
total += ret;
}
if (h1m->state == H1_MSG_DATA) {
- ret = max;
- if (ret > data_space)
- ret = data_space;
+ ret = htx_get_max_blksz(htx, max);
if ((uint64_t)ret > h1m->curr_len)
ret = h1m->curr_len;
if (ret > b_contig_data(buf, *ofs))
ret = b_contig_data(buf, *ofs);
+
if (ret) {
if (!htx_add_data(htx, ist2(b_peek(buf, *ofs), ret)))
goto end;
h1m->curr_len -= ret;
- max -= ret;
+ max -= sizeof(struct htx_blk) + ret;
*ofs += ret;
total += ret;
}
if (!h1m->curr_len) {
h1m->state = H1_MSG_CHUNK_CRLF;
- data_space = htx_free_data_space(htx);
- if (data_space <= reserve)
- goto end;
- data_space -= reserve;
goto new_chunk;
}
goto end;
if (h1s->flags & H1S_F_HAVE_I_TLR)
goto skip_tlr_parsing;
- ret = h1_measure_trailers(buf, *ofs, max);
- if (ret > data_space)
- ret = (htx_is_empty(htx) ? -1 : 0);
- if (ret <= 0)
+ ret = htx_get_max_blksz(htx, max);
+ ret = h1_measure_trailers(buf, *ofs, ret);
+ if (ret <= 0) {
+ if (!ret && b_full(buf))
+ ret = -1;
goto end;
+ }
/* Realing input buffer if tailers wrap. For now
* this is a workaroung. Because trailers are
if (!htx_add_trailer(htx, ist2(b_peek(buf, *ofs), ret)))
goto end;
h1s->flags |= H1S_F_HAVE_I_TLR;
- max -= ret;
+ max -= sizeof(struct htx_blk) + ret;
*ofs += ret;
total += ret;
skip_tlr_parsing:
- if (!htx_add_endof(htx, HTX_BLK_EOM))
+ if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
goto end;
+ max -= sizeof(struct htx_blk) + 1;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
}
/* XFER_LEN is set but not CLEN nor CHNK, it means there
* is no body. Switch the message in DONE state
*/
- if (!htx_add_endof(htx, HTX_BLK_EOM))
+ if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
goto end;
+ max -= sizeof(struct htx_blk) + 1;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
}
}
else {
/* no content length, read till SHUTW */
- ret = max;
- if (ret > data_space)
- ret = data_space;
+ ret = htx_get_max_blksz(htx, max);
if (ret > b_contig_data(buf, *ofs))
ret = b_contig_data(buf, *ofs);
+
if (ret) {
if (!htx_add_data(htx, ist2(b_peek(buf, *ofs), ret)))
goto end;
* <buf>. It returns the number of bytes parsed and transferred if > 0, or 0 if
* it couldn't proceed.
*/
-static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, int flags)
+static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count)
{
struct h1s *h1s = h1c->h1s;
struct h1m *h1m;
struct htx *htx;
- size_t data = 0;
+ size_t ret, data;
size_t total = 0;
- size_t ret = 0;
- size_t count, rsv;
int errflag;
htx = htx_from_buf(buf);
}
data = htx->data;
- count = b_data(&h1c->ibuf);
- rsv = ((flags & CO_RFL_KEEP_RSV) ? global.tune.maxrewrite : 0);
-
- if (htx_is_empty(htx))
- h1_handle_1xx_response(h1s, h1m);
-
do {
+ size_t used = htx_used_space(htx);
+
if (h1m->state <= H1_MSG_LAST_LF) {
ret = h1_process_headers(h1s, h1m, htx, &h1c->ibuf, &total, count);
if (!ret)
break;
}
else if (h1m->state <= H1_MSG_TRAILERS) {
- ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf, rsv);
+ ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf);
htx = htx_from_buf(buf);
if (!ret)
break;
break;
}
else if (h1m->state == H1_MSG_TUNNEL) {
- ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf, rsv);
+ ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf);
htx = htx_from_buf(buf);
if (!ret)
break;
break;
}
- count -= ret;
+ count -= htx_used_space(htx) - used;
} while (!(h1s->flags & errflag) && count);
if (h1s->flags & errflag)
end:
htx_to_buf(htx, buf);
- data = (htx->data - data);
+ ret = htx->data - data;
if (h1c->flags & H1C_F_IN_FULL && buf_room_for_htx_data(&h1c->ibuf)) {
h1c->flags &= ~H1C_F_IN_FULL;
tasklet_wakeup(h1c->wait_event.task);
h1s->cs->flags |= CS_FL_ERROR;
}
- return data;
+ return ret;
parsing_err:
b_reset(&h1c->ibuf);
size_t ret = 0;
if (!(h1c->flags & H1C_F_IN_ALLOC))
- ret = h1_process_input(h1c, buf, flags);
+ ret = h1_process_input(h1c, buf, count);
if (flags & CO_RFL_BUF_FLUSH) {
struct h1m *h1m = (!conn_is_back(cs->conn) ? &h1s->req : &h1s->res);