[MEDIUM] reference the current hijack function in the buffer itself

Instead of calling a hard-coded function to produce data, let's
reference this function into the buffer and call it from there
when BF_HIJACK is set. This goes in the direction of more generic
session management code.
diff --git a/include/proto/buffers.h b/include/proto/buffers.h
index 536f5a1..bc472a0 100644
--- a/include/proto/buffers.h
+++ b/include/proto/buffers.h
@@ -126,13 +126,21 @@
 	buf->flags |= BF_SHUTR_NOW | BF_SHUTW_NOW;
 }
 
-/* set the buffer to hijacking mode */
-static inline void buffer_start_hijack(struct buffer *buf)
+/* Installs <func> as a hijacker on the buffer <b> for session <s>. The hijack
+ * flag is set, and the function called once. The function is responsible for
+ * clearing the hijack bit. It is possible that the function clears the flag
+ * during this first call.
+ */
+static inline void buffer_install_hijacker(struct session *s,
+					   struct buffer *b,
+					   void (*func)(struct session *, struct buffer *))
 {
-	buf->flags |= BF_HIJACK;
+	b->hijacker = func;
+	b->flags |= BF_HIJACK;
+	func(s, b);
 }
 
-/* releases the buffer from hijacking mode */
+/* Releases the buffer from hijacking mode. Often used by the hijack function */
 static inline void buffer_stop_hijack(struct buffer *buf)
 {
 	buf->flags &= ~BF_HIJACK;
diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h
index a7c5ab4..bc6bdad 100644
--- a/include/proto/dumpstats.h
+++ b/include/proto/dumpstats.h
@@ -45,7 +45,7 @@
 #define STATS_ST_CLOSE 3
 
 int stats_dump_raw(struct session *s, struct uri_auth *uri);
-int stats_dump_raw_to_buffer(struct session *s, struct buffer *req);
+void stats_dump_raw_to_buffer(struct session *s, struct buffer *req);
 int stats_dump_http(struct session *s, struct uri_auth *uri);
 int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri);
 
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index c723651..41f1922 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -66,7 +66,7 @@
 int http_process_request_body(struct session *s, struct buffer *req);
 int process_response(struct session *t);
 
-int produce_content(struct session *s);
+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);
diff --git a/include/types/buffers.h b/include/types/buffers.h
index 665c75d..3c79a4e 100644
--- a/include/types/buffers.h
+++ b/include/types/buffers.h
@@ -80,7 +80,7 @@
  * it is strictly forbidden for the stream interface to send anything from the
  * buffer.
  */
-#define BF_HIJACK         0x040000  /* the producer is temporarily replaced */
+#define BF_HIJACK         0x040000  /* the producer is temporarily replaced by ->hijacker */
 #define BF_ANA_TIMEOUT    0x080000  /* the analyser timeout has expired */
 #define BF_READ_ATTACHED  0x100000  /* the read side is attached for the first time */
 
@@ -116,6 +116,9 @@
 	int len;	/* size of the string from first to last char. <0 = uninit. */
 };
 
