Re: HTTP/2 and Websockets

On 29 September 2014 18:26, Yutaka Hirano <yhirano@google.com> wrote:
> Hi,
>
> I am proposing a spec draft:
> http://tools.ietf.org/html/draft-hirano-httpbis-websocket-over-http2-01 .
> Since many modifications were made on on the HTTP/2 spec, some description
> may be obsolete. Please let me know if you find any flaw.

Cool - I have some questions, below.

>> Secondly, we need to define websocket frame mappings. The least work,
>> and I suspect the easiest for implementors, would be to put all the
>> websocket frames into HTTP/2's data frames, without worrying about
>> frame alignment: just treat the fully open stream as a series of bytes
>> in the same way TCP is treated by the websocket spec.
>> I suspect however that a better result would be achieved by defining
>> custom HTTP/2 frames, since websockets already have the basic support
>> for multiplexing (large application writes can be fragmented into
>> smaller frames as needed), we shouldn't run into HOL blocking issues.
>
> Yeah, we can't simply use DATA frames because intermediaries may buffer
> data. The HTTP/2 spec had "MSG_DONE" once and I wanted to use it, but it was
> removed from the spec. Currently I think introducing a new frame type is the
> right way.

I don't understand: DATA is subject to flow control, but I don't see
any prose about buffering (or not buffering) of DATA frames. Unless we
define a new frame time and explicitly prohibit buffering (but we need
to keep it flow controlled), I don't see what would be different. And
more importantly, intermediaries might still choose to buffer a new ws
frame type anyway: we know that what the spec requires and what people
do will differ :). All the implementor discussion I've seen during the
HTTP/2 discussions has focused on how intermediaries want to be
scalable: and buffering is anti-scaling. So - is it a pragmatic
concern, or do we expect DATA stream buffering to take place [outside
of protocol gateways converting to HTTP/1.1 where non upload can
require buffering - and note that such a gateway can't carry ws anyway
unless its aware of it, and if its aware of it, it can make sure it
does not buffer].

>>
>> Lastly, it seems (to me) that it would be desirable to allow
>> PUSH_PROMISE setup of websockets connections
>
> Can you tell me why it is desirable?

Same reason for PUSH_PROMISE of any stream, to avoid setup latency. If
the server is pushing down some javascript libraries that will want a
ws stream back to the same server, it could setup the stream in-line.

Ok, so my questions...

Firstly, I found the 'frame is overloaded, only specify when its
unambiguous' really hard for reading. I'd like to propose that we
fully qualify the frame type everywhere. 'h2 SETTINGS frame', 'ws
RST_STREAM frame' etc: doing otherwise requires a lot more mental
state, at least for me :).


3.2 seems like an antifeature: ALPN is for defining the base protocol
spoke, if you define a new ALPN name for things that speak websockets
over HTTP/2, but it still has all the same framing and flow control
etc, I think it just adds complexity.

There are two alternatives here:
 - ws[s] over TCP, RFC 6355, which using ALPN to select directly could
make sense [though nothing out there does it AFAIK?]
 - ws[s] over HTTP/2. which should be using ALPN to select *HTTP/2*,
since thats the protocol we need.

I say this because if adding ws to HTTP/2 makes a new ALPN, then when
we add a third thing (nt) we'll get:
h2
h2ws
h2nt
h2wsnt

And so on - its problematic :). And SETTING is sufficient to let us know.

Consider this: an h2 capable browser with ws over h2 support is asked
to connect to ws.example.com with ws. If it:
 - tries for h2
 - examines its peer setting for WEBSOCKET_CAPABLE
 - if present tries a HEADERS frame
 - if absent falls back to RFC6455

Then thats pretty sane IMO. If we get this deployed, it will work most
of the time, and when it doesn't, the fallback is there.

3.3
This section seems confused about the semantics of SETTING - see my
new post about clearing that up (whether I need the clearing up, or
the spec does :)).

Anyhow, settings is about advertising, not about negotiating - you
can't negotiate a path through an intermediary graph using it, because
there is no way for the intermediary to signal upstream that just one
client supports it and another doesn't. So the intermediary needs to
signal its support unconditionally. Likewise, the server can't use the
peer's WEBSOCKET_CAPABLE setting to replace the Sec-headers, because
a) a websocket capable browser might still have bad js executing and
trying to attack things, and b) an intermediary may be connecting
multiple different clients to the same server.

Here is an attempt at new prose:

3.3.  WebSocket over HTTP/2 capability

   Servers and intermediaries that can process WebSocket requests over
