]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: uri_normalizer: Add a `strip-dot` normalizer
authorMaximilian Mader <max@bastelstu.be>
Tue, 20 Apr 2021 22:22:50 +0000 (00:22 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 21 Apr 2021 10:15:14 +0000 (12:15 +0200)
This normalizer removes "/./" segments from the path component.
Usually the dot refers to the current directory which renders those segments redundant.

See GitHub Issue #714.

doc/configuration.txt
include/haproxy/action-t.h
include/haproxy/uri_normalizer.h
reg-tests/http-rules/normalize_uri.vtc
src/http_act.c
src/uri_normalizer.c

index ce96c44e74044fc18320ebc047eccec44a45fc05..de910043983f14801532566c2c7cd191ad11413d 100644 (file)
@@ -6013,6 +6013,7 @@ http-request early-hint <name> <fmt> [ { if | unless } <condition> ]
 
 http-request normalize-uri <normalizer> [ { if | unless } <condition> ]
 http-request normalize-uri path-merge-slashes [ { if | unless } <condition> ]
+http-request normalize-uri path-strip-dot [ { if | unless } <condition> ]
 http-request normalize-uri path-strip-dotdot [ full ] [ { if | unless } <condition> ]
 http-request normalize-uri percent-to-uppercase [ strict ] [ { if | unless } <condition> ]
 http-request normalize-uri query-sort-by-name [ { if | unless } <condition> ]
@@ -6035,6 +6036,14 @@ http-request normalize-uri query-sort-by-name [ { if | unless } <condition> ]
 
   The following normalizers are available:
 
+  - path-strip-dot: Removes "/./" segments within the "path" component.
+
+    Example:
+    - /.            -> /
+    - /./bar/       -> /bar/
+    - /a/./a        -> /a/a
+    - /.well-known/ -> /.well-known/ (no change)
+
   - path-strip-dotdot: Normalizes "/../" segments within the "path" component.
       This merges segments that attempt to access the parent directory with
       their preceding segment. Empty segments do not receive special treatment.
index 43e6b1addc2d73a3dd9f1367db38c8ed872cffe8..2e3edeaf3c8c1cb408708ba90062fe954ab06e14 100644 (file)
@@ -103,6 +103,7 @@ enum act_timeout_name {
 
 enum act_normalize_uri {
        ACT_NORMALIZE_URI_PATH_MERGE_SLASHES,
+       ACT_NORMALIZE_URI_PATH_STRIP_DOT,
        ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT,
        ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL,
        ACT_NORMALIZE_URI_QUERY_SORT_BY_NAME,
index 180936eae40afa34116788ee49465920cf75b978..81c7e00ce31ee6f2b8a4efde7cafff0b79443940 100644 (file)
@@ -19,6 +19,7 @@
 #include <haproxy/uri_normalizer-t.h>
 
 enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int strict, struct ist *dst);
+enum uri_normalizer_err uri_normalizer_path_dot(const struct ist path, struct ist *dst);
 enum uri_normalizer_err uri_normalizer_path_dotdot(const struct ist path, int full, struct ist *dst);
 enum uri_normalizer_err uri_normalizer_path_merge_slashes(const struct ist path, struct ist *dst);
 enum uri_normalizer_err uri_normalizer_query_sort(const struct ist query, const char delim, struct ist *dst);
index b997b31b8b5d16747d89bc6a807d5b59fc9b36e8..9884b6c4389adcb02f26d0a28bcb5eac92d2edb5 100644 (file)
@@ -8,7 +8,7 @@ feature ignore_unknown_macro
 server s1 {
     rxreq
     txresp
-} -repeat 43 -start
+} -repeat 54 -start
 
 haproxy h1 -conf {
     defaults
@@ -82,6 +82,18 @@ haproxy h1 -conf {
 
         default_backend be
 
+    frontend fe_dot
+        bind "fd@${fe_dot}"
+
+        http-request set-var(txn.before) url
+        http-request normalize-uri path-strip-dot
+        http-request set-var(txn.after) url
+
+        http-response add-header before  %[var(txn.before)]
+        http-response add-header after  %[var(txn.after)]
+
+        default_backend be
+
     backend be
         server s1 ${s1_addr}:${s1_port}
 
@@ -312,3 +324,70 @@ client c5 -connect ${h1_fe_percent_to_uppercase_strict_sock} {
     rxresp
     expect resp.status == 400
 } -run
+
+client c6 -connect ${h1_fe_dot_sock} {
+    txreq -url "/"
+    rxresp
+    expect resp.http.before == "/"
+    expect resp.http.after == "/"
+
+    txreq -url "/a/b"
+    rxresp
+    expect resp.http.before == "/a/b"
+    expect resp.http.after == "/a/b"
+
+    txreq -url "/."
+    rxresp
+    expect resp.http.before == "/."
+    expect resp.http.after == "/"
+
+    txreq -url "/./"
+    rxresp
+    expect resp.http.before == "/./"
+    expect resp.http.after == "/"
+
+    txreq -url "/a/."
+    rxresp
+    expect resp.http.before == "/a/."
+    expect resp.http.after == "/a/"
+
+    txreq -url "/a."
+    rxresp
+    expect resp.http.before == "/a."
+    expect resp.http.after == "/a."
+
+    txreq -url "/.a"
+    rxresp
+    expect resp.http.before == "/.a"
+    expect resp.http.after == "/.a"
+
+    txreq -url "/a/."
+    rxresp
+    expect resp.http.before == "/a/."
+    expect resp.http.after == "/a/"
+
+    txreq -url "/a/./"
+    rxresp
+    expect resp.http.before == "/a/./"
+    expect resp.http.after == "/a/"
+
+    txreq -url "/a/./a"
+    rxresp
+    expect resp.http.before == "/a/./a"
+    expect resp.http.after == "/a/a"
+
+    txreq -url "/a/../"
+    rxresp
+    expect resp.http.before == "/a/../"
+    expect resp.http.after == "/a/../"
+
+    txreq -url "/a/../a"
+    rxresp
+    expect resp.http.before == "/a/../a"
+    expect resp.http.after == "/a/../a"
+
+    txreq -url "/?a=/./"
+    rxresp
+    expect resp.http.before == "/?a=/./"
+    expect resp.http.after == "/?a=/./"
+} -run
index 5da2d7d1ab974b87a27d701b3b3ebb58c1749ef5..df2bbe41b90c3989ae8c4216d98ef9a4598520ef 100644 (file)
@@ -232,6 +232,23 @@ static enum act_return http_action_normalize_uri(struct act_rule *rule, struct p
 
                        break;
                }
+               case ACT_NORMALIZE_URI_PATH_STRIP_DOT: {
+                       const struct ist path = http_get_path(uri);
+                       struct ist newpath = ist2(replace->area, replace->size);
+
+                       if (!isttest(path))
+                               goto leave;
+
+                       err = uri_normalizer_path_dot(iststop(path, '?'), &newpath);
+
+                       if (err != URI_NORMALIZER_ERR_NONE)
+                               break;
+
+                       if (!http_replace_req_path(htx, newpath, 0))
+                               goto fail_rewrite;
+
+                       break;
+               }
                case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT:
                case ACT_NORMALIZE_URI_PATH_STRIP_DOTDOT_FULL: {
                        const struct ist path = http_get_path(uri);
@@ -350,6 +367,11 @@ static enum act_parse_ret parse_http_normalize_uri(const char **args, int *orig_
 
                rule->action = ACT_NORMALIZE_URI_PATH_MERGE_SLASHES;
        }
+       else if (strcmp(args[cur_arg], "path-strip-dot") == 0) {
+               cur_arg++;
+
+               rule->action = ACT_NORMALIZE_URI_PATH_STRIP_DOT;
+       }
        else if (strcmp(args[cur_arg], "path-strip-dotdot") == 0) {
                cur_arg++;
 
index ded9e1c01cdff7633e0d9ca808635cc660d0faf6..8d959365eccdebf55f994ebd455cf9aff72b619c 100644 (file)
@@ -75,6 +75,47 @@ enum uri_normalizer_err uri_normalizer_percent_upper(const struct ist input, int
        return err;
 }
 
+/* Removes `/./` from the given path. */
+enum uri_normalizer_err uri_normalizer_path_dot(const struct ist path, struct ist *dst)
+{
+       enum uri_normalizer_err err;
+
+       const size_t size = istclear(dst);
+       struct ist newpath = *dst;
+
+       struct ist scanner = path;
+
+       /* The path will either be shortened or have the same length. */
+       if (size < istlen(path)) {
+               err = URI_NORMALIZER_ERR_ALLOC;
+               goto fail;
+       }
+
+       while (istlen(scanner) > 0) {
+               const struct ist segment = istsplit(&scanner, '/');
+
+               if (!isteq(segment, ist("."))) {
+                       if (istcat(&newpath, segment, size) < 0) {
+                               /* This is impossible, because we checked the size of the destination buffer. */
+                               my_unreachable();
+                               err = URI_NORMALIZER_ERR_INTERNAL_ERROR;
+                               goto fail;
+                       }
+
+                       if (istend(segment) != istend(scanner))
+                               newpath = __istappend(newpath, '/');
+               }
+       }
+
+       *dst = newpath;
+
+       return URI_NORMALIZER_ERR_NONE;
+
+  fail:
+
+       return err;
+}
+
 /* Merges `/../` with preceding path segments.
  *
  * If `full` is set to `0` then `/../` will be printed at the start of the resulting