[MEDIUM] make it possible to change the buffer size in the configuration

The new tune.bufsize and tune.maxrewrite global directives allow one to
change the buffer size and the maxrewrite size. Right now, setting bufsize
too low will block stats sockets which will not be able to write at all.
An error checking must be added to buffer_write_chunk() so that if it
cannot write its message to an empty buffer, it causes the caller to abort.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 50f82e5..84fc9c9 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -381,8 +381,10 @@
    - nosepoll
    - nosplice
    - spread-checks
+   - tune.bufsize
    - tune.maxaccept
    - tune.maxpollevents
+   - tune.maxrewrite
   
  * Debugging
    - debug
@@ -557,6 +559,16 @@
   some randomness in the check interval between 0 and +/- 50%. A value between
   2 and 5 seems to show good results. The default value remains at 0.
 
+tune.bufsize <number>
+  Sets the buffer size to this size (in bytes). Lower values allow more
+  sessions to coexist in the same amount of RAM, and higher values allow some
+  applications with very large cookies to work. The default value is 16384 and
+  can be changed at build time. It is strongly recommended not to change this
+  from the default value, as very low values will break some services such as
+  statistics, and values larger than default size will increase memory usage,
+  possibly causing the system to run out of memory. At least the global maxconn
+  parameter should be decreased by the same factor as this one is increased.
+
 tune.maxaccept <number>
   Sets the maximum number of consecutive accepts that a process may perform on
   a single wake up. High values give higher priority to high connection rates,
@@ -574,6 +586,18 @@
   latency at the expense of network bandwidth, and increasing it above 200
   tends to trade latency for slightly increased bandwidth.
 
+tune.maxrewrite <number>
+  Sets the reserved buffer space to this size in bytes. The reserved space is
+  used for header rewriting or appending. The first reads on sockets will never
+  fill more than bufsize-maxrewrite. Historically it has defaulted to half of
+  bufsize, though that does not make much sense since there are rarely large
+  numbers of headers to add. Setting it too high prevents processing of large
+  requests or responses. Setting it too low prevents addition of new headers
+  to already large requests or to POST requests. It is generally wise to set it
+  to about 1024. It is automatically readjusted to half of bufsize if it is
+  larger than that. This means you don't have to worry about it when changing
+  bufsize.
+
 
 3.3. Debugging
 --------------
diff --git a/include/types/global.h b/include/types/global.h
index aefee32..4d349c3 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -79,6 +79,8 @@
 		int maxaccept;     /* max number of consecutive accept() */
 		int options;       /* various tuning options */
 		int recv_enough;   /* how many input bytes at once are "enough" */
+		int bufsize;       /* buffer size in bytes, defaults to BUFSIZE */
+		int maxrewrite;    /* buffer max rewrite size in bytes, defaults to MAXREWRITE */
 	} tune;
 	struct listener stats_sock; /* unix socket listener for statistics */
 	struct proxy *stats_fe;     /* the frontend holding the stats settings */
diff --git a/src/buffers.c b/src/buffers.c
index 55b7963..a20f3f4 100644
--- a/src/buffers.c
+++ b/src/buffers.c
@@ -17,6 +17,7 @@
 #include <common/config.h>
 #include <common/memory.h>
 #include <proto/buffers.h>
+#include <types/global.h>
 
 struct pool_head *pool2_buffer;
 
@@ -24,7 +25,7 @@
 /* perform minimal intializations, report 0 in case of error, 1 if OK. */
 int init_buffer()
 {
-	pool2_buffer = create_pool("buffer", sizeof(struct buffer) + BUFSIZE, MEM_F_SHARED);
+	pool2_buffer = create_pool("buffer", sizeof(struct buffer) + global.tune.bufsize, MEM_F_SHARED);
 	return pool2_buffer != NULL;
 }
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index a6b9f07..3828812 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -445,6 +445,26 @@
 		}
 		global.tune.maxaccept = atol(args[1]);
 	}
