]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: h2: use the normalized URI encoding for absolute form requests
authorWilly Tarreau <w@1wt.eu>
Tue, 8 Oct 2019 16:33:19 +0000 (18:33 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 9 Oct 2019 09:10:19 +0000 (11:10 +0200)
H2 strongly recommends that clients exclusively use the absolute form
for requests, which contains a scheme, an authority and a path, instead
of the old format involving the Host header and a path. Thus there is
no way to distinguish between a request intended for a proxy and an
origin request, and as such proxied requests are lost.

This patch makes sure to keep the encoding of all absolute form requests
so that the URI is kept end-to-end. If the scheme is http or https, there
is an uncertainty so the request is tagged as a normalized URI so that
the other end (H1) can decide to emit it in origin form as this is by far
the most commonly expected one, and it's certain that quite a number of
H1 setups are not ready to cope with absolute URIs.

There is a direct visible impact of this change, which is that the uri
sample fetch will now return absolute URIs (as they really come on the
wire) whenever these are used. It also means that default http logs will
report absolute URIs.

If a situation is once met where a client uses H2 to join an H1 proxy
with haproxy in the middle, then it will be trivial to add an option to
ask the H1 output to use absolute encoding for such requests.

Later we may be able to consider that the normalized URI is the default
output format and stop sending them in origin form unless an option is
set.

Now chaining multiple instances keeps the semantics as far as possible
along the whole chain :

 1) H1 to H1
  H1:"GET /"       --> H1:"GET /"       # log: /
  H1:"GET http://" --> H1:"GET http://" # log: http://
  H1:"GET ftp://"  --> H1:"GET ftp://"  # log: ftp://

 2) H2 to H1
  H2:"GET /"       --> H1:"GET /"       # log: /
  H2:"GET http://" --> H1:"GET /"       # log: http://
  H2:"GET ftp://"  --> H1:"GET ftp://"  # log: ftp://

 3) H1 to H2 to H2 to H1
  H1:"GET /"       --> H2:"GET /"       --> H2:"GET /"       --> H1:"GET /"
  H1:"GET http://" --> H2:"GET http://" --> H2:"GET http://" --> H1:"GET /"
  H1:"GET ftp://"  --> H2:"GET ftp://"  --> H2:"GET ftp://"  --> H1:"GET ftp://"

Thus there is zero loss on H1->H1, H1->H2 nor H2->H2, and H2->H1 is
normalized in origin format if ambiguous.

src/h2.c

index 254d977192117ef6ddf78ce6762cbb96aa16e671..f034e3d5694d1b592ccd8ec4cee38c2f2d916cf7 100644 (file)
--- a/src/h2.c
+++ b/src/h2.c
@@ -217,11 +217,10 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
                uri = phdr[H2_PHDR_IDX_AUTH];
                flags |= HTX_SL_F_HAS_AUTHORITY;
        }
-       else if (!(flags & (HTX_SL_F_SCHM_HTTP|HTX_SL_F_SCHM_HTTPS)) && (fields & H2_PHDR_FND_AUTH)) {
-               /* non-http/https scheme + authority, let's use the absolute
-                * form. We simply use the trash to concatenate them since all
-                * of them MUST fit in a bufsize since it's where they come
-                * from.
+       else if (fields & H2_PHDR_FND_AUTH) {
+               /* authority is present, let's use the absolute form. We simply
+                * use the trash to concatenate them since all of them MUST fit
+                * in a bufsize since it's where they come from.
                 */
                uri = ist2bin(trash.area, phdr[H2_PHDR_IDX_SCHM]);
                istcat(&uri, ist("://"), trash.size);
@@ -229,6 +228,19 @@ static struct htx_sl *h2_prepare_htx_reqline(uint32_t fields, struct ist *phdr,
                if (!isteq(phdr[H2_PHDR_IDX_PATH], ist("*")))
                        istcat(&uri, phdr[H2_PHDR_IDX_PATH], trash.size);
                flags |= HTX_SL_F_HAS_AUTHORITY;
+
+               if (flags & (HTX_SL_F_SCHM_HTTP|HTX_SL_F_SCHM_HTTPS)) {
+                       /* we don't know if it was originally an absolute or a
+                        * relative request because newer versions of HTTP use
+                        * the absolute URI format by default, which we call
+                        * the normalized URI format internally. This is the
+                        * strongly recommended way of sending a request for
+                        * a regular client, so we cannot distinguish this
+                        * from a request intended for a proxy. For other
+                        * schemes however there is no doubt.
+                        */
+                       flags |= HTX_SL_F_NORMALIZED_URI;
+               }
        }
        else {
                /* usual schemes with or without authority, use origin form */