[MAJOR] buffers: automatically compute the maximum buffer length

We used to apply a limit to each buffer's size in order to leave
some room to rewrite headers, then we used to remove this limit
once the session switched to a data state.

Proceeding that way becomes a problem with keepalive because we
have to know when to stop reading too much data into the buffer
so that we can leave some room again to process next requests.

The principle we adopt here consists in only relying on to_forward+send_max.
Indeed, both of those data define how many bytes will leave the buffer.
So as long as their sum is larger than maxrewrite, we can safely
fill the buffers. If they are smaller, then we refrain from filling
the buffer. This means that we won't risk to fill buffers when
reading last data chunk followed by a POST request and its contents.

The only impact identified so far is that we must ensure that the
BF_FULL flag is correctly dropped when starting to forward. Right
now this is OK because nobody inflates to_forward without using
buffer_forward().
diff --git a/include/proto/buffers.h b/include/proto/buffers.h
index 6f10553..c19a284 100644
--- a/include/proto/buffers.h
+++ b/include/proto/buffers.h
@@ -1,23 +1,23 @@
 /*
-  include/proto/buffers.h
-  Buffer management definitions, macros and inline functions.
-
-  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
-*/
+ * include/proto/buffers.h
+ * Buffer management definitions, macros and inline functions.
+ *
+ * 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_BUFFERS_H
 #define _PROTO_BUFFERS_H
@@ -32,18 +32,14 @@
 #include <common/time.h>
 
 #include <types/buffers.h>
+#include <types/global.h>
 
 extern struct pool_head *pool2_buffer;
 
 /* perform minimal intializations, report 0 in case of error, 1 if OK. */
 int init_buffer();
 
-/* Initializes all fields in the buffer. The ->max_len field is initialized last
- * so that the compiler can optimize it away if changed immediately after the
- * call to this function. By default, it is set to the full size of the buffer.
- * This implies that buffer_init() must only be called once ->size is set !
- * The BF_OUT_EMPTY flags is set.
- */
+/* Initialize all fields in the buffer. The BF_OUT_EMPTY flags is set. */
 static inline void buffer_init(struct buffer *buf)
 {
 	buf->send_max = 0;
@@ -54,9 +50,21 @@
 	buf->cons = NULL;
 	buf->flags = BF_OUT_EMPTY;
 	buf->r = buf->lr = buf->w = buf->data;
-	buf->max_len = buf->size;
 }
 
