Subject: Re: [hybi] Fwd: New Version Notification for draft-mcmanus-httpbis-h2-websockets-01.txt

From: Patrick McManus <[email-masked]>
Date: Thu, 2 Nov 2017 12:27:04 +0100

Here are my plans for -02

* websockets parameters (sub-protocol, etc..) into h2 HEADERS

* define the websockets target URI scheme for CONNECT as being http/https
when using 6455. I actually think this is a change from 6455 but I can live
with it if its defined. Mostly the concern is around legacy pac.

* stick with CONNECT as the method for the moment.. my thinking here is
that CONNECT is already a strange beast with special code doing almost what
we want here anyhow.. replicating that is not great. I would rather have a
pseudo header be the version specific thing (which is expected of pseudo
headers) than adding a new method which is typically not.

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 3 Nov 2017 06:55:03 +0100

> headers) than adding a new method which is typically not.

+1 on all this!

Willy

---

From: Kazuho Oku <[email-masked]>
Date: Thu, 9 Nov 2017 11:41:22 +0900

Hi Patrick,

2017-11-02 20:27 GMT+09:00 Patrick McManus <[email-masked]>:
> * stick with CONNECT as the method for the moment.. my thinking here is that
> CONNECT is already a strange beast with special code doing almost what we
> want here anyhow.. replicating that is not great. I would rather have a

This might be unnecessary, but I wonder if it's worth adjusting the
approach so that it could be converted into a generic way of upgrading
an HTTP/2 stream into a bidirectional channel.

To me it seems that we are removing almost all features specific to
WebSocket. The only things remain specific are:

* use of `CONNECT` as the method; having one method would be fine for
websocket (since it only allows the use of `GET`), but that is not
case for other upgrades (e.g. HTTP/2)
* not using 101 as a way to notify protocol change
* no way to specify which non-end-to-end header is required for
upgrade (e.g. HTTP2-Settings)

If we adjust the three issues (for example, by allowing any method to
be specified, and use the existence of :upgrade: pseudo header as a
signal; use 101 to signal protocol change; use attributes of :upgrade:
header to specify additional headers required for the upgrade), I
think the approach would become generic.

For example, creating a websocket tunnel through HTTP/2 would look like below.

Request:
  :method: GET
  :scheme: https
  :authority: server.example.com
  :path: /chat
  :upgrade: websocket
  sec-websocket-key: dGhlIHNhbXBsZSBub25jZQ==
  origin: http://example.com
  sec-websocket-protocol: chat, superchat
  sec-websocket-version: 13
  <data is same as RFC6455>

Response:
  :status: 101
  :upgrade: websocket
  sec-websocket-accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  sec-websocket-protocol: chat
  <data is same as RFC6455>

Or in case of upgrading an HTTP/2 stream to HTTP/2 connection:

Request:
  :method: GET
  :scheme: http
  :authority: server.example.com
  :path: /
  :upgrade: h2c; http2-settings
  http2-settings: <base64url encoding of HTTP/2 SETTINGS payload>
  <may contain request body>

Response:
  :status: 101
  :upgrade: h2c
  <data is same as RFC7540>

In this example, the :upgrade: pseudo header is used to convey the
connection headers required for upgrade (as the following elements of
the header field value), so that a reverse proxy receiving the request
could convert it to an HTTP/1 request with a "Connection: upgrade,
http2-settings" header.

The approach might be overly generic than what we need. OTOH, having a
generic mechanism help us, because we would no longer need to have any
protocol-specific code in a reverse proxy. Hence asking about the
possibility.

-- 
Kazuho Oku

---

From: Wenbo Zhu <[email-masked]>
Date: Thu, 9 Nov 2017 13:42:06 -0800

I think this is a great proposal, defining a generic tunneling mechanism
and treating websocket as a special case, with handshake/payload a concern
for Web APIs and frameworks.

Two more notes:
1. as I understand, RFC7540 only states that early 2xx responses are
allowed, i.e. when response headers are generated, any pending request body
becomes insignificant. Therefore the explicit tunneling mechanism is
necessary to signal to proxies/frameworks that a full-duplex byte-stream is
being tunneled as a http/2 stream.
2. with such a generic tunneling mechanism, any TCP based L7 protocol can
be multiplexed over http2, and the explicit handshake (semantically an
inbound RPC)  allows metadata to be exchanged as http/2 headers v.s. using
transport (TCP) headers.

