diff --git a/include/proto/buffers.h b/include/proto/buffers.h index a5ad2081e..ca88b1671 100644 --- a/include/proto/buffers.h +++ b/include/proto/buffers.h @@ -22,6 +22,8 @@ #ifndef _PROTO_BUFFERS_H #define _PROTO_BUFFERS_H +#include + #include #include @@ -84,6 +86,21 @@ int buffer_write(struct buffer *buf, const char *msg, int len); int buffer_replace(struct buffer *b, char *pos, char *end, char *str); int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len); +/* + * frees the destination chunk if already allocated, allocates a new string, + * and copies the source into it. The pointer to the destination string is + * returned, or NULL if the allocation fails or if any pointer is NULL.. + */ +static inline char *chunk_dup(struct chunk *dst, const struct chunk *src) { + if (!dst || !src || !src->str) + return NULL; + if (dst->str) + free(dst->str); + dst->len = src->len; + dst->str = (char *)malloc(dst->len); + memcpy(dst->str, src->str, dst->len); + return dst->str; +} #endif /* _PROTO_BUFFERS_H */ diff --git a/include/proto/httperr.h b/include/proto/httperr.h new file mode 100644 index 000000000..24a0cd1af --- /dev/null +++ b/include/proto/httperr.h @@ -0,0 +1,41 @@ +/* + include/proto/httperr.h + This file contains declarations for HTTP responses and errors. + + Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1 + exclusively. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _PROTO_HTTPERR_H +#define _PROTO_HTTPERR_H + +#include + +extern const int http_err_codes[HTTP_ERR_SIZE]; +extern const char *http_err_msgs[HTTP_ERR_SIZE]; +extern const char *HTTP_200; +extern const char *HTTP_302; +extern const char *HTTP_303; +extern const char *HTTP_401_fmt; + +#endif /* _PROTO_HTTPERR_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 61d58c2a6..5ee3a53de 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -40,10 +40,10 @@ int process_session(struct task *t); int process_cli(struct session *t); int process_srv(struct session *t); -void client_retnclose(struct session *s, int len, const char *msg); -void client_return(struct session *s, int len, const char *msg); +void client_retnclose(struct session *s, const struct chunk *msg); +void client_return(struct session *s, const struct chunk *msg); void srv_close_with_err(struct session *t, int err, int finst, - int status, int msglen, const char *msg); + int status, const struct chunk *msg); int produce_content(struct session *s); void debug_hdr(const char *dir, struct session *t, const char *start, const char *end); diff --git a/include/types/httperr.h b/include/types/httperr.h index c59404820..c466328f4 100644 --- a/include/types/httperr.h +++ b/include/types/httperr.h @@ -32,6 +32,19 @@ #define DATA_ST_INIT 0 #define DATA_ST_DATA 1 +/* + * All implemented return codes + */ +enum { + HTTP_ERR_400 = 0, + HTTP_ERR_403, + HTTP_ERR_408, + HTTP_ERR_500, + HTTP_ERR_502, + HTTP_ERR_503, + HTTP_ERR_504, + HTTP_ERR_SIZE +}; #endif /* _TYPES_HTTPERR_H */ diff --git a/include/types/proxy.h b/include/types/proxy.h index a82f195c2..848014335 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -125,22 +126,7 @@ struct proxy { int grace; /* grace time after stop request */ char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */ int check_len; /* Length of the HTTP or SSL3 request */ - struct { - char *msg400; /* message for error 400 */ - int len400; /* message length for error 400 */ - char *msg403; /* message for error 403 */ - int len403; /* message length for error 403 */ - char *msg408; /* message for error 408 */ - int len408; /* message length for error 408 */ - char *msg500; /* message for error 500 */ - int len500; /* message length for error 500 */ - char *msg502; /* message for error 502 */ - int len502; /* message length for error 502 */ - char *msg503; /* message for error 503 */ - int len503; /* message length for error 503 */ - char *msg504; /* message for error 504 */ - int len504; /* message length for error 504 */ - } errmsg; + struct chunk errmsg[HTTP_ERR_SIZE]; /* default or customized error messages for known errors */ }; extern struct proxy *proxy; diff --git a/src/backend.c b/src/backend.c index 15d85da5c..f794ea570 100644 --- a/src/backend.c +++ b/src/backend.c @@ -545,7 +545,7 @@ int srv_count_retry_down(struct session *t, int conn_err) /* if not retryable anymore, let's abort */ tv_eternity(&t->req->cex); srv_close_with_err(t, conn_err, SN_FINST_C, - 503, t->fe->errmsg.len503, t->fe->errmsg.msg503); + 503, &t->fe->errmsg[HTTP_ERR_503]); if (t->srv) t->srv->failed_conns++; t->be->beprm->failed_conns++; @@ -587,7 +587,7 @@ int srv_retryable_connect(struct session *t) case SN_ERR_INTERNAL: tv_eternity(&t->req->cex); srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C, - 500, t->fe->errmsg.len500, t->fe->errmsg.msg500); + 500, &t->fe->errmsg[HTTP_ERR_500]); if (t->srv) t->srv->failed_conns++; t->be->beprm->failed_conns++; @@ -647,7 +647,7 @@ int srv_redispatch_connect(struct session *t) /* note: it is guaranteed that t->srv == NULL here */ tv_eternity(&t->req->cex); srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C, - 503, t->fe->errmsg.len503, t->fe->errmsg.msg503); + 503, &t->fe->errmsg[HTTP_ERR_503]); if (t->srv) t->srv->failed_conns++; t->be->beprm->failed_conns++; @@ -669,7 +669,7 @@ int srv_redispatch_connect(struct session *t) default: tv_eternity(&t->req->cex); srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C, - 500, t->fe->errmsg.len500, t->fe->errmsg.msg500); + 500, &t->fe->errmsg[HTTP_ERR_500]); if (t->srv) t->srv->failed_conns++; t->be->beprm->failed_conns++; diff --git a/src/cfgparse.c b/src/cfgparse.c index 0034bdab0..f3659f503 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -25,86 +25,20 @@ #include #include +#include #include #include #include #include +#include #include +#include #include #include #include -const char *HTTP_302 = - "HTTP/1.0 302 Found\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Location: "; /* not terminated since it will be concatenated with the URL */ - -/* same as 302 except that the browser MUST retry with the GET method */ -const char *HTTP_303 = - "HTTP/1.0 303 See Other\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Location: "; /* not terminated since it will be concatenated with the URL */ - -const char *HTTP_400 = - "HTTP/1.0 400 Bad request\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

