Subject: Is the response header "Upgrade: h2" allowed when TLS is used?

From: Michael Kaufmann <[email-masked]>
Date: Tue, 19 Apr 2016 16:16:34 +0200

Hi,

I have a question regarding the "Upgrade" header. The HTTP/2  
specification says:
> A server MUST ignore an "h2" token in an Upgrade header field.  
> Presence of a token with "h2" implies HTTP/2 over TLS, which is  
> instead negotiated as described in Section 3.3.

Does this imply that a server must not (or should not) send an  
"Upgrade: h2" response header to clients?

This question is important for Apache httpd, because version 2.4.20  
sends such an "Upgrade: h2" response header to clients that speak  
HTTP/1.x. Other HTTP/2 server software does not (e.g. nginx, Google's  
and Twitter's web servers).

Related Apache httpd issue:  
https://bz.apache.org/bugzilla/show_bug.cgi?id=59311

Regards,
Michael

---

From: Lucas Pardue <[email-masked]>
Date: Tue, 19 Apr 2016 15:16:15 +0000

Hi,

Michael Kaufmann wrote:
> I have a question regarding the "Upgrade" header. The HTTP/2 specification
> says:
> This question is important for Apache httpd, because version 2.4.20 sends
> such an "Upgrade: h2" response header to clients that speak HTTP/1.x. Other
> HTTP/2 server software does not (e.g. nginx, Google's and Twitter's web
> servers).
>

There seems to be some back story here that is not immediately exposed, I found it interesting to read through but to save others some effort take a look at [1], [2] and [3]. This seems to be a bug with how NodeJS (as a client) handles the Upgrade header, which has been fixed but may not be backported to older versions[4].

Stefan and Daniel point out that the server uses the Upgrade header to "advertise support" for h2. RFC 7230 Section 6.7 [5] states that the server MAY send the Upgrade header. It seems to me like Apache is technically compliant. On an https connection this information shouldn't be used to perform an HTTP upgrade to h2, since that is invalid (but a client issue not a server one). On an http connection the info could be used by the client e.g. they decide to negotiate an h2 session using ALPN.

Lucas

[1] https://bz.apache.org/bugzilla/show_bug.cgi?id=58971
[2] https://github.com/icing/mod_h2/issues/73
[3] https://github.com/nodejs/node/issues/4334
[4] https://github.com/nodejs/node/pull/4337
[5] http://tools.ietf.org/html/rfc7230#section-6.7

-----------------------------
http://www.bbc.co.uk
This e-mail (and any attachments) is confidential and
may contain personal views which are not the views of the BBC unless specifically stated.
If you have received it in
error, please delete it from your system.
Do not use, copy or disclose the
information in any way nor act in reliance on it and notify the sender
immediately.
Please note that the BBC monitors e-mails
sent or received.
Further communication will signify your consent to
this.
-----------------------------

---

From: Cory Benfield <[email-masked]>
Date: Tue, 19 Apr 2016 16:29:54 +0100

> On 19 Apr 2016, at 16:16, Lucas Pardue <[email-masked]> wrote:
> 

I don’t think that’s really a good way to read this section of RFC 7230. The first sentence in this section is 'The "Upgrade" header field is intended to provide a simple mechanism for transitioning from HTTP/1.1 to some other protocol on the same connection.’. Note that phrase “on the same connection”. I’d argue, based on that, that the server-sent Upgrade header should only list protocols that the server is willing to upgrade to *on that connection*.

If Apache is willing to do the HTTP/2 upgrade dance from a TLS’d HTTP1.1 connection, I’d say that this argument makes sense. Otherwise, I don’t think it does: the client needs to use a new connection, which means the Upgrade header isn’t appropriate. This use-case is satisfied by RFC 7838’s HTTP Alternative Services: Apache should use that header instead, rather than this Upgrade header.

For what it’s worth, as far as I can see RFC 7540 doesn’t *forbid* doing the Upgrade dance on a TLS’d HTTP/1.1 connection: it just says nothing about it. There’s no normative language in RFC 7540 that says that we MUST use ALPN for “https” URLs, it only says that clients *do* that. I’d say that you can read that as saying that a client MUST implement *at least* support for that, but may also implement support for alternative negotiation means (e.g. HTTP Upgrade).

Cory

---

From: Daniel Stenberg <[email-masked]>
Date: Tue, 19 Apr 2016 18:00:36 +0200 (CEST)

On Tue, 19 Apr 2016, Cory Benfield wrote:

>> Stefan and Daniel point out that the server uses the Upgrade header to 
>> "advertise support" for h2.
>
> I don’t think that’s really a good way to read this section of RFC 7230.

I'd like to point out that my argument in that report was when the client in 
question originally took the Upgrade: response header as some sort of 
instruction of what to do next, while I'm saying Upgrade: in a response header 
is only an advertisement for support - not an instruction to do anything. I 
said that disregarding the actual protocol it mentioned.

I think Upgrade: should probably say h2c as a h2 in there begs the question 
what that really means...

-- 

  / daniel.haxx.se

---

From: Stefan Eissing <[email-masked]>
Date: Tue, 19 Apr 2016 18:05:42 +0200

> Am 19.04.2016 um 17:29 schrieb Cory Benfield <[email-masked]>:
> 
> 

The mechanism is there and could be use. I do not know of a client which can though...

And rfc 7540, ch. 3.2 says: "A server MUST ignore an "h2" token in an Upgrade header field. Presence of a token with "h2" implies HTTP/2 over TLS, which is instead negotiated as described in Section 3.3."

Reading that, a server can never support this. So, we are in violation...rebels almost...

---

From: Cory Benfield <[email-masked]>
Date: Tue, 19 Apr 2016 17:07:53 +0100

> On 19 Apr 2016, at 17:05, Stefan Eissing <[email-masked]> wrote:
> 
>> 
> Reading that, a server can never support this. So, we are in violation...rebels almost…

Heh, I missed that. With that note, then, I’d say that Apache should stop putting h2 in the Upgrade header on a TLS-using connection *unless* it believes that connection is for a HTTP-schemed URL, when it should put h2c.

Cory

---

From: Cory Benfield <[email-masked]>
Date: Tue, 19 Apr 2016 17:17:27 +0100

> On 19 Apr 2016, at 17:07, Cory Benfield <[email-masked]> wrote:
> 

Sorry, even that’s not right, as Section 3.3 states:

> the "h2c" protocol identifier describes a protocol that does not use TLS.

I’d say that while RFC 7540 doesn’t *explicitly* have any normative language that says you can’t do this, it has statements that “h2c” is only for cleartext, that HTTP/2 over TLS uses “h2”, and that you can’t put “h2” in an upgrade header (only the last is normative). To me, that seems to add up to “no Upgrade header in TLS”.

Cory

---

From: Amos Jeffries <[email-masked]>
Date: Wed, 20 Apr 2016 04:30:41 +1200

On 20/04/2016 4:07 a.m., Cory Benfield wrote:
>>>> Stefan and Daniel point out that the server uses the Upgrade
>>>> header to "advertise support" for h2. RFC 7230 Section 6.7 [5]
>>>> states that the server MAY send the Upgrade header. It seems to
>>>> me like Apache is technically compliant. On an https connection
>>>> this information shouldn't be used to perform an HTTP upgrade
>>>> to h2, since that is invalid (but a client issue not a server
>>>> one). On an http connection the info could be used by the
>>>> client e.g. they decide to negotiate an h2 session using ALPN.
>>> 
>>> I don’t think that’s really a good way to read this section of
>>> RFC 7230. The first sentence in this section is 'The "Upgrade"
>>> header field is intended to provide a simple mechanism for
>>> transitioning from HTTP/1.1 to some other protocol on the same
>>> connection.’. Note that phrase “on the same connection”. I’d
>>> argue, based on that, that the server-sent Upgrade header should
>>> only list protocols that the server is willing to upgrade to *on
>>> that connection*.
>> 
>> And rfc 7540, ch. 3.2 says: "A server MUST ignore an "h2" token in
>> an Upgrade header field. Presence of a token with "h2" implies
>> HTTP/2 over TLS, which is instead negotiated as described in
>> Section 3.3."
>> 
> Heh, I missed that. With that note, then, I’d say that Apache should
> stop putting h2 in the Upgrade header on a TLS-using connection
> *unless* it believes that connection is for a HTTP-schemed URL, when
> it should put h2c.
> 
> Cory
> 

I think you are all overlooking the basic details:

* RFC 7540 does not govern HTTP/1.1 connections, even TLS ones.

* RFC 7540 section 3.2 is about negotiating http:// URLs over non-TLS
connections.

When the client has negotiated for HTTP/1.1 over TLS to happen (aka
HTTPS). It is appropriate to Upgrade:h2. Since RFC 723x applies.

But when the client negotiates h2c to happen. It is forbidden from using
Upgrade:h2 to get to h2. Avoiding the issues Upgrade would have with
only one-way encryption for the message pair.

Note that h2c is also forbidden from being used in ALPN by section 3.3.
So there is no valid way to be using TLS for h2c in the first place.

If one is already using h2 it is pointless to Upgrade:h2.

Amos

---

From: Willy Tarreau <[email-masked]>
Date: Tue, 19 Apr 2016 18:45:52 +0200

On Tue, Apr 19, 2016 at 06:00:36PM +0200, Daniel Stenberg wrote:
> >I don???t think that???s really a good way to read this section of RFC 7230.
> 
> instruction of what to do next, while I'm saying Upgrade: in a response
> header is only an advertisement for support - not an instruction to do
> anything. I said that disregarding the actual protocol it mentioned.

Absolutely, "101 Switching Protocol" would be needed here for an action to
take place.

> what that really means...

Well, we knew that having two names for the same thing would cause trouble :-)