On Wed, Nov 8, 2017 at 6:41 PM, Kazuho Oku <[email-masked]> wrote:

> that
"HTTP/2 sub-connection"  ?

> _______________________________________________
> hybi mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/hybi
>

---

From: Kari Hurtta <[email-masked]>
Date: Fri, 10 Nov 2017 07:49:57 +0200 (EET)

> being tunneled as a http/2 stream.

Yes, full-duplex byte-stream is quote different from http request-response model.

8.1.  HTTP Request/Response Exchange
https://tools.ietf.org/html/rfc7540#section-8.1

states

-------------

   An HTTP request/response exchange fully consumes a single stream.  A
   request starts with the HEADERS frame that puts the stream into an
   "open" state.  The request ends with a frame bearing END_STREAM,
   which causes the stream to become "half-closed (local)" for the
   client and "half-closed (remote)" for the server.  A response starts
   with a HEADERS frame and ends with a frame bearing END_STREAM, which
   places the stream in the "closed" state.

   An HTTP response is complete after the server sends -- or the client
   receives -- a frame with the END_STREAM flag set (including any
   CONTINUATION frames needed to complete a header block).  A server can
   send a complete response prior to the client sending an entire
   request if the response does not depend on any portion of the request
   that has not been sent and received.  When this is true, a server MAY
   request that the client abort transmission of a request without error
   by sending a RST_STREAM with an error code of NO_ERROR after sending
   a complete response (i.e., a frame with the END_STREAM flag).
   Clients MUST NOT discard responses as a result of receiving such a
   RST_STREAM, though clients can always discard responses at their
   discretion for other reasons.

--------------

/ Kari Hurtta

---

From: Kari Hurtta <[email-masked]>
Date: Fri, 10 Nov 2017 08:14:53 +0200 (EET)

> Therefore the explicit tunneling mechanism is

>>   :authority: server.example.com

8.1.2.1.  Pseudo-Header Fields
https://tools.ietf.org/html/rfc7540#section-8.1.2.1

states

--------

   Pseudo-header fields are only valid in the context in which they are
   defined.  Pseudo-header fields defined for requests MUST NOT appear
   in responses; pseudo-header fields defined for responses MUST NOT
   appear in requests.  Pseudo-header fields MUST NOT appear in
   trailers.  Endpoints MUST treat a request or response that contains
   undefined or invalid pseudo-header fields as malformed
   (Section 8.1.2.6).

--------

Then that ":upgrade" works as explicit tunneling mechanism, IF you can trust
that response is treated as mailformed (stream error of type PROTOCOL_ERROR)
when proxies/frameworks does not subscribe that tunneling mechanism.

Can you trust that?

/ Kari Hurtta

---

From: Andy Green <[email-masked]>
Date: Fri, 10 Nov 2017 14:50:32 +0800

> when proxies/frameworks does not subscribe that tunneling mechanism.

How about putting a gratuitous

content-length: 0

in one side's headers or both?  If somebody doesn't really understand 
what the endpoints are up to and continues along normal h2 lines, they 
should fail the stream on the first DATA turning up then.

-Andy

---

From: Kazuho Oku <[email-masked]>
Date: Fri, 10 Nov 2017 16:05:20 +0900

> when proxies/frameworks does not subscribe that tunneling mechanism.

Note that we would be doing negotiation beforehand using a SETTINGS
parameter. Otherwise, a client cannot send a request with `:upgrade:`
pseudo header.

I believe that a successful negotiation would be sufficient to
guarantee that the `:upgrade:` response header will be recognized as a
signal (or a 101 status code as a signal).

-- 
Kazuho Oku

---

From: Takeshi Yoshino <[email-masked]>
Date: Sat, 11 Nov 2017 01:51:05 +0900

> signal (or a 101 status code as a signal).

Agreed with Kazuho about this point.