+/* Return the max number of bytes the buffer can contain so that once all the
+ * pending bytes are forwarded, the buffer still has global.tune.maxrewrite
+ * bytes free. The result sits between buf->size - maxrewrite and buf->size.
+ */
+static inline int buffer_max_len(struct buffer *buf)
+{
+	if (buf->to_forward == BUF_INFINITE_FORWARD ||
+	    buf->to_forward + buf->send_max >= global.tune.maxrewrite)
+		return buf->size;
+	else
+		return buf->size - global.tune.maxrewrite + buf->to_forward + buf->send_max;
+}
+
 /* Check buffer timeouts, and set the corresponding flags. The
  * likely/unlikely have been optimized for fastest normal path.
  * The read/write timeouts are not set if there was activity on the buffer.
@@ -102,12 +110,16 @@
 	if (buf->send_max)
 		buf->flags &= ~BF_OUT_EMPTY;
 
-	if (buf->to_forward == BUF_INFINITE_FORWARD)
-		return;
+	if (buf->to_forward != BUF_INFINITE_FORWARD) {
+		buf->to_forward += bytes - data_left;
+		if (bytes == BUF_INFINITE_FORWARD)
+			buf->to_forward = bytes;
+	}
 
-	buf->to_forward += bytes - data_left;
-	if (bytes == BUF_INFINITE_FORWARD)
-		buf->to_forward = bytes;
+	if (buf->l < buffer_max_len(buf))
+		buf->flags &= ~BF_FULL;
+	else
+		buf->flags |= BF_FULL;
 }
 
 /* Schedule all remaining buffer data to be sent. send_max is not touched if it
@@ -135,8 +147,6 @@
 	buf->flags &= ~(BF_FULL | BF_OUT_EMPTY);
 	if (!buf->pipe)
 		buf->flags |= BF_OUT_EMPTY;
-	if (!buf->max_len)
-		buf->flags |= BF_FULL;
 }
 
 /* Cut the "tail" of the buffer, which means strip it to the length of unsent
@@ -159,7 +169,7 @@
 		buf->r -= buf->size;
 	buf->lr = buf->r;
 	buf->flags &= ~BF_FULL;
-	if (buf->l >= buf->max_len)
+	if (buf->l >= buffer_max_len(buf))
 		buf->flags |= BF_FULL;
 }
 
@@ -238,18 +248,6 @@
 		return buf->w - buf->r;
 }
 
-/* sets the buffer read limit to <size> bytes, and adjusts the FULL
- * flag accordingly.
- */
-static inline void buffer_set_rlim(struct buffer *buf, int size)
-{
-	buf->max_len = size;
-	if (buf->l < size)
-		buf->flags &= ~BF_FULL;
-	else
-		buf->flags |= BF_FULL;
-}
-
 /*
  * Tries to realign the given buffer, and returns how many bytes can be written
  * there at once without overwriting anything.
@@ -275,15 +273,15 @@
 
 	if (buf->l == 0) {
 		buf->r = buf->w = buf->lr = buf->data;
-		ret = buf->max_len;
+		ret = buffer_max_len(buf);
 	}
 	else if (buf->r > buf->w) {
-		ret = buf->data + buf->max_len - buf->r;
+		ret = buf->data + buffer_max_len(buf) - buf->r;
 	}
 	else {
 		ret = buf->w - buf->r;
-		if (ret > buf->max_len)
-			ret = buf->max_len;
+		if (ret > buffer_max_len(buf))
+			ret = buffer_max_len(buf);
 	}
 	return ret;
 }
@@ -337,7 +335,7 @@
 	if (!buf->l)
 		buf->r = buf->w = buf->lr = buf->data;
 
-	if (buf->l < buf->max_len)
+	if (buf->l < buffer_max_len(buf))
 		buf->flags &= ~BF_FULL;
 
 	buf->send_max -= len;
@@ -378,7 +376,7 @@
 	*buf->r = c;
 
 	buf->l++;
-	if (buf->l >= buf->max_len)
+	if (buf->l >= buffer_max_len(buf))
 		buf->flags |= BF_FULL;
 
 	buf->r++;
diff --git a/include/types/buffers.h b/include/types/buffers.h
index e8ac641..322f21e 100644
--- a/include/types/buffers.h
+++ b/include/types/buffers.h
@@ -1,23 +1,23 @@
 /*
-  include/types/buffers.h
-  Buffer management definitions, macros and inline functions.
-
-  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
-*/
+ * include/types/buffers.h
+ * Buffer management definitions, macros and inline functions.
+ *
+ * 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 _TYPES_BUFFERS_H
 #define _TYPES_BUFFERS_H
@@ -57,7 +57,7 @@
 #define BF_READ_ERROR     0x000008  /* unrecoverable error on producer side */
 #define BF_READ_ACTIVITY  (BF_READ_NULL|BF_READ_PARTIAL|BF_READ_ERROR)
 
-#define BF_FULL           0x000010  /* buffer cannot accept any more data (l >= max_len) */
+#define BF_FULL           0x000010  /* buffer cannot accept any more data (l >= max len) */
 #define BF_SHUTR          0x000020  /* producer has already shut down */
 #define BF_SHUTR_NOW      0x000040  /* the producer must shut down for reads ASAP */
 #define BF_READ_NOEXP     0x000080  /* producer should not expire */