Willy

---

From: Michael Kaufmann <[email-masked]>
Date: Tue, 19 Apr 2016 23:30:14 +0200

> If one is already using h2 it is pointless to Upgrade:h2.

I have just realized that RFC 7540 registers the "h2c" upgrade token  
(section 11.8) but it does NOT register an "h2" upgrade token. (it  
also registers the identification strings "h2" and "h2c" for ALPN in  
section 11.1, but that is a different thing)

The upgrade token "h2" probably does not exist according to the RFCs.  
It is also not mentioned here:  
http://www.iana.org/assignments/http-upgrade-tokens/http-upgrade-tokens.xhtml

So when using TLS, servers should not send "Upgrade: h2" because "h2"  
is an undefined upgrade token. And also when using TLS, servers should  
not send "Upgrade: h2c" because h2c means "HTTP/2 using cleartext  
TCP", and a connection that uses TLS cannot be "upgraded" to a  
connection that does not use TLS.

Do you agree...?

Regards,
Michael

---

From: Mike Bishop <[email-masked]>
Date: Tue, 19 Apr 2016 21:47:06 +0000

That would seem reasonable.  Hypothetically, a server could attempt RFC 2817-style Upgrade to TLS and then select "h2" as the ALPN token during the TLS handshake.  A logical flow for carrying the initial request into the h2 context would look a lot like that for h2c -- stream 1 is half-closed and already "contains" the client's initial request.  However, that still wouldn't use an "h2" Upgrade token and isn't actually defined anywhere.