Depending on such detection of a spec violation is interesting, but I think
we shouldn't. It's fragile. We're already clearing the path between the
endpoints by the forwarding of the SETTINGS. That should be enough, yes.

I'd rather suggest that we don't violate RFC 7540. As we'll be using a
pseudo header, we anyway need special logic to generate/parse it. So, it
doesn't matter so much whether the name is "upgrade" or not. We could e.g.
use ":upgrade", and ":upgrade-response" in each direction respectively.

I listed a few more points below that I think we should get consensus. But
basically, I like the direction (use of HEADERS, signaling by SETTINGS).

----

I remember that we had some discussion about this kind of usage of the
SETTINGS frame mechanism with H2 connection bundling/un-bundling proxies on
the way, when we were discussing Yutaka's WS/H2 I-D
<https://tools.ietf.org/html/draft-hirano-httpbis-websocket-over-http2-01> 3
y ago.

Suppose that we have a reverse proxy that has a single H2 connection with a
browser which is unbundled by the proxy to different target backends (still
H2 but with fewer streams) where some backends can handle tunneling, but
some cannot. The proxy needs to either:
- forward the SETTINGS to the browser to allow it to use tunneling, but
convert tunneling requests to the tunneling/H2 non-capable backends to one
over H1
- don't forward the SETTINGS as not all backends are capable of it

As SETTINGS is about hop-by-hop characteristics, we need to consolidate the
bit as above at bundling points. I think this is fine and we don't want to
introduce any complicated mechanism to perform e2e negotiation. Do you all
agree on this?

----

In Yutaka's I-D, we tried to allow speculatively initiating a WebSocket
over H2 without waiting for capability announcement by the server. In
Patrick's proposal, we instead wait for the SETTINGS frame before
initiating a WebSocket.

How and when should a browser determine whether a certain H2 connection is
tunneling capable or not?

What's in my mind is:

- When there's no connection at all,
  - ws: RFC 6455 connection attempt is made on TCP but with the
HTTP2-Settings header in addition to the standard RFC 6455 headers, which
might get upgraded to WS/H2 (indicated by e.g. an additional header, or a
special Upgrade value "websocket-h2c") or just proceed as RFC 6455
  - wss: Use ALPN? See
https://tools.ietf.org/html/draft-hirano-httpbis-websocket-over-http2-01#section-3.4
- When there's ongoing H2 connection establishing, the browser waits until
the initial SETTINGS is received following the preface. If there
isn't ENABLE_CONNECT_PROTOCOL, the browser should use WS/TCP. Otherwise, it
should use WS/H2.
- When there's existing H2 connection established, if there wasn't
ENABLE_CONNECT_PROTOCOL in the initial SETTINGS from the server, use
WS/TCP, and WS/H2 otherwise.

Initially, I thought it might be fine to assume that there's existing H2
when WS attempt is made, but it's not always true, I guess.

----

Is it ok that we cannot announce tunneling capability for each protocol
separately? draft-hirano-httpbis-websocket-over-http2-01 allocated a
dedicated SETTINGS identifier for WebSockets
while draft-hirano-httpbis-websocket-over-http2-00 let each bit indicate
capability for each protocol.

In Patrick's proposal, currently we just determine tunneling yes/no and
then everything beyond is upper layer protocol's work.

I think this is fine if this tunneling is made completely independent from
the upper layer protocol.

---

From: Kari Hurtta <[email-masked]>
Date: Fri, 10 Nov 2017 18:50:11 +0200 (EET)

Kazuho Oku <[email-masked]>: (Fri Nov 10 09:05:20 2017)
> signal (or a 101 status code as a signal).

Yes, 

5.5.  Extending HTTP/2
https://tools.ietf.org/html/rfc7540#section-5.5

-----------------
"   Extensions that could change the semantics of existing protocol
"   components MUST be negotiated before being used. 
-----------------

seems imply that :upgrade pseudo header maut be negotiated.

SETTINGS is one possibility:

----------------
"   This document doesn't mandate a specific method for negotiating the
"   use of an extension but notes that a setting (Section 6.5.2) could be
"   used for that purpose. 
----------------

Seems that existing of new pseudo header itself can not be used as
negotiation:

