Subject: Re: Working Group Last Call: HTTP/2 revision

From: Willy Tarreau <[email-masked]>
Date: Wed, 1 Sep 2021 15:05:58 +0200

On Wed, Sep 01, 2021 at 08:35:49AM +0200, Julian Reschke wrote:
> > > 8.1.1
> > > 
> > > A malformed request or response is one that is an otherwise valid
> > > sequence of HTTP/2 frames but is invalid due to the presence of
> > > extraneous frames, prohibited fields or pseudo-header fields, the
> > > absence of mandatory fields or pseudo-header fields, the inclusion of
> > > uppercase field names, or invalid field names and/or values (in certain
> > > circumstances; see Section 8.2).
> > > 
> > > Q: are there any mandatory fields that are not pseudo-header fields?
> > 
> > We could say "Host" if there's no ":authority", but in general if we
> > defer to semantics the rules for validating a HEADERS frame, I find
> > it useful to just give rough examples of what could be covered by
> > the core spec.
> 
> Well, the way it's written had me wonder what HTTP fields can be
> mandatory now that control data is in pseudo fields.

Yes and that's definitely a difficulty in implementations :-/

> To call "Host"
> mandatoy is just confusing. Maybe the whole statement should be
> simplified somewhat.

It's possible, nothing comes to my mind right now. The reality is
that Host has become a second place for authority long ago but is
weaker than authority... In an ideal world we'd say that an authority
is mandatory and must be provided as :authority or Host (or both),
preferably :authority. But with the split inherited from H1 and the
need to pass H1 semantics to H2 it's not that easy.

> > > 8.2
> > > 
> > > To improve efficiency and interoperability, field names MUST be
> > > converted to lowercase when constructing an HTTP/2 message.
> > > 
> > > Q: I think this is somewhat misleading, it just provides the motivation
> > > why the lowercase format was introduced initially. I would just remove
> > > the sentence and potentially insert a note about lower-casing into the
> > > field name validity statements.
> > 
> > Or maybe just turn it like this:
> > 
> >    To improve efficiency and interoperability, HTTP/2 only uses lowercase
> >    header field names, so all field names MUST be converted to lowercase
> >    when constructing an HTTP/2 message.
> 
> That's somehat better. Maybe:
> 
> "To improve compression efficiency and interoperability, HTTP/2 uses
> lowercase field names only. All field names MUST be converted to
> lowercase when constructing an HTTP/2 message."
> 
> (note that also drops one instance of "header")

It reads better to me, +1 for this one!

Thanks,
willy

---

From: "Roy T. Fielding" <[email-masked]>
Date: Thu, 2 Sep 2021 10:47:58 -0700

> need to pass H1 semantics to H2 it's not that easy.

Well, it could have been that easy, but that's water under the bridge.

I think the rationale was to be able to send a request across versions

     h1   h2   h1
   A -> B -> C -> D

and have both h1 requests look the same by preserving Host instead
of :authority. Is that right? Maybe the spec should explain it.

Personally, I would have preferred that Host be deleted in passing
and :authority be used consistently to generate Host only when
forwarding to HTTP/1.1.

HTTP/1.1 (sec 3.2.2) requires that a message to a proxy be in absolute
form:

   When making a request to a proxy, other than a CONNECT or server-wide
   OPTIONS request (as detailed below), a client MUST send the target
   URI in _absolute-form_ as the request-target.

and that a received Host be replaced with the :authority

   A client MUST send a Host header field in an HTTP/1.1 request even if
   the request-target is in the absolute-form, since this allows the
   Host information to be forwarded through ancient HTTP/1.0 proxies
   that might not have implemented Host.

   When a proxy receives a request with an absolute-form of request-
   target, the proxy MUST ignore the received Host header field (if any)
   and instead replace it with the host information of the request-
   target.  A proxy that forwards such a request MUST generate a new
   Host field value based on the received request-target rather than
   forward the received Host field value.

   When an origin server receives a request with an absolute-form of
   request-target, the origin server MUST ignore the received Host
   header field (if any) and instead use the host information of the
   request-target.  Note that if the request-target does not have an
   authority component, an empty Host header field will be sent in this
   case.

