WebSockets over HTTP/2, and GOAWAY - websocket

I am interested in how WebSockets over HTTP/2 work on browsers (Firefox, specifically, since that 's the only one I'm aware of which supports Websockets over H2) with GOAWAYs.
Now, I know that GOAWAYs are connection-level events, and if IIUC, a WebSocket/H2 connection is actually a single H2 stream which has been upgraded.
Let's say that we nowget a GOAWAY from the server - do we expect the browser to just close the H2 stream which was supporting the WebSocket connection with a CloseEvent with code 1001? Is there some other event which gets fired/which needs to be added to the WebSocket API, for eg, ongoaway?

Now, I know that GOAWAYs are connection-level events, and if IIUC, a WebSocket/H2 connection is actually a single H2 stream which has been upgraded.
A single HTTP/2 connection may have multiple HTTP/2 streams, each one possibly being a WebSocket over HTTP/2 "upgraded" stream (see this section).
The goal is to use one single TCP connection carry possibly multiple protocols, so a situation where some stream are normal HTTP/2 and some are WebSocket over HTTP/2 is possible.
When connection level GOAWAY is sent by the server, I would expect the client to react as specified in this section.
In particular the server may send a "graceful" GOAWAY, so clients don't open new streams.
When the server is about to send the GOAWAY, it should arrange to send a terminal (i.e. endStream=true) DATA frame containing a WebSocket Close frame, which would result in a CloseEvent in the browser.
I don't think any event should be added to browsers' WebSocket APIs, the transport over HTTP/2 should be completely transparent for client applications, like it is transparent using HTTP/1.1 or HTTP/2 when using XMLHttpRequest or fetch().

Related

Will websockets over HTTP2 also be multiplexed in streams?

