MEDIUM: http: make url_param iterate over multiple occurrences

There are some situations hwere it's desirable to scan multiple occurrences
of a same parameter name in the query string. This change ensures this can
work, even with an empty name which will then iterate over all parameters.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 1688bd7..6cac747 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -12709,17 +12709,18 @@
   restrict access to certain systems through a proxy, for example when combined
   with option "http_proxy".
 
-urlp(<name>[,<delim>]) : string
-url_param(<name>[,<delim>]) : string
+urlp([<name>[,<delim>]]) : string
+url_param([<name>[,<delim>]]) : string
   This extracts the first occurrence of the parameter <name> in the query
   string, which begins after either '?' or <delim>, and which ends before '&',
-  ';' or <delim>. The parameter name is case-sensitive. The result is a string
-  corresponding to the value of the parameter <name> as presented in the
-  request (no URL decoding is performed). This can be used for session
+  ';' or <delim>. The parameter name is case-sensitive. If no name is given,
+  any parameter will match, and the first one will be returned. The result is
+  a string corresponding to the value of the parameter <name> as presented in
+  the request (no URL decoding is performed). This can be used for session
   stickiness based on a client ID, to extract an application cookie passed as a
   URL parameter, or in ACLs to apply some checks. Note that the ACL version of
-  this fetch do not iterate over multiple parameters and stop at the first one
-  as well.
+  this fetch iterates over multiple parameters and will iteratively report all
+  parameters values if no name is given
 
   ACL derivatives :
     urlp(<name>[,<delim>])     : exact string match
@@ -12738,7 +12739,7 @@
       # match http://example.com/foo;JSESSIONID=some_id
       stick on urlp(JSESSIONID,;)
 
-urlp_val(<name>[,<delim>]) : integer
+urlp_val([<name>[,<delim>])] : integer
   See "urlp" above. This one extracts the URL parameter <name> in the request
   and converts it to an integer value. This can be used for session stickiness
   based on a user ID for example, or with ACLs to match a page number or price.
diff --git a/src/proto_http.c b/src/proto_http.c
index 7ae4ed9..16d531d 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -11488,7 +11488,7 @@
  *
  * Example: if path = "/foo/bar/fubar?yo=mama;ye=daddy", and n = 22:
  *
