[MINOR] Collect & provide http response codes received from servers

Additional data is provided on both html & csv stats:
 - html: when passing a mouse over Sessions -> Total (servers, backends)
 - cvs: by 6 additional fields (hrsp_1xx, hrsp_2xx, hrsp_3xx, hrsp_4xx, hrsp_5xx, hspr_other)

Patch inspired by:
 http://www.formilux.org/archives/haproxy/0910/2528.html
 http://www.formilux.org/archives/haproxy/0910/2529.html
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 2a34d22..0e6eff8 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -6741,6 +6741,12 @@
 	L7STS  -> layer 7 response error, for example HTTP 5xx
  37. check_code: layer5-7 code, if available
  38. check_duration: time in ms took to finish last health check
+ 39. hrsp_1xx: http responces with 1xx code
+ 40. hrsp_2xx: http responces with 2xx code
+ 41. hrsp_3xx: http responces with 3xx code
+ 42. hrsp_4xx: http responces with 4xx code
+ 43. hrsp_5xx: http responces with 5xx code
+ 44. hrsp_other: http responces with other codes (protocol error)
 
 
 9.2. Unix Socket commands
diff --git a/include/types/counters.h b/include/types/counters.h
index 6dfef22..f551bf0 100644
--- a/include/types/counters.h
+++ b/include/types/counters.h
@@ -38,6 +38,12 @@
 	long long denied_req, denied_resp;	/* blocked requests/responses because of security concerns */
 	long long failed_req;			/* failed requests (eg: invalid or timeout) */
 
+	union {
+		struct {
+			long long rsp[6];		/* http resonse codes */
+		} http;
+	} p;
+
 	long long failed_conns, failed_resp;	/* failed connect() and responses */
 	long long retries, redispatches;	/* retried and redispatched connections */
 };
@@ -69,6 +75,12 @@
 	long long retries, redispatches;	/* retried and redispatched connections */
 	long long failed_secu;			/* blocked responses because of security concerns */
 
+	union {
+		struct {
+			long long rsp[6];		/* http resonse codes */
+		} http;
+	} p;
+
 	long long failed_checks, down_trans;	/* failed checks and up->down transitions */
 };
 
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 86b8892..032921b 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -244,6 +244,7 @@
 			    "pid,iid,sid,throttle,lbtot,tracked,type,"
 			    "rate,rate_lim,rate_max,"
 			    "check_status,check_code,check_duration,"
+			    "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hspr_other,"
 			    "\n");
 }
 
@@ -1317,6 +1318,8 @@
 				     "%u,%u,%u,"
 				     /* check_status, check_code, check_duration */
 				     ",,,"
+				     /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				     ",,,,,,"
 				     "\n",
 				     px->id,
 				     px->feconn, px->counters.feconn_max, px->maxconn, px->counters.cum_feconn,
@@ -1411,6 +1414,8 @@
 				     ",,,"
 				     /* check_status, check_code, check_duration */
 				     ",,,"
+				     /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				     ",,,,,,"
 				     "\n",
 				     px->id, l->name,
 				     l->nbconn, l->counters->conn_max,
@@ -1489,18 +1494,34 @@
 				     "<td>%s</td><td>%s</td><td>%s</td>"
 				     /* sessions rate : current, max, limit */
 				     "<td>%s</td><td>%s</td><td></td>"
-				     /* sessions : current, max, limit, total, lbtot */
+				     /* sessions: current, max, limit */
 				     "<td>%s</td><td>%s</td><td>%s</td>"
-				     "<td>%s</td><td>%s</td>"
+				     "<td"
 				     "",
 				     (sv->state & SRV_BACKUP) ? "backup" : "active",
 				     sv_state, sv->id,
 				     U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"),
 				     U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max),
-				     U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"),
-				     U2H8(sv->counters.cum_sess), U2H9(sv->counters.cum_lbconn));
+				     U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"));
+
+				/* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				if (px->mode == PR_MODE_HTTP) {
+					int i;
+
+					chunk_printf(&msg, " title=\"rsp codes:");
+
+					for (i = 1; i < 6; i++)
+						chunk_printf(&msg, " %dxx=%lld,", i, sv->counters.p.http.rsp[i]);
+
+					chunk_printf(&msg, " other=%lld\"", sv->counters.p.http.rsp[0]);
+				}
 
 				chunk_printf(&msg,
+				     /* sessions: total, lbtot */
+				     ">%s</td><td>%s</td>",
+				     U2H0(sv->counters.cum_sess), U2H1(sv->counters.cum_lbconn));
+
+				chunk_printf(&msg,
 				     /* bytes : in, out */
 				     "<td>%s</td><td>%s</td>"
 				     /* denied: req, resp */
@@ -1696,6 +1717,18 @@
 					chunk_printf(&msg, ",,,");
 				}
 