I am trying to clarify/understand whether websockets over HTTP/2 will also be multiplexed over a TCP connection using streams. Section 5 of RFC8441 seems to suggest it
After successfully processing the opening handshake, the peers should proceed with the WebSocket Protocol [RFC6455] using the HTTP/2 stream from the CONNECT transaction as if it were the TCP connection referred to in [RFC6455]. The state of the WebSocket connection at this point is OPEN, as defined by [RFC6455], Section 4.1.
The HTTP/2 stream closure is also analogous to the TCP connection closure of [RFC6455]. Orderly TCP-level closures are represented as END_STREAM flags ([RFC7540], Section 6.1). RST exceptions are represented with the RST_STREAM frame ([RFC7540], Section 6.4) with the CANCEL error code ([RFC7540], Section 7).
But my confusion arises from the fact that even with HTTP/1.1, while tabs in a browser share the underlying TCP connections (e.g. chrome makes 6 TCP connections) to the same host, creating a websocket to the same host in different tabs leads to distinct TCP connection in each tab.
I am not sure why the difference between the two & if it is likely to be the same for websockets over HTTP/2 as well.
Any experts out here who can clarify. Thanks.
But my confusion arises from the fact that even with HTTP/1.1, while tabs in a browser share the underlying TCP connections (e.g. chrome makes 6 TCP connections) to the same host, creating a websocket to the same host in different tabs leads to distinct TCP connection in each tab.
You are right that this is the current state of affairs, unfortunately, for HTTP/1.1.
RFC 8441, as you point out, has been specified to solve this problem and piggy back WebSocket "connections" over HTTP/2 streams, so it would be possible to open just one TCP connection to an origin server and use that connection for both HTTP/2 communication and for WebSocket communication.
The difference between HTTP/1.1 and HTTP/2 stems from the fact that HTTP/1.1 WebSocket connections cannot be (efficiently) pooled.
Every WebSocket connection is tied to a specific URI (e.g. ws://host/path1) and it's more typical for an application to open different WebSocket connections for different URIs (rather than many WebSocket connections for the same URI).
Because they cannot be pooled, browsers basically have to allow an unlimited number of them, a new one every time you call new WebSocket(...) from JavaScript.
With HTTP/2 instead, you will be able to open a new HTTP/2 stream inside the same HTTP/2 connection.
The number of concurrent streams depends on browser implementations, but it's typically around 100 if not more, which leaves plenty of concurrency for both HTTP/2 and WebSocket (unless the client application is really abusing WebSocket).
Fortunately, client applications won't need to be changed to leverage this feature.
When browsers and server will support it, your application will use less resources (just one TCP connection) rather than the many it's using now.
[Disclaimer, I'm the implementer of such feature in Jetty]
We have seen a few browsers implementing this feature and we are finalizing the implementation of this feature in the Jetty 10.0.x server, see https://github.com/eclipse/jetty.project/issues/3537.
creating a websocket to the same host in different tabs leads to distinct TCP connection in each tab
A WebSocket connection is always a new TCP connection, since it has to perform an HTTP/S request that Upgrades to a WebSocket connection and is therefore no longer an HTTP/S connection if successful. WebSocket connections are distinct and can't be shared or reused, unlike HTTP/S connections (assuming keep-alives are used).

How to tunnel websockets over TCP in node?

I am trying to tunnel websockets over TCP. I know how to tunnel HTTPS - "Connect URL:port" is where I start. From there, one opens a socket to the target and then just pipe between the client and the target. Where do I start with websockets? is there something similar to a "Connect url:port" to begin with, which I can catch in my http server and then do some upgrade processing?
You sure you want to tunnel WebSocket over TCP? WebSocket runs over TCP.
I think you mean you want to tunnel TCP traffic with WebSocket, i.e., take an existing, traditional Socket-based application and make it work over the web. If that's what you mean, you essentially put your TCP data in a WebSocket frame and on the receiving end you read the WebSocket frame and extract the data. Of course this is easier said than done. You have to make sure you create the WebSocket frame correctly on the sender side (also handling the TCP data stream, which also may be tricky), encrypt the data (its going over the web, right?) and on the receiving side read the WebSocket frame, extract the data from the right parts of the frame. And also you need to check to see if all the data is in one WebSocket frame or multiple frames.
As I said, its not dead simple.
There are several WebSocket libraries out there that may (or may) not handle all of this for you (many do not handle the multiple WebSocket frame situation).

Does CometD (Comet with Bayeux Protocol) use HTTP streaming or HTTP long polling?

There are two techniques for implementing Comet. One uses HTTP streaming, which uses a single persisted TCP connection to send and receive multiple HTTP requests/responses between client/server.The second is HTTP long polling, which keeps a connection open by the server, and, as soon as an event occurs, the response is committed and the connection is closed. Then, a new long-polling connection is reopened immediately by the client waiting for new events to arrive.
I am using the Faye ruby gem and I noticed it uses Comet/Bayeux out of the box. But I cannot find out which type of Comet technique it uses. I just gather that Bayeux is publish-subscribe protocol. I'm curious to know if it suffers the same shortcomings of HTTP streaming and long polling. Does it allow full-duplex communication (communication in both directions, and, unlike half-duplex, allows this to happen simultaneously.)?
Your definition of HTTP streaming and long-polling are not correct.
In HTTP streaming, the client sends a request to the server, and the server replies with an "infinite" response that contains small chunks of data (messages), typically using the chunked transfer encoding.
This mechanism has been standardized as EventSource (a.k.a Server-Sent Events).
It is a server-to-client only push of events.
For the client to send another message to the server, it has to open a new connection.
In HTTP long-polling, the client sends a request that is held by the server until an event (or a timeout) occurs, then the response is committed but the connection is not closed.
The connection is kept open and other requests may be sent on that connection, both normal or long-polling requests (one at a time, of course).
The Bayeux protocol is an application protocol on top of a transport protocol such as HTTP or WebSocket.
HTTP is a full duplex protocol in the context of a single request/response exchange. Multiple HTTP exchanges are serialized (that is, executed one after the other). The HTTP request/response exchange is the unit of serialization.
WebSocket is a full duplex protocol in the context of WebSocket messages. WebSocket messages may be sent and received simultaneously. The WebSocket message is the unit of serialization.
Bayeux inherits the characteristics of the transport protocol is it carried on. The Bayeux protocol itself does not itself have any "duplexness" characteristics, you can think of it just as a way to format messages in a particular textual form.
Both CometD and Faye use Bayeux over both WebSocket and HTTP long-polling.

Does HTTP/2 make websockets obsolete?

I'm learning about HTTP/2 protocol. It's a binary protocol with small message frames. It allows stream multiplexing over single TCP connection. Conceptually it seems very similar to WebSockets.
Are there plans to obsolete websockets and replace them with some kind of headerless HTTP/2 requests and server-initiated push messages? Or will WebSockets complement HTTP/2?
After just getting finished reading RFC 7540, HTTP/2 does obsolete websockets for all use cases except for pushing binary data from the server to a JS webclient. HTTP/2 fully supports binary bidi streaming (read on), but browser JS doesn't have an API for consuming binary data frames and AFAIK such an API is not planned.
For every other application of bidi streaming, HTTP/2 is as good or better than websockets, because (1) the spec does more work for you, and (2) in many cases it allows fewer TCP connections to be opened to an origin.
PUSH_PROMISE (colloquially known as server push) is not the issue here. That's just a performance optimization.
The main use case for Websockets in a browser is to enable bidirectional streaming of data. So, I think the OP's question becomes whether HTTP/2 does a better job of enabling bidirectional streaming in the browser, and I think that yes, it does.
First of all, it is bi-di. Just read the introduction to the streams section:
A "stream" is an independent, bidirectional sequence of frames
exchanged between the client and server within an HTTP/2 connection.
Streams have several important characteristics:
A single HTTP/2 connection can contain multiple concurrently open
streams, with either endpoint interleaving frames from multiple
streams.
Streams can be established and used unilaterally or shared by
either the client or server.
Streams can be closed by either endpoint.
Articles like this (linked in another answer) are wrong about this aspect of HTTP/2. They say it's not bidi. Look, there is one thing that can't happen with HTTP/2: After the connection is opened, the server can't initiate a regular stream, only a push stream. But once the client opens a stream by sending a request, both sides can send DATA frames across a persistent socket at any time - full bidi.
That's not much different from websockets: the client has to initiate a websocket upgrade request before the server can send data across, too.
The biggest difference is that, unlike websockets, HTTP/2 defines its own multiplexing semantics: how streams get identifiers and how frames carry the id of the stream they're on. HTTP/2 also defines flow control semantics for prioritizing streams. This is important in most real-world applications of bidi.
(That wrong article also says that the Websocket standard has multiplexing. No, it doesn't. It's not really hard to find that out, just open the Websocket RFC 6455 and press ⌘-F, and type "multiplex". After you read
The protocol is intended to be extensible; future versions will likely introduce additional concepts such as multiplexing.
You will find that there is 2013 draft extension for Websocket multiplexing. But I don't know which browsers, if any, support that. I wouldn't try to build my SPA webapp on the back of that extension, especially with HTTP/2 coming, the support may never arrive).
Multiplexing is exactly the kind of thing that you normally have to do yourself whenever you open up a websocket for bidi, say, to power a reactively updating single page app. I'm glad it's in the HTTP/2 spec, taken care of once and for all.
If you want to know what HTTP/2 can do, just look at gRPC. gRPC is implemented across HTTP/2. Look specifically at the half and full duplex streaming options that gRPC offers. (Note that gRPC doesn't currently work in browsers, but that is actually because browsers (1) don't expose the HTTP/2 frame to the client javascript, and (2) don't generally support Trailers, which are used in the gRPC spec.)
Where might websockets still have a place? The big one is server->browser pushed binary data. HTTP/2 does allow server->browser pushed binary data, but it isn't exposed in browser JS. For applications like pushing audio and video frames, this is a reason to use websockets.
Edit: Jan 17 2020
Over time this answer has gradually risen up to the top (which is good, because this answer is more-or-less correct). However there are still occasional comments saying that it is not correct for various reasons, usually related to some confusion about PUSH_PROMISE or how to actually consume message-oriented server -> client push in a single page app.
If you need to build a real-time chat app, let's say, where you need to broadcast new chat messages to all the clients in the chat room that have open connections, you can (and probably should) do this without websockets.
You would use Server-Sent Events to push messages down and the Fetch api to send requests up. Server-Sent Events (SSE) is a little-known but well supported API that exposes a message-oriented server-to-client stream. Although it doesn't look like it to the client JavaScript, under the hood your browser (if it supports HTTP/2) will reuse a single TCP connection to multiplex all of those messages. There is no efficiency loss and in fact it's a gain over websockets because all the other requests on your page are also sharing that same TCP connection. Need multiple streams? Open multiple EventSources! They'll be automatically multiplexed for you.
Besides being more resource efficient and having less initial latency than a websocket handshake, Server-Sent Events have the nice property that they automatically fall back and work over HTTP/1.1. But when you have an HTTP/2 connection they work incredibly well.
Here's a good article with a real-world example of accomplishing the reactively-updating SPA.
From what I understood HTTP/2 is not a replacement for websocket but aims to standardize SPDY protocol.
In HTTP/2, server-push is used behind the scene to improve resource loading by the client from the browser. As a developer, you don't really care about it during your development. However, with Websocket, the developer is allowed to use API which is able to consume and push message with an unique full-duplex connection.
These are not the same things, and they should complement each other.
I say Nay (Websockets aren't obsolete).
The first and most often ignored issue is that HTTP/2 push isn't enforceable and might be ignored by proxies, routers, other intermediaries or even the browser.
i.e. (from the HTTP2 draft):
An intermediary can receive pushes from the server and choose not to forward them on to the client. In other words, how to make use of the pushed information is up to that intermediary. Equally, the intermediary might choose to make additional pushes to the client, without any action taken by the server.
Hence, HTTP/2 Push can't replace WebSockets.
Also, HTTP/2 connections do close after a while.
It's true that the standard states that:
HTTP/2 connections are persistent. For best performance, it is expected that clients will not close connections until it is determined that no further communication with a server is necessary (for example, when a user navigates away from a particular web page) or until the server closes the connection.
But...
Servers are encouraged to maintain open connections for as long as possible but are permitted to terminate idle connections if necessary. When either endpoint chooses to close the transport-layer TCP connection, the terminating endpoint SHOULD first send a GOAWAY (Section 6.8) frame so that both endpoints can reliably determine whether previously sent frames have been processed and gracefully complete or terminate any necessary remaining tasks.
Even if the same connection allows for pushing content while it is open and even if HTTP/2 resolves some of the performance issues introduced by HTTP/1.1's 'keep-alive'... HTTP/2 connections aren't kept open indefinitely.
Nor can a webpage re-initiate an HTTP/2 connection once closed (unless we're back to long-pulling, that is).
EDIT (2017, two years later)
Implementations of HTTP/2 show that multiple browser tabs/windows share a single HTTP/2 connection, meaning that push will never know which tab / window it belongs to, eliminating the use of push as a replacement for Websockets.
EDIT (2020)
I'm not sure why people started downvoting the answer. If anything, the years since the answer was initially posted proved that HTTP/2 can't replace WebSockets and wasn't designed to do so.
Granted, HTTP/2 might be used to tunnel WebSocket connections, but these tunneled connections will still require the WebSocket protocol and they will effect the way the HTTP/2 container behaves.
The answer is no. The goal between the two are very different. There is even an RFC for WebSocket over HTTP/2 which allows you to make multiple WebSocket connections over a single HTTP/2 TCP pipe.
WS over HTTP/2 will be a resource conservation play by decreasing the time to open new connections and allowing for more communication channels without the added expense of more sockets, soft IRQs, and buffers.
https://datatracker.ietf.org/doc/html/draft-hirano-httpbis-websocket-over-http2-01
Well, to quote from this InfoQ article:
Well, the answer is clearly no, for a simple reason: As we have seen above, HTTP/2 introduces Server Push which enables the server to proactively send resources to the client cache. It does not, however, allow for pushing data down to the client application itself. Server pushes are only processed by the browser and do not pop up to the application code, meaning there is no API for the application to get notifications for those events.
And so HTTP2 push is really something between your browser and server, while Websockets really expose the APIs that can be used by both client (javascript, if its running on browser) and application code (running on server) for transferring real time data.
As of today, no.
HTTP/2, compared to HTTP, allows you to maintain a connection with a server. From there, you can have multiple streams of data at the same time. The intent is that you can push multiple things at the same time even without the client requesting it. For example, when a browser asks for a index.html, the server might want to also push index.css and index.js. The browser didn't ask for it, but the server might provide it without being asked because it can assume you're going to want in a few seconds.
This is faster than the HTTP/1 alternative of getting index.html, parsing it, discovering it needs index.js and index.css and then building 2 other requests for those files. HTTP/2 lets the server push data the client hasn't even asked for.
In that context, it's similar to WebSocket, but not really by design. WebSocket is supposed to allow a bi-directional communication similar to a TCP connection, or a serial connection. It's a socket where both communicate with each other. Also, the major difference is that you can send any arbitrary data packets in raw bytes, not encapsulated in HTTP protocol. The concepts of headers, paths, query strings only happen during the handshake, but WebSocket opens up a data stream.
The other difference is you get a lot more fine-tuned access to WebSocket in Javascript, whereas with HTTP, it's handled by the browser. All you get with HTTP is whatever you can fit in XHR/fetch(). That also means the browser will get to intercept and modify HTTP headers without you being able to control it (eg: Origin, Cookies, etc). Also, what HTTP/2 is able to push is sent to the browser. That means JS doesn't always (if ever) know things are being pushed. Again, it makes sense for index.css and index.js because the browser will cache it, but not so much for data packets.
It's really all in the name. HTTP stands for HyperText Transfer Protocol. We're geared around the concept of transferring assets. WebSocket is about building a socket connection where binary data gets passed around bidirectionally.
The one we're not really discussing is SSE (Server-Sent Events). Pushing data to the application (JS) isn't HTTP/2's intent, but it is for SSE. SSE gets really strengthened with HTTP/2. But it's a not a real replacement for WebSockets when what's important is the data itself, not the variable endpoints being reached. For each endpoint in with WebSocket a new data stream is created, but with SSE it's shared between the already existing HTTP/2 session.
Summarized here are the objectives for each:
HTTP - Respond to a request with one asset
HTTP/2 - Respond to a request with multiple assets
SSE - Respond with a unidirectional text (UTF-8) event stream
WebSocket - Create a bidirectional binary data stream
Message exchange and simple streaming(not audio, video streaming) can be done via both Http/2 multiplexing and WebSockets. So there is some overlap, but WebSockets have well established protocol, a lot of frameworks/APIs and less headers overhead.
Here is nice article about the topic.
No, WebSockets are not obsolete. However, HTTP/2 breaks websockets as defined for HTTP/1.1 (mostly by forbidding protocol updates using the Upgrade header). Which is why this rfc:
https://datatracker.ietf.org/doc/html/rfc8441
defines a websocket bootstrapping procedure for HTTP/2.
For the time being April 2020, HTTP/2 is not making WebSockets obsolete. The greatest advantage of WebSockets over HTTP2 is that
HTTP/2 works only on Browser Level not Application Level
Means that HTTP/2 does not offer any JS API like WebSockets to allow communication and transfer some kind of JSON or other data to server directly from Application (e.g. Website). So, as far as I believe, HTTP/2 will only make WebSockets obsolete if it starts offering API like WebSockets to talk to server. Till that it is just updated and faster version of HTTP 1.1.
No HTTP/2 does not make websockets obsolete, but SSE over HTTP/2 offers a viable alternative. The minor caveat is that SSE does not support unsolicited events from server to client (and neither does HTTP/2): i.e. the client has to explicitly subscribe by creating an EventSource instance specifying the event source endpoint. So you may have to slightly reorganise how the client arranges for events to be delivered - I can't think of a scenario where this is actually a technical barrier.
SSE works with HTTP/1.1. But HTTP/2 makes using SSE generally viable and competitive with websockets in terms of efficiency, instead of practically unusable in the case of HTTP/1.1. Firstly, HTTP/2 multiplexes many event source connections (or rather "streams" in HTTP/2 terms) onto a single TCP connection where as in HTTP/1.1 you'd need one connection for each. According to the HTTP/2 spec, millions of streams can be created per connection by default with the recommended (configurable) minimum being 100, where as browsers maybe severly limited in the number of TCP connections they can make. Second reason is efficiency: many streams in HTTP/2 is requires much less overhead than the many connections required in HTTP/1.1.
One final thing is, if you want to replace websockets with SSE your forgoing some of the tools / middlewares built on top of websockets. In particular I'm thinking of socket.io (which is how a lot of people actually use websockets), but I'm sure there is a ton more.

Does the connection get closed at any point during the WebSocket handshake or immediately after?

According to the Wikipedia article: http://en.wikipedia.org/wiki/WebSocket,
The server sends back this response to the client during handshake:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Does this close the connection (as HTTP responses usually do) or it is kept open throughout the entire handshake and it can start sending WebSocket frames straight away (assuming that it succeeds)?
An HTTP socket going through the handshake process to be upgraded to the webSocket protocol is not closed during that process. The same open socket goes through the whole process and then becomes the socket used for the webSocket protocol. As soon as the upgrade is complete, that very socket is ready for messages to be sent per the webSocket protocol.
It is this use of the exact same socket that enables a webSocket connection to run on the same port as an HTTP request (no extra port is needed) because it literally starts out as an HTTP request (with some extra headers attached) and then when those headers are recognized and both sides agree, the socket from that original HTTP request on the original web port (often port 80) is then switched to use the webSocket protocol. No additional connection on some new port is needed.
I actually find it a relatively elegant design because it makes for easy coexistence with a web server which was an important design parameter. And, a slight extra bit of connection overhead (protocol upgrade negotiation) is generally not an issue because webSocket connections by their very nature are designed to be long running sockets which you open once and use over an extended period of time so a little extra overhead to open them doesn't generally bother their use.
If, for any reason, the upgrade is not completed (both sides don't agree on the upgrade to webSocket), then the socket would remain an HTTP socket and would behave as HTTP sockets normally do (likely getting closed right away, but subject to normal HTTP interactions).
You can see this answer for more details on the back and forth during an upgrade to webSocket: SocketIO tries to connect using same port as the browser used to get web page

Resources