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

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 session *sess = s->sess;
struct http_txn *txn = s->txn; struct http_txn *txn = s->txn;
struct http_msg *msg = &s->txn->req; struct http_msg *msg = &s->txn->req;
struct htx *htx;
// TODO: Disabled for now
req->analyse_exp = TICK_ETERNITY; DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%lu analysers=%02x\n",
req->analysers &= ~an_bit; now_ms, __FUNCTION__,
return 1; 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. /* 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 * "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. * related structures are ready.
*/ */
if (msg->msg_state < HTTP_MSG_CHUNK_SIZE) { if (msg->msg_state < HTTP_MSG_DATA) {
/* This is the first call */ /* If we have HTTP/1.1 and Expect: 100-continue, then we must
if (msg->msg_state < HTTP_MSG_BODY) * send an HTTP/1.1 100 Continue intermediate response.
goto missing_data; */
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) { ctx.blk = NULL;
/* If we have HTTP/1.1 and Expect: 100-continue, then we must /* Expect is allowed in 1.1, look for it */
* send an HTTP/1.1 100 Continue intermediate response. if (http_find_header(htx, hdr, &ctx, 0) &&
*/ unlikely(isteqi(ctx.value, ist2("100-continue", 12)))) {
if (msg->flags & HTTP_MSGF_VER_11) { struct htx *rsp = htx_from_buf(&s->res.buf);
struct hdr_ctx ctx; struct htx_blk *blk;
ctx.idx = 0;
/* Expect is allowed in 1.1, look for it */ blk = htx_add_oob(rsp, HTTP_100);
if (http_find_header2("Expect", 6, ci_head(req), &txn->hdr_idx, &ctx) && if (!blk)
unlikely(ctx.vlen == 12 && strncasecmp(ctx.line+ctx.val, "100-continue", 12) == 0)) { goto missing_data;
co_inject(&s->res, HTTP_100.ptr, HTTP_100.len); b_set_data(&s->res.buf, b_size(&s->res.buf));
http_remove_header2(&txn->req, &txn->hdr_idx, &ctx); 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)) { msg->msg_state = HTTP_MSG_DATA;
/* 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;
/* OK we have everything we need now */ /* Now we're in HTTP_MSG_DATA. We just need to know if all data have
goto http_end; * been received or if the buffer is full.
}
/* 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.
*/ */
if (msg->msg_state == HTTP_MSG_TRAILERS) if (htx_get_tail_type(htx) >= HTX_BLK_EOD ||
goto http_end; htx_used_space(htx) + global.tune.maxrewrite >= htx->size)
if (http_body_bytes(msg) >= msg->body_len) /* we have enough bytes now */
goto http_end; goto http_end;
missing_data: 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)) { if ((req->flags & CF_READ_TIMEOUT) || tick_is_expired(req->analyse_exp, now_ms)) {
txn->status = 408; 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)) if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_CLITO; 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.err_state = txn->req.msg_state;
txn->req.msg_state = HTTP_MSG_ERROR; txn->req.msg_state = HTTP_MSG_ERROR;
txn->status = 400; 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)) if (!(s->flags & SF_ERR_MASK))
s->flags |= SF_ERR_PRXCOND; s->flags |= SF_ERR_PRXCOND;