400 Bad request

\nYour browser sent an invalid request.\n\n"; - -const char *HTTP_403 = - "HTTP/1.0 403 Forbidden\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

403 Forbidden

\nRequest forbidden by administrative rules.\n\n"; - -const char *HTTP_408 = - "HTTP/1.0 408 Request Time-out\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

408 Request Time-out

\nYour browser didn't send a complete request in time.\n\n"; - -const char *HTTP_500 = - "HTTP/1.0 500 Server Error\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

500 Server Error

\nAn internal server error occured.\n\n"; - -const char *HTTP_502 = - "HTTP/1.0 502 Bad Gateway\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

502 Bad Gateway

\nThe server returned an invalid or incomplete response.\n\n"; - -const char *HTTP_503 = - "HTTP/1.0 503 Service Unavailable\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

503 Service Unavailable

\nNo server is available to handle this request.\n\n"; - -const char *HTTP_504 = - "HTTP/1.0 504 Gateway Time-out\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n" - "\r\n" - "

504 Gateway Time-out

\nThe server didn't respond in time.\n\n"; - /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the * ssl-hello-chk option to ensure that the remote server speaks SSL. * @@ -477,33 +411,11 @@ int cfg_parse_listen(const char *file, int linenum, char **args) curproxy->capture_namelen = defproxy.capture_namelen; curproxy->capture_len = defproxy.capture_len; - if (defproxy.errmsg.msg400) - curproxy->errmsg.msg400 = strdup(defproxy.errmsg.msg400); - curproxy->errmsg.len400 = defproxy.errmsg.len400; - if (defproxy.errmsg.msg403) - curproxy->errmsg.msg403 = strdup(defproxy.errmsg.msg403); - curproxy->errmsg.len403 = defproxy.errmsg.len403; - - if (defproxy.errmsg.msg408) - curproxy->errmsg.msg408 = strdup(defproxy.errmsg.msg408); - curproxy->errmsg.len408 = defproxy.errmsg.len408; - - if (defproxy.errmsg.msg500) - curproxy->errmsg.msg500 = strdup(defproxy.errmsg.msg500); - curproxy->errmsg.len500 = defproxy.errmsg.len500; - - if (defproxy.errmsg.msg502) - curproxy->errmsg.msg502 = strdup(defproxy.errmsg.msg502); - curproxy->errmsg.len502 = defproxy.errmsg.len502; - - if (defproxy.errmsg.msg503) - curproxy->errmsg.msg503 = strdup(defproxy.errmsg.msg503); - curproxy->errmsg.len503 = defproxy.errmsg.len503; - - if (defproxy.errmsg.msg504) - curproxy->errmsg.msg504 = strdup(defproxy.errmsg.msg504); - curproxy->errmsg.len504 = defproxy.errmsg.len504; + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (defproxy.errmsg[rc].str) + chunk_dup(&curproxy->errmsg[rc], &defproxy.errmsg[rc]); + } curproxy->clitimeout = defproxy.clitimeout; curproxy->contimeout = defproxy.contimeout; @@ -533,14 +445,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args) if (defproxy.check_req) free(defproxy.check_req); if (defproxy.cookie_name) free(defproxy.cookie_name); if (defproxy.capture_name) free(defproxy.capture_name); - if (defproxy.errmsg.msg400) free(defproxy.errmsg.msg400); - if (defproxy.errmsg.msg403) free(defproxy.errmsg.msg403); - if (defproxy.errmsg.msg408) free(defproxy.errmsg.msg408); - if (defproxy.errmsg.msg500) free(defproxy.errmsg.msg500); - if (defproxy.errmsg.msg502) free(defproxy.errmsg.msg502); - if (defproxy.errmsg.msg503) free(defproxy.errmsg.msg503); - if (defproxy.errmsg.msg504) free(defproxy.errmsg.msg504); if (defproxy.monitor_uri) free(defproxy.monitor_uri); + + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (defproxy.errmsg[rc].len) + free(defproxy.errmsg[rc].str); + } + /* we cannot free uri_auth because it might already be used */ init_default_instance(); curproxy = &defproxy; @@ -1778,7 +1689,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args) // } if (*(args[2]) == 0) { - Alert("parsing [%s:%d] : expects and as arguments.\n", file, linenum); + Alert("parsing [%s:%d] : <%s> expects and as arguments.\n", file, linenum); return -1; } @@ -1791,64 +1702,19 @@ int cfg_parse_listen(const char *file, int linenum, char **args) errlen = sprintf(err, "%s%s\r\n\r\n", HTTP_302, args[2]); } - if (errnum == 400) { - if (curproxy->errmsg.msg400) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg400); + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (http_err_codes[rc] == errnum) { + if (curproxy->errmsg[rc].str) + free(curproxy->errmsg[rc].str); + curproxy->errmsg[rc].str = err; + curproxy->errmsg[rc].len = errlen; + break; } - curproxy->errmsg.msg400 = err; - curproxy->errmsg.len400 = errlen; } - else if (errnum == 403) { - if (curproxy->errmsg.msg403) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg403); - } - curproxy->errmsg.msg403 = err; - curproxy->errmsg.len403 = errlen; - } - else if (errnum == 408) { - if (curproxy->errmsg.msg408) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg408); - } - curproxy->errmsg.msg408 = err; - curproxy->errmsg.len408 = errlen; - } - else if (errnum == 500) { - if (curproxy->errmsg.msg500) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg500); - } - curproxy->errmsg.msg500 = err; - curproxy->errmsg.len500 = errlen; - } - else if (errnum == 502) { - if (curproxy->errmsg.msg502) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg502); - } - curproxy->errmsg.msg502 = err; - curproxy->errmsg.len502 = errlen; - } - else if (errnum == 503) { - if (curproxy->errmsg.msg503) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg503); - } - curproxy->errmsg.msg503 = err; - curproxy->errmsg.len503 = errlen; - } - else if (errnum == 504) { - if (curproxy->errmsg.msg504) { - //Warning("parsing [%s:%d] : error %d already defined.\n", file, linenum, errnum); - free(curproxy->errmsg.msg504); - } - curproxy->errmsg.msg504 = err; - curproxy->errmsg.len504 = errlen; - } - else { - Warning("parsing [%s:%d] : error %d relocation will be ignored.\n", file, linenum, errnum); + + if (rc >= HTTP_ERR_SIZE) { + Warning("parsing [%s:%d] : status code %d not handled, error relocation will be ignored.\n", + file, linenum, errnum); free(err); } } @@ -1874,7 +1740,7 @@ int readcfgfile(const char *file) char *args[MAX_LINE_ARGS]; int arg; int cfgerr = 0; - int nbchk, mininter; + int nbchk, mininter, rc; int confsect = CFG_NONE; struct proxy *curproxy = NULL; @@ -2163,33 +2029,15 @@ int readcfgfile(const char *file) if (curproxy->options & PR_O_LOGASAP) curproxy->to_log &= ~LW_BYTES; - if (curproxy->errmsg.msg400 == NULL) { - curproxy->errmsg.msg400 = (char *)HTTP_400; - curproxy->errmsg.len400 = strlen(HTTP_400); - } - if (curproxy->errmsg.msg403 == NULL) { - curproxy->errmsg.msg403 = (char *)HTTP_403; - curproxy->errmsg.len403 = strlen(HTTP_403); - } - if (curproxy->errmsg.msg408 == NULL) { - curproxy->errmsg.msg408 = (char *)HTTP_408; - curproxy->errmsg.len408 = strlen(HTTP_408); - } - if (curproxy->errmsg.msg500 == NULL) { - curproxy->errmsg.msg500 = (char *)HTTP_500; - curproxy->errmsg.len500 = strlen(HTTP_500); - } - if (curproxy->errmsg.msg502 == NULL) { - curproxy->errmsg.msg502 = (char *)HTTP_502; - curproxy->errmsg.len502 = strlen(HTTP_502); - } - if (curproxy->errmsg.msg503 == NULL) { - curproxy->errmsg.msg503 = (char *)HTTP_503; - curproxy->errmsg.len503 = strlen(HTTP_503); - } - if (curproxy->errmsg.msg504 == NULL) { - curproxy->errmsg.msg504 = (char *)HTTP_504; - curproxy->errmsg.len504 = strlen(HTTP_504); + for (rc = 0; rc < HTTP_ERR_SIZE; rc++) { + if (!http_err_msgs[rc]) { + Alert("Internal error: no message defined for HTTP return code %d. Aborting.\n"); + abort(); + } + if (!curproxy->errmsg[rc].str) { + curproxy->errmsg[rc].str = strdup(http_err_msgs[rc]); + curproxy->errmsg[rc].len = strlen(http_err_msgs[rc]); + } } /* diff --git a/src/client.c b/src/client.c index 8386d7823..8e26a9542 100644 --- a/src/client.c +++ b/src/client.c @@ -378,14 +378,17 @@ int event_accept(int fd) { fdtab[cfd].cb[DIR_WR].b = s->rep; if ((p->mode == PR_MODE_HTTP && (s->flags & SN_MONITOR)) || - (p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK))) + (p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK))) { /* Either we got a request from a monitoring system on an HTTP instance, * or we're in health check mode with the 'httpchk' option enabled. In * both cases, we return a fake "HTTP/1.0 200 OK" response and we exit. */ - client_retnclose(s, 19, "HTTP/1.0 200 OK\r\n\r\n"); /* forge a 200 response */ + struct chunk msg = { .str = "HTTP/1.0 200 OK\r\n\r\n", .len = 19 }; + client_retnclose(s, &msg); /* forge a 200 response */ + } else if (p->mode == PR_MODE_HEALTH) { /* health check mode, no client reading */ - client_retnclose(s, 3, "OK\n"); /* forge an "OK" response */ + struct chunk msg = { .str = "OK\n", .len = 3 }; + client_retnclose(s, &msg); /* forge an "OK" response */ } else { MY_FD_SET(cfd, StaticReadEvent); diff --git a/src/proto_http.c b/src/proto_http.c index 9cce59ba7..89b5b60f6 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -67,7 +67,7 @@ #endif /* This is used by remote monitoring */ -const char *HTTP_200 = +const char HTTP_200[] = "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n" "Connection: close\r\n" @@ -75,6 +75,24 @@ const char *HTTP_200 = "\r\n" "

200 OK

\nHAProxy: service ready.\n\n"; +const struct chunk http_200_chunk = { + .str = (char *)&HTTP_200, + .len = sizeof(HTTP_200)-1 +}; + +const char *HTTP_302 = + "HTTP/1.0 302 Found\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Location: "; /* not terminated since it will be concatenated with the URL */ + +/* same as 302 except that the browser MUST retry with the GET method */ +const char *HTTP_303 = + "HTTP/1.0 303 See Other\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Location: "; /* not terminated since it will be concatenated with the URL */ + /* Warning: this one is an sprintf() fmt string, with as its only argument */ const char *HTTP_401_fmt = "HTTP/1.0 401 Unauthorized\r\n" @@ -86,6 +104,76 @@ const char *HTTP_401_fmt = "

401 Unauthorized

\nYou need a valid user and password to access this content.\n\n"; +const int http_err_codes[HTTP_ERR_SIZE] = { + [HTTP_ERR_400] = 400, + [HTTP_ERR_403] = 403, + [HTTP_ERR_408] = 408, + [HTTP_ERR_500] = 500, + [HTTP_ERR_502] = 502, + [HTTP_ERR_503] = 503, + [HTTP_ERR_504] = 504, +}; + +const char *http_err_msgs[HTTP_ERR_SIZE] = { + [HTTP_ERR_400] = + "HTTP/1.0 400 Bad request\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

400 Bad request

\nYour browser sent an invalid request.\n\n", + + [HTTP_ERR_403] = + "HTTP/1.0 403 Forbidden\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

403 Forbidden

\nRequest forbidden by administrative rules.\n\n", + + [HTTP_ERR_408] = + "HTTP/1.0 408 Request Time-out\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

408 Request Time-out

\nYour browser didn't send a complete request in time.\n\n", + + [HTTP_ERR_500] = + "HTTP/1.0 500 Server Error\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

500 Server Error

\nAn internal server error occured.\n\n", + + [HTTP_ERR_502] = + "HTTP/1.0 502 Bad Gateway\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

502 Bad Gateway

\nThe server returned an invalid or incomplete response.\n\n", + + [HTTP_ERR_503] = + "HTTP/1.0 503 Service Unavailable\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

503 Service Unavailable

\nNo server is available to handle this request.\n\n", + + [HTTP_ERR_504] = + "HTTP/1.0 504 Gateway Time-out\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

504 Gateway Time-out

\nThe server didn't respond in time.\n\n", + +}; + + /* * We have 26 list of methods (1 per first letter), each of which can have * up to 3 entries (2 valid, 1 null). @@ -131,10 +219,11 @@ static char *srv_stnames[7] = {"IDL", "CON", "HDR", "DAT", "SHR", "SHW", "CLS" } * returns a message to the client ; the connection is shut down for read, * and the request is cleared so that no server connection can be initiated. * The client must be in a valid state for this (HEADER, DATA ...). - * Nothing is performed on the server side. + * Nothing is performed on the server side. The message is contained in a + * "chunk". If it is null, then an empty message is used. * The reply buffer doesn't need to be empty before this. */ -void client_retnclose(struct session *s, int len, const char *msg) +void client_retnclose(struct session *s, const struct chunk *msg) { MY_FD_CLR(s->cli_fd, StaticReadEvent); MY_FD_SET(s->cli_fd, StaticWriteEvent); @@ -146,35 +235,39 @@ void client_retnclose(struct session *s, int len, const char *msg) shutdown(s->cli_fd, SHUT_RD); s->cli_state = CL_STSHUTR; buffer_flush(s->rep); - buffer_write(s->rep, msg, len); + if (msg->len) + buffer_write(s->rep, msg->str, msg->len); s->req->l = 0; } /* * returns a message into the rep buffer, and flushes the req buffer. - * The reply buffer doesn't need to be empty before this. + * The reply buffer doesn't need to be empty before this. The message + * is contained in a "chunk". If it is null, then an empty message is + * used. */ -void client_return(struct session *s, int len, const char *msg) +void client_return(struct session *s, const struct chunk *msg) { buffer_flush(s->rep); - buffer_write(s->rep, msg, len); + if (msg->len) + buffer_write(s->rep, msg->str, msg->len); s->req->l = 0; } /* This function turns the server state into the SV_STCLOSE, and sets - * indicators accordingly. Note that if is 0, no message is - * returned. + * indicators accordingly. Note that if is 0, or if the message + * pointer is NULL, then no message is returned. */ void srv_close_with_err(struct session *t, int err, int finst, - int status, int msglen, const char *msg) + int status, const struct chunk *msg) { t->srv_state = SV_STCLOSE; - if (status > 0) { + if (status > 0 && msg) { t->logs.status = status; if (t->fe->mode == PR_MODE_HTTP) - client_return(t, msglen, msg); + client_return(t, msg); } if (!(t->flags & SN_ERR_MASK)) t->flags |= err; @@ -814,7 +907,7 @@ int process_cli(struct session *t) else if (tv_cmp2_ms(&req->rex, &now) <= 0) { /* read timeout : give up with an error message. */ t->logs.status = 408; - client_retnclose(t, t->fe->errmsg.len408, t->fe->errmsg.msg408); + client_retnclose(t, &t->fe->errmsg[HTTP_ERR_408]); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLITO; if (!(t->flags & SN_FINST_MASK)) @@ -876,7 +969,7 @@ int process_cli(struct session *t) */ t->flags |= SN_MONITOR; t->logs.status = 200; - client_retnclose(t, strlen(HTTP_200), HTTP_200); + client_retnclose(t, &http_200_chunk); goto return_prx_cond; } } @@ -937,7 +1030,7 @@ int process_cli(struct session *t) t->logs.status = 403; /* let's log the request time */ t->logs.t_request = tv_diff(&t->logs.tv_accept, &now); - client_retnclose(t, t->fe->errmsg.len403, t->fe->errmsg.msg403); + client_retnclose(t, &t->fe->errmsg[HTTP_ERR_403]); goto return_prx_cond; } @@ -1140,7 +1233,7 @@ int process_cli(struct session *t) return_bad_req: /* let's centralize all bad requests */ t->hreq.hdr_state = HTTP_PA_ERROR; t->logs.status = 400; - client_retnclose(t, t->fe->errmsg.len400, t->fe->errmsg.msg400); + client_retnclose(t, &t->fe->errmsg[HTTP_ERR_400]); return_prx_cond: if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; @@ -1469,9 +1562,9 @@ int process_srv(struct session *t) * overwrite the client_retnclose() output. */ if (t->flags & SN_CLTARPIT) - srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, 0, NULL); + srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_T, 0, NULL); else - srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, 0, NULL); + srv_close_with_err(t, SN_ERR_CLICL, t->pend_pos ? SN_FINST_Q : SN_FINST_C, 0, NULL); return 1; } @@ -1493,7 +1586,7 @@ int process_srv(struct session *t) tv_eternity(&req->cex); t->logs.t_queue = tv_diff(&t->logs.tv_accept, &now); srv_close_with_err(t, SN_ERR_PRXCOND, SN_FINST_T, - 500, t->fe->errmsg.len500, t->fe->errmsg.msg500); + 500, &t->fe->errmsg[HTTP_ERR_500]); return 1; } @@ -1510,7 +1603,7 @@ int process_srv(struct session *t) tv_eternity(&req->cex); t->logs.t_queue = tv_diff(&t->logs.tv_accept, &now); srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_Q, - 503, t->fe->errmsg.len503, t->fe->errmsg.msg503); + 503, &t->fe->errmsg[HTTP_ERR_503]); if (t->srv) t->srv->failed_conns++; t->fe->failed_conns++; @@ -1546,7 +1639,7 @@ int process_srv(struct session *t) /* note that this must not return any error because it would be able to * overwrite the client_retnclose() output. */ - srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, 0, NULL); + srv_close_with_err(t, SN_ERR_CLICL, SN_FINST_C, 0, NULL); return 1; } if (!(req->flags & BF_WRITE_STATUS) && tv_cmp2_ms(&req->cex, &now) > 0) { @@ -1695,7 +1788,7 @@ int process_srv(struct session *t) t->be->failed_secu++; t->srv_state = SV_STCLOSE; t->logs.status = 502; - client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502); + client_return(t, &t->fe->errmsg[HTTP_ERR_502]); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; if (!(t->flags & SN_FINST_MASK)) @@ -1726,7 +1819,7 @@ int process_srv(struct session *t) t->be->failed_secu++; t->srv_state = SV_STCLOSE; t->logs.status = 502; - client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502); + client_return(t, &t->fe->errmsg[HTTP_ERR_502]); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; if (!(t->flags & SN_FINST_MASK)) @@ -2166,7 +2259,7 @@ int process_srv(struct session *t) t->srv_state = SV_STCLOSE; t->logs.status = 502; - client_return(t, t->fe->errmsg.len502, t->fe->errmsg.msg502); + client_return(t, &t->fe->errmsg[HTTP_ERR_502]); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_SRVCL; if (!(t->flags & SN_FINST_MASK)) @@ -2204,7 +2297,7 @@ int process_srv(struct session *t) t->be->failed_resp++; t->srv_state = SV_STCLOSE; t->logs.status = 504; - client_return(t, t->fe->errmsg.len504, t->fe->errmsg.msg504); + client_return(t, &t->fe->errmsg[HTTP_ERR_504]); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_SRVTO; if (!(t->flags & SN_FINST_MASK)) @@ -2596,14 +2689,15 @@ int produce_content(struct session *s) struct buffer *rep = s->rep; struct proxy *px; struct server *sv; - int msglen; + struct chunk msg; if (s->data_source == DATA_SRC_NONE) { s->flags &= ~SN_SELF_GEN; return 1; } else if (s->data_source == DATA_SRC_STATS) { - msglen = 0; + msg.len = 0; + msg.str = trash; if (s->data_state == DATA_ST_INIT) { /* the function had not been called yet */ unsigned int up; @@ -2611,7 +2705,7 @@ int produce_content(struct session *s) s->flags |= SN_SELF_GEN; // more data will follow /* send the start of the HTTP response */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "HTTP/1.0 200 OK\r\n" "Cache-Control: no-cache\r\n" "Connection: close\r\n" @@ -2619,8 +2713,8 @@ int produce_content(struct session *s) "\r\n\r\n"); s->logs.status = 200; - client_retnclose(s, msglen, trash); // send the start of the response. - msglen = 0; + client_retnclose(s, &msg); // send the start of the response. + msg.len = 0; if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy @@ -2628,7 +2722,7 @@ int produce_content(struct session *s) s->flags |= SN_FINST_R; /* WARNING! This must fit in the first buffer !!! */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "Statistics Report for " PRODUCT_NAME "\n" "\n" ""); - if (buffer_write(rep, trash, msglen) != 0) + if (buffer_write(rep, trash, msg.len) != 0) return 0; - msglen = 0; + msg.len = 0; up = (now.tv_sec - start_date.tv_sec); /* WARNING! this has to fit the first packet too */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "

" PRODUCT_NAME "

\n" "

Statistics Report for pid %d

\n" "
\n" @@ -2725,9 +2819,9 @@ int produce_content(struct session *s) actconn ); - if (buffer_write(rep, trash, msglen) != 0) + if (buffer_write(rep, trash, msg.len) != 0) return 0; - msglen = 0; + msg.len = 0; s->data_state = DATA_ST_DATA; memset(&s->data_ctx, 0, sizeof(s->data_ctx)); @@ -2773,7 +2867,7 @@ int produce_content(struct session *s) goto next_proxy; } - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "

> Proxy instance %s : " "%d front conns (max=%d), %d back, " "%d queued (%d unassigned), %d total front conns, %d back

\n" @@ -2782,7 +2876,7 @@ int produce_content(struct session *s) px->feconn, px->maxconn, px->beconn, px->totpend, px->nbpend, px->cum_feconn, px->cum_beconn); - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n" "" "" @@ -2795,9 +2889,9 @@ int produce_content(struct session *s) "" "\n"); - if (buffer_write(rep, trash, msglen) != 0) + if (buffer_write(rep, trash, msg.len) != 0) return 0; - msglen = 0; + msg.len = 0; s->data_ctx.stats.sv = px->srv; s->data_ctx.stats.px_st = DATA_ST_DATA; @@ -2829,48 +2923,48 @@ int produce_content(struct session *s) sv_state = 0; /* DOWN */ /* name, weight */ - msglen += snprintf(trash, sizeof(trash), + msg.len += snprintf(trash, sizeof(trash), "", (sv->state & SRV_BACKUP) ? "-" : "Y", (sv->state & SRV_BACKUP) ? "Y" : "-"); /* queue : current, max */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", sv->nbpend, sv->nbpend_max); /* sessions : current, max, limit, cumul */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess); /* errors : connect, response, security */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n", sv->failed_conns, sv->failed_resp, sv->failed_secu); /* check failures : unique, fatal */ if (sv->state & SRV_CHECKED) - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n", sv->failed_checks, sv->down_trans); else - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n"); - if (buffer_write(rep, trash, msglen) != 0) + if (buffer_write(rep, trash, msg.len) != 0) return 0; - msglen = 0; + msg.len = 0; s->data_ctx.stats.sv = sv->next; } /* while sv */ @@ -2904,35 +2998,35 @@ int produce_content(struct session *s) } /* name, weight, status, act, bck */ - msglen += snprintf(trash + msglen, sizeof(trash), + msg.len += snprintf(trash + msg.len, sizeof(trash), "" "" "", px->state == PR_STRUN ? "UP" : "DOWN"); /* queue : current, max */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", px->nbpend, px->nbpend_max); /* sessions : current, max, limit, cumul. */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", dispatch_sess, px->beconn_max, dispatch_cum); /* errors : connect, response, security */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n", failed_conns, failed_resp, failed_secu); /* check failures : unique, fatal */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n"); /* now the summary for the whole proxy */ /* name, weight, status, act, bck */ - msglen += snprintf(trash + msglen, sizeof(trash), + msg.len += snprintf(trash + msg.len, sizeof(trash), "" "" "", @@ -2940,30 +3034,30 @@ int produce_content(struct session *s) px->srv_act, px->srv_bck); /* queue : current, max */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", px->totpend, px->nbpend_max); /* sessions : current, max, limit, cumul */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "", px->beconn, px->beconn_max, px->cum_beconn); /* errors : connect, response, security */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n", px->failed_conns, px->failed_resp, px->failed_secu); /* check failures : unique, fatal */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "\n", failed_checks, down_trans); - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, "
ServerCurr.Max.LimitCumul.Conn.Resp.Sec.CheckDown
%s%d", (sv->state & SRV_BACKUP) ? bck_tab_bg[sv_state] : act_tab_bg[sv_state], sv->id, sv->uweight+1); /* status */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, srv_hlt_st[sv_state], + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, srv_hlt_st[sv_state], (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); /* act, bck */ - msglen += snprintf(trash + msglen, sizeof(trash) - msglen, + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "%s%s%d%d%d%d%s%d%d%d%d%d%d
--
Dispatcher-%s--%d%d%d%d-%d%d%d%d--
Total-%s%d%d%d%d%d%d-%d%d%d%d%d%d

\n"); + msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "

\n"); - if (buffer_write(rep, trash, msglen) != 0) + if (buffer_write(rep, trash, msg.len) != 0) return 0; - msglen = 0; + msg.len = 0; s->data_ctx.stats.px_st = DATA_ST_INIT; next_proxy: @@ -2976,7 +3070,7 @@ int produce_content(struct session *s) else { /* unknown data source */ s->logs.status = 500; - client_retnclose(s, s->fe->errmsg.len500, s->fe->errmsg.msg500); + client_retnclose(s, &s->fe->errmsg[HTTP_ERR_500]); if (!(s->flags & SN_ERR_MASK)) s->flags |= SN_ERR_PRXCOND; if (!(s->flags & SN_FINST_MASK)) @@ -3631,12 +3725,13 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) } if (!authenticated) { - int msglen; + struct chunk msg; /* no need to go further */ - msglen = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm); + msg.str = trash; + msg.len = sprintf(trash, HTTP_401_fmt, uri_auth->auth_realm); t->logs.status = 401; - client_retnclose(t, msglen, trash); + client_retnclose(t, &msg); if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; if (!(t->flags & SN_FINST_MASK))