Http-request replace-uri seems to be handled differently between 2.0.9 and 2.1.0

Hi everyone

I updated my haproxy from 2.0.9 to 2.1.0 and it seems that http-request replace-uri is handled differently between these two versions.

I’m using the following backend configuration where I use http-request replace-uri (.*) /guacamole\1 to rewrite the document root to /guacamole.

backend guacamole_backend
    balance leastconn
    option http-keep-alive
    option forwardfor
    http-reuse safe
    option httpchk HEAD / HTTP/1.1\r\nHost:\ cloud.foo.bar.de
    http-request set-header X-ORIGIN-URL http://%[hdr(host)]%[path] 
    http-request set-header X-REMOTE-IP %[src]
    http-request replace-uri (.*) /guacamole\1
    default-server inter 2s downinter 5s rise 3 fall 2
    server docker_guacamole 127.0.0.1:8080 check

With version 2.0.9 this works as I expected and the document root is rewritten to /guacamole.
Backend log:

[26/Nov/2019:20:12:34 +0000] "GET /guacamole/ HTTP/1.1" 200 4534
[26/Nov/2019:20:12:34 +0000] "GET /guacamole/relocateParameters.js HTTP/1.1" 200 4505
[26/Nov/2019:20:12:34 +0000] "GET /guacamole/webjars/angular-touch/1.6.9/angular-touch.min.js HTTP/1.1" 200 4074

When I switch to version 2.1.0 the backend logs request like this:

[26/Nov/2019:20:13:28 +0000] "GET /guacamolehttps://cloud.foo.bar.de/ HTTP/1.1" 404 1122
[26/Nov/2019:20:13:28 +0000] "GET /guacamolehttps://cloud.foo.bar.de/favicon.ico HTTP/1.1" 404 1133

Am I using http-request replace-uri the wrong way?

The documentation for “http-request replace-uri” is different between 2.0 and 2.1.

The 2.1 docs mention this excerpt:

The URI part may contain an optional scheme, authority or
query string. These are considered to be part of the value that is matched
against.

So it definitely looks like there was a change of behaviour between 2.0 and 2.1…however the example in the 2.1 docs is exactly as you are doing so maybe there needs to be some clarification done.

It seems this only happens if I use alpn h2/http1.1 in the https frontend configuration and the client uses http/2. I used curl to verify this behavior.

If I use curl --http2 -v https://cloud.foo.bar/ the request send to the backend looks like this: /guacamolehttps://cloud.foo.bar/

Using curl --http1.1 -v https://cloud.foo.bar/ with the same configuration works without problems.

Furthermore I found a solution which works for me:
Using http-request set-path %[path,regsub(^/?,/guacamole/)] instead of http-request replace-uri (.*) /guacamole\1 works correctly with http/2 and http1.1