Note that it does not place those requirements on intermediaries,
in general, because gateways/CDNs are allowed/expected to modify
the request target when it is being forwarded. 

http2bis 8.3.1 says:

   Clients that generate HTTP/2 requests directly SHOULD use the
   :authority pseudo-header field instead of the Host header field.

   An intermediary that forwards a request to HTTP/2 MUST construct
   an :authority pseudo-header field using the authority information
   from the control data in the original request. If control data
   does not contain authority, an intermediary MUST NOT add an
   :authority pseudo-header field. Note that while the Host header
   field can determine a request target, it is not control data
   for this purpose; see Section 7.2 of [HTTP].

Martin, I find that last sentence confusing, as is the requirement
here on "intermediary" (which would include both proxy and gateway).
Why would a gateway be prevented from changing :authority? Or are you
making a distinction here between "forwarding" a request unchanged
versus satisfying a request by accessing an internal resource?

   Request targets for CONNECT or asterisk-form OPTIONS requests
   do not include authority.

   An intermediary that forwards from HTTP/2 can construct a Host
   header field by copying the value of the :authority pseudo-header
   field. This might be necessary if that version requires that Host
   be included in a request, as HTTP/1.1 does for some forms of
   request target (see Section 3.2 of [HTTP11]).

   An intermediary that forwards a request to HTTP/2 MUST retain
   any Host header field, even if an authority is part of control
   data.

   The value of the Host header field MUST be ignored if control
   data contains authority (that is, the :authority pseudo-header
   field is present).

When an HTTP/1.1 request is received in absolute form (i.e.,
with the equivalent of :authority supplied), we require above
that Host be ignored and replaced with the absolute authority.
The last paragraph above requires to ignore (without saying who
is being required), but it doesn't require that Host be replaced
when forwarded by a proxy.

I would have written that as

   A recipient MUST ignore the Host header field in a request
   that contains an :authority pseudo-header field. If an
   intermediary forwards such a request via HTTP/1.1 without
   changing the request target, the intermediary MUST send
   the :authority pseudo-header field value as the Host field
   in the forwarded request (replacing any existing Host field)
   to avoid potential vulnerabilities in HTTP routing.

Is that something we should add now?

....Roy

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 3 Sep 2021 07:55:18 +0200

Hi Roy,

> of :authority. Is that right? Maybe the spec should explain it.

I'm fine with clarifying something for this. We already support various
combinations of host/authority in haproxy in order to preserve as much
as possible the origin vs absolute form for H1, with the exception that
H2->H1 always switches to origin form due to the ambiguity in H2 that
doesn't provide the distinction between the two forms. But it could be
stated that:
  - origin forms have either authority or host
  - absolute forms have both

And then this removes one ambiguity. However I'm pretty sure I've already
seen quite a bunch of H2 requests coming with both, probably because that
was easy to do for the client and that no distinction was ever proposed
between them.

> and :authority be used consistently to generate Host only when

That's what we currently do, indeed, since we do not have a reliable
signal to say "this H2 request was in absolute form, please do that
for H1 as well".

> HTTP/1.1 (sec 3.2.2) requires that a message to a proxy be in absolute

Yep.

>    that might not have implemented Host.

I'm seeing a concerning change here from 7230 that I did not notice
previously, the requirement that the Host value MUST be identical to
the authority:

   If the target URI includes an authority component, then a
   client MUST send a field-value for Host that is identical to that
   authority component.

This MUST combined with the last paragraph:

   A server MUST respond with a 400 (Bad Request) status code to any
   HTTP/1.1 request message that lacks a Host header field and to any
   request message that contains more than one Host header field or a
   Host header field with an invalid field-value.

allowed to reject requests with mismatching Host vs authority, which
is the basis of the security issues. And now I'm not seeing it that
clear anymore.

>    target.
>    A proxy that forwards such a request MUST generate a new

Sure but a number of intermediaries can be arbitrarily placed in
front of either proxies or servers. And a number of proxies nowadays
also support working as reverse-proxies since they can find all the
info they need in the request.