"Upgrade: h2c" or offer Alt-Svc if you want to switch to HTTP/2 on a different, encrypted connection.

-----Original Message-----
From: Michael Kaufmann [mailto:[email-masked]] 
Sent: Tuesday, April 19, 2016 2:30 PM
To: [email-masked]
Subject: Re: Is the response header "Upgrade: h2" allowed when TLS is used?

> If one is already using h2 it is pointless to Upgrade:h2.

I have just realized that RFC 7540 registers the "h2c" upgrade token (section 11.8) but it does NOT register an "h2" upgrade token. (it also registers the identification strings "h2" and "h2c" for ALPN in section 11.1, but that is a different thing)

So when using TLS, servers should not send "Upgrade: h2" because "h2"  
is an undefined upgrade token. And also when using TLS, servers should not send "Upgrade: h2c" because h2c means "HTTP/2 using cleartext TCP", and a connection that uses TLS cannot be "upgraded" to a connection that does not use TLS.

Do you agree...?

Regards,
Michael

---

From: Stefan Eissing <[email-masked]>
Date: Wed, 20 Apr 2016 11:45:33 +0200

If backport gets the votes, next Apache httpd release will no longer carry h2 in an Upgrade: announcement. Thanks for your time.

-Stefan

PS. Which still does not make sense to me, but seems to be what the specification says...