+/* needed for a declaration below */
+struct session;
+
 struct buffer {
 	unsigned int flags;             /* BF_* */
 	int rex;                        /* expiration date for a read, in ticks */
@@ -128,6 +131,7 @@
 	char *rlim;                     /* read limit, used for header rewriting */
 	unsigned int analysers;         /* bit field indicating what to do on the buffer */
 	int analyse_exp;                /* expiration date for current analysers (if set) */
+	void (*hijacker)(struct session *, struct buffer *); /* alternative content producer */
 	unsigned char xfer_large;       /* number of consecutive large xfers */
 	unsigned char xfer_small;       /* number of consecutive small xfers */
 	unsigned long long total;       /* total data read */
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 215e149..590edbe 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -290,18 +290,18 @@
 
 /* This function is called to send output to the response buffer. It simply
  * calls stats_dump_raw(), and releases the buffer's hijack bit when the dump
- * is finished. It always returns 0.
+ * is finished.
  */
-int stats_dump_raw_to_buffer(struct session *s, struct buffer *req)
+void stats_dump_raw_to_buffer(struct session *s, struct buffer *req)
 {
 	if (s->ana_state != STATS_ST_REP)
-		return 0;
+		return;
 
 	if (stats_dump_raw(s, NULL) != 0) {
 		buffer_stop_hijack(s->rep);
 		s->ana_state = STATS_ST_CLOSE;
 	}
-	return 0;
+	return;
 }
 
 
@@ -327,9 +327,6 @@
 
 	switch (s->data_state) {
 	case DATA_ST_INIT:
-		/* the function had not been called yet */
-		buffer_start_hijack(rep);
-
 		chunk_printf(&msg, sizeof(trash),
 			     "HTTP/1.0 200 OK\r\n"
 			     "Cache-Control: no-cache\r\n"
diff --git a/src/proto_http.c b/src/proto_http.c
index e24b01b..092c730 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2989,33 +2989,32 @@
  * 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. It returns 1
- * when it wants to stop sending data, otherwise 0.
+ * the buffer, or simply by letting an empty buffer upon return.
  */
-int produce_content(struct session *s)
+void produce_content(struct session *s, struct buffer *rep)
 {
 	if (s->data_source == DATA_SRC_NONE) {
-		buffer_stop_hijack(s->rep);
-		return 1;
+		buffer_stop_hijack(rep);
+		return;
 	}
 	else if (s->data_source == DATA_SRC_STATS) {
 		/* dump server statistics */
 		int ret = stats_dump_http(s, s->be->uri_auth);
 		if (ret >= 0)
-			return ret;
+			return;
 		/* -1 indicates an error */
 	}
 
 	/* unknown data source or internal error */
 	s->txn.status = 500;
-	stream_int_retnclose(s->rep->cons, error_message(s, HTTP_ERR_500));
+	stream_int_retnclose(rep->cons, error_message(s, HTTP_ERR_500));
 	trace_term(s, TT_HTTP_CNT_1);
 	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(s->rep);
-	return 1;
+	buffer_stop_hijack(rep);
+	return;
 }
 
 
@@ -4392,12 +4391,11 @@
 	buffer_write_dis(t->req);
 	buffer_shutw_now(t->req);
 	buffer_shutr_now(t->rep);
-	buffer_start_hijack(t->rep);
 	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 */
-	produce_content(t);
+	buffer_install_hijacker(t, t->rep, produce_content);
 	return 1;
 }
 
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index d756750..06ec185 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -596,15 +596,13 @@
 			s->data_ctx.stats.flags |= STAT_SHOW_STAT;
 			s->data_ctx.stats.flags |= STAT_FMT_CSV;
 			s->ana_state = STATS_ST_REP;
-			buffer_start_hijack(s->rep);
-			stats_dump_raw_to_buffer(s, s->rep);
+			buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer);
 		}
 		else if (strcmp(args[1], "info") == 0) {
 			s->data_ctx.stats.flags |= STAT_SHOW_INFO;
 			s->data_ctx.stats.flags |= STAT_FMT_CSV;
 			s->ana_state = STATS_ST_REP;
-			buffer_start_hijack(s->rep);
-			stats_dump_raw_to_buffer(s, s->rep);
+			buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer);
 		}
 		else { /* neither "stat" nor "info" */
 			return 0;
@@ -837,8 +835,7 @@
 
 		if ((s->rep->flags & (BF_WRITE_PARTIAL|BF_WRITE_ERROR|BF_SHUTW)) &&
 		    !(s->rep->flags & BF_FULL)) {
-			/* it is the only hijacker right now */
-			stats_dump_raw_to_buffer(s, s->rep);
+			s->rep->hijacker(s, s->rep);
 		}
 		s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
 		flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
diff --git a/src/session.c b/src/session.c
index e19bb56..a619073 100644
--- a/src/session.c
+++ b/src/session.c
@@ -805,7 +805,7 @@
 
 		if ((s->rep->flags & (BF_WRITE_PARTIAL|BF_WRITE_ERROR|BF_SHUTW)) &&
 		    !(s->rep->flags & BF_FULL)) {
-			produce_content(s);
+			s->rep->hijacker(s, s->rep);
 		}
 		s->rep->flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;
 		flags &= BF_CLEAR_READ & BF_CLEAR_WRITE & BF_CLEAR_TIMEOUT;