I suspect that by virtue of staying compatible with HTTP/1.0 we've
enumerated more real cases than really exist from the combinations
of authority vs host. Let me try to enumerate the type of requests
that come to my mind (and let's see which ones are forgotten, I'm
purposely leaving CONNECT aside):

  - HTTP/1.0 requests in origin form without Host. These ones are
    for a server and they provide no authority either. The server
    or intermediary that receives them may only rely on the target
    IP:port to figure what they were for.

  - HTTP/1.0 requests in absolute form without Host. These ones
    are explicitly for a proxy and do provide an authority. Any
    server or intermediary can figure the authority from the URI,
    so an intermediary may transform them to HTTP/1.1 on the other
    side and provide an matching Host header field if needed (e.g.
    a proxy may coalesce them on a reused idle HTTP/1.1 connection
    to the same host).

  - HTTP/1.0 requests in origin form with Host. A Host information
    was provided and the recipient is expected to use that as the
    only source of authority if needed in order to determine a
    virtual host. These ones are equivalent to HTTP/1.1 ones it seems.

  - HTTP/1.0 requests in absolute form with Host. These ones are
    equivalent to HTTP/1.1 ones it seems, with the possible exception
    that since Host was not mandatory in 1.0, Host is not guaranted
    to convery valid information, thus it makes sense to prefer the
    authority part from the URI and ignore Host.

  - HTTP/1.1 requests in origin form with empty Host. Host is present
    but has no value, similar to HTTP/1.0 above. The recipient will
    use connection-level info if desired.

  - HTTP/1.1 requests in origin form with non-empty Host. The recipient
    must use the Host field value as the sole source of authority info.

  - HTTP/1.1 requests in absolute form with empty Host. These are
    similar to HTTP/1.0 absolute requests. The authority is extracted
    from the URI.

  - HTTP/1.1 requests in absolute form with non-empty Host. This one
    was produced by an HTTP/1.1 agent which complies with 7230, so
    Host MUST match the authority in the URI. Any of them may be used
    as the source of authority, and either of them may be dropped along
    the path without losing information.

  - HTTP/2 requests with no :authority and no Host. These are not valid.

  - HTTP/2 requests with no :authority and empty Host. These are similar
    to HTTP/1.0 absolute above.

  - HTTP/2 requests with no :authority and non-empty Host. These are
    similar to HTTP/1.1 origin + Host above. Authority comes from Host.

  - HTTP/2 requests with :authority and no Host. They're similar to
    HTTP/1.0 in absolute form (i.e. authority comes from :authority)

  - HTTP/2 requests with :authority and empty Host. Same as the if
    ther is no Host.

  - HTTP/2 requests with :authority and non-empty Host. These are
    similar to HTTP/1.1 absolute + Host above. The sender had to respect
    the "MUST be identical" rule, so either can be used and the other one
    dropped. Note that they're pretty common in field from what I've seen
    (no numbers), which is a difference compared to HTTP/1.1.

As such it seems to me that these are the situations we are trying
to cover:
  - HTTP/1.0 in origin form and no (or empty) Host, HTTP/1.1 in
    origin form and empty Host, HTTP/2 with no authority and empty
    Host
    => authority unknown, decide based on the connection-level info

  - HTTP/{1.0,1.1} in origin form and valid Host, HTTP/2 with no
    :authority but valid Host
    => authority comes from Host

  - HTTP/1.0 in absolute form with or without Host
    => authority comes from URI (host ignored)

  - HTTP/1.1 in absolute form with empty or non-empty Host
    => authority comes from URI (host ignored)

  - HTTP/2 with both :authority and Host
    => authority comes from :authority (Host ignored)

And in any case, with HTTP/1.1 and above, a non-empty Host must
always match any provided authority.

Once we have this rules in place, an intermediary can safely convert
any version into any other one and emit the outgoing request according
to the target protocol's preferred conventions. It's up to the
intermediary to decide whether to emit an HTTP/1.x request as an
origin request or an absolute one depending on configuration or
hints (in haproxy we try to mimmick what we've observed on the other
side, but I would also find it normal to have it by configuration
since the notion of proxy on the next hop is very connection-centric).