+				/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				if (px->mode == PR_MODE_HTTP) {
+					int i;
+
+					for (i=1; i<6; i++)
+						chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[i]);
+
+					chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[0]);
+				} else {
+					chunk_printf(&msg, ",,,,,,");
+				}
+
 				/* finish with EOL */
 				chunk_printf(&msg, "\n");
 			}
@@ -1723,13 +1756,30 @@
 				     U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->counters.be_sps_max));
 
 				chunk_printf(&msg,
-				     /* sessions : current, max, limit, total, lbtot */
+				     /* sessions: current, max, limit */
 				     "<td>%s</td><td>%s</td><td>%s</td>"
-				     "<td>%s</td><td>%s</td>"
-				     /* bytes : in, out */
+				     "<td"
+				     "",
+				     U2H2(px->beconn), U2H3(px->counters.beconn_max), U2H4(px->fullconn));
+
+				/* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				if (px->mode == PR_MODE_HTTP) {
+					int i;
+
+					chunk_printf(&msg, " title=\"rsp codes:");
+
+					for (i = 1; i < 6; i++)
+						chunk_printf(&msg, " %dxx=%lld", i, px->counters.p.http.rsp[i]);
+
+					chunk_printf(&msg, " other=%lld\"", px->counters.p.http.rsp[0]);
+				}
+
+				chunk_printf(&msg,
+				     /* sessions: total, lbtot */
+				     ">%s</td><td>%s</td>"
+				     /* bytes: in, out */
 				     "<td>%s</td><td>%s</td>"
 				     "",
-				     U2H2(px->beconn), U2H3(px->counters.beconn_max), U2H4(px->fullconn),
 				     U2H6(px->counters.cum_beconn), U2H7(px->counters.cum_lbconn),
 				     U2H8(px->counters.bytes_in), U2H9(px->counters.bytes_out));
 
@@ -1793,8 +1843,7 @@
 				     /* rate, rate_lim, rate_max, */
 				     "%u,,%u,"
 				     /* check_status, check_code, check_duration */
-				     ",,,"
-				     "\n",
+				     ",,,",
 				     px->id,
 				     px->nbpend /* or px->totpend ? */, px->counters.nbpend_max,
 				     px->beconn, px->counters.beconn_max, px->fullconn, px->counters.cum_beconn,
@@ -1811,6 +1860,22 @@
 				     px->counters.cum_lbconn, STATS_TYPE_BE,
 				     read_freq_ctr(&px->be_sess_per_sec),
 				     px->counters.be_sps_max);
+
+				/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
+				if (px->mode == PR_MODE_HTTP) {
+					int i;
+
+					for (i=1; i<6; i++)
+						chunk_printf(&msg, "%lld,", px->counters.p.http.rsp[i]);
+
+					chunk_printf(&msg, "%lld,", px->counters.p.http.rsp[0]);
+				} else {
+					chunk_printf(&msg, ",,,,,,");
+				}
+
+				/* finish with EOL */
+				chunk_printf(&msg, "\n");
+
 			}
 			if (buffer_feed_chunk(rep, &msg) >= 0)
 				return 0;
diff --git a/src/proto_http.c b/src/proto_http.c
index 8698594..2300738 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2775,6 +2775,7 @@
 	struct http_txn *txn = &t->txn;
 	struct buffer *req = t->req;
 	struct buffer *rep = t->rep;
+	int n;
 
  next_response:
 	DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
@@ -2966,6 +2967,13 @@
 		t->logs.logwait &= ~LW_RESP;
 		txn->status = strl2ui(rep->data + msg->sl.st.c, msg->sl.st.c_l);
 
+		n = rep->data[msg->sl.st.c] - '0';
+		if (n < 1 || n > 5)
+			n = 0;
+
+		t->srv->counters.p.http.rsp[n]++;
+		t->be->counters.p.http.rsp[n]++;
+
 		switch (txn->status) {
 		case 200:
 		case 203: