[MEDIUM] stats: make HTTP stats use an I/O handler
Doing this, we can remove the last BF_HIJACK user and remove
produce_content(). s->data_source could also be removed but
it is currently used to detect if the stats or a server was
used.
diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h
index 79ae404..700fc95 100644
--- a/include/proto/dumpstats.h
+++ b/include/proto/dumpstats.h
@@ -59,6 +59,7 @@
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep);
int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep);
+void http_stats_io_handler(struct stream_interface *si);
#endif /* _PROTO_DUMPSTATS_H */
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 35a216c..5a12998 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -1,23 +1,23 @@
/*
- include/proto/proto_http.h
- This file contains HTTP protocol definitions.
-
- Copyright (C) 2000-2008 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
-*/
+ * include/proto/proto_http.h
+ * This file contains HTTP protocol definitions.
+ *
+ * Copyright (C) 2000-2009 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_PROTO_HTTP_H
#define _PROTO_PROTO_HTTP_H
@@ -68,9 +68,6 @@
int http_process_request_body(struct session *s, struct buffer *req, int an_bit);
int process_response(struct session *t);
-void produce_content(struct session *s, struct buffer *rep);
-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, int len);
int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hdr_exp *exp);
diff --git a/include/types/session.h b/include/types/session.h
index 226e00c..d3dbe67 100644
--- a/include/types/session.h
+++ b/include/types/session.h
@@ -211,7 +211,7 @@
int ptr; /* <0: headers, >=0 : text pointer to restart from */
int bol; /* pointer to beginning of current line */
} errors;
- } data_ctx; /* used by produce_content to dump the stats right now */
+ } data_ctx; /* used by stats I/O handlers to dump the stats */
unsigned int uniq_id; /* unique ID used for the traces */
};
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 14cc519..e8eef21 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -587,7 +587,6 @@
/* skip the disabled proxies and non-networked ones */
if (px->state != PR_STSTOPPED &&
(px->cap & (PR_CAP_FE | PR_CAP_BE))) {
- rep->flags |= BF_READ_PARTIAL; /* remove this once stats_dump_proxy uses buffer_feed */
if (stats_dump_proxy(s, px, NULL) == 0)
return 0;
}
@@ -616,6 +615,57 @@
}
+/* This I/O handler runs as an applet embedded in a stream interface. It is
+ * used to send HTTP stats over a TCP socket. The mechanism is very simple.
+ * si->st0 becomes non-zero once the transfer is finished. The handler
+ * automatically unregisters itself once transfer is complete.
+ */
+void http_stats_io_handler(struct stream_interface *si)
+{
+ struct session *s = si->private;
+ struct buffer *req = si->ob;
+ struct buffer *res = si->ib;
+
+ if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
+ goto out;
+
+ /* check that the output is not closed */
+ if (res->flags & (BF_SHUTW|BF_SHUTW_NOW))
+ si->st0 = 1;
+
+ if (!si->st0) {
+ if (stats_dump_http(s, res, s->be->uri_auth)) {
+ si->st0 = 1;
+ si->shutw(si);
+ } else {
+ /* buffer full */
+ si->flags |= SI_FL_WAIT_ROOM;
+ }
+ }
+
+ if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST))
+ si->shutw(si);
+
+ if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && si->st0) {
+ si->shutr(si);
+ res->flags |= BF_READ_NULL;
+ }
+
+ /* update all other flags and resync with the other side */
+ si->update(si);
+
+ /* we don't want to expire timeouts while we're processing requests */
+ si->ib->rex = TICK_ETERNITY;
+ si->ob->wex = TICK_ETERNITY;
+
+ out:
+ if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
+ /* check that we have released everything then unregister */
+ stream_int_unregister_handler(si);
+ }
+}
+
+
/*
* Produces statistics data for the session <s>. Expects to be called with
* client socket shut down on input. It stops by itself by unsetting the
@@ -650,11 +700,9 @@
chunk_printf(&msg, "\r\n");
s->txn.status = 200;
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
- 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))
@@ -663,7 +711,6 @@
if (s->txn.meth == HTTP_METH_HEAD) {
/* that's all we return in case of HEAD request */
s->data_state = DATA_ST_FIN;
- buffer_stop_hijack(rep);
return 1;
}
@@ -754,7 +801,7 @@
} else {
print_csv_header(&msg);
}
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
s->data_state = DATA_ST_INFO;
@@ -866,7 +913,7 @@
""
);
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
@@ -895,7 +942,7 @@
case DATA_ST_END:
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, "</body></html>\n");
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
@@ -903,12 +950,11 @@
/* fall through */
case DATA_ST_FIN:
- buffer_stop_hijack(rep);
return 1;
default:
/* unknown state ! */
- buffer_stop_hijack(rep);
+ s->data_state = DATA_ST_FIN;
return -1;
}
}
@@ -994,7 +1040,7 @@
px->id,
px->desc ? "desc" : "empty", px->desc ? px->desc : "");
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
@@ -1075,7 +1121,7 @@
px->fe_sps_lim, px->fe_sps_max);
}
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
@@ -1339,7 +1385,7 @@
/* finish with EOL */
chunk_printf(&msg, "\n");
}
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
} /* for sv */
@@ -1452,7 +1498,7 @@
read_freq_ctr(&px->be_sess_per_sec),
px->be_sps_max);
}
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
@@ -1463,7 +1509,7 @@
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, "</table><p>\n");
- if (buffer_write_chunk(rep, &msg) >= 0)
+ if (buffer_feed_chunk(rep, &msg) >= 0)
return 0;
}
diff --git a/src/proto_http.c b/src/proto_http.c
index 33f0131..025cd64 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3238,40 +3238,6 @@
return 0;
}
-/*
- * Produces data for the session <s> depending on its source. Expects to be
- * called with client socket shut down on input. Right now, only statistics can
- * be produced. It stops by itself by unsetting the BF_HIJACK flag from the
- * buffer, which it uses to keep on being called when there is free space in
- * the buffer, or simply by letting an empty buffer upon return.
- */
-void produce_content(struct session *s, struct buffer *rep)
-{
- if (s->data_source == DATA_SRC_NONE) {
- buffer_stop_hijack(rep);
- return;
- }
- else if (s->data_source == DATA_SRC_STATS) {
- /* dump server statistics */
- int ret;
- ret = stats_dump_http(s, rep, s->be->uri_auth);
- if (ret >= 0)
- return;
- /* -1 indicates an error */
- }
-
- /* unknown data source or internal error */
- s->txn.status = 500;
- stream_int_retnclose(rep->cons, 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;
- buffer_stop_hijack(rep);
- return;
-}
-
-
/* Iterate the same filter through all request headers.
* Returns 1 if this filter can be stopped upon return, otherwise 0.
* Since it can manage the switch to another backend, it updates the per-proxy
@@ -4508,7 +4474,7 @@
*
* It is assumed that the request is either a HEAD or GET and that the
* t->be->uri_auth field is valid. An HTTP/401 response may be sent, or
- * produce_content() can be called to start sending data.
+ * the stats I/O handler will be registered to start sending data.
*
* Returns 1 if the session's state changes, otherwise 0.
*/
@@ -4625,15 +4591,13 @@
/* The request is valid, the user is authenticated. Let's start sending
* data.
*/
- buffer_dont_connect(t->req);
- buffer_shutw_now(t->req);
- buffer_shutr_now(t->rep);
- stream_int_retnclose(t->rep->cons, NULL);
t->logs.tv_request = now;
t->data_source = DATA_SRC_STATS;
t->data_state = DATA_ST_INIT;
t->task->nice = -32; /* small boost for HTTP statistics */
- buffer_install_hijacker(t, t->rep, produce_content);
+ stream_int_register_handler(t->rep->prod, http_stats_io_handler);
+ t->rep->prod->private = t;
+ t->rep->prod->st0 = t->rep->prod->st1 = 0;
return 1;
}