]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http: make url_param iterate over multiple occurrences
authorWilly Tarreau <w@1wt.eu>
Thu, 7 May 2015 14:06:18 +0000 (16:06 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 19 May 2015 11:16:07 +0000 (13:16 +0200)
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.

doc/configuration.txt
src/proto_http.c

index 1688bd7b4bc335a90d02281960b6af11812edde4..6cac747b3b2382da2045da3733ac48579e930327 100644 (file)
@@ -12709,17 +12709,18 @@ url_port : integer
   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 @@ url_param(<name>[,<delim>]) : string
       # 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.
index 7ae4ed9ed4c281fecef01cac2fb9985a2381f443..16d531d2b44b7f282b9d44a003ec4dbe83dabab0 100644 (file)
@@ -11488,7 +11488,7 @@ smp_fetch_cookie_val(const struct arg *args, struct sample *smp, const char *kw,
  *
  * 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 inline int is_param_delimiter(char c, char delim)
  */
 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 @@ find_url_param_pos(char* query_string, size_t query_string_l,
 }
 
 /*
- * 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 @@ smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw,
 {
        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 @@ smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw,
        if (args[1].type)
                delim = *args[1].data.str.str;
 
-       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,
+       query_string = smp->ctx.a[0];
+       qs_end = smp->ctx.a[1];
+
+       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 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
        { "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 */ },
 }};