diff --git a/include/common/http.h b/include/common/http.h index 0835d078d..66acfd32d 100644 --- a/include/common/http.h +++ b/include/common/http.h @@ -119,6 +119,7 @@ struct http_method_desc { }; extern const int http_err_codes[HTTP_ERR_SIZE]; +extern const char *http_err_msgs[HTTP_ERR_SIZE]; extern struct buffer http_err_chunks[HTTP_ERR_SIZE]; const struct ist http_known_methods[HTTP_METH_OTHER]; extern const uint8_t http_char_classes[256]; diff --git a/include/proto/http_htx.h b/include/proto/http_htx.h index 9afea5c99..7a2e008ae 100644 --- a/include/proto/http_htx.h +++ b/include/proto/http_htx.h @@ -24,10 +24,13 @@ #define _PROTO_HTTP_HTX_H #include +#include #include #include +extern struct buffer htx_err_chunks[HTTP_ERR_SIZE]; + struct htx_sl *http_find_stline(struct htx *htx); int http_find_header(const struct htx *htx, const struct ist name, struct http_hdr_ctx *ctx, int full); int http_add_header(struct htx *htx, const struct ist n, const struct ist v); diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 7546f0197..73946198a 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -78,7 +78,7 @@ int htx_send_name_header(struct stream *s, struct proxy *be, const char *srv_nam void htx_perform_server_redirect(struct stream *s, struct stream_interface *si); void htx_server_error(struct stream *s, struct stream_interface *si, int err, int finst, const struct buffer *msg); void htx_reply_and_close(struct stream *s, short status, struct buffer *msg); - +struct buffer *htx_error_message(struct stream *s); void debug_hdr(const char *dir, struct stream *s, const char *start, const char *end); int apply_filter_to_req_headers(struct stream *s, struct channel *req, struct hdr_exp *exp); diff --git a/src/http.c b/src/http.c index a556a88a1..bd8f96f57 100644 --- a/src/http.c +++ b/src/http.c @@ -230,7 +230,7 @@ const int http_err_codes[HTTP_ERR_SIZE] = { [HTTP_ERR_504] = 504, }; -static const char *http_err_msgs[HTTP_ERR_SIZE] = { +const char *http_err_msgs[HTTP_ERR_SIZE] = { [HTTP_ERR_200] = "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n" diff --git a/src/http_htx.c b/src/http_htx.c index 82f9497d9..58629785c 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -11,11 +11,15 @@ */ #include +#include #include +#include #include #include +struct buffer htx_err_chunks[HTTP_ERR_SIZE]; + /* Finds the start line in the HTX message stopping at the first * end-of-message. It returns NULL when not found, otherwise, it returns the * pointer on the htx_sl structure. The HTX message may be updated if the @@ -614,3 +618,117 @@ unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr, *vlen = val_hist[hist_idx].len; return 1; } + +static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw) +{ + struct htx *htx; + struct htx_sl *sl; + struct h1m h1m; + struct http_hdr hdrs[MAX_HTTP_HDR]; + union h1_sl h1sl; + unsigned int flags = HTX_SL_F_IS_RESP; + int ret = 0; + + buf->size = global.tune.bufsize; + buf->area = (char *)malloc(buf->size); + if (!buf->area) + goto error; + b_reset(buf); + + h1m_init_res(&h1m); + h1m.flags |= H1_MF_NO_PHDR; + ret = h1_headers_to_hdr_list(raw.ptr, raw.ptr + raw.len, + hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl); + if (ret <= 0) + goto error; + + if (unlikely(h1sl.st.v.len != 8)) + goto error; + if ((*(h1sl.st.v.ptr + 5) > '1') || + ((*(h1sl.st.v.ptr + 5) == '1') && (*(h1sl.st.v.ptr + 7) >= '1'))) + h1m.flags |= H1_MF_VER_11; + + if (h1m.flags & H1_MF_VER_11) + flags |= HTX_SL_F_VER_11; + if (h1m.flags & H1_MF_XFER_ENC) + flags |= HTX_SL_F_XFER_ENC; + if (h1m.flags & H1_MF_XFER_LEN) { + flags |= HTX_SL_F_XFER_LEN; + if (h1m.flags & H1_MF_CHNK) + goto error; /* Unsupported because there is no body parsing */ + else if (h1m.flags & H1_MF_CLEN) { + flags |= HTX_SL_F_CLEN; + if (h1m.body_len == 0) + flags |= HTX_SL_F_BODYLESS; + } + } + + htx = htx_from_buf(buf); + 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)) + goto error; + sl->info.res.status = h1sl.st.status; + + if (raw.len > ret) { + if (!htx_add_data(htx, ist2(raw.ptr + ret, raw.len - ret))) + goto error; + } + if (!htx_add_endof(htx, HTX_BLK_EOM)) + goto error; + + b_set_data(buf, b_size(buf)); + return htx; + +error: + if (buf->size) + free(buf->area); + return NULL; +} + +static int http_htx_init(void) +{ + struct proxy *px; + struct buffer chk; + struct ist raw; + int rc; + int err_code = 0; + + for (px = proxies_list; px; px = px->next) { + if (!(px->options2 & PR_O2_USE_HTX)) + continue; + + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (!b_data(&px->errmsg[rc])) + continue; + + raw = ist2(b_head(&px->errmsg[rc]), b_data(&px->errmsg[rc])); + if (!http_str_to_htx(&chk, raw)) { + ha_alert("config: %s '%s': Unable to convert message in HTX for HTTP return code %d.\n", + proxy_type_str(px), px->id, http_err_codes[rc]); + err_code |= ERR_ALERT | ERR_FATAL; + } + chunk_destroy(&px->errmsg[rc]); + px->errmsg[rc] = chk; + } + } + + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (!http_err_msgs[rc]) { + ha_alert("Internal error: no message defined for HTTP return code %d", rc); + err_code |= ERR_ALERT | ERR_FATAL; + continue; + } + + raw = ist2(http_err_msgs[rc], strlen(http_err_msgs[rc])); + if (!http_str_to_htx(&chk, raw)) { + ha_alert("Internal error: Unable to convert message in HTX for HTTP return code %d.\n", + http_err_codes[rc]); + err_code |= ERR_ALERT | ERR_FATAL; + } + htx_err_chunks[rc] = chk; + } +end: + return err_code; +} + +REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init); diff --git a/src/proto_http.c b/src/proto_http.c index 43c1fcd45..0f8bc22f9 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -413,6 +413,9 @@ struct buffer *http_error_message(struct stream *s) { const int msgnum = http_get_status_idx(s->txn->status); + if (IS_HTX_STRM(s)) + return htx_error_message(s); + if (s->be->errmsg[msgnum].area) return &s->be->errmsg[msgnum]; else if (strm_fe(s)->errmsg[msgnum].area) diff --git a/src/proto_htx.c b/src/proto_htx.c index 6e961f96e..90e743b1b 100644 --- a/src/proto_htx.c +++ b/src/proto_htx.c @@ -185,7 +185,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) txn->status = 408; msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); req->analysers &= AN_REQ_FLT_END; if (!(s->flags & SF_FINST_MASK)) @@ -214,7 +214,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) txn->status = 400; msg->err_state = msg->msg_state; msg->msg_state = HTTP_MSG_ERROR; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); req->analysers &= AN_REQ_FLT_END; if (!(s->flags & SF_FINST_MASK)) @@ -347,7 +347,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) if (ret) { /* we fail this request, let's return 503 service unavail */ txn->status = 503; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */ goto return_prx_cond; @@ -356,7 +356,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) /* nothing to fail, let's reply normaly */ txn->status = 200; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_LOCAL; /* we don't want a real error here */ goto return_prx_cond; @@ -446,7 +446,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) txn->status = 400; txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); if (sess->listener->counters) HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); @@ -551,7 +551,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st if (unlikely(!stream_int_register_handler(&s->si[1], objt_applet(s->target)))) { txn->status = 500; s->logs.tv_request = now; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_RESOURCE; @@ -703,7 +703,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st txn->flags |= TX_CLDENY; txn->status = http_err_codes[deny_status]; s->logs.tv_request = now; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); stream_inc_http_err_ctr(s); HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1); if (sess->fe != s->be) @@ -716,7 +716,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st txn->req.err_state = txn->req.msg_state; txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); if (sess->listener->counters) @@ -789,7 +789,7 @@ int htx_process_request(struct stream *s, struct channel *req, int an_bit) txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 500; req->analysers &= AN_REQ_FLT_END; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_RESOURCE; @@ -979,7 +979,7 @@ int htx_process_request(struct stream *s, struct channel *req, int an_bit) txn->req.msg_state = HTTP_MSG_ERROR; txn->status = 400; req->analysers &= AN_REQ_FLT_END; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); if (sess->listener->counters) @@ -1019,7 +1019,7 @@ int htx_process_tarpit(struct stream *s, struct channel *req, int an_bit) s->logs.t_queue = tv_ms_elapsed(&s->logs.tv_accept, &now); if (!(req->flags & CF_READ_ERROR)) - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); req->analysers &= AN_REQ_FLT_END; req->analyse_exp = TICK_ETERNITY; @@ -1102,7 +1102,7 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit) if ((req->flags & CF_READ_TIMEOUT) || tick_is_expired(req->analyse_exp, now_ms)) { txn->status = 408; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_CLITO; @@ -1136,7 +1136,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; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_PRXCOND; @@ -1354,7 +1354,7 @@ int htx_request_forward_body(struct stream *s, struct channel *req, int an_bit) htx_reply_and_close(s, txn->status, NULL); } else { txn->status = 400; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); } req->analysers &= AN_REQ_FLT_END; s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */ @@ -1377,7 +1377,7 @@ int htx_request_forward_body(struct stream *s, struct channel *req, int an_bit) htx_reply_and_close(s, txn->status, NULL); } else { txn->status = 502; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); } req->analysers &= AN_REQ_FLT_END; s->res.analysers &= AN_RES_FLT_END; /* we're in data phase, we want to abort both directions */ @@ -1477,7 +1477,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) } s->si[1].flags |= SI_FL_NOLINGER; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_SRVCL; @@ -1497,7 +1497,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) rep->analysers &= AN_RES_FLT_END; txn->status = 504; s->si[1].flags |= SI_FL_NOLINGER; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_SRVTO; @@ -1515,7 +1515,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) rep->analysers &= AN_RES_FLT_END; txn->status = 400; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_CLICL; @@ -1540,7 +1540,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) rep->analysers &= AN_RES_FLT_END; txn->status = 502; s->si[1].flags |= SI_FL_NOLINGER; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_SRVCL; @@ -1750,7 +1750,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) } txn->status = 502; s->si[1].flags |= SI_FL_NOLINGER; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); rep->analysers &= AN_RES_FLT_END; if (!(s->flags & SF_ERR_MASK)) @@ -2063,7 +2063,7 @@ int htx_process_res_common(struct stream *s, struct channel *rep, int an_bit, st txn->status = 502; s->logs.t_data = -1; /* was not a valid response */ s->si[1].flags |= SI_FL_NOLINGER; - htx_reply_and_close(s, txn->status, http_error_message(s)); + htx_reply_and_close(s, txn->status, htx_error_message(s)); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_PRXCOND; if (!(s->flags & SF_FINST_MASK)) @@ -5283,13 +5283,17 @@ void htx_server_error(struct stream *s, struct stream_interface *si, int err, channel_erase(si_oc(si)); channel_auto_close(si_ic(si)); channel_auto_read(si_ic(si)); + + /* is an HTX structure. So we copy it in the response's + * channel */ if (msg) { struct channel *chn = si_ic(si); struct htx *htx; - htx = htx_from_buf(&chn->buf); - htx_add_oob(htx, ist2(msg->area, msg->data)); //FLT_STRM_CB(s, flt_htx_reply(s, s->txn->status, htx)); + chn->buf.data = msg->data; + memcpy(chn->buf.area, msg->area, msg->data); + htx = htx_from_buf(&chn->buf); b_set_data(&chn->buf, b_size(&chn->buf)); c_adv(chn, htx->data); chn->total += htx->data; @@ -5309,13 +5313,18 @@ void htx_reply_and_close(struct stream *s, short status, struct buffer *msg) channel_truncate(&s->res); s->txn->flags &= ~TX_WAIT_NEXT_RQ; + + /* is an HTX structure. So we copy it in the response's + * channel */ + /* FIXME: It is a problem for now if there is some outgoing data */ if (msg) { struct channel *chn = &s->res; struct htx *htx; - htx = htx_from_buf(&chn->buf); - htx_add_oob(htx, ist2(msg->area, msg->data)); //FLT_STRM_CB(s, flt_htx_reply(s, s->txn->status, htx)); + chn->buf.data = msg->data; + memcpy(chn->buf.area, msg->area, msg->data); + htx = htx_from_buf(&chn->buf); b_set_data(&chn->buf, b_size(&chn->buf)); c_adv(chn, htx->data); chn->total += htx->data; @@ -5327,6 +5336,19 @@ void htx_reply_and_close(struct stream *s, short status, struct buffer *msg) channel_shutr_now(&s->res); } +struct buffer *htx_error_message(struct stream *s) +{ + const int msgnum = http_get_status_idx(s->txn->status); + + if (s->be->errmsg[msgnum].area) + return &s->be->errmsg[msgnum]; + else if (strm_fe(s)->errmsg[msgnum].area) + return &strm_fe(s)->errmsg[msgnum]; + else + return &htx_err_chunks[msgnum]; +} + + /* Send a 100-Continue response to the client. It returns 0 on success and -1 * on error. The response channel is updated accordingly. */