@@ -173,7 +173,6 @@
 	unsigned int l;                 /* data length */
 	char *r, *w, *lr;               /* read ptr, write ptr, last read */
 	unsigned int size;              /* buffer size in bytes */
-	unsigned int max_len;           /* read limit, used to keep room for header rewriting */
 	unsigned int send_max;          /* number of bytes the sender can consume om this buffer, <= l */
 	unsigned long to_forward;       /* number of bytes to forward after send_max without a wake-up */
 	unsigned int analysers;         /* bit field indicating what to do on the buffer */
@@ -238,6 +237,24 @@
    whole buffer, and reduce ->to_forward to 8000. After that, the producer may
    try to feed the additional data through the invisible buffer using a
    platform-specific method such as splice().
+
+   The ->to_forward entry is also used to detect whether we can fill the buffer
+   or not. The idea is that we need to save some space for data manipulation
+   (mainly header rewriting in HTTP) so we don't want to have a full buffer on
+   input before processing a request or response. Thus, we ensure that there is
+   always global.maxrewrite bytes of free space. Since we don't want to forward
+   chunks without filling the buffer, we rely on ->to_forward. When ->to_forward
+   is null, we may have some processing to do so we don't want to fill the
+   buffer. When ->to_forward is non-null, we know we don't care for at least as
+   many bytes. In the end, we know that each of the ->to_forward bytes will
+   eventually leave the buffer. So as long as ->to_forward is larger than
+   global.maxrewrite, we can fill the buffer. If ->to_forward is smaller than
+   global.maxrewrite, then we don't want to fill the buffer with more than
+   ->size - global.maxrewrite + ->to_forward.
+
+   Note that this also means that anyone touching ->to_forward must also take
+   care of updating the BF_FULL flag. For this reason, it's really advised to
+   use buffer_forward() only.
  */
 
 #endif /* _TYPES_BUFFERS_H */
diff --git a/src/buffers.c b/src/buffers.c
index 68ede26..939b03d 100644
--- a/src/buffers.c
+++ b/src/buffers.c
@@ -66,7 +66,7 @@
 		buf->r = buf->data;
 
 	buf->flags &= ~(BF_OUT_EMPTY|BF_FULL);
-	if (buf->l >= buf->max_len)
+	if (buf->l >= buffer_max_len(buf))
 		buf->flags |= BF_FULL;
 
 	return -1;
@@ -86,7 +86,7 @@
 	if (len == 0)
 		return -1;
 