----------------
"   Extensions are permitted to use new frame types (Section 4.1), new
"   settings (Section 6.5.2), or new error codes (Section 7).  Registries
"   are established for managing these extension points: frame types
"   (Section 11.2), settings (Section 11.3), and error codes
"   (Section 11.4).
"
"   Implementations MUST ignore unknown or unsupported values in all
"   extensible protocol elements.  
----------------

/ Kari Hurtta

---

From: Takeshi Yoshino <[email-masked]>
Date: Sat, 11 Nov 2017 02:28:08 +0900

On Sun, Oct 29, 2017 at 10:17 AM, Patrick McManus <[email-masked]>
wrote:

> +Alexey
>
> That interpretation doesn't seem consistent with RFC-6455, section 4.1
>> regarding absolute form (copied below).
>>
>> *The "Request-URI" part of the request MUST match the /resource*
>> *        name/ defined in Section 3 (a relative URI) or be an absolute*
>> *        http/https URI that, when parsed, has a /resource name/, /host/,*
>> *        and /port/ that match the corresponding ws/wss URI.*
>>
>
> That's really interesting and is the only argument I find at all
> compelling.Thanks.  Basically its saying that the URI of the GET is not the
> websockets URI used in other places such as the cache and the pac. Which is
> totally insane imo.
>

This change happened on draft-ietf-hybi-thewebsocketprotocol-11. I see some
arguments saying that it's HTTP/HTTPS until it's upgraded, in some
discussion about DNS SRV and cookie handling, which happened before
publication of -11. But not sure which of them is really the motivation of
the change.

---

From: Kari Hurtta <[email-masked]>
Date: Fri, 10 Nov 2017 19:51:52 +0200 (EET)

Takeshi Yoshino <[email-masked]>: (Fri Nov 10 18:51:05 2017)

> introduce any complicated mechanism to perform e2e negotiation. Do you all

SETTINGS   ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL) from server
end of connection to client end of connection tells on that case

that server end of connection understand
	:upgrade
pseudo header.

It tells nothing about that what is behind of that server end of connection.

Only after request is sent, http response tells if that authority
and path supporting that upgrade. Error is reported as http status code.

I see that reverse proxy can send SETTINGS   ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL)
even when it does not konw if next hop supports that. Support
of next hop or origin server is reported by when that protocol
is triedm failure is reported on http status code.

I assume that that SETTINGS is send on initial server settings
(connection preface SETTINGS).

/ Kari Hurtta

---

From: Takeshi Yoshino <[email-masked]>
Date: Sat, 11 Nov 2017 03:10:10 +0900

> is triedm failure is reported on http status code.

Ideally I want to allow a browser to know as much as possible info about
the capability of the path with less speculative attempts, for less
fallback. So, I investigated this situation and explored how much info we
could give to the SETTINGS ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL).

But yes, the tool, "SETTINGS", we have is only about the peer end of a
connection. I think it does make sense to just follow that principle. We
can build the opt-in mechanism by that for each hop, and then, proxies are
still allowed to emit errors on capability mismatch between connections, as
you said.

We could design error code, fallback, etc. for this kind of cases if it
turns out we really need to take care of. For the initial implementation,
maybe we could just let browsers give up when a connection attempt fails on
a connection with ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL) announced.

---

From: Amos Jeffries <[email-masked]>
Date: Sat, 11 Nov 2017 15:37:26 +1300

On 11/11/17 07:10, Takeshi Yoshino wrote:
> On Sat, Nov 11, 2017 at 2:51 AM, Kari Hurtta wrote:
> 

A proxy that sends that ENABLE_UPGRADE is guaranteeing that it *will* 
service the upgrade and handle the resulting traffic syntax. By itself 
if necessary.

In the general case a proxy that negotiates a SETTING it cannot 
guarantee support for is broken. It must instead negotiate a SETTINGS 
without the feature and re-negotiate with another SETTINGS later when it 
has better information.

Amos

---

From: Kazuho Oku <[email-masked]>
Date: Sat, 11 Nov 2017 15:46:58 +0800

