]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: proto_htx: Convert all HTTP error messages into HTX
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 29 Nov 2018 15:48:49 +0000 (16:48 +0100)
committerWilly Tarreau <w@1wt.eu>
Sat, 1 Dec 2018 16:37:27 +0000 (17:37 +0100)
During startup, after the configuration parsing, all HTTP error messages
(errorloc, errorfile or default messages) are converted into HTX messages and
stored in dedicated buffers. We use it to return errors in the HTX analyzers
instead of using ugly OOB blocks.

include/common/http.h
include/proto/http_htx.h
include/proto/proto_http.h
src/http.c
src/http_htx.c
src/proto_http.c
src/proto_htx.c

index 0835d078d7e6357a786e42ccb94a4ef056cd4153..66acfd32dae2f2515cc49608a8be79017d27bc3a 100644 (file)
@@ -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];
index 9afea5c99ead7c74ad6bea9cf9fd7a51233165fe..7a2e008ae7600b7042b4c0486f9e1aaca118b350 100644 (file)
 #define _PROTO_HTTP_HTX_H
 
 #include <common/buf.h>
+#include <common/ist.h>
 
 #include <types/h1.h>
 #include <types/http_htx.h>
 
+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);
index 7546f019704f51751c1d484f32d028952a695220..73946198a0848dcd46f1e3ee63db0ffb85fc2da9 100644 (file)
@@ -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);
index a556a88a139fa9b2e58bb0a330d3cacc497ef0cf..bd8f96f572c88c3a699f886d073611c9bc027e33 100644 (file)
@@ -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"
index 82f9497d951b43ff4a7cd3a3f2c70acda7fead85..58629785cb8383fb05b4c1d66760438b9230ba8e 100644 (file)
  */
 
 #include <common/config.h>
+#include <common/cfgparse.h>
 #include <common/http.h>
 
+#include <proto/h1.h>
 #include <proto/http_htx.h>
 #include <proto/htx.h>
 
+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);
index 43c1fcd4562ffd65ddfcb4629ff170dc741b04b5..0f8bc22f9907b4cc5ce9e1cfba159e9a3ed1d8a3 100644 (file)
@@ -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)
index 6e961f96e37f43b9d97abfebc434c1c08e8438f8..90e743b1bf6a07610f3139b459ef0fb7a45645fb 100644 (file)
@@ -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));
+
+       /* <msg> 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;
+
+       /* <msg> 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.
  */