-	if (len > buf->max_len) {
+	if (len > buffer_max_len(buf)) {
 		/* we can't write this chunk and will never be able to, because
 		 * it is larger than the buffer's current max size.
 		 */
@@ -117,7 +117,7 @@
 		buf->r = buf->data;
 
 	buf->flags &= ~BF_FULL;
-	if (buf->l >= buf->max_len)
+	if (buf->l >= buffer_max_len(buf))
 		buf->flags |= BF_FULL;
 
 	/* notify that some data was read from the SI into the buffer */
@@ -211,7 +211,7 @@
 	b->flags &= ~BF_FULL;
 	if (b->l == 0)
 		b->r = b->w = b->lr = b->data;
-	if (b->l >= b->max_len)
+	if (b->l >= buffer_max_len(b))
 		b->flags |= BF_FULL;
 
 	return delta;
@@ -252,7 +252,7 @@
 	b->flags &= ~BF_FULL;
 	if (b->l == 0)
 		b->r = b->w = b->lr = b->data;
-	if (b->l >= b->max_len)
+	if (b->l >= buffer_max_len(b))
 		b->flags |= BF_FULL;
 
 	return delta;
@@ -294,7 +294,7 @@
 	b->l += delta;
 
 	b->flags &= ~BF_FULL;
-	if (b->l >= b->max_len)
+	if (b->l >= buffer_max_len(b))
 		b->flags |= BF_FULL;
 
 	return delta;
diff --git a/src/client.c b/src/client.c
index 79bb9d9..492cae7 100644
--- a/src/client.c
+++ b/src/client.c
@@ -400,10 +400,8 @@
 
 		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 -= global.tune.maxrewrite;
+		if (p->mode == PR_MODE_HTTP)
 			s->req->flags |= BF_READ_DONTWAIT; /* one read is usually enough */
-		}
 
 		/* activate default analysers enabled for this listener */
 		s->req->analysers = l->analysers;
diff --git a/src/proto_http.c b/src/proto_http.c
index 02be35c..f61bfe6 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -2694,7 +2694,6 @@
 	req->analyse_exp = TICK_ETERNITY;
 	req->analysers &= ~an_bit;
 
-	buffer_set_rlim(req, req->size); /* no more rewrite needed */
 	s->logs.tv_request = now;
 	/* OK let's go on with the BODY now */
 	return 1;
@@ -3569,7 +3568,6 @@
 		 * could. Let's switch to the DATA state.                    *
 		 ************************************************************/
 
-		buffer_set_rlim(rep, rep->size); /* no more rewrite needed */
 		t->logs.t_data = tv_ms_elapsed(&t->logs.tv_accept, &now);
 
 		/* if the user wants to log as soon as possible, without counting
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f13c321..b2efa0b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1021,8 +1021,8 @@
 	 * all the part of the request which fits in a buffer is already
 	 * there.
 	 */
-	if (msg_len > l4->req->max_len + l4->req->data - l4->req->w)
-		msg_len = l4->req->max_len + l4->req->data - l4->req->w;
+	if (msg_len > buffer_max_len(l4->req) + l4->req->data - l4->req->w)
+		msg_len = buffer_max_len(l4->req) + l4->req->data - l4->req->w;
 
 	if (bleft < msg_len)
 		goto too_short;
diff --git a/src/session.c b/src/session.c
index 81e4a32..b86526a 100644
--- a/src/session.c
+++ b/src/session.c
@@ -334,8 +334,6 @@
 		health_adjust(s->srv, HANA_STATUS_L4_OK);
 
 	if (s->be->mode == PR_MODE_TCP) { /* let's allow immediate data connection in this case */
-		buffer_set_rlim(rep, rep->size); /* no rewrite needed */
-
 		/* if the user wants to log as soon as possible, without counting
 		 * bytes from the server, then this is the right moment. */
 		if (s->fe->to_log && !(s->logs.logwait & LW_BYTES)) {
@@ -344,7 +342,6 @@
 		}
 	}
 	else {
-		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.
diff --git a/src/stream_sock.c b/src/stream_sock.c
index c1f0dfa..e75bdaf 100644
--- a/src/stream_sock.c
+++ b/src/stream_sock.c
@@ -302,15 +302,15 @@
 		if (b->l == 0) {
 			/* let's realign the buffer to optimize I/O */
 			b->r = b->w = b->lr = b->data;
-			max = b->max_len;
+			max = buffer_max_len(b);
 		}
 		else if (b->r > b->w) {
-			max = b->data + b->max_len - b->r;
+			max = b->data + buffer_max_len(b) - b->r;
 		}
 		else {
 			max = b->w - b->r;
-			if (max > b->max_len)
-				max = b->max_len;
+			if (max > buffer_max_len(b))
+				max = buffer_max_len(b);
 		}
 
 		if (max == 0) {
@@ -363,7 +363,7 @@
 
 			b->total += ret;
 
-			if (b->l >= b->max_len) {
+			if (b->l >= buffer_max_len(b)) {
 				/* The buffer is now full, there's no point in going through
 				 * the loop again.
 				 */
@@ -642,7 +642,7 @@
 				b->w = b->data; /* wrap around the buffer */
 
 			b->l -= ret;
-			if (likely(b->l < b->max_len))
+			if (likely(b->l < buffer_max_len(b)))
 				b->flags &= ~BF_FULL;
 
 			if (likely(!b->l))