> versus satisfying a request by accessing an internal resource?

I participated to this one, the cases where the intermediary changed
the contents was not thought about there, the focus was on where to
pick the original information. But this is another example of how bad
it is to discuss about protocol translations, which is the job of the
component itself, we ought to be very careful to only explain where
to find the required information in the protocol and where to place
it. The choice of transforming that information should be left to the
implementation, it's not our business.

That's why I really think that we need to solve this ambiguity around
the notion of "authority". In HTTP/1.1 Host definitely conveys such an
authority. Once we can make it clear that there is AT MOST one authority
in a request (possibly from multiple sources which must match), then
it's each protocol's business to indicate where to emit the authority,
regardless of where it was found or how it was transformed.

> when forwarded by a proxy.

This is the exact same problem as above, we should not discuss about
transforming but about receiving and sending only.

> Is that something we should add now?

I'd rather proceed differently then, we explain how to extract
that authority info for the protocol itself without precluding
anything about other protocols, then we provide non-normative
examples of what an intermediary would do, e.g. when forwarding
such a request to H2, to an H1 server or to an H1 proxy. This
will illustrate the purpose of the rules, will attract attention
on certain delicate corner cases but will be more useful, because
it's impossible to enumerate all combinations of other protocols
in the spec itself.

Regards,
Willy

---

From: "Martin Thomson" <[email-masked]>
Date: Mon, 06 Sep 2021 15:20:54 +1000

>   - absolute forms have both

In effect, RFC 7540 says:

1. If :authority is present, servers must use :authority and ignore Host.
2. Servers use Host if :authority is absent.
3. Intermediaries must preserve Host when forwarding requests.
4. Clients are encouraged to generate requests with :authority, but can use Host if they choose.

Roy's theory about h2->h1->h2 being the reason for (3) is correct, at least as far as I can remember (something I've confirmed with others who were there).  My *opinion* is that this is a poor design for a few reasons, but mostly because Host and :authority can disagree.

Is this really so bad that it needs to be fixed?

I've seen a few amendments suggested in this thread (and others):

a. We might make :authority mandatory in more cases.
b. We might prohibit inclusion of both values.
c. We might prohibit different values if the fields are both present.
d. We might avoid levying requirements on intermediaries that forward requests.

We are limited in what we can do here because we can't require anything that might result in an unmodified RFC 7540 implementation producing fatal errors.  For instance, we might insist that clients not include both fields, but we can't require servers to enforce that.  That limits the value of any change.

The best option I've seen for any change is to remove the bit where we mandate that Host is preserved when forwarding.  We might further recommend against including Host in favour of :authority.  That's in direct contradiction to decisions in RFC 7540, but I think we have some support for the notion that we made a mistake in mandating that Host be preserved.

Doing nothing is also OK.  It is a invalid to route on Host when :authority exists; that some implementations might do that is not something we are obligated to fix in the specification.  Were this a new protocol, with fewer constraints that might affect interoperability, choosing the safest option would be best, but we're talking about a very narrow update.

This sort of issue is exactly the sort of thing that our initial scoping exercise was designed to avoid.  There are lots of decisions in RFC 7540 that we might second guess; we deliberately chose not to redo all of them.  And yet here we are.

Cheers,
Martin

p.s., the editor's draft contains a revision to the text in question, that I think greatly improves its clarity; see https://httpwg.org/http2-spec/draft-ietf-httpbis-http2bis.html#section-8.3.1-2.3.1

---

From: Willy Tarreau <[email-masked]>
Date: Mon, 6 Sep 2021 18:47:48 +0200

On Mon, Sep 06, 2021 at 03:20:54PM +1000, Martin Thomson wrote:
> Roy's theory about h2->h1->h2 being the reason for (3) is correct, at least
> as far as I can remember (something I've confirmed with others who were
> there).

I agree with this, but this doesn't require that the contents of an invalid
Host are preserved, just that the field's presence is preserved.

>  My *opinion* is that this is a poor design for a few reasons, but
> mostly because Host and :authority can disagree.

