varnishtest "normalize-uri tests"
#REQUIRE_VERSION=2.4

# This reg-test tests the http-request normalize-uri action.

feature ignore_unknown_macro

server s1 {
    rxreq
    txresp -hdr "connection: close"
} -repeat 70 -start

haproxy h1 -conf {
    global
        # WT: limit false-positives causing "HTTP header incomplete" due to
        # idle server connections being randomly used and randomly expiring
        # under us.
        tune.idle-pool.shared off
        expose-experimental-directives

    defaults
        mode http
        timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
        timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
        timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"

    frontend fe_path_merge_slashes
        bind "fd@${fe_path_merge_slashes}"

        http-request set-var(txn.before) url
        http-request normalize-uri path-merge-slashes
        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

    frontend fe_path_strip_dotdot
        bind "fd@${fe_path_strip_dotdot}"

        http-request set-var(txn.before) url
        http-request normalize-uri path-strip-dotdot
        http-request set-var(txn.after) url

        http-request set-uri %[var(txn.before)]
        http-request normalize-uri path-strip-dotdot full
        http-request set-var(txn.after_full) url

        http-response add-header before  %[var(txn.before)]
        http-response add-header after  %[var(txn.after)]
        http-response add-header after-full  %[var(txn.after_full)]

        default_backend be

    frontend fe_sort_query_by_name
        bind "fd@${fe_sort_query_by_name}"

        http-request set-var(txn.before) url
        http-request normalize-uri query-sort-by-name
        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

    frontend fe_percent_to_uppercase
        bind "fd@${fe_percent_to_uppercase}"

        http-request set-var(txn.before) url
        http-request normalize-uri percent-to-uppercase
        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

    frontend fe_percent_to_uppercase_strict
        bind "fd@${fe_percent_to_uppercase_strict}"

        http-request set-var(txn.before) url
        http-request normalize-uri percent-to-uppercase strict
        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

    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

    frontend fe_percent_decode_unreserved
        bind "fd@${fe_percent_decode_unreserved}"

        http-request set-var(txn.before) url
        http-request normalize-uri percent-decode-unreserved
        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

    frontend fe_percent_decode_unreserved_strict
        bind "fd@${fe_percent_decode_unreserved_strict}"

        http-request set-var(txn.before) url
        http-request normalize-uri percent-decode-unreserved strict
        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

    frontend fe_fragment_strip
        bind "fd@${fe_fragment_strip}"
        option accept-invalid-http-request

        http-request set-var(txn.before) url
        http-request normalize-uri fragment-strip
        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

    frontend fe_fragment_encode
        bind "fd@${fe_fragment_encode}"
        option accept-invalid-http-request

        http-request set-var(txn.before) url
        http-request normalize-uri fragment-encode
        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

    frontend fe_fragment_block
        bind "fd@${fe_fragment_block}"
        http-request normalize-uri fragment-strip
        default_backend be

    backend be
        server s1 ${s1_addr}:${s1_port}

} -start