+	else if (!strcmp(args[0], "tune.bufsize")) {
+		if (*(args[1]) == 0) {
+			Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		global.tune.bufsize = atol(args[1]);
+		if (global.tune.maxrewrite >= global.tune.bufsize / 2)
+			global.tune.maxrewrite = global.tune.bufsize / 2;
+	}
+	else if (!strcmp(args[0], "tune.maxrewrite")) {
+		if (*(args[1]) == 0) {
+			Alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		global.tune.maxrewrite = atol(args[1]);
+		if (global.tune.maxrewrite >= global.tune.bufsize / 2)
+			global.tune.maxrewrite = global.tune.bufsize / 2;
+	}
 	else if (!strcmp(args[0], "uid")) {
 		if (global.uid != 0) {
 			Alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
@@ -3482,13 +3502,13 @@
 			goto out;
 		}
 
-		if (stat.st_size <= BUFSIZE) {
+		if (stat.st_size <= global.tune.bufsize) {
 			errlen = stat.st_size;
 		} else {
 			Warning("parsing [%s:%d] : custom error message file <%s> larger than %d bytes. Truncating.\n",
-				file, linenum, args[2], BUFSIZE);
+				file, linenum, args[2], global.tune.bufsize);
 			err_code |= ERR_WARN;
-			errlen = BUFSIZE;
+			errlen = global.tune.bufsize;
 		}
 
 		err = malloc(errlen); /* malloc() must succeed during parsing */
diff --git a/src/client.c b/src/client.c
index 92baefc..76ea122 100644
--- a/src/client.c
+++ b/src/client.c
@@ -384,7 +384,7 @@
 		if ((s->req = pool_alloc2(pool2_buffer)) == NULL)
 			goto out_fail_req; /* no memory */
 
-		s->req->size = BUFSIZE;
+		s->req->size = global.tune.bufsize;
 		buffer_init(s->req);
 		s->req->prod = &s->si[0];
 		s->req->cons = &s->si[1];
@@ -393,7 +393,7 @@
 		s->req->flags |= BF_READ_ATTACHED; /* the producer is already connected */
 
 		if (p->mode == PR_MODE_HTTP) { /* reserve some space for header rewriting */
-			s->req->max_len -= MAXREWRITE;
+			s->req->max_len -= global.tune.maxrewrite;
 			s->req->flags |= BF_READ_DONTWAIT; /* one read is usually enough */
 		}
 
@@ -411,7 +411,7 @@
 		if ((s->rep = pool_alloc2(pool2_buffer)) == NULL)
 			goto out_fail_rep; /* no memory */
 
-		s->rep->size = BUFSIZE;
+		s->rep->size = global.tune.bufsize;
 		buffer_init(s->rep);
 		s->rep->prod = &s->si[1];
 		s->rep->cons = &s->si[0];
diff --git a/src/haproxy.c b/src/haproxy.c
index 09f8288..dd03e32 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -122,7 +122,11 @@
 				 .mode = 0,
 			 }
 		 }
-	}
+	},
+	.tune = {
+		.bufsize = BUFSIZE,
+		.maxrewrite = MAXREWRITE,
+	},
 	/* others NULL OK */
 };
 
@@ -177,7 +181,10 @@
 #ifdef BUILD_OPTIONS
 	       "\n  OPTIONS = " BUILD_OPTIONS
 #endif
-	       "\n\n");
+	       "\n\nDefault settings :"
+	       "\n  maxconn = %d, bufsize = %d, maxrewrite = %d, maxpollevents = %d"
+	       "\n\n",
+	       DEFAULT_MAXCONN, BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS);
 }
 
 /*
@@ -604,6 +611,9 @@
 	if (global.tune.recv_enough == 0)
 		global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH;
 
+	if (global.tune.maxrewrite >= global.tune.bufsize / 2)
+		global.tune.maxrewrite = global.tune.bufsize / 2;
+
 	if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) {
 		/* command line debug mode inhibits configuration mode */
 		global.mode &= ~(MODE_DAEMON | MODE_QUIET);
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 0cd6a35..c811f64 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -482,7 +482,7 @@
 		if ((s->req = pool_alloc2(pool2_buffer)) == NULL)
 			goto out_free_task;
 
-		s->req->size = BUFSIZE;
+		s->req->size = global.tune.bufsize;
 		buffer_init(s->req);
 		s->req->prod = &s->si[0];
 		s->req->cons = &s->si[1];
@@ -499,7 +499,7 @@
 		if ((s->rep = pool_alloc2(pool2_buffer)) == NULL)
 			goto out_free_req;
 
-		s->rep->size = BUFSIZE;
+		s->rep->size = global.tune.bufsize;
 		buffer_init(s->rep);
 
 		s->rep->prod = &s->si[1];
diff --git a/src/session.c b/src/session.c
index 23c4409..f0c2d86 100644
--- a/src/session.c
+++ b/src/session.c
@@ -330,7 +330,7 @@
 		}
 	}
 	else {
-		buffer_set_rlim(rep, req->size - MAXREWRITE); /* rewrite needed */
+		buffer_set_rlim(rep, req->size - global.tune.maxrewrite); /* rewrite needed */
 		s->txn.rsp.msg_state = HTTP_MSG_RPBEFORE;
 		/* reset hdr_idx which was already initialized by the request.
 		 * right now, the http parser does it.