But the problem in the first place is: "why and when could Host and authority
disagree" ? There's a MUST in the specs regarding their equality when producing
them. I don't know if you've seen my other message in this mail where I
enumerated the different combinations, I think it's a good start to verify
how we could end up with this problem in the first place.

> Is this really so bad that it needs to be fixed?

It's trivially exploitable, especially in dual-stack servers and/or
intermediaries. At some point the admin must configure a rule to decide
what to serve. Usually it's host-based (by heritage from H1 which almost
did not use authority in servers). When two agents along the chain use a
different one to route the request, some requests get misrouted to the
wrong server, and this can have really bad effects, such as permitting
access to private resources that were expected to be blocked at the edge.

> We are limited in what we can do here because we can't require anything that
> might result in an unmodified RFC 7540 implementation producing fatal errors.
> For instance, we might insist that clients not include both fields, but we
> can't require servers to enforce that.  That limits the value of any change.

I think that the least impacting one would be C. We still failed to enumerate
a single valid case for mismatching host and authority and this is the basis
of the problem. If we decide that when set and non-empty both must match,
in my opinion the problem is solved.

> The best option I've seen for any change is to remove the bit where we
> mandate that Host is preserved when forwarding.  We might further recommend
> against including Host in favour of :authority.  That's in direct
> contradiction to decisions in RFC 7540, but I think we have some support for
> the notion that we made a mistake in mandating that Host be preserved.
> 
> Doing nothing is also OK.  It is a invalid to route on Host when :authority
> exists; that some implementations might do that is not something we are
> obligated to fix in the specification.

It's particularly dangerous once converted to HTTP/1, because there you're
certain that Host will be used and not all implementations will check that
the authority matches it.

> This sort of issue is exactly the sort of thing that our initial scoping
> exercise was designed to avoid.  There are lots of decisions in RFC 7540 that
> we might second guess; we deliberately chose not to redo all of them.  And
> yet here we are.

I know, but if there are bugs that are causing vulnerabilities in field,
we should address them.

Maybe the very least we could do is to add:

   An implementation which detects that Host and :authority do not match
   MAY reject the request as malformed.

We don't take risks and I'm sure most of us will instantly enforce this.
I'd personally prefer a SHOULD or even a MUST (to match what is inferred
by the two rules in H1 that state that they must be produced identical
and that non-conformant ones must be dropped) but at least with this we
can all plug this hole.

> p.s., the editor's draft contains a revision to the text in question, that I
> think greatly improves its clarity; see
> https://httpwg.org/http2-spec/draft-ietf-httpbis-http2bis.html#section-8.3.1-2.3.1

Sure, I definitely agree, it's just that the "must retain" rule maintains
the ambiguity regarding non-matching values.

Cheers,
willy

---

From: Greg Wilkins <[email-masked]>
Date: Tue, 7 Sep 2021 10:46:09 +1000

On Tue, 7 Sept 2021 at 02:50, Willy Tarreau <[email-masked]> wrote:

> I think that the least impacting one would be C.
>

+1

> I'd personally prefer a SHOULD or even a MUST

I'd also be OK with a MUST here.

For my own code base, the code that will be determining if a Host header
should be passed onto the next layer up has no idea if it is going to be
proxied or not.  So even if passing on a mismatched Host header was OK for
a proxy, I'd have trouble enforcing that and no matter what it would end up
as some extra expense for the 99.999% of requests that are not like that.

If we have a "MUST be the same", then it is simple for an impl to check if
Host header is the same as the :authority (which must have already been
parsed) and the treat as malformed if different.  If it is the same, then
it is safe to pass on to the next layer regardless of how it is going to be
used.

-- 
Greg Wilkins <[email-masked]> CTO http://webtide.com

---

From: Stefan Eissing <[email-masked]>
Date: Tue, 7 Sep 2021 10:19:31 +0200

> Am 06.09.2021 um 18:47 schrieb Willy Tarreau <[email-masked]>:
> 

Current Apache httpd h2 implementation:
- if :authority: is given, it overrides host: before internal processing starts
- if :authority: is missing, host: is used instead
- if both are missing, request is rejected

Proposal C would mean we check before overwrite and fail if a 'different' value is present. We then need to define 'different'. It seems quite common in the h1 world to be forgiving on port numbers, afaik. And how we match names...

