diff --git a/include/common/version.h b/include/common/version.h index d137b4f8c..c8fed3db9 100644 --- a/include/common/version.h +++ b/include/common/version.h @@ -30,6 +30,30 @@ #define PRODUCT_NAME "HAProxy" #endif +#ifdef CONFIG_PRODUCT_BRANCH +#define PRODUCT_BRANCH CONFIG_PRODUCT_BRANCH +#else +#define PRODUCT_BRANCH "1.3" +#endif + +#ifdef CONFIG_PRODUCT_URL +#define PRODUCT_URL CONFIG_PRODUCT_URL +#else +#define PRODUCT_URL "http://haproxy.1wt.eu/" +#endif + +#ifdef CONFIG_PRODUCT_URL_UPD +#define PRODUCT_URL_UPD CONFIG_PRODUCT_URL_UPD +#else +#define PRODUCT_URL_UPD "http://haproxy.1wt.eu/#down" +#endif + +#ifdef CONFIG_PRODUCT_URL_DOC +#define PRODUCT_URL_DOC CONFIG_PRODUCT_URL_DOC +#else +#define PRODUCT_URL_DOC "http://haproxy.1wt.eu/#docs" +#endif + #ifdef CONFIG_HAPROXY_VERSION #define HAPROXY_VERSION CONFIG_HAPROXY_VERSION #else diff --git a/include/proto/buffers.h b/include/proto/buffers.h index ca88b1671..657786026 100644 --- a/include/proto/buffers.h +++ b/include/proto/buffers.h @@ -83,8 +83,10 @@ static inline int buffer_realign(struct buffer *buf) int buffer_write(struct buffer *buf, const char *msg, int len); +int buffer_write_chunk(struct buffer *buf, struct chunk *chunk); 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); +int chunk_printf(struct chunk *chk, int size, const char *fmt, ...); /* * frees the destination chunk if already allocated, allocates a new string, diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 82f68a19d..45a42d792 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -46,6 +46,8 @@ void srv_close_with_err(struct session *t, int err, int finst, int status, const struct chunk *msg); int produce_content(struct session *s); +int produce_content_stats(struct session *s); +int produce_content_stats_proxy(struct session *s, struct proxy *px); void debug_hdr(const char *dir, struct session *t, const char *start, const char *end); void get_srv_from_appsession(struct session *t, const char *begin, const char *end); void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_exp *exp); diff --git a/include/types/httperr.h b/include/types/httperr.h index c466328f4..48c013a14 100644 --- a/include/types/httperr.h +++ b/include/types/httperr.h @@ -24,14 +24,6 @@ #include -/* various data sources for the responses */ -#define DATA_SRC_NONE 0 -#define DATA_SRC_STATS 1 - -/* data transmission states for the responses */ -#define DATA_ST_INIT 0 -#define DATA_ST_DATA 1 - /* * All implemented return codes */ diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 6e6472e6f..82c969282 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -65,6 +65,31 @@ * an LF is expected before entering the * designated state. */ +/* various data sources for the responses */ +#define DATA_SRC_NONE 0 +#define DATA_SRC_STATS 1 + +/* data transmission states for the stats responses */ +enum { + DATA_ST_INIT = 0, + DATA_ST_HEAD, + DATA_ST_INFO, + DATA_ST_LIST, + DATA_ST_END, + DATA_ST_FIN, +}; + +/* data transmission states for the stats responses inside a proxy */ +enum { + DATA_ST_PX_INIT = 0, + DATA_ST_PX_TH, + DATA_ST_PX_FE, + DATA_ST_PX_SV, + DATA_ST_PX_BE, + DATA_ST_PX_END, + DATA_ST_PX_FIN, +}; + #endif /* _TYPES_PROTO_HTTP_H */ diff --git a/include/types/proxy.h b/include/types/proxy.h index bf946a3cd..d710be8d5 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -108,7 +108,8 @@ struct proxy { unsigned int maxconn; /* max # of active sessions on the frontend */ unsigned int fullconn; /* #conns on backend above which servers are used at full load */ unsigned failed_conns, failed_resp; /* failed connect() and responses */ - unsigned failed_secu; /* blocked responses because of security concerns */ + unsigned denied_req, denied_resp; /* blocked requests/responses because of security concerns */ + unsigned failed_req; /* failed requests (eg: invalid or timeout) */ int conn_retries; /* maximum number of connect retries */ int options; /* PR_O_REDISP, PR_O_TRANSP, ... */ int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */ diff --git a/src/buffers.c b/src/buffers.c index 5e71b532c..def985623 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -10,6 +10,8 @@ * */ +#include +#include #include #include @@ -38,6 +40,31 @@ int buffer_write(struct buffer *buf, const char *msg, int len) return 0; } +/* writes the chunk to buffer . Returns 0 in case of + * success, or the number of bytes available otherwise. If the chunk + * has been written, its size is automatically reset to zero. + */ +int buffer_write_chunk(struct buffer *buf, struct chunk *chunk) +{ + int max; + + if (chunk->len == 0) + return 0; + + max = buffer_realign(buf); + + if (chunk->len > max) + return max; + + memcpy(buf->r, chunk->str, chunk->len); + buf->l += chunk->len; + buf->r += chunk->len; + if (buf->r == buf->data + BUFSIZE) + buf->r = buf->data; + chunk->len = 0; + return 0; +} + /* * this function writes the string at position which must be in buffer , * and moves just after the end of . @@ -110,6 +137,22 @@ int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len) } +/* + * Does an snprintf() at the end of chunk , respecting the limit of + * at most chars. If the size is over, nothing is added. Returns + * the new chunk size. + */ +int chunk_printf(struct chunk *chk, int size, const char *fmt, ...) +{ + va_list argp; + + va_start(argp, fmt); + chk->len += vsnprintf(chk->str + chk->len, size - chk->len, fmt, argp); + va_end(argp); + return chk->len; +} + + /* * Local variables: * c-indent-level: 8 diff --git a/src/log.c b/src/log.c index 851cba432..ba795dc2a 100644 --- a/src/log.c +++ b/src/log.c @@ -26,8 +26,8 @@ #include #include -#include #include +#include #include #include diff --git a/src/proto_http.c b/src/proto_http.c index 21ea4c910..4d6076f00 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -928,6 +928,7 @@ int process_cli(struct session *t) tv_eternity(&req->rex); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; + t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLICL; if (!(t->flags & SN_FINST_MASK)) @@ -940,6 +941,7 @@ int process_cli(struct session *t) /* read timeout : give up with an error message. */ t->logs.status = 408; client_retnclose(t, error_message(t, HTTP_ERR_408)); + t->fe->failed_req++; if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_CLITO; if (!(t->flags & SN_FINST_MASK)) @@ -1266,6 +1268,7 @@ int process_cli(struct session *t) t->hreq.hdr_state = HTTP_PA_ERROR; t->logs.status = 400; client_retnclose(t, error_message(t, HTTP_ERR_400)); + t->fe->failed_req++; return_prx_cond: if (!(t->flags & SN_ERR_MASK)) t->flags |= SN_ERR_PRXCOND; @@ -1817,7 +1820,7 @@ int process_srv(struct session *t) t->srv->cur_sess--; t->srv->failed_secu++; } - t->be->failed_secu++; + t->be->beprm->denied_resp++; t->srv_state = SV_STCLOSE; t->logs.status = 502; client_return(t, error_message(t, HTTP_ERR_502)); @@ -1848,7 +1851,7 @@ int process_srv(struct session *t) t->srv->cur_sess--; t->srv->failed_secu++; } - t->be->failed_secu++; + t->be->beprm->denied_resp++; t->srv_state = SV_STCLOSE; t->logs.status = 502; client_return(t, error_message(t, HTTP_ERR_502)); @@ -2719,386 +2722,13 @@ int process_srv(struct session *t) */ int produce_content(struct session *s) { - struct buffer *rep = s->rep; - struct proxy *px; - struct server *sv; - 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) { - msg.len = 0; - msg.str = trash; - - if (s->data_state == DATA_ST_INIT) { /* the function had not been called yet */ - unsigned int up; - - s->flags |= SN_SELF_GEN; // more data will follow - - /* send the start of the HTTP response */ - 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" - "Content-Type: text/html\r\n" - "\r\n"); - - s->logs.status = 200; - 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 - if (!(s->flags & SN_FINST_MASK)) - s->flags |= SN_FINST_R; - - /* WARNING! This must fit in the first buffer !!! */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "Statistics Report for " PRODUCT_NAME "\n" - "\n" - ""); - - if (buffer_write(rep, trash, msg.len) != 0) - return 0; - msg.len = 0; - - up = (now.tv_sec - start_date.tv_sec); - - /* WARNING! this has to fit the first packet too */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "

" PRODUCT_NAME "

\n" - "

Statistics Report for pid %d

\n" - "
\n" - "

> General process information

\n" - "
\n" - "

pid = %d (nbproc = %d)
\n" - "uptime = %dd %dh%02dm%02ds
\n" - "system limits : memmax = %s%s ; ulimit-n = %d
\n" - "maxsock = %d
\n" - "maxconn = %d (current conns = %d)
\n" - "

\n" - "\n" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
\n" - "
\n" - "", - pid, pid, global.nbproc, - up / 86400, (up % 86400) / 3600, - (up % 3600) / 60, (up % 60), - global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited", - global.rlimit_memmax ? " MB" : "", - global.rlimit_nofile, - global.maxsock, - global.maxconn, - actconn - ); - - if (buffer_write(rep, trash, msg.len) != 0) - return 0; - msg.len = 0; - - s->data_state = DATA_ST_DATA; - memset(&s->data_ctx, 0, sizeof(s->data_ctx)); - - px = s->data_ctx.stats.px = proxy; - s->data_ctx.stats.px_st = DATA_ST_INIT; - } - - while (s->data_ctx.stats.px) { - int dispatch_sess, dispatch_cum; - int failed_checks, down_trans; - int failed_secu, failed_conns, failed_resp; - - if (s->data_ctx.stats.px_st == DATA_ST_INIT) { - /* we are on a new proxy */ - px = s->data_ctx.stats.px; - - /* skip the disabled proxies */ - if (px->state == PR_STSTOPPED) - goto next_proxy; - - if (s->be->fiprm->uri_auth && s->be->fiprm->uri_auth->scope) { - /* we have a limited scope, we have to check the proxy name */ - struct stat_scope *scope; - int len; - - len = strlen(px->id); - scope = s->be->fiprm->uri_auth->scope; - - while (scope) { - /* match exact proxy name */ - if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) - break; - - /* match '.' which means 'self' proxy */ - if (!strcmp(scope->px_id, ".") && px == s->fe) - break; - scope = scope->next; - } - - /* proxy name not found */ - if (scope == NULL) - goto next_proxy; - } - - 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" - "", - px->id, - px->feconn, px->maxconn, px->beconn, - px->totpend, px->nbpend, px->cum_feconn, px->cum_beconn); - - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n" - "" - "" - "" - "" - "\n" - "" - "" - "" - "" - "\n"); - - if (buffer_write(rep, trash, msg.len) != 0) - return 0; - msg.len = 0; - - s->data_ctx.stats.sv = px->srv; - s->data_ctx.stats.px_st = DATA_ST_DATA; - } - - px = s->data_ctx.stats.px; - - /* stats.sv has been initialized above */ - while (s->data_ctx.stats.sv != NULL) { - static char *act_tab_bg[5] = { /*down*/"#FF9090", /*rising*/"#FFD020", /*failing*/"#FFFFA0", /*up*/"#C0FFC0", /*unchecked*/"#E0E0E0" }; - static char *bck_tab_bg[5] = { /*down*/"#FF9090", /*rising*/"#FF80ff", /*failing*/"#C060FF", /*up*/"#B0D0FF", /*unchecked*/"#E0E0E0" }; - static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d ↑", "UP %d/%d ↓", "UP", "no check" }; - int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP */ - - sv = s->data_ctx.stats.sv; - - /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ - if (!(sv->state & SRV_CHECKED)) - sv_state = 4; - else if (sv->state & SRV_RUNNING) - if (sv->health == sv->rise + sv->fall - 1) - sv_state = 3; /* UP */ - else - sv_state = 2; /* going down */ - else - if (sv->health) - sv_state = 1; /* going up */ - else - sv_state = 0; /* DOWN */ - - /* name, weight */ - msg.len += snprintf(trash, sizeof(trash), - "", - (sv->state & SRV_BACKUP) ? "-" : "Y", - (sv->state & SRV_BACKUP) ? "Y" : "-"); - - /* queue : current, max */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "", - sv->nbpend, sv->nbpend_max); - - /* sessions : current, max, limit, cumul */ - 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 */ - 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) - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n", - sv->failed_checks, sv->down_trans); - else - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n"); - - if (buffer_write(rep, trash, msg.len) != 0) - return 0; - msg.len = 0; - - s->data_ctx.stats.sv = sv->next; - } /* while sv */ - - /* now we are past the last server, we'll dump information about the dispatcher */ - - /* We have to count down from the proxy to the servers to tell how - * many sessions are on the dispatcher, and how many checks have - * failed. We cannot count this during the servers dump because it - * might be interrupted multiple times. - */ - dispatch_sess = px->beconn; - dispatch_cum = px->cum_beconn; - failed_secu = px->failed_secu; - failed_conns = px->failed_conns; - failed_resp = px->failed_resp; - failed_checks = down_trans = 0; - - sv = px->srv; - while (sv) { - dispatch_sess -= sv->cur_sess; - dispatch_cum -= sv->cum_sess; - failed_conns -= sv->failed_conns; - failed_resp -= sv->failed_resp; - failed_secu -= sv->failed_secu; - if (sv->state & SRV_CHECKED) { - failed_checks += sv->failed_checks; - down_trans += sv->down_trans; - } - sv = sv->next; - } - - /* name, weight, status, act, bck */ - msg.len += snprintf(trash + msg.len, sizeof(trash), - "" - "" - "", - px->state == PR_STRUN ? "UP" : "DOWN"); - - /* queue : current, max */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "", - px->nbpend, px->nbpend_max); - - /* sessions : current, max, limit, cumul. */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "", - dispatch_sess, px->beconn_max, dispatch_cum); - - /* errors : connect, response, security */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n", - failed_conns, failed_resp, failed_secu); - - /* check failures : unique, fatal */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n"); - - - /* now the summary for the whole proxy */ - /* name, weight, status, act, bck */ - msg.len += snprintf(trash + msg.len, sizeof(trash), - "" - "" - "", - (px->state == PR_STRUN && ((px->srv == NULL) || px->srv_act || px->srv_bck)) ? "UP" : "DOWN", - px->srv_act, px->srv_bck); - - /* queue : current, max */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "", - px->totpend, px->nbpend_max); - - /* sessions : current, max, limit, cumul */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "", - px->beconn, px->beconn_max, px->cum_beconn); - - /* errors : connect, response, security */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n", - px->failed_conns, px->failed_resp, px->failed_secu); - - /* check failures : unique, fatal */ - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, - "\n", - failed_checks, down_trans); - - msg.len += snprintf(trash + msg.len, sizeof(trash) - msg.len, "
ServerQueueSessionsErrors
NameWeightStatusAct.Bck.Curr.Max.Curr.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 */ - 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 */ - 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"); - - if (buffer_write(rep, trash, msg.len) != 0) - return 0; - msg.len = 0; - - s->data_ctx.stats.px_st = DATA_ST_INIT; - next_proxy: - s->data_ctx.stats.px = px->next; - } /* proxy loop */ - /* here, we just have reached the sv == NULL and px == NULL */ - s->flags &= ~SN_SELF_GEN; - return 1; + /* dump server statistics */ + return produce_content_stats(s); } else { /* unknown data source */ @@ -3108,7 +2738,505 @@ int produce_content(struct session *s) s->flags |= SN_ERR_PRXCOND; if (!(s->flags & SN_FINST_MASK)) s->flags |= SN_FINST_R; - s->flags &= SN_SELF_GEN; + s->flags &= ~SN_SELF_GEN; + return 1; + } +} + + +/* + * Produces statistics data for the session . Expects to be called with + * s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN + * flag from the session, which it uses to keep on being called when there is + * free space in the buffer, of simply by letting an empty buffer upon return. + * It returns 1 if it changes the session state from CL_STSHUTR, otherwise 0. + */ +int produce_content_stats(struct session *s) +{ + struct buffer *rep = s->rep; + struct proxy *px; + struct chunk msg; + unsigned int up; + + msg.len = 0; + msg.str = trash; + + switch (s->data_state) { + case DATA_ST_INIT: + /* the function had not been called yet */ + s->flags |= SN_SELF_GEN; // more data will follow + + chunk_printf(&msg, sizeof(trash), + "HTTP/1.0 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "\r\n"); + + s->logs.status = 200; + 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 + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + + s->data_state = DATA_ST_HEAD; /* let's start producing data */ + /* fall through */ + + case DATA_ST_HEAD: + /* WARNING! This must fit in the first buffer !!! */ + chunk_printf(&msg, sizeof(trash), + "Statistics Report for " PRODUCT_NAME "\n" + "\n" + ""); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_state = DATA_ST_INFO; + /* fall through */ + + case DATA_ST_INFO: + up = (now.tv_sec - start_date.tv_sec); + + /* WARNING! this has to fit the first packet too. + * We are around 3.5 kB, add adding entries will + * become tricky if we want to support 4kB buffers ! + */ + chunk_printf(&msg, sizeof(trash), + "

" + PRODUCT_NAME "

\n" + "

Statistics Report for pid %d

\n" + "
\n" + "

> General process information

\n" + "" + "" + "
\n" + "

pid = %d (nbproc = %d)
\n" + "uptime = %dd %dh%02dm%02ds
\n" + "system limits : memmax = %s%s ; ulimit-n = %d
\n" + "maxsock = %d
\n" + "maxconn = %d (current conns = %d)
\n" + "

\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
\n" + "
" + "External ressources:" + "
\n" + "", + pid, pid, global.nbproc, + up / 86400, (up % 86400) / 3600, + (up % 3600) / 60, (up % 60), + global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited", + global.rlimit_memmax ? " MB" : "", + global.rlimit_nofile, + global.maxsock, + global.maxconn, + actconn + ); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + memset(&s->data_ctx, 0, sizeof(s->data_ctx)); + + s->data_ctx.stats.px = proxy; + s->data_ctx.stats.px_st = DATA_ST_PX_INIT; + s->data_state = DATA_ST_LIST; + /* fall through */ + + case DATA_ST_LIST: + /* dump proxies */ + while (s->data_ctx.stats.px) { + px = s->data_ctx.stats.px; + /* skip the disabled proxies and non-networked ones */ + if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE))) + if (produce_content_stats_proxy(s, px) == 0) + return 0; + + s->data_ctx.stats.px = px->next; + s->data_ctx.stats.px_st = DATA_ST_PX_INIT; + } + /* here, we just have reached the last proxy */ + + s->data_state = DATA_ST_END; + /* fall through */ + + case DATA_ST_END: + chunk_printf(&msg, sizeof(trash), ""); + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_state = DATA_ST_FIN; + /* fall through */ + + case DATA_ST_FIN: + s->flags &= ~SN_SELF_GEN; + return 1; + + default: + /* unknown state ! */ + s->logs.status = 500; + client_retnclose(s, error_message(s, HTTP_ERR_500)); + if (!(s->flags & SN_ERR_MASK)) + s->flags |= SN_ERR_PRXCOND; + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + s->flags &= ~SN_SELF_GEN; + return 1; + } +} + + +/* + * Dumps statistics for a proxy. + * Returns 0 if it had to stop dumping data because of lack of buffer space, + * ot non-zero if everything completed. + */ +int produce_content_stats_proxy(struct session *s, struct proxy *px) +{ + struct buffer *rep = s->rep; + struct server *sv; + struct chunk msg; + + msg.len = 0; + msg.str = trash; + + switch (s->data_ctx.stats.px_st) { + case DATA_ST_PX_INIT: + /* we are on a new proxy */ + + if (s->be->fiprm->uri_auth && s->be->fiprm->uri_auth->scope) { + /* we have a limited scope, we have to check the proxy name */ + struct stat_scope *scope; + int len; + + len = strlen(px->id); + scope = s->be->fiprm->uri_auth->scope; + + while (scope) { + /* match exact proxy name */ + if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) + break; + + /* match '.' which means 'self' proxy */ + if (!strcmp(scope->px_id, ".") && px == s->fe) + break; + scope = scope->next; + } + + /* proxy name not found : don't dump anything */ + if (scope == NULL) + return 1; + } + + s->data_ctx.stats.px_st = DATA_ST_PX_TH; + /* fall through */ + + case DATA_ST_PX_TH: + /* print a new table */ + chunk_printf(&msg, sizeof(trash), + "\n" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "\n" + "", + px->id); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.px_st = DATA_ST_PX_FE; + /* fall through */ + + case DATA_ST_PX_FE: + /* print the frontend */ + if (px->cap & PR_CAP_FE) { + /* name, queue */ + chunk_printf(&msg, sizeof(trash), + ""); + + /* sessions : current, max, limit, cumul. */ + chunk_printf(&msg, sizeof(trash), + "", + px->feconn, px->feconn_max, px->maxconn, px->cum_feconn); + + /* bytes : in, out */ + chunk_printf(&msg, sizeof(trash), + ""); + + /* denied: req, resp */ + chunk_printf(&msg, sizeof(trash), + "", + px->denied_req, px->denied_resp); + + /* errors : request, connect, response */ + chunk_printf(&msg, sizeof(trash), + "", + px->failed_req); + + /* server status : reflect backend status */ + chunk_printf(&msg, sizeof(trash), "", + px->state == PR_STRUN ? "OPEN" : + px->state == PR_STIDLE ? "FULL" : "STOP"); + + /* rest of server: nothing */ + chunk_printf(&msg, sizeof(trash), ""); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + } + + s->data_ctx.stats.sv = px->srv; /* may be NULL */ + s->data_ctx.stats.px_st = DATA_ST_PX_SV; + /* fall through */ + + case DATA_ST_PX_SV: + /* stats.sv has been initialized above */ + while (s->data_ctx.stats.sv != NULL) { + static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d ↑", "UP %d/%d ↓", "UP", "no check" }; + int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4=unchecked */ + + sv = s->data_ctx.stats.sv; + + /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ + if (!(sv->state & SRV_CHECKED)) + sv_state = 4; + else if (sv->state & SRV_RUNNING) + if (sv->health == sv->rise + sv->fall - 1) + sv_state = 3; /* UP */ + else + sv_state = 2; /* going down */ + else + if (sv->health) + sv_state = 1; /* going up */ + else + sv_state = 0; /* DOWN */ + + /* name */ + chunk_printf(&msg, sizeof(trash), + "", + (sv->state & SRV_BACKUP) ? "active" : "backup", + sv_state, sv->id); + + /* queue : current, max */ + chunk_printf(&msg, sizeof(trash), + "", + sv->nbpend, sv->nbpend_max); + + /* sessions : current, max, limit, cumul */ + chunk_printf(&msg, sizeof(trash), + "", + sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess); + + /* bytes : in, out */ + chunk_printf(&msg, sizeof(trash), + ""); + + /* denied: req, resp */ + chunk_printf(&msg, sizeof(trash), + "", + sv->failed_secu); + + /* errors : request, connect, response */ + chunk_printf(&msg, sizeof(trash), + "\n", + sv->failed_conns, sv->failed_resp); + + /* status */ + chunk_printf(&msg, sizeof(trash), "", sv->uweight+1); + + /* act, bck */ + chunk_printf(&msg, sizeof(trash), "", + (sv->state & SRV_BACKUP) ? "-" : "Y", + (sv->state & SRV_BACKUP) ? "Y" : "-"); + + /* check failures : unique, fatal */ + if (sv->state & SRV_CHECKED) + chunk_printf(&msg, sizeof(trash), + "\n", + sv->failed_checks, sv->down_trans); + else + chunk_printf(&msg, sizeof(trash), + "\n"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.sv = sv->next; + } /* while sv */ + + s->data_ctx.stats.px_st = DATA_ST_PX_BE; + /* fall through */ + + case DATA_ST_PX_BE: + /* print the backend */ + if (px->cap & PR_CAP_BE) { + /* name */ + chunk_printf(&msg, sizeof(trash), + ""); + + /* queue : current, max */ + chunk_printf(&msg, sizeof(trash), + "", + px->nbpend /* or px->totpend ? */, px->nbpend_max); + + /* sessions : current, max, limit, cumul. */ + chunk_printf(&msg, sizeof(trash), + "", + px->beconn, px->beconn_max, px->fullconn, px->cum_beconn); + + /* bytes : in, out */ + chunk_printf(&msg, sizeof(trash), + ""); + + /* denied: req, resp */ + chunk_printf(&msg, sizeof(trash), + "", + px->denied_req, px->denied_resp); + + /* errors : request, connect, response */ + chunk_printf(&msg, sizeof(trash), + "\n", + px->failed_conns, px->failed_resp); + + /* server status : reflect backend status (up/down) : we display UP + * if the backend has known working servers or if it has no server at + * all (eg: for stats). Tthen we display the total weight, number of + * active and backups. */ + chunk_printf(&msg, sizeof(trash), + "" + "" + "", + (px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN", + px->srv_map_sz, px->srv_act, px->srv_bck); + + /* rest of server: nothing */ + chunk_printf(&msg, sizeof(trash), ""); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + } + + s->data_ctx.stats.px_st = DATA_ST_PX_END; + /* fall through */ + + case DATA_ST_PX_END: + chunk_printf(&msg, sizeof(trash), "
%s
QueueSessionsBytesDeniedErrorsServer
Curr.Max.Curr.Max.LimitCumul.InOutReqRespReqConnRespStatusWeightActBckCheckDown
Frontend%d%d%d%d%d%d%d%s
%s%d%d%d%d%s%d%d%d%d"); + chunk_printf(&msg, sizeof(trash), + srv_hlt_st[sv_state], + (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), + (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); + + /* weight */ + chunk_printf(&msg, sizeof(trash), "%d%s%s%d%d
Backend%d%d%d%d%d%d%d%d%d%d%s%d%d%d

\n"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.px_st = DATA_ST_PX_FIN; + /* fall through */ + + case DATA_ST_PX_FIN: + return 1; + + default: + /* unknown state, we should put an abort() here ! */ return 1; } } @@ -3243,12 +3371,14 @@ void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_ t->flags |= SN_CLDENY; abort_filt = 1; } + t->be->beprm->denied_req++; break; case ACT_TARPIT: if (!(t->flags & (SN_CLALLOW | SN_CLDENY))) { t->flags |= SN_CLTARPIT; abort_filt = 1; } + t->be->beprm->denied_req++; break; //case ACT_PASS: /* FIXME: broken as of now. We should mark the header as "ignored". */ // break;