HTTP/2 MUST advertise the SETTINGS_WEBSOCKET_CAPABLE setting in their
SETTINGS frame.

   Websocket over HTTP/2 MUST NOT be attempted to peers that have not
set SETTINGS_WEBSOCKET_CAPABLE

   Clients do not need to advertise this capability as their making a
valid websockets request signals they are capable.

3.4 secure connection

   If the h2 connection is not secure, wss connections MUST NOT be
intiated over it.
   If the h2 connection is secure, both ws and wss connections MAY be
initiated over it.

3.5 Intermediaries

   Intermediaries that have advertised SETTINGS_WEBSOCKET_CAPABLE may
receive websocket requests which are for origins that do not advertise
SETTINGS_WEBSOCKET_CAPABLE (or may not even support HTTP/2). For
example, it is nearly certain that a forward proxy that speaks HTTP/2
will receive requests for origins that have not yet upgraded to
HTTP/2.

  In such a situation Intermediaries MUST either: initiate a RFC6455
websocket connection with the origin, and translate frames between the
two sides in conformance with both RFC6455 and this RFC. Or they may
return 501 (Not Implemented) to indicate that they cannot forward the
request.

  To illustrate: consider a Client(C), a websocket aware Proxy(P) and
a Server(S).
  P will include SETTINGS_WEBSOCKET_CAPABLE in its SETTINGS frame from P to C.
  C sees that P is capable, and initiates a websocket connection over
HTTP/2 to Origin S.
  P initiates an HTTP/2 connection with S
  S is a baseline HTTP/2 server and does not include
SETTINGS_WEBSOCKET_CAPABLE in its SETTINGS frame from S to P.
  At this point P can either error with 501, signalling that this
particular request cannot be carried, or it can fall back to RFC6455
on behalf of the client.

4.1
You say we can skip "Upgrade, Connection, Sec-WebSocket-Key, and
Sec-WebSocket-Version", because we don't need to do verification. I
think this prose is missing an explanation of why we don't need to do
verification.

There are two failure modes RFC6455 talks about:
A - connections to existing SMTP etc servers
B - submitting data from FORM posts to ws servers

The former is guarded against by looking for a ws specific handshake
from the server.
The latter is guarded against by looking for a ws specific header from
the client *which Javascript APIs do not permit javascript code to
set*.

Your draft defines ws over existing HTTP/2 connections and also new
connections to HTTP/2 endpoints. If we limit ourselves to just ws[s]
over existing HTTP/2 connections, then we maybe we can say:
A) is protected against by RFC6455, and any new connection made for ws
should follow that spec with one exception: If the server negotiates
as a valid HTTP/2 endpoint, then the SETTINGS_WEBSOCKET_CAPABLE
setting from the server is inspected to determine if ws over HTTP/2
can be used - and that supplances the server side calculation that was
used to prove websocket readiness in RFC6455.

B)  the presence of Sec-Websocket-Key and Sec-WebSocket-Version is
used to ensure that a WS endpoint doesn't get form data posted to it.
I see no replacement for that in your draft: we need to keep it,
because its protecting against javascript programming models. (Unless
I've missed something?)

4.2
'"101" or "101 Switching Protocols"' - AIUI in HTTP/2 the reason text
is gone. The status pseudo header is numeric only.

4.2.1 - the ALTSVC draft suggests doing this gracefully - e.g.
openning up the new connection then dropping the old one. We should
include a reason for not following that advice.

5.
There's nothing specified here - neither what frame types we need to
add, nor discussion on the [in]applicability of HTTP/2 DATA. I'd like
to try to use HTTP/2 data I think - the discussion about frame type
compatibility makes me think that we'll be more compatible with
RFC6455 if we just tunnel over the h2 DATA frame: remember that
RFC6455 targetted TCP as a transport, and a series of h2 DATA frames
is most analogous to that. In particular, if we use dedicated control
frames, we could hit out of order behaviour with control frames
forward before DATA frames, because DATA frames are flow controlled:
it will be more complex to specify, and I don't see a benefit.

7
I'm not aware of any equivalent to the masking in HTTP/2, and there is
no discussion of BEAST in the HTTP/2 spec: if we're delegating to
HTTP/2 to solve those issues, I think we need to talk about that now
:)


-Rob

-- 
Robert Collins <rbtcollins@hp.com>
Distinguished Technologist
HP Converged Cloud

Received on Monday, 29 September 2014 21:46:37 UTC