After living for 5 years with the current implementation, I am not sure I'd like to change it.

- Stefan

---

From: Willy Tarreau <[email-masked]>
Date: Tue, 7 Sep 2021 10:29:53 +0200

Hi Stefan,

> - if both are missing, request is rejected
 
That's what we currently do in haproxy as well.

> Proposal C would mean we check before overwrite and fail if a 'different'
> value is present. We then need to define 'different'. It seems quite common
> in the h1 world to be forgiving on port numbers, afaik. And how we match
> names...

Yep absolutely, that's the scheme-based URI normalization from RFC3986
IIRC that we're already applying to H1.

> After living for 5 years with the current implementation, I am not sure I'd

I can understand, however, when working as a proxy, what do you pass to the
backend server ? I guess you're passing the only value you kept (i.e.
:authority when present), not two possibly different values ?

I'm OK with that (and that's what we're currently doing as well), but the
rule saying that Host must be preserved contradicts this as is suggests
the original one must be retained. I'm fine with retaining the header field
as a copy of :authority on output if it was present in order to address the
h2->h1->h2 case.

What I want to be certain of is that we do not make it possible for an
intermediary to pass mismatching host and :authority by default on output
and that the value emitted matches what was used internally.

Willy

---

From: Greg Wilkins <[email-masked]>
Date: Wed, 8 Sep 2021 08:44:04 +1000

On Tue, 7 Sept 2021 at 18:32, Willy Tarreau <[email-masked]> wrote:

> > After living for 5 years with the current implementation, I am not sure
> I'd
> > like to change it.
>

 Currently RFC7540 says:

      An intermediary that converts an HTTP/2 request to HTTP/1.1 MUST
      create a Host header field if one is not present in a request by
      copying the value of the ":authority" pseudo-header field.

So that is kind of a loophole as it says that a proxy must use the
:authority only if a Host header is not present.
If a Host header is present, but has been ignored due to the presence of an
:authority header, then a proxy may decide to act based on the :authority,
but send a request using a host header with a differing value that it had
previously ignored.

I think we can clarify this without making significant (any?) changes in
behavior. I'd expect that most implementations would not need to change as
they are likely to pass only a single value to the layer that does the
proxying, but it would be good if the spec could back them up by saying
that rewriting a host header is the correct thing to do when acting as an
intermediary converting to HTTP/1.   There may be some impls that decide to
proxy based on the :authority, but then just copy over a different existing
Host header, and I think such impls probably should change as that feels
like tunneling misinformation.

How about something like:

An intermediary that converts an HTTP/2 request to HTTP/1.1 MUST include a
Host header field in a request, using the value of the ":authority"
pseudo-header field if available or the received Host header otherwise.

I'd also be OK with making it just a SHOULD use the :authority if there is
a use-case for not doing so?

cheers

---

From: Willy Tarreau <[email-masked]>
Date: Wed, 8 Sep 2021 08:46:04 +0200

Hi Greg,

> but send a request using a host header with a differing value that it had

Exactly.

> pseudo-header field if available or the received Host header otherwise.

We really must stop speaking about HTTP/1.1 in the HTTP/2 spec. There's
no way multi-protocol implementations are aware of what the other side
will use. Sometimes you even receive an H2 request, try to pass it over
H2 then fall back to H1 (due to ALPN for example), or various other
situations. That's why we really must focus on translating bytes to
semantics and use semantics only. Semantics govern how HTTP must be
interpreted and what each field (or combination of) means. Messaging
explains how to recover the elements from the wire representation. We
must really stick to this otherwise we'll continue such mistakes that
do have consequences.

But my question still holds: are we aware of any valid case for Host
and :authority to mismatch ? I've enumerated a dozen of combinations
earlier, none of which would result in this, and yet we're trying hard
to make sure we can continue to forward attacks unmolested to the next
hop. This is a real problem :-/

Regards,
Willy

---

From: Stefan Eissing <[email-masked]>
Date: Wed, 8 Sep 2021 10:04:09 +0200