client c1 -connect ${h1_fe_path_merge_slashes_sock} {
    txreq -url "/foo/bar"
    rxresp
    expect resp.http.before == "/foo/bar"
    expect resp.http.after == "/foo/bar"

    txreq -url "/foo//bar"
    rxresp
    expect resp.http.before == "/foo//bar"
    expect resp.http.after == "/foo/bar"

    txreq -url "/foo///bar"
    rxresp
    expect resp.http.before == "/foo///bar"
    expect resp.http.after == "/foo/bar"

    txreq -url "///foo///bar"
    rxresp
    expect resp.http.before == "///foo///bar"
    expect resp.http.after == "/foo/bar"

    txreq -url "///foo/bar"
    rxresp
    expect resp.http.before == "///foo/bar"
    expect resp.http.after == "/foo/bar"

    txreq -url "///foo///bar///"
    rxresp
    expect resp.http.before == "///foo///bar///"
    expect resp.http.after == "/foo/bar/"

    txreq -url "///"
    rxresp
    expect resp.http.before == "///"
    expect resp.http.after == "/"

    txreq -url "/foo?bar=///"
    rxresp
    expect resp.http.before == "/foo?bar=///"
    expect resp.http.after == "/foo?bar=///"

    txreq -url "//foo?bar=///"
    rxresp
    expect resp.http.before == "//foo?bar=///"
    expect resp.http.after == "/foo?bar=///"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c2 -connect ${h1_fe_path_strip_dotdot_sock} {
    txreq -url "/foo/bar"
    rxresp
    expect resp.http.before == "/foo/bar"
    expect resp.http.after == "/foo/bar"
    expect resp.http.after-full == "/foo/bar"

    txreq -url "/foo/.."
    rxresp
    expect resp.http.before == "/foo/.."
    expect resp.http.after == "/"
    expect resp.http.after-full == "/"

    txreq -url "/foo/../"
    rxresp
    expect resp.http.before == "/foo/../"
    expect resp.http.after == "/"
    expect resp.http.after-full == "/"

    txreq -url "/foo/bar/../"
    rxresp
    expect resp.http.before == "/foo/bar/../"
    expect resp.http.after == "/foo/"
    expect resp.http.after-full == "/foo/"

    txreq -url "/foo/../bar"
    rxresp
    expect resp.http.before == "/foo/../bar"
    expect resp.http.after == "/bar"
    expect resp.http.after-full == "/bar"

    txreq -url "/foo/../bar/"
    rxresp
    expect resp.http.before == "/foo/../bar/"
    expect resp.http.after == "/bar/"
    expect resp.http.after-full == "/bar/"

    txreq -url "/foo/../../bar/"
    rxresp
    expect resp.http.before == "/foo/../../bar/"
    expect resp.http.after == "/../bar/"
    expect resp.http.after-full == "/bar/"

    txreq -url "/foo//../../bar/"
    rxresp
    expect resp.http.before == "/foo//../../bar/"
    expect resp.http.after == "/bar/"
    expect resp.http.after-full == "/bar/"

    txreq -url "/foo/?bar=/foo/../"
    rxresp
    expect resp.http.before == "/foo/?bar=/foo/../"
    expect resp.http.after == "/foo/?bar=/foo/../"
    expect resp.http.after-full == "/foo/?bar=/foo/../"

    txreq -url "/foo/../?bar=/foo/../"
    rxresp
    expect resp.http.before == "/foo/../?bar=/foo/../"
    expect resp.http.after == "/?bar=/foo/../"
    expect resp.http.after-full == "/?bar=/foo/../"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
    expect resp.http.after-full == "*"
} -run

client c3 -connect ${h1_fe_sort_query_by_name_sock} {
    txreq -url "/?a=a"
    rxresp
    expect resp.http.before == "/?a=a"
    expect resp.http.after == "/?a=a"

    txreq -url "/?a=a&z=z"
    rxresp
    expect resp.http.before == "/?a=a&z=z"
    expect resp.http.after == "/?a=a&z=z"

    txreq -url "/?z=z&a=a"
    rxresp
    expect resp.http.before == "/?z=z&a=a"
    expect resp.http.after == "/?a=a&z=z"

    txreq -url "/?a=z&z=a"
    rxresp
    expect resp.http.before == "/?a=z&z=a"
    expect resp.http.after == "/?a=z&z=a"

    txreq -url "/?z=a&a=z"
    rxresp
    expect resp.http.before == "/?z=a&a=z"
    expect resp.http.after == "/?a=z&z=a"

    txreq -url "/?c&b&a&z&x&y"
    rxresp
    expect resp.http.before == "/?c&b&a&z&x&y"
    expect resp.http.after == "/?a&b&c&x&y&z"

    txreq -url "/?a=&aa=&aaa=&aaaa="
    rxresp
    expect resp.http.before == "/?a=&aa=&aaa=&aaaa="
    expect resp.http.after == "/?a=&aa=&aaa=&aaaa="

    txreq -url "/?aaaa=&a=&aa=&aaa="
    rxresp
    expect resp.http.before == "/?aaaa=&a=&aa=&aaa="
    expect resp.http.after == "/?a=&aa=&aaa=&aaaa="

    txreq -url "/?a=5&a=3&a=1&a=2&a=4"
    rxresp
    expect resp.http.before == "/?a=5&a=3&a=1&a=2&a=4"
    expect resp.http.after == "/?a=5&a=3&a=1&a=2&a=4"

    txreq -url "/?a=5&b=3&a=1&a=2&b=4"
    rxresp
    expect resp.http.before == "/?a=5&b=3&a=1&a=2&b=4"
    expect resp.http.after == "/?a=5&a=1&a=2&b=3&b=4"

    txreq -url "/"
    rxresp
    expect resp.http.before == "/"
    expect resp.http.after == "/"

    txreq -url "/?"
    rxresp
    expect resp.http.before == "/?"
    expect resp.http.after == "/?"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c4 -connect ${h1_fe_percent_to_uppercase_sock} {
    txreq -url "/a?a=a"
    rxresp
    expect resp.http.before == "/a?a=a"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%aa?a=%aa"
    rxresp
    expect resp.http.before == "/%aa?a=%aa"
    expect resp.http.after == "/%AA?a=%AA"

    txreq -url "/%zz?a=%zz"
    rxresp
    expect resp.status == 200
    expect resp.http.before == "/%zz?a=%zz"
    expect resp.http.after == "/%zz?a=%zz"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c5 -connect ${h1_fe_percent_to_uppercase_strict_sock} {
    txreq -url "/a?a=a"
    rxresp
    expect resp.http.before == "/a?a=a"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%aa?a=%aa"
    rxresp
    expect resp.http.before == "/%aa?a=%aa"
    expect resp.http.after == "/%AA?a=%AA"

    txreq -url "/%zz?a=%zz"
    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

client c7 -connect ${h1_fe_percent_decode_unreserved_sock} {
    txreq -url "/a?a=a"
    rxresp
    expect resp.http.before == "/a?a=a"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%61?%61=%61"
    rxresp
    expect resp.http.before == "/%61?%61=%61"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%3F?foo=bar"
    rxresp
    expect resp.http.before == "/%3F?foo=bar"
    expect resp.http.after == "/%3F?foo=bar"

    txreq -url "/%%36%36"
    rxresp
    expect resp.status == 200
    expect resp.http.before == "/%%36%36"
    expect resp.http.after == "/%66"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c8 -connect ${h1_fe_percent_decode_unreserved_strict_sock} {
    txreq -url "/a?a=a"
    rxresp
    expect resp.http.before == "/a?a=a"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%61?%61=%61"
    rxresp
    expect resp.http.before == "/%61?%61=%61"
    expect resp.http.after == "/a?a=a"

    txreq -url "/%3F?foo=bar"
    rxresp
    expect resp.http.before == "/%3F?foo=bar"
    expect resp.http.after == "/%3F?foo=bar"

    txreq -url "/%%36%36"
    rxresp
    expect resp.status == 400
} -run

client c9 -connect ${h1_fe_fragment_strip_sock} {
    txreq -url "/#foo"
    rxresp
    expect resp.http.before == "/#foo"
    expect resp.http.after == "/"

    txreq -url "/%23foo"
    rxresp
    expect resp.http.before == "/%23foo"
    expect resp.http.after == "/%23foo"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c10 -connect ${h1_fe_fragment_encode_sock} {
    txreq -url "/#foo"
    rxresp
    expect resp.http.before == "/#foo"
    expect resp.http.after == "/%23foo"

    txreq -url "/#foo/#foo"
    rxresp
    expect resp.http.before == "/#foo/#foo"
    expect resp.http.after == "/%23foo/%23foo"

    txreq -url "/%23foo"
    rxresp
    expect resp.http.before == "/%23foo"
    expect resp.http.after == "/%23foo"

    txreq -req OPTIONS -url "*"
    rxresp
    expect resp.http.before == "*"
    expect resp.http.after == "*"
} -run

client c11 -connect ${h1_fe_fragment_block_sock} {
    txreq -url "/#foo"
    rxresp
    expect resp.status == 400
} -run