2017-11-11 2:10 GMT+08:00 Takeshi Yoshino <[email-masked]>:
> Ideally I want to allow a browser to know as much as possible info about the
> capability of the path with less speculative attempts, for less fallback.
> So, I investigated this situation and explored how much info we could give
> to the SETTINGS ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL).
>
> connection. I think it does make sense to just follow that principle. We can
> build the opt-in mechanism by that for each hop, and then, proxies are still
> allowed to emit errors on capability mismatch between connections, as you
> said.
>

I agree.

While I agree that having a status code that indicates failure to
upgrade the connection "end-to-end" might be a nice idea, I would also
argue that the necessity is not specific to HTTP/2.

We could have had a connection-cannot-be-upgraded status code for
HTTP/1.1. But in reality, we do not have such a status code, and
nobody has argue for having that (as I know of).

Considering the fact, I would anticipate that we will be fine without
adding a new status code to indicate such failure.

-- 
Kazuho Oku

---

From: Kari Hurtta <[email-masked]>
Date: Sat, 11 Nov 2017 11:19:41 +0200 (EET)

> service the upgrade and handle the resulting traffic syntax. By itself 

I ligthly disagreed.

ENABLE_UPGRADE just tells that :upgrade is not considered to
be error which causes stream error of type PROTOCOL_ERROR
emitted and tells possible full duplex handling (as was on
:method = CONNECT).

You can also try Upgrade: on HTTP/1.1 and server or proxy
have permission to ignore it.   Upgrade is just suggestion
from client.  

In that case request is processed as upgrade is not done.
It can still succeed (without protocol change) or fail
as any http request can.

/ Kari Hurtta

---

From: Kari Hurtta <[email-masked]>
Date: Sat, 11 Nov 2017 12:45:12 +0200 (EET)

Kazuho Oku <[email-masked]>: (Sat Nov 11 09:46:58 2017)
<...>
> nobody has argue for having that (as I know of).

There is (sort of)

426 Upgrade Required

normally, if Upgrade can not done, http request is processed without protocol
change.  This status code allows refusing of processing.

426 Upgrade Required

is defined on

RFC 2817: Upgrading to TLS Within HTTP/1.1
https://tools.ietf.org/html/rfc2817

Yes, that status code is for somewhat different purpose.

/ Kari Hurtta

---

From: Amos Jeffries <[email-masked]>
Date: Sun, 12 Nov 2017 03:17:01 +1300

> have permission to ignore it.   Upgrade is just suggestion

If ":upgrade" has the same optional nature as Upgrade did there is zero 
point in creating it. Just use the existing Upgrade header in the 
HEADERS frame. That does not require any negotiation.

The whole point of SETTINGS is to promise the recipient of that frame 
that the things mentioned in it will work. No maybes or guessing.

This guarantee provided by SETTINGS is one of the ways HTTP/2 improves 
over HTTP/1.1. No more sending maybe-ignored headers and hoping for 
success a few RTT later. The client can know up front whether that 
connection is a usable channel for the negotiable feature or not.

...

---

From: Kari Hurtta <[email-masked]>
Date: Sat, 11 Nov 2017 17:50:08 +0200 (EET)

Amos Jeffries <[email-masked]>: (Sat Nov 11 16:17:01 2017)
> HEADERS frame. That does not require any negotiation.

Wrong.  HTTP/2 does not support Upgrade, therefore it needs negotiation.

8.1.2.2.  Connection-Specific Header Fields
https://tools.ietf.org/html/rfc7540#section-8.1.2.2

"   HTTP/2 does not use the Connection header field to indicate
"   connection-specific header fields; in this protocol, connection-
"   specific metadata is conveyed by other means.  An endpoint MUST NOT
"   generate an HTTP/2 message containing connection-specific header
"   fields; any message containing connection-specific header fields MUST
"   be treated as malformed (Section 8.1.2.6).

And

"      Note: HTTP/2 purposefully does not support upgrade to another
"      protocol.  The handshake methods described in Section 3 are
"      believed sufficient to negotiate the use of alternative protocols.

Indication that Upgrade: is "upgrade HTTP/2 stream to tunnel of another protocol"
need negotiation.