> Am 08.09.2021 um 08:46 schrieb Willy Tarreau <[email-masked]>:
> 
> Hi Greg,
> 

+1

I heard also that there is yet another HTTP version in the works.

> hop. This is a real problem :-/

Not aware: that such requests are made against Apache httpd
Aware: that we flatten them an no one has complained.

FWIW, from our perspective, no mixed authority/host requests are
expected to work, other than given the response based on authority alone.

That is of course only a very limited sample.

Cheers,
Stefan

---

From: Willy Tarreau <[email-masked]>
Date: Wed, 8 Sep 2021 10:49:15 +0200

Hi Stefan,

> I heard also that there is yet another HTTP version in the works.

I think I heard something about this as well :-)

> Aware: that we flatten them an no one has complained.

Same here. We did enforce the matching between Host and authority in H1
a while ago and nobody reported any issue. We enforced it recently on H2
as well and the only failure we got was a :443 port that was left after
the host, until we refined this to apply RFC3986's scheme-based
normalization to drop the port. Of course I'd rather define something
solid than have to compare strings.

> That is of course only a very limited sample.

Let's assemble many limited samples until we have enough :-)

Thanks,
Willy

---

From: Greg Wilkins <[email-masked]>
Date: Fri, 10 Sep 2021 10:25:29 +1000

> in a request (possibly from multiple sources which must match),
>

+1

For my implementation, we convert the request from both h1, h2 (and h3)
into a canonical form that we pass to the layer above, which may or may not
be an intermediary/proxy.   The layer doing the canonicalization has no
idea if it is an intermediary or not.   Thus I don't think we should have
language that makes :authority/host handling conditional on being an
intermediary or not - as that would require my canonical form to carry two
authorities and for the layer above to make a choice about which one it
used, depending on if it is a proxy or not.

So I definitely support language that indicates that there is "AT MOST one
authority"  that can be transported via a :authority and/or host header.

> anything about other protocols,
>

Exactly.   The proposed text is about how intermediaries should work, which
presupposes that both copies of the authority will be made available to the
code that is the intermediary.     I think the text achieves the goal, but
hints at a less than optimal implementation.

I'd much rather that we define how an impl determines THE authority and
then define how an intermediary can send THE authority in Host and/or
:authority headers.

cheers

PS.  Yes - I know I posted a proposal earlier that was based on defining
intermediary language.... but I like the emphasis suggested by Highlander
Willy that there can only be one!

---

From: Ryan Hamilton <[email-masked]>
Date: Thu, 9 Sep 2021 20:17:57 -0700

> then define how an intermediary can send THE authority in Host and/or

Hear! Hear! Totally agree.

---

From: "Martin Thomson" <[email-masked]>
Date: Tue, 14 Sep 2021 13:27:08 +1000

Having taken some time to look at the discussion here and on GitHub, I think that - chairs willing - there seems to be a lot of support for a change here.

I've based this work on Roy's suggestion, but that is not sufficient on its own to be complete and consistent, so there are several changes:

1. clients MUST NOT generate a request where Host and :authority differ

  --> This was always implied, but was originally left out deliberately to allow for divergence.  That's no longer the case.

2. servers MAY treat requests as malformed if Host and :authority are different

  --> I don't think this needs to be stronger.  Servers that do check create enough incentive for clients not to do the wrong thing.  It also means that servers that allow for the difference for the same reasons behind the original text won't technically be non-compliant, even if their clients are.

3. Intermediaries MUST overwrite Host with :authority when forwarding to HTTP/1.1 (as Roy suggested)

   --> I'm a little reluctant to talk about HTTP/1.1 behaviour, but I think that this is warranted in this case.

4. An intermediary that forwards a request over HTTP/2 MAY retain any Host header field.

 --> This used to say "MUST", but in order to be consistent with the rest of this text I think we can leave this as optional.  I'm less sure about this as it leaves unsaid whether Host can be inconsistent in this case (point 1 only talks about generating requests, not forwarding them).  The reason to keep this is to permit legacy (RFC 7540) behaviour.

We already say to ignore Host if :authority is present, which is the most important protection.

Pull request: https://github.com/httpwg/http2-spec/pull/968