> Am 19.04.2016 um 23:47 schrieb Mike Bishop <[email-masked]>:
>

---

From: Mike Bishop <[email-masked]>
Date: Wed, 20 Apr 2016 17:01:08 +0000

I agree with you that using different tokens for the same protocol is a bit silly -- when are we discussing a protocol at one layer versus when are we discussing a stack of protocols?  But that's the way the RFC fell.  

-----Original Message-----
From: Stefan Eissing [mailto:[email-masked]] 
Sent: Wednesday, April 20, 2016 2:46 AM
To: Mike Bishop <[email-masked]>
Cc: Michael Kaufmann <[email-masked]>; [email-masked]
Subject: Re: Is the response header "Upgrade: h2" allowed when TLS is used?

-Stefan

---

From: Stefan Eissing <[email-masked]>
Date: Wed, 20 Apr 2016 20:12:59 +0200

Thanks, Mike. I do not want to fight windmills. It is more an itch that our implementation reads a bit quirky in this case and cohesion ist lost. But you are right: it is what it is. 

> Am 20.04.2016 um 19:01 schrieb Mike Bishop <[email-masked]>:
>

---

From: "Roy T. Fielding" <[email-masked]>
Date: Wed, 20 Apr 2016 11:54:38 -0700

>>> HTTPS). It is appropriate to Upgrade:h2. Since RFC 723x applies.

It can be interpreted that way, yes.

>>> only one-way encryption for the message pair.

Well, no, h2 as a token presumes a completely secured TLS connection.  How you get
there is left up to the client and server to negotiate.

>>> So there is no valid way to be using TLS for h2c in the first place.

No, there is just no way defined by RFC7540. That doesn't mean it can't be implemented.
TLS is just a connection.

>> is an undefined upgrade token.

No, that presumes only registered tokens can be used.  HTTP/1.1 is not so limited.

>> And also when using TLS, servers should not send "Upgrade: h2c" because h2c means "HTTP/2 using cleartext TCP", and a connection that uses TLS cannot be "upgraded" to a connection that does not use TLS.

No, because TLS is just a connection.  For example, it might be opportunistic TLS.
Or it might be a configured tunnel over TLS or SSH of which the HTTP level is unaware.

It is better to think of the tokens as a protocol of their own, rather than a reference
to a specific protocol with the same name (or not, in the case of h2).  In other words,
the "h2c" token has a defined way of being interpreted that might result in some set of
actions by the recipient to reach some other state.  Likewise for h2, even though its
interpretation has not been defined by the registry.

BTW, if you send "Upgrade: HTTP/2.0, TLS", its interpretation is defined by RFC7230.

Cheers,

Roy T. Fielding                     <http://roy.gbiv.com/>
Senior Principal Scientist, Adobe   <http://www.adobe.com/>

---

From: Amos Jeffries <[email-masked]>
Date: Thu, 21 Apr 2016 22:50:55 +1200

> there is left up to the client and server to negotiate.

No right back. It is actively forbidden with a MUST ignore.

RFC 7540 section 3.1:
"
"

The loophole for 1.1 case is that RFC7230 is applicable. In this case
where RFC 7540 is applicable that MUST ignore comes into play and comes
down hard.

> TLS is just a connection.

It is actively forbidden with a MUST NOT. Which is very different from
"no way defined".

