MEDIUM: compression: limit RAM usage

With the global maxzlibmem option, you are able ton control the maximum
amount of RAM usable for HTTP compression.

A test is done before each zlib allocation, if the there isn't available
memory, the test fail and so the zlib initialization, so data won't be
compressed.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index eb24988..c6eed08 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -688,6 +688,12 @@
   that the limit applies both to incoming and outgoing connections, so one
   connection which is deciphered then ciphered accounts for 2 SSL connections.
 
+maxzlibmem <number>
+  Sets the maximum amount of RAM in megabytes per process usable by the zlib.
+  When the maximum amount is reached, future sessions will not compress as long
+  as RAM is unavailable. When sets to 0, there is no limit.
+  The default value is 0.
+
 noepoll
   Disables the use of the "epoll" event polling system on Linux. It is
   equivalent to the command-line argument "-de". The next polling system
diff --git a/include/types/global.h b/include/types/global.h
index 2f41be5..e4e317b 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -85,6 +85,7 @@
 	int maxsock;		/* max # of sockets */
 	int rlimit_nofile;	/* default ulimit-n value : 0=unset */
 	int rlimit_memmax;	/* default ulimit-d in megs value : 0=unset */
+	int maxzlibmem;         /* max RAM for zlib in megs */
 	int mode;
 	unsigned int req_count; /* HTTP request counter */
 	int last_checks;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 4a1ca75..d5ada1d 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -841,6 +841,14 @@
 		}
 		global.maxpipes = atol(args[1]);
 	}
+	else if (!strcmp(args[0], "maxzlibmem")) {
+		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.maxzlibmem = atol(args[1]);
+	}
 	else if (!strcmp(args[0], "ulimit-n")) {
 		if (global.rlimit_nofile != 0) {
 			Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
diff --git a/src/compression.c b/src/compression.c
index 3c88a6c..4c942aa 100644
--- a/src/compression.c
+++ b/src/compression.c
@@ -43,6 +43,9 @@
 static struct pool_head *zlib_pool_head = NULL;
 static struct pool_head *zlib_pool_pending_buf = NULL;
 
+static long long zlib_memory_available = -1;
+
+
 #endif
 
 
@@ -341,6 +344,11 @@
 	static char round = 0; /* order in deflateInit2 */
 	void *buf = NULL;
 
+	if (global.maxzlibmem > 0 && zlib_memory_available < items * size){
+		buf = NULL;
+		goto end;
+	}
+
 	switch (round) {
 		case 0:
 			if (zlib_pool_deflate_state == NULL)
@@ -372,6 +380,10 @@
 			ctx->zlib_pending_buf = buf = pool_alloc2(zlib_pool_pending_buf);
 		break;
 	}
+	if (buf != NULL && global.maxzlibmem > 0)
+		zlib_memory_available -= items * size;
+
+end:
 
 	round = (round + 1) % 5;   /* there are 5 zalloc call in deflateInit2 */
 	return buf;
@@ -380,18 +392,22 @@
 static void free_zlib(void *opaque, void *ptr)
 {
 	struct comp_ctx *ctx = opaque;
+	struct pool_head *pool;
 
 	if (ptr == ctx->zlib_window)
-		pool_free2(zlib_pool_window, ptr);
+		pool = zlib_pool_window;
 	else if (ptr == ctx->zlib_deflate_state)
-		pool_free2(zlib_pool_deflate_state, ptr);
+		pool = zlib_pool_deflate_state;
 	else if (ptr == ctx->zlib_prev)
-		pool_free2(zlib_pool_prev, ptr);
+		pool = zlib_pool_prev;
 	else if (ptr == ctx->zlib_head)
-		pool_free2(zlib_pool_head, ptr);
+		pool = zlib_pool_head;
 	else if (ptr == ctx->zlib_pending_buf)
-		pool_free2(zlib_pool_pending_buf, ptr);
+		pool = zlib_pool_pending_buf;
 
+	pool_free2(pool, ptr);
+	if (global.maxzlibmem > 0)
+		zlib_memory_available += pool->size;
 }
 
 
@@ -402,6 +418,9 @@
 {
 	z_stream *strm = &comp_ctx->strm;
 
+	if (global.maxzlibmem > 0 && zlib_memory_available < 0)
+		zlib_memory_available = global.maxzlibmem * 1024 * 1024;  /*  Megabytes to bytes */
+
 	strm->zalloc = alloc_zlib;
 	strm->zfree = free_zlib;
 	strm->opaque = comp_ctx;
@@ -487,10 +506,10 @@
 {
 	z_stream *strm = &comp_ctx->strm;
 
-	if (deflateEnd(strm) == Z_OK)
-		return 0;
+	if (deflateEnd(strm) != Z_OK)
+		return -1;
 
-	return -1;
+	return 0;
 }
 
 #endif /* USE_ZLIB */
diff --git a/src/haproxy.c b/src/haproxy.c
index d85a46a..d0e740b 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -112,6 +112,7 @@
 struct global global = {
 	.req_count = 0,
 	.logsrvs = LIST_HEAD_INIT(global.logsrvs),
+	.maxzlibmem = 0,
 	.unix_bind = {
 		 .ux = {
 			 .uid = -1,