diff --git a/include/proto/http_htx.h b/include/proto/http_htx.h index 0cec9db58..3f8634010 100644 --- a/include/proto/http_htx.h +++ b/include/proto/http_htx.h @@ -49,9 +49,9 @@ unsigned int http_get_htx_fhdr(const struct htx *htx, const struct ist hdr, int occ, struct http_hdr_ctx *ctx, char **vptr, size_t *vlen); int http_str_to_htx(struct buffer *buf, struct ist raw); -int http_load_errorfile(const char *file, struct buffer *buf, char **errmsg); -int http_load_errormsg(const struct ist msg, struct buffer *buf, char **errmsg); -int http_parse_errorfile(int status, const char *file, struct buffer *buf, char **errmsg); -int http_parse_errorloc(int errloc, int status, const char *url, struct buffer *buf, char **errmsg); +struct buffer *http_load_errorfile(const char *file, char **errmsg); +struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg); +struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg); +struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg); #endif /* _PROTO_HTTP_HTX_H */ diff --git a/include/types/http_htx.h b/include/types/http_htx.h index 3f9a7f4b0..d9c3052f9 100644 --- a/include/types/http_htx.h +++ b/include/types/http_htx.h @@ -23,6 +23,9 @@ #ifndef _TYPES_HTTP_HTX_H #define _TYPES_HTTP_HTX_H +#include + +#include #include #include @@ -34,4 +37,12 @@ struct http_hdr_ctx { uint16_t lws_after; }; +/* A custom HTTP error message load from a row file and converted in HTX. The + * node key is the file path. + */ +struct http_error { + struct buffer msg; + struct ebpt_node node; +}; + #endif /* _TYPES_HTTP_HTX_H */ diff --git a/include/types/proxy.h b/include/types/proxy.h index 6ea96b3ad..232634dec 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -427,7 +427,7 @@ struct proxy { char *check_path; /* PATH environment to use for external agent checks */ char *expect_str; /* http-check expected content : string or text version of the regex */ struct my_regex *expect_regex; /* http-check expected content */ - struct buffer errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */ + struct buffer *errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */ int uuid; /* universally unique proxy ID, used for SNMP */ unsigned int backlog; /* force the frontend's listen backlog */ unsigned long bind_proc; /* bitmask of processes using this proxy */ diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index b42fe536d..9177181e7 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -262,8 +262,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } /* initialize error relocations */ - for (rc = 0; rc < HTTP_ERR_SIZE; rc++) - chunk_dup(&curproxy->errmsg[rc], &defproxy.errmsg[rc]); + memcpy(&curproxy->errmsg, &defproxy.errmsg, sizeof(defproxy.errmsg)); if (curproxy->cap & PR_CAP_FE) { curproxy->maxconn = defproxy.maxconn; @@ -503,8 +502,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) free(defproxy.conf.logformat_sd_string); free(defproxy.conf.lfsd_file); - for (rc = 0; rc < HTTP_ERR_SIZE; rc++) - chunk_destroy(&defproxy.errmsg[rc]); + memset(&defproxy.errmsg, 0, sizeof(defproxy.errmsg)); /* we cannot free uri_auth because it might already be used */ init_default_instance(); @@ -3810,8 +3808,8 @@ stats_error_parsing: else if (!strcmp(args[0], "errorloc") || !strcmp(args[0], "errorloc302") || !strcmp(args[0], "errorloc303")) { /* error location */ - struct buffer chk; - int errloc, status, rc; + struct buffer *msg; + int errloc, status; if (warnifnotcap(curproxy, PR_CAP_FE | PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; @@ -3824,39 +3822,38 @@ stats_error_parsing: status = atol(args[1]); errloc = (!strcmp(args[0], "errorloc303") ? 303 : 302); - rc = http_parse_errorloc(errloc, status, args[2], &chk, &errmsg); - if (rc == -1) { + msg = http_parse_errorloc(errloc, status, args[2], &errmsg); + if (!msg) { ha_alert("parsing [%s:%d] : %s: %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - - chunk_destroy(&curproxy->errmsg[rc]); - curproxy->errmsg[rc] = chk; + rc = http_get_status_idx(status); + curproxy->errmsg[rc] = msg; } else if (!strcmp(args[0], "errorfile")) { /* error message from a file */ - struct buffer chk; - int status, rc; + struct buffer *msg; + int status; if (warnifnotcap(curproxy, PR_CAP_FE | PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; if (*(args[1]) == 0 || *(args[2]) == 0) { - ha_alert("parsing [%s:%d] : <%s> expects and as arguments.\n", file, linenum, args[0]); + ha_alert("parsing [%s:%d] : %s: expects and as arguments.\n", + file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; goto out; } status = atol(args[1]); - rc = http_parse_errorfile(status, args[2], &chk, &errmsg); - if (rc == -1) { + msg = http_parse_errorfile(status, args[2], &errmsg); + if (!msg) { ha_alert("parsing [%s:%d] : %s: %s\n", file, linenum, args[0], errmsg); err_code |= ERR_ALERT | ERR_FATAL; goto out; } - - chunk_destroy(&curproxy->errmsg[rc]); - curproxy->errmsg[rc] = chk; + rc = http_get_status_idx(status); + curproxy->errmsg[rc] = msg; } else { struct cfg_kw_list *kwl; diff --git a/src/haproxy.c b/src/haproxy.c index 12d59c383..78143d7e7 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2331,7 +2331,6 @@ void deinit(void) struct post_deinit_fct *pdf; struct proxy_deinit_fct *pxdf; struct server_deinit_fct *srvdf; - int i; deinit_signals(); while (p) { @@ -2359,9 +2358,6 @@ void deinit(void) free(p->conf.logformat_sd_string); free(p->conf.lfsd_file); - for (i = 0; i < HTTP_ERR_SIZE; i++) - chunk_destroy(&p->errmsg[i]); - list_for_each_entry_safe(cond, condb, &p->mon_fail_cond, list) { LIST_DEL(&cond->list); prune_acl_cond(cond); diff --git a/src/http_ana.c b/src/http_ana.c index e100589a8..c6e0bc7b5 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -4597,10 +4597,10 @@ struct buffer *http_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]; + if (s->be->errmsg[msgnum]) + return s->be->errmsg[msgnum]; + else if (strm_fe(s)->errmsg[msgnum]) + return strm_fe(s)->errmsg[msgnum]; else return &http_err_chunks[msgnum]; } diff --git a/src/http_htx.c b/src/http_htx.c index 2b368a4ec..33edcac80 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -29,6 +29,7 @@ #include struct buffer http_err_chunks[HTTP_ERR_SIZE]; +struct eb_root http_error_messages = EB_ROOT; static int http_update_authority(struct htx *htx, struct htx_sl *sl, const struct ist host); static int http_update_host(struct htx *htx, struct htx_sl *sl, const struct ist uri); @@ -836,21 +837,50 @@ end: return err_code; } -REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init); - -/* Reads content of an error file and convert it in an HTX message. On success, - * the result is stored in and 1 is returned. On error, 0 is returned and - * an error message is written into the buffer. It is this function - * responsibility to allocate and to release it if an error occurred. - */ -int http_load_errorfile(const char *file, struct buffer *buf, char **errmsg) +static void http_htx_deinit(void) { + struct ebpt_node *node, *next; + struct http_error *http_err; + + node = ebpt_first(&http_error_messages); + while (node) { + next = ebpt_next(node); + ebpt_delete(node); + http_err = container_of(node, typeof(*http_err), node); + chunk_destroy(&http_err->msg); + free(node->key); + free(http_err); + node = next; + } +} + +REGISTER_CONFIG_POSTPARSER("http_htx", http_htx_init); +REGISTER_POST_DEINIT(http_htx_deinit); + +/* Reads content of the error file and convert it into an HTX message. On + * success, the HTX message is returned. On error, NULL is returned and an error + * message is written into the buffer. + */ +struct buffer *http_load_errorfile(const char *file, char **errmsg) +{ + struct buffer *buf = NULL; + struct buffer chk; + struct ebpt_node *node; + struct http_error *http_err; struct stat stat; char *err = NULL; int errnum, errlen; int fd = -1; - int ret = 0; + /* already loaded */ + node = ebis_lookup_len(&http_error_messages, file, strlen(file)); + if (node) { + http_err = container_of(node, typeof(*http_err), node); + buf = &http_err->msg; + goto out; + } + + /* Read the error file content */ fd = open(file, O_RDONLY); if ((fd < 0) || (fstat(fd, &stat) < 0)) { memprintf(errmsg, "error opening file '%s'.", file); @@ -877,78 +907,125 @@ int http_load_errorfile(const char *file, struct buffer *buf, char **errmsg) goto out; } - if (!http_str_to_htx(buf, ist2(err, errlen))) { - memprintf(errmsg, "unable to convert custom error message file '%s' in HTX.", file); + /* Create the node corresponding to the error file */ + http_err = calloc(1, sizeof(*http_err)); + if (!http_err) { + memprintf(errmsg, "out of memory."); + goto out; + } + http_err->node.key = strdup(file); + if (!http_err->node.key) { + memprintf(errmsg, "out of memory."); goto out; } - ret = 1; + /* Convert the error file into an HTX message */ + if (!http_str_to_htx(&chk, ist2(err, errlen))) { + memprintf(errmsg, "unable to convert custom error message file '%s' in HTX.", file); + free(http_err->node.key); + free(http_err); + goto out; + } + + /* Insert the node in the tree and return the HTX message */ + http_err->msg = chk; + ebis_insert(&http_error_messages, &http_err->node); + buf = &http_err->msg; + out: if (fd >= 0) close(fd); free(err); - return ret; + return buf; } -/* Convert the raw http message into an HTX message. On success, the - * result is stored in and 1 is returned. On error, 0 is returned and an - * error message is written into the buffer. It is this function - * responsibility to allocate and to release it if an error occurred. +/* Convert the raw http message into an HTX message. On sucess, the HTX + * message is returned. On error, NULL is returned and an error message is + * written into the buffer. */ -int http_load_errormsg(const struct ist msg, struct buffer *buf, char **errmsg) +struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg) { - int ret = 0; + struct buffer *buf = NULL; + struct buffer chk; + struct ebpt_node *node; + struct http_error *http_err; - /* Convert the error file into an HTX message */ - if (!http_str_to_htx(buf, msg)) { - memprintf(errmsg, "unable to convert message in HTX."); + /* already loaded */ + node = ebis_lookup_len(&http_error_messages, key, strlen(key)); + if (node) { + http_err = container_of(node, typeof(*http_err), node); + buf = &http_err->msg; + goto out; + } + /* Create the node corresponding to the error file */ + http_err = calloc(1, sizeof(*http_err)); + if (!http_err) { + memprintf(errmsg, "out of memory."); + goto out; + } + http_err->node.key = strdup(key); + if (!http_err->node.key) { + memprintf(errmsg, "out of memory."); goto out; } - ret = 1; + /* Convert the error file into an HTX message */ + if (!http_str_to_htx(&chk, msg)) { + memprintf(errmsg, "unable to convert message in HTX."); + free(http_err->node.key); + free(http_err); + goto out; + } + + /* Insert the node in the tree and return the HTX message */ + http_err->msg = chk; + ebis_insert(&http_error_messages, &http_err->node); + buf = &http_err->msg; out: - return ret; + return buf; } - /* This function parses the raw HTTP error file for the status code - * . On success, it returns the HTTP_ERR_* value corresponding to the - * specified status code and it allocated and fills the buffer with the - * HTX message. On error, it returns -1 and nothing is allocated. + * . It returns NULL if there is any error, otherwise it return the + * corresponding HTX message. */ -int http_parse_errorfile(int status, const char *file, struct buffer *buf, char **errmsg) +struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg) { - int rc, ret = -1; + struct buffer *buf = NULL; + int rc; for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { if (http_err_codes[rc] == status) { - if (http_load_errorfile(file, buf, errmsg)) - ret = rc; + buf = http_load_errorfile(file, errmsg); break; } } if (rc >= HTTP_ERR_SIZE) memprintf(errmsg, "status code '%d' not handled.", status); - return ret; + return buf; } /* This function creates HTX error message corresponding to a redirect message * for the status code . is used as location url for the - * redirect. is used to know if it is a 302 or a 303 redirect. On - * success, it returns the HTTP_ERR_* value corresponding to the specified - * status code and it allocated and fills the buffer with the HTX - * message. On error, it returns -1 and nothing is allocated. + * redirect. is used to know if it is a 302 or a 303 redirect. It + * returns NULL if there is any error, otherwise it return the corresponding HTX + * message. */ -int http_parse_errorloc(int errloc, int status, const char *url, struct buffer *buf, char **errmsg) +struct buffer *http_parse_errorloc(int errloc, int status, const char *url, char **errmsg) { + struct buffer *buf = NULL; const char *msg; - char *err = NULL; + char *key = NULL, *err = NULL; int rc, errlen; - int ret = -1; for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { if (http_err_codes[rc] == status) { + /* Create the error key */ + if (!memprintf(&key, "errorloc%d %s", errloc, url)) { + memprintf(errmsg, "out of memory."); + goto out; + } /* Create the error message */ msg = (errloc == 302 ? HTTP_302 : HTTP_303); errlen = strlen(msg) + strlen(url) + 5; @@ -960,8 +1037,7 @@ int http_parse_errorloc(int errloc, int status, const char *url, struct buffer * errlen = snprintf(err, errlen, "%s%s\r\n\r\n", msg, url); /* Load it */ - if (http_load_errormsg(ist2(err, errlen), buf, errmsg)) - ret = rc; + buf = http_load_errormsg(key, ist2(err, errlen), errmsg); break; } } @@ -969,8 +1045,9 @@ int http_parse_errorloc(int errloc, int status, const char *url, struct buffer * if (rc >= HTTP_ERR_SIZE) memprintf(errmsg, "status code '%d' not handled.", status); out: + free(key); free(err); - return ret; + return buf; } /************************************************************************/