[MEDIUM] moved stats and buffer generic functions to new files

Neither the primitives used to write data to a buffer, nor the stats
dump functions are HTTP-specific anymore. Move them to dedicated
files
This commit is contained in:
Willy Tarreau 2007-10-17 17:06:05 +02:00
parent e6ad2b165e
commit 9186126e1c
8 changed files with 778 additions and 593 deletions

View File

@ -231,6 +231,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
src/senddata.o src/dumpstats.o \
src/session.o src/hdr_idx.o src/ev_select.o src/acl.o src/memory.o
haproxy: $(OBJS) $(OPT_OBJS)

View File

@ -103,6 +103,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
src/senddata.o src/dumpstats.o \
src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o \
src/ev_kqueue.o src/acl.o src/memory.o

View File

@ -100,6 +100,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
src/senddata.o src/dumpstats.o \
src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o src/acl.o \
src/memory.o

41
include/proto/dumpstats.h Normal file
View File

@ -0,0 +1,41 @@
/*
include/proto/dumpstats.h
This file contains definitions of some primitives to dedicated to
statistics output.
Copyright (C) 2000-2007 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_DUMPSTATS_H
#define _PROTO_DUMPSTATS_H
#include <common/config.h>
#include <types/buffers.h>
#include <types/session.h>
int stats_dump_http(struct session *s, struct uri_auth *uri, int flags);
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags);
#endif /* _PROTO_DUMPSTATS_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

40
include/proto/senddata.h Normal file
View File

@ -0,0 +1,40 @@
/*
include/proto/senddata.h
This file contains definitions of some primitives to send data to
clients over a socket and a buffer.
Copyright (C) 2000-2007 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_SENDDATA_H
#define _PROTO_SENDDATA_H
#include <common/config.h>
#include <types/buffers.h>
#include <types/session.h>
void client_retnclose(struct session *s, const struct chunk *msg);
void client_return(struct session *s, const struct chunk *msg);
#endif /* _PROTO_SENDDATA_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

591
src/dumpstats.c Normal file
View File

@ -0,0 +1,591 @@
/*
* Functions dedicated to statistics output
*
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <common/compat.h>
#include <common/config.h>
#include <common/debug.h>
#include <common/memory.h>
#include <common/mini-clist.h>
#include <common/standard.h>
#include <common/time.h>
#include <common/uri_auth.h>
#include <common/version.h>
#include <types/client.h>
#include <types/global.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/server.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
#include <proto/senddata.h>
#include <proto/session.h>
/*
* Produces statistics data for the session <s>. 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 0 if it had to stop writing data and an I/O is needed, 1 if the
* dump is finished and the session must be closed, or -1 in case of any error.
*/
int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
{
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");
if (uri->refresh > 0 && !(s->flags & SN_STAT_NORFRSH))
chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
uri->refresh);
chunk_printf(&msg, sizeof(trash), "\r\n");
s->txn.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;
if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request */
s->data_state = DATA_ST_FIN;
s->flags &= ~SN_SELF_GEN;
return 1;
}
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),
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
"<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
"<style type=\"text/css\"><!--\n"
"body {"
" font-family: helvetica, arial;"
" font-size: 12px;"
" font-weight: normal;"
" color: black;"
" background: white;"
"}\n"
"th,td {"
" font-size: 0.8em;"
" align: center;"
"}\n"
"h1 {"
" font-size: xx-large;"
" margin-bottom: 0.5em;"
"}\n"
"h2 {"
" font-family: helvetica, arial;"
" font-size: x-large;"
" font-weight: bold;"
" font-style: italic;"
" color: #6020a0;"
" margin-top: 0em;"
" margin-bottom: 0em;"
"}\n"
"h3 {"
" font-family: helvetica, arial;"
" font-size: 16px;"
" font-weight: bold;"
" color: #b00040;"
" background: #e8e8d0;"
" margin-top: 0em;"
" margin-bottom: 0em;"
"}\n"
"li {"
" margin-top: 0.25em;"
" margin-right: 2em;"
"}\n"
".hr {margin-top: 0.25em;"
" border-color: black;"
" border-bottom-style: solid;"
"}\n"
".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
".total {background: #20D0D0;color: #ffff80;}\n"
".frontend {background: #e8e8d0;}\n"
".backend {background: #e8e8d0;}\n"
".active0 {background: #ff9090;}\n"
".active1 {background: #ffd020;}\n"
".active2 {background: #ffffa0;}\n"
".active3 {background: #c0ffc0;}\n"
".active4 {background: #e0e0e0;}\n"
".backup0 {background: #ff9090;}\n"
".backup1 {background: #ff80ff;}\n"
".backup2 {background: #c060ff;}\n"
".backup3 {background: #b0d0ff;}\n"
".backup4 {background: #e0e0e0;}\n"
"table.tbl { border-collapse: collapse; border-style: none;}\n"
"table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
"table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
"table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
"table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
"table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
"table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
"-->\n"
"</style></head>\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),
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
PRODUCT_NAME "%s</a></h1>\n"
"<h2>Statistics Report for pid %d</h2>\n"
"<hr width=\"100%%\" class=\"hr\">\n"
"<h3>&gt; General process information</h3>\n"
"<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
"<p><b>pid = </b> %d (nbproc = %d)<br>\n"
"<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
"<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
"<b>maxsock = </b> %d<br>\n"
"<b>maxconn = </b> %d (current conns = %d)<br>\n"
"</td><td align=\"center\" nowrap>\n"
"<table class=\"lgd\"><tr>\n"
"<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
"<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
"</tr><tr>\n"
"<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
"<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
"</tr><tr>\n"
"<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
"<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
"</tr><tr>\n"
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
"<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
"</tr></table>\n"
"</td>"
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
"",
(uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
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 (s->flags & SN_STAT_HIDEDWN)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
uri->uri_prefix,
"",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
uri->uri_prefix,
";up",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
if (uri->refresh > 0) {
if (s->flags & SN_STAT_NORFRSH)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
uri->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
"");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
uri->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
";norefresh");
}
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
uri->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
chunk_printf(&msg, sizeof(trash),
"</td>"
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
"<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
"<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
"<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
"<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
"</ul>"
"</td>"
"</tr></table>\n"
""
);
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 (stats_dump_proxy(s, px, uri, flags) == 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), "</body></html>\n");
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->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 stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags)
{
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 (uri && uri->scope) {
/* we have a limited scope, we have to check the proxy name */
struct stat_scope *scope;
int len;
len = strlen(px->id);
scope = uri->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),
"<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
"<tr align=\"center\" class=\"titre\">"
"<th colspan=2 class=\"pxname\">%s</th>"
"<th colspan=18 class=\"empty\"></th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th rowspan=2></th>"
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
"<th colspan=3>Errors</th><th colspan=6>Server</th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
"<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
"<th>Bck</th><th>Check</th><th>Down</th></tr>\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) {
chunk_printf(&msg, sizeof(trash),
/* name, queue */
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
/* sessions : current, max, limit, cumul. */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right>%d</td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right>%d</td><td align=right></td><td align=right></td>"
/* server status : reflect backend status */
"<td align=center>%s</td>"
/* rest of server: nothing */
"<td align=center colspan=5></td></tr>"
"",
px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
px->bytes_in, px->bytes_out,
px->denied_req, px->denied_resp,
px->failed_req,
px->state == PR_STRUN ? "OPEN" :
px->state == PR_STIDLE ? "FULL" : "STOP");
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 &uarr;", "UP %d/%d &darr;", "UP", "<i>no check</i>" };
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 */
if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) {
/* do not report servers which are DOWN */
s->data_ctx.stats.sv = sv->next;
continue;
}
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
/* sessions : current, max, limit, cumul */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right></td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
"",
(sv->state & SRV_BACKUP) ? "backup" : "active",
sv_state, sv->id,
sv->nbpend, sv->nbpend_max,
sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess,
sv->bytes_in, sv->bytes_out,
sv->failed_secu,
sv->failed_conns, sv->failed_resp);
/* status */
chunk_printf(&msg, sizeof(trash), "<td nowrap>");
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));
chunk_printf(&msg, sizeof(trash),
/* weight */
"</td><td>%d</td>"
/* act, bck */
"<td>%s</td><td>%s</td>"
"",
sv->uweight,
(sv->state & SRV_BACKUP) ? "-" : "Y",
(sv->state & SRV_BACKUP) ? "Y" : "-");
/* check failures : unique, fatal */
if (sv->state & SRV_CHECKED)
chunk_printf(&msg, sizeof(trash),
"<td align=right>%d</td><td align=right>%d</td></tr>\n",
sv->failed_checks, sv->down_trans);
else
chunk_printf(&msg, sizeof(trash),
"<td colspan=2></td></tr>\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) {
int gcd = 1;
if (px->map_state & PR_MAP_RECALC)
recalc_server_map(px);
/* The GCD which was computed causes the total effective
* weight to appear lower than all weights. Let's
* recompute it.
*/
if (px->srv && px->srv->eweight)
gcd = px->srv->uweight / px->srv->eweight;
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=center class=\"backend\"><td>Backend</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
/* sessions : current, max, limit, cumul. */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right>%d</td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
/* 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. */
"<td align=center>%s</td><td align=center>%d</td>"
"<td align=center>%d</td><td align=center>%d</td>"
/* rest of server: nothing */
"<td align=center colspan=2></td></tr>"
"",
px->nbpend /* or px->totpend ? */, px->nbpend_max,
px->beconn, px->beconn_max, px->fullconn, px->cum_beconn,
px->bytes_in, px->bytes_out,
px->denied_req, px->denied_resp,
px->failed_conns, px->failed_resp,
(px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN",
px->srv_map_sz * gcd, px->srv_act, px->srv_bck);
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), "</table><p>\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;
}
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -46,11 +46,13 @@
#include <proto/acl.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/dumpstats.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/hdr_idx.h>
#include <proto/proto_http.h>
#include <proto/queue.h>
#include <proto/senddata.h>
#include <proto/session.h>
#include <proto/task.h>
@ -533,44 +535,6 @@ int http_find_header(const char *name,
return http_find_header2(name, strlen(name), sol, idx, ctx);
}
/*
* 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. 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, const struct chunk *msg)
{
EV_FD_CLR(s->cli_fd, DIR_RD);
EV_FD_SET(s->cli_fd, DIR_WR);
buffer_shutr(s->req);
if (!tv_add_ifset(&s->rep->wex, &now, &s->fe->clitimeout))
tv_eternity(&s->rep->wex);
s->cli_state = CL_STSHUTR;
buffer_flush(s->rep);
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 message
* is contained in a "chunk". If it is null, then an empty message is
* used.
*/
void client_return(struct session *s, const struct chunk *msg)
{
buffer_flush(s->rep);
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 <status> is 0, or if the message
* pointer is NULL, then no message is returned.
@ -3423,563 +3387,21 @@ int produce_content(struct session *s)
}
else if (s->data_source == DATA_SRC_STATS) {
/* dump server statistics */
return produce_content_stats(s);
int ret = stats_dump_http(s, s->be->uri_auth, 0);
if (ret >= 0)
return ret;
/* -1 indicates an error */
}
else {
/* unknown data source */
s->txn.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;
}
}
/*
* Produces statistics data for the session <s>. 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");
if (s->be->uri_auth->refresh > 0 && !(s->flags & SN_STAT_NORFRSH))
chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
s->be->uri_auth->refresh);
chunk_printf(&msg, sizeof(trash), "\r\n");
s->txn.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;
if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request */
s->data_state = DATA_ST_FIN;
s->flags &= ~SN_SELF_GEN;
return 1;
}
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),
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
"<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
"<style type=\"text/css\"><!--\n"
"body {"
" font-family: helvetica, arial;"
" font-size: 12px;"
" font-weight: normal;"
" color: black;"
" background: white;"
"}\n"
"th,td {"
" font-size: 0.8em;"
" align: center;"
"}\n"
"h1 {"
" font-size: xx-large;"
" margin-bottom: 0.5em;"
"}\n"
"h2 {"
" font-family: helvetica, arial;"
" font-size: x-large;"
" font-weight: bold;"
" font-style: italic;"
" color: #6020a0;"
" margin-top: 0em;"
" margin-bottom: 0em;"
"}\n"
"h3 {"
" font-family: helvetica, arial;"
" font-size: 16px;"
" font-weight: bold;"
" color: #b00040;"
" background: #e8e8d0;"
" margin-top: 0em;"
" margin-bottom: 0em;"
"}\n"
"li {"
" margin-top: 0.25em;"
" margin-right: 2em;"
"}\n"
".hr {margin-top: 0.25em;"
" border-color: black;"
" border-bottom-style: solid;"
"}\n"
".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
".total {background: #20D0D0;color: #ffff80;}\n"
".frontend {background: #e8e8d0;}\n"
".backend {background: #e8e8d0;}\n"
".active0 {background: #ff9090;}\n"
".active1 {background: #ffd020;}\n"
".active2 {background: #ffffa0;}\n"
".active3 {background: #c0ffc0;}\n"
".active4 {background: #e0e0e0;}\n"
".backup0 {background: #ff9090;}\n"
".backup1 {background: #ff80ff;}\n"
".backup2 {background: #c060ff;}\n"
".backup3 {background: #b0d0ff;}\n"
".backup4 {background: #e0e0e0;}\n"
"table.tbl { border-collapse: collapse; border-style: none;}\n"
"table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
"table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
"table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
"table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
"table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
"table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
"-->\n"
"</style></head>\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),
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
PRODUCT_NAME "%s</a></h1>\n"
"<h2>Statistics Report for pid %d</h2>\n"
"<hr width=\"100%%\" class=\"hr\">\n"
"<h3>&gt; General process information</h3>\n"
"<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
"<p><b>pid = </b> %d (nbproc = %d)<br>\n"
"<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
"<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
"<b>maxsock = </b> %d<br>\n"
"<b>maxconn = </b> %d (current conns = %d)<br>\n"
"</td><td align=\"center\" nowrap>\n"
"<table class=\"lgd\"><tr>\n"
"<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
"<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
"</tr><tr>\n"
"<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
"<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
"</tr><tr>\n"
"<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
"<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
"</tr><tr>\n"
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
"<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
"</tr></table>\n"
"</td>"
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
"",
(s->be->uri_auth->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
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 (s->flags & SN_STAT_HIDEDWN)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
s->be->uri_auth->uri_prefix,
"",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
s->be->uri_auth->uri_prefix,
";up",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
if (s->be->uri_auth->refresh > 0) {
if (s->flags & SN_STAT_NORFRSH)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
s->be->uri_auth->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
"");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
s->be->uri_auth->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
";norefresh");
}
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
s->be->uri_auth->uri_prefix,
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
chunk_printf(&msg, sizeof(trash),
"</td>"
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
"<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
"<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
"<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
"<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
"</ul>"
"</td>"
"</tr></table>\n"
""
);
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), "</body></html>\n");
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->txn.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->uri_auth && s->be->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->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),
"<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
"<tr align=\"center\" class=\"titre\">"
"<th colspan=2 class=\"pxname\">%s</th>"
"<th colspan=18 class=\"empty\"></th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th rowspan=2></th>"
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
"<th colspan=3>Errors</th><th colspan=6>Server</th>"
"</tr>\n"
"<tr align=\"center\" class=\"titre\">"
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
"<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
"<th>Bck</th><th>Check</th><th>Down</th></tr>\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) {
chunk_printf(&msg, sizeof(trash),
/* name, queue */
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
/* sessions : current, max, limit, cumul. */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right>%d</td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right>%d</td><td align=right></td><td align=right></td>"
/* server status : reflect backend status */
"<td align=center>%s</td>"
/* rest of server: nothing */
"<td align=center colspan=5></td></tr>"
"",
px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
px->bytes_in, px->bytes_out,
px->denied_req, px->denied_resp,
px->failed_req,
px->state == PR_STRUN ? "OPEN" :
px->state == PR_STIDLE ? "FULL" : "STOP");
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 &uarr;", "UP %d/%d &darr;", "UP", "<i>no check</i>" };
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 */
if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) {
/* do not report servers which are DOWN */
s->data_ctx.stats.sv = sv->next;
continue;
}
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
/* sessions : current, max, limit, cumul */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right></td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
"",
(sv->state & SRV_BACKUP) ? "backup" : "active",
sv_state, sv->id,
sv->nbpend, sv->nbpend_max,
sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess,
sv->bytes_in, sv->bytes_out,
sv->failed_secu,
sv->failed_conns, sv->failed_resp);
/* status */
chunk_printf(&msg, sizeof(trash), "<td nowrap>");
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));
chunk_printf(&msg, sizeof(trash),
/* weight */
"</td><td>%d</td>"
/* act, bck */
"<td>%s</td><td>%s</td>"
"",
sv->uweight,
(sv->state & SRV_BACKUP) ? "-" : "Y",
(sv->state & SRV_BACKUP) ? "Y" : "-");
/* check failures : unique, fatal */
if (sv->state & SRV_CHECKED)
chunk_printf(&msg, sizeof(trash),
"<td align=right>%d</td><td align=right>%d</td></tr>\n",
sv->failed_checks, sv->down_trans);
else
chunk_printf(&msg, sizeof(trash),
"<td colspan=2></td></tr>\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) {
int gcd = 1;
if (px->map_state & PR_MAP_RECALC)
recalc_server_map(px);
/* The GCD which was computed causes the total effective
* weight to appear lower than all weights. Let's
* recompute it.
*/
if (px->srv && px->srv->eweight)
gcd = px->srv->uweight / px->srv->eweight;
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=center class=\"backend\"><td>Backend</td>"
/* queue : current, max */
"<td align=right>%d</td><td align=right>%d</td>"
/* sessions : current, max, limit, cumul. */
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
/* bytes : in, out */
"<td align=right>%lld</td><td align=right>%lld</td>"
/* denied: req, resp */
"<td align=right>%d</td><td align=right>%d</td>"
/* errors : request, connect, response */
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
/* 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. */
"<td align=center>%s</td><td align=center>%d</td>"
"<td align=center>%d</td><td align=center>%d</td>"
/* rest of server: nothing */
"<td align=center colspan=2></td></tr>"
"",
px->nbpend /* or px->totpend ? */, px->nbpend_max,
px->beconn, px->beconn_max, px->fullconn, px->cum_beconn,
px->bytes_in, px->bytes_out,
px->denied_req, px->denied_resp,
px->failed_conns, px->failed_resp,
(px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN",
px->srv_map_sz * gcd, px->srv_act, px->srv_bck);
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), "</table><p>\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;
}
/* unknown data source or internal error */
s->txn.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;
}

88
src/senddata.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Helper functions to send data over a socket and buffer.
* Should probably move somewhere else, but where ?
*
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <common/compat.h>
#include <common/config.h>
#include <common/debug.h>
#include <common/memory.h>
#include <common/standard.h>
#include <common/time.h>
#include <common/version.h>
#include <types/client.h>
#include <types/global.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/server.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/fd.h>
#include <proto/senddata.h>
#include <proto/session.h>
/*
* 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. 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, const struct chunk *msg)
{
EV_FD_CLR(s->cli_fd, DIR_RD);
EV_FD_SET(s->cli_fd, DIR_WR);
buffer_shutr(s->req);
if (!tv_add_ifset(&s->rep->wex, &now, &s->rep->wto))
tv_eternity(&s->rep->wex);
s->cli_state = CL_STSHUTR;
buffer_flush(s->rep);
if (msg && 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 message
* is contained in a "chunk". If it is null, then an empty message is
* used.
*/
void client_return(struct session *s, const struct chunk *msg)
{
buffer_flush(s->rep);
if (msg && msg->len)
buffer_write(s->rep, msg->str, msg->len);
s->req->l = 0;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/