MEDIUM: proto_htx: Adapt htx_wait_for_request_body to handle HTX messages

This version is simpler than the legacy one because the parsing is no more
handled by the analyzer. So now we just need to wait to have more data to move
on.
This commit is contained in:
Christopher Faulet 2018-10-24 11:16:22 +02:00 committed by Willy Tarreau
parent 8137c27094
commit f76ebe8bc7
1 changed files with 43 additions and 86 deletions

View File

@ -1029,11 +1029,22 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit)
struct session *sess = s->sess;
struct http_txn *txn = s->txn;
struct http_msg *msg = &s->txn->req;
struct htx *htx;
// TODO: Disabled for now
req->analyse_exp = TICK_ETERNITY;
req->analysers &= ~an_bit;
return 1;
DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%lu analysers=%02x\n",
now_ms, __FUNCTION__,
s,
req,
req->rex, req->wex,
req->flags,
ci_data(req),
req->analysers);
htx = htx_from_buf(&req->buf);
if (msg->msg_state < HTTP_MSG_BODY)
goto missing_data;
/* We have to parse the HTTP request body to find any required data.
* "balance url_param check_post" should have been the only way to get
@ -1041,99 +1052,45 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit)
* related structures are ready.
*/
if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) {
/* This is the first call */
if (msg->msg_state < HTTP_MSG_BODY)
goto missing_data;
if (msg->msg_state < HTTP_MSG_DATA) {
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
* send an HTTP/1.1 100 Continue intermediate response.
*/
if (msg->flags & HTTP_MSGF_VER_11) {
struct ist hdr = { .ptr = "Expect", .len = 6 };
struct http_hdr_ctx ctx;
if (msg->msg_state < HTTP_MSG_100_SENT) {
/* If we have HTTP/1.1 and Expect: 100-continue, then we must
* send an HTTP/1.1 100 Continue intermediate response.
*/
if (msg->flags & HTTP_MSGF_VER_11) {
struct hdr_ctx ctx;
ctx.idx = 0;
/* Expect is allowed in 1.1, look for it */
if (http_find_header2("Expect", 6, ci_head(req), &txn->hdr_idx, &ctx) &&
unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0)) {
co_inject(&s->res, HTTP_100.ptr, HTTP_100.len);
http_remove_header2(&txn->req, &txn->hdr_idx, &ctx);
}
ctx.blk = NULL;
/* Expect is allowed in 1.1, look for it */
if (http_find_header(htx, hdr, &ctx, 0) &&
unlikely(isteqi(ctx.value, ist2("100-continue", 12)))) {
struct htx *rsp = htx_from_buf(&s->res.buf);
struct htx_blk *blk;
blk = htx_add_oob(rsp, HTTP_100);
if (!blk)
goto missing_data;
b_set_data(&s->res.buf, b_size(&s->res.buf));
c_adv(&s->res, HTTP_100.len);
s->res.total += HTTP_100.len;
http_remove_header(htx, &ctx);
}
msg->msg_state = HTTP_MSG_100_SENT;
}
/* we have msg->sov which points to the first byte of message body.
* ci_head(req) still points to the beginning of the message. We
* must save the body in msg->next because it survives buffer
* re-alignments.
*/
msg->next = msg->sov;
if (msg->flags & HTTP_MSGF_TE_CHNK)
msg->msg_state = HTTP_MSG_CHUNK_SIZE;
else
msg->msg_state = HTTP_MSG_DATA;
}
if (!(msg->flags & HTTP_MSGF_TE_CHNK)) {
/* We're in content-length mode, we just have to wait for enough data. */
if (http_body_bytes(msg) < msg->body_len)
goto missing_data;
msg->msg_state = HTTP_MSG_DATA;
/* OK we have everything we need now */
goto http_end;
}
/* OK here we're parsing a chunked-encoded message */
if (msg->msg_state == HTTP_MSG_CHUNK_SIZE) {
/* read the chunk size and assign it to ->chunk_len, then
* set ->sov and ->next to point to the body and switch to DATA or
* TRAILERS state.
*/
unsigned int chunk;
int ret = h1_parse_chunk_size(&req->buf, co_data(req) + msg->next, c_data(req), &chunk);
if (!ret)
goto missing_data;
else if (ret < 0) {
msg->err_pos = ci_data(req) + ret;
if (msg->err_pos < 0)
msg->err_pos += req->buf.size;
stream_inc_http_err_ctr(s);
goto return_bad_req;
}
msg->chunk_len = chunk;
msg->body_len += chunk;
msg->sol = ret;
msg->next += ret;
msg->msg_state = msg->chunk_len ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
}
/* Now we're in HTTP_MSG_DATA or HTTP_MSG_TRAILERS state.
* We have the first data byte is in msg->sov + msg->sol. We're waiting
* for at least a whole chunk or the whole content length bytes after
* msg->sov + msg->sol.
/* Now we're in HTTP_MSG_DATA. We just need to know if all data have
* been received or if the buffer is full.
*/
if (msg->msg_state == HTTP_MSG_TRAILERS)
goto http_end;
if (http_body_bytes(msg) >= msg->body_len) /* we have enough bytes now */
if (htx_get_tail_type(htx) >= HTX_BLK_EOD ||
htx_used_space(htx) + global.tune.maxrewrite >= htx->size)
goto http_end;
missing_data:
/* we get here if we need to wait for more data. If the buffer is full,
* we have the maximum we can expect.
*/
if (channel_full(req, global.tune.maxrewrite))
goto http_end;
if ((req->flags & CF_READ_TIMEOUT) || tick_is_expired(req->analyse_exp, now_ms)) {
txn->status = 408;
http_reply_and_close(s, txn->status, http_error_message(s));
htx_reply_and_close(s, txn->status, http_error_message(s));
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_CLITO;
@ -1167,7 +1124,7 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit)
txn->req.err_state = txn->req.msg_state;
txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 400;
http_reply_and_close(s, txn->status, http_error_message(s));
htx_reply_and_close(s, txn->status, http_error_message(s));
if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND;