MEDIUM: vars: adds support of variables

This patch adds support of variables during the processing of each stream. The
variables scope can be set as 'session', 'transaction', 'request' or 'response'.
The variable type is the type returned by the assignment expression. The type
can change while the processing.

The allocated memory can be controlled for each scope and each request, and for
the global process.
diff --git a/Makefile b/Makefile
index f36ed5b..3438ab4 100644
--- a/Makefile
+++ b/Makefile
@@ -733,7 +733,7 @@
        src/session.o src/stream.o src/hdr_idx.o src/ev_select.o src/signal.o \
        src/acl.o src/sample.o src/memory.o src/freq_ctr.o src/auth.o src/proto_udp.o \
        src/compression.o src/payload.o src/hash.o src/pattern.o src/map.o \
-       src/namespace.o src/mailers.o src/dns.o
+       src/namespace.o src/mailers.o src/dns.o src/vars.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 163e873..a2133cd 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -585,6 +585,10 @@
    - tune.ssl.maxrecord
    - tune.ssl.default-dh-param
    - tune.ssl.ssl-ctx-cache-size
+   - tune.vars.global-max-size
+   - tune.vars.reqres-max-size
+   - tune.vars.sess-max-size
+   - tune.vars.txn-max-size
    - tune.zlib.memlevel
    - tune.zlib.windowsize
 
@@ -1288,6 +1292,21 @@
   dynamically is expensive, they are cached. The default cache size is set to
   1000 entries.
 
+tune.vars.global-max-size <size>
+tune.vars.reqres-max-size <size>
+tune.vars.sess-max-size <size>
+tune.vars.txn-max-size <size>
+  These four tunes helps to manage the allowed amount of memory used by the
+  variables system. "global" limits the memory for all the systems. "sess" limit
+  the memory by session, "txn" limits the memory by transaction and "reqres"
+  limits the memory for each request or response processing. during the
+  accounting, "sess" embbed "txn" and "txn" embed "reqres".
+
+  By example, we considers that "tune.vars.sess-max-size" is fixed to 100,
+  "tune.vars.txn-max-size" is fixed to 100, "tune.vars.reqres-max-size" is
+  also fixed to 100. If we create a variable "txn.var" that contains 100 bytes,
+  we cannot create any more variable in the other contexts.
+
 tune.zlib.memlevel <number>
   Sets the memLevel parameter in zlib initialization for each session. It
   defines how much memory should be allocated for the internal compression
@@ -3350,6 +3369,7 @@
               del-acl(<file name>) <key fmt> |
               del-map(<file name>) <key fmt> |
               set-map(<file name>) <key fmt> <value fmt> |
+              set-var(<var name>) <expr> |
               { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] |
               lua <function name>
              }
@@ -3618,6 +3638,30 @@
       parameter is the name of the function to run. The prototype of the
       function is documented in the API documentation.
 
+    - set-var(<var-name>) <expr> :
+      Is used to set the contents of a variable. The variable is declared
+      inline.
+
+        <var-name> The name of the variable starts by an indication about its
+                   scope. The allowed scopes are:
+                     "sess" : the variable is shared with all the session,
+                     "txn"  : the variable is shared with all the transaction
+                              (request and response)
+                     "req"  : the variable is shared only during the request
+                              processing
+                     "res"  : the variable is shared only during the response
+                              processing.
+                   This prefix is followed by a name. The separator is a '.'.
+                   The name may only contain characters 'a-z', 'A-Z', '0-9',
+                   and '_'.
+
+         <expr>    Is a standard HAProxy expression formed by a sample-fetch
+                   followed by some converters.
+
+      Example:
+
+         http-request set-var(req.my_var) req.fhdr(user-agent),lower
+
   There is no limit to the number of http-request statements per instance.
 
   It is important to know that http-request rules are processed very early in
@@ -3683,6 +3727,7 @@
                 del-acl(<file name>) <key fmt> |
                 del-map(<file name>) <key fmt> |
                 set-map(<file name>) <key fmt> <value fmt> |
+                set-var(<var-name>) <expr> |
                 lua <function name>
               }
               [ { if | unless } <condition> ]
@@ -3857,6 +3902,30 @@
       a redirect rule is applied during a response, connections to the server
       are closed so that no data can be forwarded from the server to the client.
 
+    - set-var(<var-name>) expr:
+      Is used to set the contents of a variable. The variable is declared
+      inline.
+
+        <var-name> The name of the variable starts by an indication about its
+                   scope. The allowed scopes are:
+                     "sess" : the variable is shared with all the session,
+                     "txn"  : the variable is shared with all the transaction
+                              (request and response)
+                     "req"  : the variable is shared only during the request
+                              processing
+                     "res"  : the variable is shared only during the response
+                              processing.
+                   This prefix is followed by a name. The separator is a '.'.
+                   The name may only contain characters 'a-z', 'A-Z', '0-9',
+                   and '_'.
+
+         <expr>    Is a standard HAProxy expression formed by a sample-fetch
+                   followed by some converters.
+
+      Example:
+
+         http-response set-var(sess.last_redir) res.hdr(location)
+
   There is no limit to the number of http-response statements per instance.
 
   It is important to know that http-response rules are processed very early in
@@ -8209,6 +8278,7 @@
     - capture : the specified sample expression is captured
     - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>]
     - lua <function>
+    - set-var(<var-name>) <expr>
 
   They have the same meaning as their counter-parts in "tcp-request connection"
   so please refer to that section for a complete description.
@@ -8243,6 +8313,28 @@
   function to run. The prototype of the function is documented in the API
   documentation.
 
+  The "set-var" is used to set the content of a variable. The variable is
+  declared inline.
+
+    <var-name> The name of the variable starts by an indication about its scope.
+               The allowed scopes are:
+                 "sess" : the variable is shared with all the session,
+                 "txn"  : the variable is shared with all the transaction
+                          (request and response)
+                 "req"  : the variable is shared only during the request
+                          processing
+                 "res"  : the variable is shared only during the response
+                          processing.
+               This prefix is followed by a name. The separator is a '.'.
+               The name may only contain characters 'a-z', 'A-Z', '0-9' and '_'.
+
+    <expr>     Is a standard HAProxy expression formed by a sample-fetch
+               followed by some converters.
+
+  Example:
+
+        tcp-request content set-var(sess.my_var) src
+
   Example:
         # Accept HTTP requests containing a Host header saying "example.com"
         # and reject everything else.
@@ -8393,6 +8485,9 @@
     - lua <function>
         Executes Lua.
 
+    - set-var(<var-name>) <expr>
+        Sets a variable.
+
   Note that the "if/unless" condition is optional. If no condition is set on
   the action, it is simply performed unconditionally. That can be useful for
   for changing the default action to a reject.
@@ -8408,6 +8503,28 @@
   function to run. The prototype of the function is documented in the API
   documentation.
 
+  The "set-var" is used to set the content of a variable. The variable is
+  declared inline.
+
+    <var-name> The name of the variable starts by an indication about its scope.
+               The allowed scopes are:
+                 "sess" : the variable is shared with all the session,
+                 "txn"  : the variable is shared with all the transaction
+                          (request and response)
+                 "req"  : the variable is shared only during the request
+                          processing
+                 "res"  : the variable is shared only during the response
+                          processing.
+               This prefix is followed by a name. The separator is a '.'.
+               The name may only contain characters 'a-z', 'A-Z', '0-9' and '_'.
+
+    <expr>     Is a standard HAProxy expression formed by a sample-fetch
+               followed by some converters.
+
+  Example:
+
+        tcp-request content set-var(sess.my_var) src
+
   See section 7 about ACL usage.
 
   See also : "tcp-request content", "tcp-response inspect-delay"
@@ -11208,6 +11325,18 @@
   32-bit hash is trivial to break. See also "crc32", "djb2", "wt6" and the
   "hash-type" directive.
 
+set-var(<var name>)
+  Sets a variable with the input content and return the content on the output as
+  is. The variable keep the value and the associated input type. The name of the
+  variable starts by an indication about it scope. The scope allowed are:
+    "sess" : the variable is shared with all the session,
+    "txn"  : the variable is shared with all the transaction (request and
+             response),
+    "req"  : the variable is shared only during the request processing,
+    "res"  : the variable is shared only during the response processing.
+  This prefix is followed by a name. The separator is a '.'. The name may only
+  contain characters 'a-z', 'A-Z', '0-9' and '_'.
+
 sub(<value>)
   Subtracts <value> from the input value of type unsigned integer, and returns
   the result as an unsigned integer. Note: in order to subtract the input from
@@ -11617,6 +11746,18 @@
   stick-table or in the designated stick-table. See also src_conn_cnt and
   table_avl for other entry counting methods.
 
+var(<var-name>) : undefined
+  Returns a variable with the stored type. If the variable is not set, the
+  sample fetch fails. The name of the variable starts by an indication about its
+  scope. The scope allowed are:
+    "sess" : the variable is shared with all the session,
+    "txn"  : the variable is shared with all the transaction (request and
+             response),
+    "req"  : the variable is shared only during the request processing,
+    "res"  : the variable is shared only during the response processing.
+  This prefix is followed by a name. The separator is a '.'. The name may only
+  contain characters 'a-z', 'A-Z', '0-9' and '_'.
+
 
 7.3.3. Fetching samples at Layer 4
 ----------------------------------
diff --git a/include/proto/vars.h b/include/proto/vars.h
new file mode 100644
index 0000000..dadbf3b
--- /dev/null
+++ b/include/proto/vars.h
@@ -0,0 +1,10 @@
+#ifndef _PROTO_VARS_H
+#define _PROTO_VARS_H
+
+#include <types/vars.h>
+
+void vars_init(struct vars *vars, enum vars_scope scope);
+void vars_prune(struct vars *vars, struct stream *strm);
+int vars_check_arg(struct arg *arg, char **err);
+
+#endif
diff --git a/include/types/arg.h b/include/types/arg.h
index c621025..cccc565 100644
--- a/include/types/arg.h
+++ b/include/types/arg.h
@@ -28,6 +28,8 @@
 #include <common/chunk.h>
 #include <common/mini-clist.h>
 
+#include <types/vars.h>
+
 /* encoding of each arg type : up to 31 types are supported */
 #define ARGT_BITS      5
 #define ARGT_NBTYPES   (1 << ARGT_BITS)
@@ -58,6 +60,7 @@
 	ARGT_USR,      /* a pointer to a user list */
 	ARGT_MAP,      /* a pointer to a map descriptor */
 	ARGT_REG,      /* a pointer to a regex */
+	ARGT_VAR,      /* contains a variable description. */
 };
 
 /* context where arguments are used, in order to help error reporting */
@@ -94,6 +97,7 @@
 	struct userlist *usr;
 	struct map_descriptor *map;
 	struct my_regex *reg;
+	struct var_desc var;
 };
 
 struct arg {
diff --git a/include/types/stream.h b/include/types/stream.h
index 4203af8..953f9d1 100644
--- a/include/types/stream.h
+++ b/include/types/stream.h
@@ -43,6 +43,7 @@
 #include <types/stream_interface.h>
 #include <types/task.h>
 #include <types/stick_table.h>
+#include <types/vars.h>
 
 
 /* Various Stream Flags, bits values 0x01 to 0x100 (shift 0) */
@@ -143,6 +144,9 @@
 
 	char **req_cap;                         /* array of captures from the request (may be NULL) */
 	char **res_cap;                         /* array of captures from the response (may be NULL) */
+	struct vars vars_sess;                  /* list of variables for the session scope. */
+	struct vars vars_txn;                   /* list of variables for the txn scope. */
+	struct vars vars_reqres;                /* list of variables for the request and resp scope. */
 
 	struct stream_interface si[2];          /* client and server stream interfaces */
 	struct strm_logs logs;                  /* logs for this stream */
diff --git a/include/types/vars.h b/include/types/vars.h
new file mode 100644
index 0000000..c387a77
--- /dev/null
+++ b/include/types/vars.h
@@ -0,0 +1,33 @@
+#ifndef _TYPES_VARS_H
+#define _TYPES_VARS_H
+
+#include <common/mini-clist.h>
+
+#include <types/sample.h>
+
+enum vars_scope {
+	SCOPE_SESS = 0,
+	SCOPE_TXN,
+	SCOPE_REQ,
+	SCOPE_RES,
+};
+
+struct vars {
+	struct list head;
+	enum vars_scope scope;
+	unsigned int size;
+};
+
+/* This struct describes a variable. */
+struct var_desc {
+	const char *name; /* Contains the normalized variable name. */
+	enum vars_scope scope;
+};
+
+struct var {
+	struct list l; /* Used for chaining vars. */
+	const char *name; /* Contains the variable name. */
+	struct sample_storage data; /* data storage. */
+};
+
+#endif
diff --git a/src/proto_http.c b/src/proto_http.c
index 5b06233..1c30b3b 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -65,6 +65,7 @@
 #include <proto/stream_interface.h>
 #include <proto/task.h>
 #include <proto/pattern.h>
+#include <proto/vars.h>
 
 const char HTTP_100[] =
 	"HTTP/1.1 100 Continue\r\n\r\n";
@@ -9006,6 +9007,9 @@
 
 	if (txn->hdr_idx.v)
 		hdr_idx_init(&txn->hdr_idx);
+
+	vars_init(&s->vars_txn,    SCOPE_TXN);
+	vars_init(&s->vars_reqres, SCOPE_REQ);
 }
 
 /* to be used at the end of a transaction */
@@ -9047,6 +9051,8 @@
 		memset(s->res_cap, 0, fe->nb_rsp_cap * sizeof(void *));
 	}
 
+	vars_prune(&s->vars_txn, s);
+	vars_prune(&s->vars_reqres, s);
 }
 
 /* to be used at the end of a transaction to prepare a new one */
@@ -9533,7 +9539,7 @@
 			goto out_err;
 		}
 	} else {
-		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', but got '%s'%s.\n",
+		Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'set-var', but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}
@@ -9888,7 +9894,7 @@
 			goto out_err;
 		}
 	} else {
-		Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', but got '%s'%s.\n",
+		Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', 'set-var' but got '%s'%s.\n",
 		      file, linenum, args[0], *args[0] ? "" : " (missing argument)");
 		goto out_err;
 	}
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 036191b..737a32e 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1473,7 +1473,7 @@
 				return -1;
 		} else {
 			memprintf(err,
-			          "'%s %s' expects 'accept', 'close' or 'reject' in %s '%s' (got '%s')",
+			          "'%s %s' expects 'accept', 'close', 'reject' or 'set-var' in %s '%s' (got '%s')",
 			          args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
 			return -1;
 		}
@@ -1678,8 +1678,8 @@
 				return -1;
 		} else {
 			memprintf(err,
-			          "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d' "
-			          " in %s '%s' (got '%s')",
+			          "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', "
+			          " or 'set-var' in %s '%s' (got '%s')",
 			          args[0], args[1], MAX_SESS_STKCTR-1, proxy_type_str(curpx),
 			          curpx->id, args[arg]);
 			return -1;
diff --git a/src/stream.c b/src/stream.c
index 0f9fbe2..0b70e28 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -50,6 +50,7 @@
 #include <proto/stick_table.h>
 #include <proto/stream_interface.h>
 #include <proto/task.h>
+#include <proto/vars.h>
 
 struct pool_head *pool2_stream;
 struct list streams;
@@ -136,6 +137,13 @@
 	s->req_cap = NULL;
 	s->res_cap = NULL;
 
+	/* Initialise alle the variable context even if will not use.
+	 * This permits to prune these context without errors.
+	 */
+	vars_init(&s->vars_sess,   SCOPE_SESS);
+	vars_init(&s->vars_txn,    SCOPE_TXN);
+	vars_init(&s->vars_reqres, SCOPE_REQ);
+
 	/* this part should be common with other protocols */
 	si_reset(&s->si[0]);
 	si_set_state(&s->si[0], SI_ST_EST);
@@ -293,6 +301,11 @@
 		pool_free2(fe->req_cap_pool, s->req_cap);
 	}
 
+	/* Cleanup all variable contexts. */
+	vars_prune(&s->vars_sess, s);
+	vars_prune(&s->vars_txn, s);
+	vars_prune(&s->vars_reqres, s);
+
 	stream_store_counters(s);
 
 	list_for_each_entry_safe(bref, back, &s->back_refs, users) {
@@ -2060,6 +2073,13 @@
 	 * for completion.
 	 */
 	if (si_b->state >= SI_ST_REQ && si_b->state < SI_ST_CON) {
+
+		/* prune the request variables and swap to the response variables. */
+		if (s->vars_reqres.scope != SCOPE_RES) {
+			vars_prune(&s->vars_reqres, s);
+			vars_init(&s->vars_reqres, SCOPE_RES);
+		}
+
 		do {
 			/* nb: step 1 might switch from QUE to ASS, but we first want
 			 * to give a chance to step 2 to perform a redirect if needed.
diff --git a/src/vars.c b/src/vars.c
new file mode 100644
index 0000000..74033a5
--- /dev/null
+++ b/src/vars.c
@@ -0,0 +1,685 @@
+#include <ctype.h>
+
+#include <common/cfgparse.h>
+#include <common/mini-clist.h>
+
+#include <types/vars.h>
+
+#include <proto/arg.h>
+#include <proto/proto_http.h>
+#include <proto/proto_tcp.h>
+#include <proto/sample.h>
+#include <proto/stream.h>
+
+/* This contains a pool of struct vars */
+static struct pool_head *var_pool = NULL;
+
+/* This array contain all the names of all the HAProxy vars.
+ * This permits to identify two variables name with
+ * only one pointer. It permits to not using  strdup() for
+ * each variable name used during the runtime.
+ */
+static char **var_names = NULL;
+static int var_names_nb = 0;
+
+/* This array of int contains the system limits per context. */
+static unsigned int var_global_limit = 0;
+static unsigned int var_global_size = 0;
+static unsigned int var_sess_limit = 0;
+static unsigned int var_txn_limit = 0;
+static unsigned int var_reqres_limit = 0;
+
+/* This function adds or remove memory size from the accounting. */
+static void var_accounting_diff(struct vars *vars, struct stream *strm, int size)
+{
+	switch (vars->scope) {
+	case SCOPE_REQ:
+	case SCOPE_RES:
+		strm->vars_reqres.size += size;
+	case SCOPE_TXN:
+		strm->vars_txn.size += size;
+	case SCOPE_SESS:
+		strm->vars_sess.size += size;
+		var_global_size += size;
+	}
+}
+
+/* This function returns 1 if the <size> is available in the var
+ * pool <vars>, otherwise returns 0. If the space is avalaible,
+ * the size is reserved.
+ */
+static int var_accounting_add(struct vars *vars, struct stream *strm, int size)
+{
+	switch (vars->scope) {
+	case SCOPE_REQ:
+	case SCOPE_RES:
+		if (var_reqres_limit && strm->vars_reqres.size + size > var_reqres_limit)
+			return 0;
+	case SCOPE_TXN:
+		if (var_txn_limit && strm->vars_txn.size + size > var_txn_limit)
+			return 0;
+	case SCOPE_SESS:
+		if (var_sess_limit && strm->vars_sess.size + size > var_sess_limit)
+			return 0;
+		if (var_global_limit && var_global_size + size > var_global_limit)
+			return 0;
+	}
+	var_accounting_diff(vars, strm, size);
+	return 1;
+}
+
+/* This function free all the memory used by all the varaibles
+ * in the list.
+ */
+void vars_prune(struct vars *vars, struct stream *strm)
+{
+	struct var *var, *tmp;
+
+	list_for_each_entry_safe(var, tmp, &vars->head, l) {
+		if (var->data.type == SMP_T_STR ||
+		    var->data.type == SMP_T_BIN) {
+			free(var->data.data.str.str);
+			var_accounting_diff(vars, strm, -var->data.data.str.len);
+		}
+		else if (var->data.type == SMP_T_METH) {
+			free(var->data.data.meth.str.str);
+			var_accounting_diff(vars, strm, -var->data.data.meth.str.len);
+		}
+		LIST_DEL(&var->l);
+		pool_free2(var_pool, var);
+		var_accounting_diff(vars, strm, -(int)sizeof(struct var));
+	}
+}
+
+/* This function init a list of variabes. */
+void vars_init(struct vars *vars, enum vars_scope scope)
+{
+	LIST_INIT(&vars->head);
+	vars->scope = scope;
+	vars->size = 0;
+}
+
+/* This function declares a new variable name. It returns a pointer
+ * on the string identifying the name. This function assures that
+ * the same name exists only once.
+ *
+ * This function check if the variable name is acceptable.
+ *
+ * The function returns NULL if an error occurs, and <err> is filled.
+ * In this case, the HAProxy must be stopped because the structs are
+ * left inconsistent. Otherwise, it returns the pointer on the global
+ * name.
+ */
+static char *register_name(const char *name, int len, enum vars_scope *scope, char **err)
+{
+	int i;
+	const char *tmp;
+
+	/* Check length. */
+	if (len == 0) {
+		memprintf(err, "Empty variable name cannot be accepted");
+		return NULL;
+	}
+
+	/* Check scope. */
+	if (len > 5 && strncmp(name, "sess.", 5) == 0) {
+		name += 5;
+		len -= 5;
+		*scope = SCOPE_SESS;
+	}
+	else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
+		name += 4;
+		len -= 4;
+		*scope = SCOPE_TXN;
+	}
+	else if (len > 4 && strncmp(name, "req.", 4) == 0) {
+		name += 4;
+		len -= 4;
+		*scope = SCOPE_REQ;
+	}
+	else if (len > 4 && strncmp(name, "res.", 4) == 0) {
+		name += 4;
+		len -= 4;
+		*scope = SCOPE_RES;
+	}
+	else {
+		memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. "
+		               "The scope can be 'sess', 'txn', 'req' or 'res'", name);
+		return NULL;
+	}
+
+	/* Look for existing variable name. */
+	for (i = 0; i < var_names_nb; i++)
+		if (strncmp(var_names[i], name, len) == 0)
+			return var_names[i];
+
+	/* Store variable name. */
+	var_names_nb++;
+	var_names = realloc(var_names, var_names_nb * sizeof(*var_names));
+	if (!var_names) {
+		memprintf(err, "out of memory error");
+		return NULL;
+	}
+	var_names[var_names_nb - 1] = malloc(len + 1);
+	if (!var_names[var_names_nb - 1]) {
+		memprintf(err, "out of memory error");
+		return NULL;
+	}
+	memcpy(var_names[var_names_nb - 1], name, len);
+	var_names[var_names_nb - 1][len] = '\0';
+
+	/* Check variable name syntax. */
+	tmp = var_names[var_names_nb - 1];
+	while (*tmp) {
+		if (!isalnum((int)(unsigned char)*tmp) && *tmp != '_') {
+			memprintf(err, "invalid syntax at char '%s'", tmp);
+			return NULL;
+		}
+		tmp++;
+	}
+
+	/* Return the result. */
+	return var_names[var_names_nb - 1];
+}
+
+/* This function returns an existing variable or returns NULL. */
+static inline struct var *var_get(struct vars *vars, const char *name)
+{
+	struct var *var;
+
+	list_for_each_entry(var, &vars->head, l)
+		if (var->name == name)
+			return var;
+	return NULL;
+}
+
+/* Returns 0 if fails, else returns 1. */
+static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
+{
+	const struct var_desc *var_desc = &args[0].data.var;
+	struct var *var;
+	struct vars *vars;
+
+	/* Check the availibity of the variable. */
+	switch (var_desc->scope) {
+	case SCOPE_SESS: vars = &smp->strm->vars_sess;   break;
+	case SCOPE_TXN:  vars = &smp->strm->vars_txn;    break;
+	case SCOPE_REQ:
+	case SCOPE_RES:  vars = &smp->strm->vars_reqres; break;
+	}
+	if (vars->scope != var_desc->scope)
+		return 0;
+	var = var_get(vars, var_desc->name);
+
+	/* check for the variable avalaibility */
+	if (!var)
+		return 0;
+
+	/* Copy sample. */
+	smp->type = var->data.type;
+	smp->flags |= SMP_F_CONST;
+	memcpy(&smp->data, &var->data.data, sizeof(smp->data));
+	return 1;
+}
+
+/* This function search in the <head> a variable with the same
+ * pointer value that the <name>. If the variable doesn't exists,
+ * create it. The function stores a copy of smp> if the variable.
+ * It returns 0 if fails, else returns 1.
+ */
+static int sample_store(struct vars *vars, const char *name, struct stream *strm, struct sample *smp)
+{
+	struct var *var;
+
+	/* Look for existing variable name. */
+	var = var_get(vars, name);
+
+	if (var) {
+		/* free its used memory. */
+		if (var->data.type == SMP_T_STR ||
+		    var->data.type == SMP_T_BIN) {
+			free(var->data.data.str.str);
+			var_accounting_diff(vars, strm, -var->data.data.str.len);
+		}
+		else if (var->data.type == SMP_T_METH) {
+			free(var->data.data.meth.str.str);
+			var_accounting_diff(vars, strm, -var->data.data.meth.str.len);
+		}
+	} else {
+
+		/* Check memory avalaible. */
+		if (!var_accounting_add(vars, strm, sizeof(struct var)))
+			return 0;
+
+		/* Create new entry. */
+		var = pool_alloc2(var_pool);
+		if (!var)
+			return 0;
+		LIST_ADDQ(&vars->head, &var->l);
+		var->name = name;
+	}
+
+	/* Set type. */
+	var->data.type = smp->type;
+
+	/* Copy data. If the data needs memory, the function can fail. */
+	switch (var->data.type) {
+	case SMP_T_BOOL:
+	case SMP_T_UINT:
+	case SMP_T_SINT:
+		var->data.data.sint = smp->data.sint;
+		break;
+	case SMP_T_IPV4:
+		var->data.data.ipv4 = smp->data.ipv4;
+		break;
+	case SMP_T_IPV6:
+		var->data.data.ipv6 = smp->data.ipv6;
+		break;
+	case SMP_T_STR:
+	case SMP_T_BIN:
+		if (!var_accounting_add(vars, strm, smp->data.str.len)) {
+			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
+			return 0;
+		}
+		var->data.data.str.str = malloc(smp->data.str.len);
+		if (!var->data.data.str.str) {
+			var_accounting_diff(vars, strm, -smp->data.str.len);
+			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
+			return 0;
+		}
+		var->data.data.str.len = smp->data.str.len;
+		memcpy(var->data.data.str.str, smp->data.str.str, var->data.data.str.len);
+		break;
+	case SMP_T_METH:
+		if (!var_accounting_add(vars, strm, smp->data.meth.str.len)) {
+			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
+			return 0;
+		}
+		var->data.data.meth.str.str = malloc(smp->data.meth.str.len);
+		if (!var->data.data.meth.str.str) {
+			var_accounting_diff(vars, strm, -smp->data.meth.str.len);
+			var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
+			return 0;
+		}
+		var->data.data.meth.meth = smp->data.meth.meth;
+		var->data.data.meth.str.len = smp->data.meth.str.len;
+		var->data.data.meth.str.size = smp->data.meth.str.len;
+		memcpy(var->data.data.meth.str.str, smp->data.meth.str.str, var->data.data.meth.str.len);
+		break;
+	}
+	return 1;
+}
+
+/* Returns 0 if fails, else returns 1. */
+static inline int sample_store_stream(const char *name, enum vars_scope scope,
+                                      struct stream *strm, struct sample *smp)
+{
+	struct vars *vars;
+
+	switch (scope) {
+	case SCOPE_SESS: vars = &strm->vars_sess;   break;
+	case SCOPE_TXN:  vars = &strm->vars_txn;    break;
+	case SCOPE_REQ:
+	case SCOPE_RES:  vars = &strm->vars_reqres; break;
+	}
+	if (vars->scope != scope)
+		return 0;
+	return sample_store(vars, name, strm, smp);
+}
+
+/* Returns 0 if fails, else returns 1. */
+static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
+{
+	return sample_store_stream(args[0].data.var.name, args[1].data.var.scope, smp->strm, smp);
+}
+
+/* This fucntions check an argument entry and fill it with a variable
+ * type. The argumen must be a string. If the variable lookup fails,
+ * the function retuns 0 and fill <err>, otherwise it returns 1.
+ */
+int vars_check_arg(struct arg *arg, char **err)
+{
+	char *name;
+	enum vars_scope scope;
+
+	/* Check arg type. */
+	if (arg->type != ARGT_STR) {
+		memprintf(err, "unexpected argument type");
+		return 0;
+	}
+
+	/* Register new variable name. */
+	name = register_name(arg->data.str.str, arg->data.str.len, &scope, err);
+	if (!name)
+		return 0;
+
+	/* Use the global variable name pointer. */
+	arg->type = ARGT_VAR;
+	arg->data.var.name = name;
+	arg->data.var.scope = scope;
+	return 1;
+}
+
+/* Returns 0 if miss data, else returns 1. */
+static inline int action_store(struct sample_expr *expr, const char *name,
+                               enum vars_scope scope, struct proxy *px,
+                               struct stream *s, int sens)
+{
+	struct sample smp;
+
+	/* Process the expression. */
+	memset(&smp, 0, sizeof(smp));
+	if (!sample_process(px, s->sess, s, sens|SMP_OPT_FINAL, expr, &smp))
+		return 0;
+
+	/* Store the sample, and ignore errors. */
+	sample_store_stream(name, scope, s, &smp);
+	return 1;
+}
+
+/* Returns 0 if miss data, else returns 1. */
+static int action_tcp_req_store(struct tcp_rule *rule, struct proxy *px, struct stream *s)
+{
+	struct sample_expr *expr = rule->act_prm.data[0];
+	const char *name = rule->act_prm.data[1];
+	int scope = (long)rule->act_prm.data[2];
+
+	return action_store(expr, name, scope, px, s, SMP_OPT_DIR_REQ);
+}
+
+/* Returns 0 if miss data, else returns 1. */
+static int action_tcp_res_store(struct tcp_rule *rule, struct proxy *px, struct stream *s)
+{
+	struct sample_expr *expr = rule->act_prm.data[0];
+	const char *name = rule->act_prm.data[1];
+	int scope = (long)rule->act_prm.data[2];
+
+	return action_store(expr, name, scope, px, s, SMP_OPT_DIR_RES);
+}
+
+/* Returns 0 if miss data, else returns 1. */
+static int action_http_req_store(struct http_req_rule *rule, struct proxy *px, struct stream *s)
+{
+	struct sample_expr *expr = rule->arg.act.p[0];
+	const char *name = rule->arg.act.p[1];
+	int scope = (long)rule->arg.act.p[2];
+
+	return action_store(expr, name, scope, px, s, SMP_OPT_DIR_REQ);
+}
+
+/* Returns 0 if miss data, else returns 1. */
+static int action_http_res_store(struct http_res_rule *rule, struct proxy *px, struct stream *s)
+{
+	struct sample_expr *expr = rule->arg.act.p[0];
+	const char *name = rule->arg.act.p[1];
+	int scope = (long)rule->arg.act.p[2];
+
+	return action_store(expr, name, scope, px, s, SMP_OPT_DIR_RES);
+}
+
+/* This two function checks the variable name and replace the
+ * configuration string name by the global string name. its
+ * the same string, but the global pointer can be easy to
+ * compare.
+ *
+ * The first function checks a sample-fetch and the second
+ * checks a converter.
+ */
+static int smp_check_var(struct arg *args, char **err)
+{
+	return vars_check_arg(&args[0], err);
+}
+
+static int conv_check_var(struct arg *args, struct sample_conv *conv,
+                          const char *file, int line, char **err_msg)
+{
+	return vars_check_arg(&args[0], err_msg);
+}
+
+/* This function is a common parser for using variables. It understands
+ * the format:
+ *
+ *   set-var(<variable-name>) <expression>
+ *
+ * It returns 0 if fails and <err> is filled with an error message. Otherwise,
+ * it returns 1 and the variable <expr> is filled with the pointer to the
+ * expression to execute.
+ */
+static int parse_vars(const char **args, int *arg, struct proxy *px,
+                      struct sample_expr **expr, char **name,
+                      enum vars_scope *scope, char **err)
+{
+	const char *var_name = args[*arg-1];
+	int var_len;
+
+	var_name += strlen("set-var");
+	if (*var_name != '(') {
+		memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)'", args[*arg-1]);
+		return 0;
+	}
+	var_name++; /* jump the '(' */
+	var_len = strlen(var_name);
+	var_len--; /* remove the ')' */
+	if (var_name[var_len] != ')') {
+		memprintf(err, "invalid variable '%s'. Expects 'set-var(<var-name>)'", args[*arg-1]);
+		return 0;
+	}
+
+	*name = register_name(var_name, var_len, scope, err);
+	if (!*name)
+		return 0;
+
+	*expr = sample_parse_expr((char **)args, arg, px->conf.args.file, px->conf.args.line,
+	                          err, &px->conf.args);
+	if (!*expr)
+		return 0;
+
+	return 1;
+}
+
+static int parse_tcp_req_store(const char **args, int *arg, struct proxy *px,
+                               struct tcp_rule *rule, char **err)
+{
+	struct sample_expr *expr;
+	int cur_arg = *arg;
+	char *name;
+	enum vars_scope scope;
+
+	if (!parse_vars(args, arg, px, &expr, &name, &scope, err))
+		return 0;
+
+	if (!(expr->fetch->val & SMP_VAL_FE_REQ_CNT)) {
+		memprintf(err,
+			  "fetch method '%s' extracts information from '%s', none of which is available here",
+			  args[cur_arg-1], sample_src_names(expr->fetch->use));
+		free(expr);
+		return 0;
+	}
+
+	rule->action       = TCP_ACT_CUSTOM_CONT;
+	rule->action_ptr   = action_tcp_req_store;
+	rule->act_prm.data[0] = expr;
+	rule->act_prm.data[1] = name;
+	rule->act_prm.data[2] = (void *)(long)scope;
+
+	return 1;
+}
+
+static int parse_tcp_res_store(const char **args, int *arg, struct proxy *px,
+                         struct tcp_rule *rule, char **err)
+{
+	struct sample_expr *expr;
+	int cur_arg = *arg;
+	char *name;
+	enum vars_scope scope;
+
+	if (!parse_vars(args, arg, px, &expr, &name, &scope, err))
+		return 0;
+
+	if (!(expr->fetch->val & SMP_VAL_BE_RES_CNT)) {
+		memprintf(err,
+		          "fetch method '%s' extracts information from '%s', none of which is available here",
+		          args[cur_arg-1], sample_src_names(expr->fetch->use));
+		free(expr);
+		return 0;
+	}
+
+	rule->action       = TCP_ACT_CUSTOM_CONT;
+	rule->action_ptr   = action_tcp_res_store;
+	rule->act_prm.data[0] = expr;
+	rule->act_prm.data[1] = name;
+	rule->act_prm.data[2] = (void *)(long)scope;
+
+	return 1;
+}
+
+static int parse_http_req_store(const char **args, int *arg, struct proxy *px,
+                         struct http_req_rule *rule, char **err)
+{
+	struct sample_expr *expr;
+	int cur_arg = *arg;
+	char *name;
+	enum vars_scope scope;
+
+	if (!parse_vars(args, arg, px, &expr, &name, &scope, err))
+		return -1;
+
+	if (!(expr->fetch->val & SMP_VAL_FE_HRQ_HDR)) {
+		memprintf(err,
+		          "fetch method '%s' extracts information from '%s', none of which is available here",
+		          args[cur_arg-1], sample_src_names(expr->fetch->use));
+		free(expr);
+		return -1;
+	}
+
+	rule->action       = HTTP_REQ_ACT_CUSTOM_CONT;
+	rule->action_ptr   = action_http_req_store;
+	rule->arg.act.p[0] = expr;
+	rule->arg.act.p[1] = name;
+	rule->arg.act.p[2] = (void *)(long)scope;
+
+	return 0;
+}
+
+static int parse_http_res_store(const char **args, int *arg, struct proxy *px,
+                         struct http_res_rule *rule, char **err)
+{
+	struct sample_expr *expr;
+	int cur_arg = *arg;
+	char *name;
+	enum vars_scope scope;
+
+	if (!parse_vars(args, arg, px, &expr, &name, &scope, err))
+		return -1;
+
+	if (!(expr->fetch->val & SMP_VAL_BE_HRS_HDR)) {
+		memprintf(err,
+		          "fetch method '%s' extracts information from '%s', none of which is available here",
+		          args[cur_arg-1], sample_src_names(expr->fetch->use));
+		free(expr);
+		return -1;
+	}
+
+	rule->action       = HTTP_RES_ACT_CUSTOM_CONT;
+	rule->action_ptr   = action_http_res_store;
+	rule->arg.act.p[0] = expr;
+	rule->arg.act.p[1] = name;
+	rule->arg.act.p[2] = (void *)(long)scope;
+
+	return 0;
+}
+
+static int vars_max_size(char **args, int section_type, struct proxy *curpx,
+                         struct proxy *defpx, const char *file, int line,
+                         char **err, unsigned int *limit)
+{
+	char *error;
+
+	*limit = strtol(args[1], &error, 10);
+	if (*error != 0) {
+		memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
+		return -1;
+	}
+	return 0;
+}
+
+static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
+                                struct proxy *defpx, const char *file, int line,
+                                char **err)
+{
+	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
+}
+
+static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
+                              struct proxy *defpx, const char *file, int line,
+                              char **err)
+{
+	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
+}
+
+static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
+                             struct proxy *defpx, const char *file, int line,
+                             char **err)
+{
+	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
+}
+
+static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
+                                struct proxy *defpx, const char *file, int line,
+                                char **err)
+{
+	return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
+}
+
+static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
+
+	{ "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_HTTP_ANY },
+	{ /* END */ },
+}};
+
+static struct sample_conv_kw_list sample_conv_kws = {ILH, {
+	{ "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
+	{ /* END */ },
+}};
+
+static struct tcp_action_kw_list tcp_req_kws = {"vars", { }, {
+	{ "set-var", parse_tcp_req_store, 1 },
+	{ /* END */ }
+}};
+
+static struct tcp_action_kw_list tcp_res_kws = {"vars", { }, {
+	{ "set-var", parse_tcp_res_store, 1 },
+	{ /* END */ }
+}};
+
+static struct http_req_action_kw_list http_req_kws = {"vars", { }, {
+	{ "set-var", parse_http_req_store, 1 },
+	{ /* END */ }
+}};
+
+static struct http_res_action_kw_list http_res_kws = {"vars", { }, {
+	{ "set-var", parse_http_res_store, 1 },
+	{ /* END */ }
+}};
+
+static struct cfg_kw_list cfg_kws = {{ },{
+	{ CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
+	{ CFG_GLOBAL, "tune.vars.sess-max-size",   vars_max_size_sess   },
+	{ CFG_GLOBAL, "tune.vars.txn-max-size",    vars_max_size_txn    },
+	{ CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
+	{ /* END */ }
+}};
+
+__attribute__((constructor))
+static void __http_protocol_init(void)
+{
+	var_pool = create_pool("vars", sizeof(struct var), MEM_F_SHARED);
+
+	sample_register_fetches(&sample_fetch_keywords);
+	sample_register_convs(&sample_conv_kws);
+	tcp_req_cont_keywords_register(&tcp_req_kws);
+	tcp_res_cont_keywords_register(&tcp_res_kws);
+	http_req_keywords_register(&http_req_kws);
+	http_res_keywords_register(&http_res_kws);
+	cfg_register_keywords(&cfg_kws);
+}