HTTP/1.1 Upgrade: is "upgrade connection to another protocol".

> that the things mentioned in it will work. No maybes or guessing.

Point of SETTINGS is to promise that that frame is interepreted 
accoding of new specification and not accoding of RFC 7540
which means "stream error (Section 5.4.2) of type PROTOCOL_ERROR".

That is treatment of ":upgrade" pseudo header and "Upgrade" connection
header according of RFC 7540.

SETTINGS can not promise anything about next hops on multihop
environment when there is several upstream targets.

You can NOT know that there is Websocket on particular
:path bofore you know this path. SETTINGS can not tell that.

Ability to do Upgrade to websocket is granularity of
URL path. If you change URL path then may be that 
Upgrade is not possible.

/ Kari Hurtta

---

From: Kari Hurtta <[email-masked]>
Date: Sat, 11 Nov 2017 21:30:19 +0200 (EET)

> connection is a usable channel for the negotiable feature or not.

Sidenote:

If you want SETTINGS to tell that websocket can be used
over HTTP/2 then 
	:upgrade =
and SETTINGS ENABLE_UPGRADE is no go. No general tunneling
as Kazuho Oku <[email-masked]> as suggesting in that thread:

https://lists.w3.org/Archives/Public/ietf-http-wg/2017OctDec/0209.html

> For example, creating a websocket tunnel through HTTP/2 would look like below.

In that case SETTINGS ENABLE_UPGRADE tells nothing what upgrades are available
(or you need to do registry for different 32-bit values of
 ENABLE_UPGRADE to map possible upgrade protocols).

Same to say about SETTINGS  ENABLE_CONNECT_PROTOCOL and
    :protocol = 

You are asking SETTINGS ENABLE_WEBSOCKET then
and that is not proposed on draft-mcmanus-httpbis-h2-websockets-01
either.

/ Kari Hurtta

---

From: Kazuho Oku <[email-masked]>
Date: Sun, 12 Nov 2017 08:31:45 +0800

Hi,

> Yes, that status code is for somewhat different purpose.

Thank you for pointing that out.

My view is that 426 is a status code that instructs a client to
upgrade the connection (or a stream).

I do not think that it would be the status code that we can use for
telling the client that the stream could not be upgraded even though
the client requested the upgrade.

-- 
Kazuho Oku

---

From: Mike Bishop <[email-masked]>
Date: Sun, 12 Nov 2017 04:41:25 +0000

It tells the client what protocol(s) it must upgrade to if the request is to succeed; it doesn't seem an unreasonable response if the client didn't offer the protocol(s) the server was expecting.

-----Original Message-----
From: Kazuho Oku [mailto:[email-masked]] 
Sent: Sunday, November 12, 2017 8:32 AM
To: Kari Hurtta <[email-masked]>
Cc: Takeshi Yoshino <[email-masked]>; hybi <[email-masked]>; Patrick McManus <[email-masked]>; HTTP Working Group <[email-masked]>
Subject: Re: [hybi] Fwd: New Version Notification for draft-mcmanus-httpbis-h2-websockets-01.txt

Hi,

2017-11-11 18:45 GMT+08:00 Kari Hurtta <[email-masked]>:
> Kazuho Oku <[email-masked]>: (Sat Nov 11 09:46:58 2017) <...>
>> > We could design error code, fallback, etc. for this kind of cases 
>> > if it turns out we really need to take care of. For the initial 
>> > implementation, maybe we could just let browsers give up when a 
>> > connection attempt fails on a connection with ENABLE_UPGRADE (or ENABLE_CONNECT_PROTOCOL) announced.
>>
>> I agree.
>>
>> upgrade the connection "end-to-end" might be a nice idea, I would 
>> also argue that the necessity is not specific to HTTP/2.
>>
> normally, if Upgrade can not done, http request is processed without 
> protocol change.  This status code allows refusing of processing.
>
> 426 Upgrade Required
>
> is defined on
>

My view is that 426 is a status code that instructs a client to upgrade the connection (or a stream).

I do not think that it would be the status code that we can use for telling the client that the stream could not be upgraded even though the client requested the upgrade.

--
Kazuho Oku