- * find_query_string(path, n) points to "yo=mama;ye=daddy" string.
+ * find_query_string(path, n, '?') points to "yo=mama;ye=daddy" string.
  */
 static inline char *find_param_list(char *path, size_t path_l, char delim)
 {
@@ -11512,7 +11512,7 @@
  */
 static char*
 find_url_param_pos(char* query_string, size_t query_string_l,
-                   char* url_param_name, size_t url_param_name_l,
+                   const char* url_param_name, size_t url_param_name_l,
                    char delim)
 {
 	char *pos, *last;
@@ -11534,31 +11534,37 @@
 }
 
 /*
- * Given a url parameter name, returns its value and size into *value and
- * *value_l respectively, and returns non-zero. If the parameter is not found,
- * zero is returned and value/value_l are not touched.
+ * Given a url parameter name and a query string, returns its value and size
+ * into *value and *value_l respectively, and returns non-zero. An empty
+ * url_param_name matches the first available parameter. If the parameter is
+ * not found, zero is returned and value/value_l are not touched.
  */
 static int
-find_url_param_value(char* path, size_t path_l,
-                     char* url_param_name, size_t url_param_name_l,
-                     char** value, int* value_l, char delim)
+find_next_url_param(char* query_string, char *qs_end,
+                    const char* url_param_name, size_t url_param_name_l,
+                    char** value, int* value_l, char delim)
 {
-	char *query_string, *qs_end;
 	char *arg_start;
 	char *value_start, *value_end;
 
-	query_string = find_param_list(path, path_l, delim);
-	if (!query_string)
-		return 0;
-
-	qs_end = path + path_l;
-	arg_start = find_url_param_pos(query_string, qs_end - query_string,
-                                      url_param_name, url_param_name_l,
-                                      delim);
+	arg_start = query_string;
+	if (url_param_name_l) {
+		arg_start = find_url_param_pos(query_string, qs_end - query_string,
+		                               url_param_name, url_param_name_l,
+		                               delim);
+	}
 	if (!arg_start)
 		return 0;
 
-	value_start = arg_start + url_param_name_l + 1;
+	if (!url_param_name_l) {
+		value_start = memchr(arg_start, '=', qs_end - arg_start);
+		if (!value_start)
+			return 0;
+		value_start++;
+	}
+	else
+		value_start = arg_start + url_param_name_l + 1;
+
 	value_end = value_start;
 
 	while ((value_end < qs_end) && !is_param_delimiter(*value_end, delim))
@@ -11574,8 +11580,12 @@
 {
 	char delim = '?';
 	struct http_msg *msg;
+	char *query_string, *qs_end;
+	const char *name;
+	int name_len;
 
-	if (!args || args[0].type != ARGT_STR ||
+	if (!args ||
+	    (args[0].type && args[0].type != ARGT_STR) ||
 	    (args[1].type && args[1].type != ARGT_STR))
 		return 0;
 
@@ -11586,14 +11596,42 @@
 	if (args[1].type)
 		delim = *args[1].data.str.str;
 
+	query_string = smp->ctx.a[0];
+	qs_end = smp->ctx.a[1];
+
-	if (!find_url_param_value(msg->chn->buf->p + msg->sl.rq.u, msg->sl.rq.u_l,
-                                 args->data.str.str, args->data.str.len,
+	if (!query_string) { // first call, find the query string
+		query_string = find_param_list(msg->chn->buf->p + msg->sl.rq.u,
+		                               msg->sl.rq.u_l, delim);
+		if (!query_string)
+			return 0;
+
+		qs_end = msg->chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l;
+		smp->ctx.a[0] = query_string;
+		smp->ctx.a[1] = qs_end;
+	}
+
+	name = "";
+	name_len = 0;
+	if (args->type == ARGT_STR) {
+		name     = args->data.str.str;
+		name_len = args->data.str.len;
+	}
+
+	if (!find_next_url_param(query_string, qs_end,
+                                 name, name_len,
                                  &smp->data.str.str, &smp->data.str.len,
                                  delim))
 		return 0;
 
+	query_string = smp->data.str.str + smp->data.str.len + 1;
+	smp->ctx.a[0] = query_string;
+
 	smp->type = SMP_T_STR;
 	smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
+
+	if (query_string < qs_end)
+		smp->flags |= SMP_F_NOT_LAST;
+
 	return 1;
 }
 
@@ -12466,9 +12504,9 @@
 	{ "url32+src",       smp_fetch_url32_src,      0,                NULL,    SMP_T_BIN,  SMP_USE_HRQHV },
 	{ "url_ip",          smp_fetch_url_ip,         0,                NULL,    SMP_T_IPV4, SMP_USE_HRQHV },
 	{ "url_port",        smp_fetch_url_port,       0,                NULL,    SMP_T_UINT, SMP_USE_HRQHV },
-	{ "url_param",       smp_fetch_url_param,      ARG2(1,STR,STR),  NULL,    SMP_T_STR,  SMP_USE_HRQHV },
-	{ "urlp"     ,       smp_fetch_url_param,      ARG2(1,STR,STR),  NULL,    SMP_T_STR,  SMP_USE_HRQHV },
-	{ "urlp_val",        smp_fetch_url_param_val,  ARG2(1,STR,STR),  NULL,    SMP_T_UINT, SMP_USE_HRQHV },
+	{ "url_param",       smp_fetch_url_param,      ARG2(0,STR,STR),  NULL,    SMP_T_STR,  SMP_USE_HRQHV },
+	{ "urlp"     ,       smp_fetch_url_param,      ARG2(0,STR,STR),  NULL,    SMP_T_STR,  SMP_USE_HRQHV },
+	{ "urlp_val",        smp_fetch_url_param_val,  ARG2(0,STR,STR),  NULL,    SMP_T_UINT, SMP_USE_HRQHV },
 	{ /* END */ },
 }};