[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;
 }