RFC 7540 section 3.3:
"
   ... The "h2c"
   protocol identifier MUST NOT be sent by a client or selected by a
   server; the "h2c" protocol identifier describes a protocol that does
   not use TLS.
"

> No, that presumes only registered tokens can be used.  HTTP/1.1 is not so limited.

Exactly my point in that earlier mail.

> Or it might be a configured tunnel over TLS or SSH of which the HTTP level is unaware.

RFC 7540 section 3.3 contradicts you. see above for the applicable quote.

Once you have h2 active you are forbidden from opportunistic downgrade
to the nsecure version of HTTP/2. Even using a secondary TLS layer with
h2c ALPN.

You can move to HTTP/1, HTTP/3, or non-HTTP. But not to h2c.

> interpretation has not been defined by the registry.

RFC 723x and prior describe things that are allowed and leave the rest
undefined. RFC 7540 breaks that pattern and actively prohibits things -
namely h2c in TLS and h2 outside it.

That makes it rather important to be aware of which RFC a previous
Upgrade or ALPN (if any) negotiated to be applicable. Because being
within 7540 scope limits what edge cases can be using for subsequent
sub-levels of transport.

> BTW, if you send "Upgrade: HTTP/2.0, TLS", its interpretation is defined by RFC7230.

What RFC defines HTTP/2.0 ?

RFC 7540 does not. It is clear on that.

At best you end up with TLS negotiated, which bring in the ALPN choice
between 'h2', 'http/1.1' or non-HTTP. Still not having reached h2c over TLS.

IIRC we had these arguments already when a group of us tried to prevent
the h2/h2c split. And still we are where we are.

Amos

---

From: "Roy T. Fielding" <[email-masked]>
Date: Thu, 21 Apr 2016 10:34:19 -0700

> On Apr 21, 2016, at 3:50 AM, Amos Jeffries <[email-masked]> wrote:
> 

No, we are talking about use of h2 in responses.  3.1 only applies to requests.

>   server; the "h2c" protocol identifier describes a protocol that does

Again, applies only to requests, not responses.

> You can move to HTTP/1, HTTP/3, or non-HTTP. But not to h2c.

Not relevant to this discussion.

> within 7540 scope limits what edge cases can be using for subsequent

No, that is completely irrelevant.  Upgrade is defined by HTTP/1.1.  Whatever
semantics are implied by h2c are defined by RFC7540 to the extent that it does
not contradict HTTP/1.1.  ALPN doesn't even apply -- only its namespace is being
foolishly reused here.  h2 is not defined as an Upgrade token, but its implications
as a protocol are pretty clearly defined by RFC7540 except for the bits that are
TLS-specific and limited to the browser paradigm.

If you look at the semantics, HTTP/2 over an opportunistic TLS connection is h2c,
not h2.  The connection just has another layer of special sauce.  HTTP/2 over a
properly negotiated and secured TLS connection is h2, even if an h2 client is
forbidden to requesting it in Upgrade.

The fact of the matter is that HTTP (both 1.1 and 2) can be used over any connection
regardless of how many layers of security are applied under that connection.
We can't make categorical statements about what h2 runs over.  RFC7540 doesn't
(or if it does, that's an errata), but what it does do is limit what kind of
upgrades an h2 client can request. This does not limit a server.

> What RFC defines HTTP/2.0 ?

http://tools.ietf.org/html/rfc7230#section-6.7

   This specification only defines the protocol name "HTTP" for use by
   the family of Hypertext Transfer Protocols, as defined by the HTTP
   version rules of Section 2.6 and future updates to this
   specification.  Additional tokens ought to be registered with IANA
   using the registration procedure defined in Section 8.6.

> the h2/h2c split. And still we are where we are.

It was never in scope for this working group.  These decisions were discussed in 1992,
implemented in 1993, reaffirmed in 1995-97 as RFCs, and have not been changed since.
For the same reason, what we call HTTP/2 is actually "HTTP/2.0" as far as HTTP/1.x is
concerned, regardless of anyone's opinions about the value of minor versions.
We can't adopt a legacy protocol's name and ignore the implications of its forward
versioning requirements.

....Roy