Will websockets over HTTP2 also be multiplexed in streams? - websocket

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).

Related

WebSockets over HTTP/2, and GOAWAY

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().

Why HTTP/2 does multiplexing altough tcp does same thing?

As far as i know, TCP break down a message into segments. So, Why is multiplexing again on HTTP2? What are the benefits of multiplexing twice?
TCP isn’t multiplexed. TCP is just a guaranteed messaging stream (i.e. missing packets are re-requested and the TCP stream is basically temporarily blocked while this happens).
TCP, as a packet based protocol, can be used for multiplexed connections if the higher level application protocol (e.g. HTTP) allows sending of multiple messages. Unfortunately HTTP/1.1 does not allow this: once a HTTP/1.1 message is sent, no other message can be sent on that connection until that message is returned in full (ignoring the badly supported pipelining concept). This means HTTP/1.1 is basically synchronous and, if the full bandwidth is not used and other HTTP messages are queued, then it wastes any extra capacity that could be used on the underlying TCP connection.
To get around this more TCP connections can be opened, which basically allows HTTP/1.1 to act like a (limited) multiplexed protocol. If the network bandwidth was fully utilised then those extra connections would not add any benefit - it’s the fact there is capacity and that the other TCP connections are not being fully utilised that means this makes sense.
So HTTP/2 adds multiplexing to the protocol to allow a single TCP connection to be used for multiple in flight HTTP requests.
It does this by changing the text-based HTTP/1.1 protocol to a binary, packet-based protocol. These may look like TCP packets but that’s not really relevant (in the same way that saying TCP is similar to IP because it’s packet based is not relevant). Splitting messages into packets is really the only way of allowing multiple messages to be in flight at the same time.
HTTP/2 also adds the concept of streams so that packets can belong to different requests - TCP has no such concept - and this is what really makes HTTP/2 multiplexed.
In fact, because TCP doesn’t allow separate, independents streams (i.e. multiplexing), and because it is guaranteed, this actually introduces a new problem where a single dropped TCP packet holds up all the HTTP/2 streams on that connection, despite the fact that only one stream should really be affected and the other streams should be able to carry on despite this. This can even make HTTP/2 slower in certain conditions. Google is experimenting with moving away from TCP to QUIC to address this.
More details on what multiplexing means under HTTP/2 (and why it is a good improvement!) in my answer here: What does multiplexing mean in HTTP/2
TCP doesn't do multiplexing. The TCP segments just means that the (single) stream data is chopped up into pieces that can be sent in IP packets. Each TCP segment is only identified with a stream offset (sequence number), not with any useful way to identify separate streams. (We'll ignore the rarely-useful Urgent Pointer thing.)
So to do multiplexing, you need to put something on top of TCP. Which HTTP/2 does.
HTTP & HTTP/2 are both application level protocols that must utilize a lower level protocol like TCP to actually talk on the Internet. The protocol of the Internet is generally TCP over IP over Ethernet.
It looks like this:
As you can see HTTP is sitting above TCP. Below TCP is IP. One of the main protocols of the Internet. IP itself deals with packets which are switched/multiplexed. I think that's where you might be getting the idea that TCP is multiplexed, it's not. Think of a TCP connection as being like a single lane road tunnel where no one can pass. Lets say it has one single lane in each direction. This is what a TCP connection would look like. A tunnel where you put data in one end, and it comes out the other in the same order it went in. That is TCP. You can see there is no multiplexing on that. However, TCP does provides a reliable connection protocol for which other protocols may be built on top of like HTTP. And reliability is essential for HTTP.
HTTP 1.1 is simply a request response protocol. But as you know, it's not multiplexed. So only allow one outstanding request at a time and has to send the whole response to each request at a time. Previously the browsers got around that limitation by creating multiple TCP connections (tunnels) to the server with which to make more requests.
HTTP 2 actually splits the data up again and allows multiplexing over the one connection so that no further connections need to be created. It means the server can start servicing multiple requests and multiplex the responses so that the browser can start receiving images, pages and other resources at the same time, not one at a time.
Hope that makes it clear.

Does http2 include a demux mechanism?

As I simply understands HTTP2 is m:1 pattern where you put m logical connections into 1 TCP stream
Is it possible to do m:n pattern in http2?
m streams are demuxed into n connections for better reliability, because often one single TCP breaks all h2 hangs.
It would be possible, but in practice it is not done.
Browsers especially try hard to open just one connection to a domain, and even reuse the same connection for different subdomains if they can figure out that it resolves to the same IP address and same certificate.
Other clients may implement a m:n scheme (for example, Jetty 9.4.x HTTP/2 client does - disclaimer: I'm the maintainer).
The problem of choosing a good n may not be trivial, and it risks to go back to HTTP/1.1 6-8 TCP connections per domain.
Since each connection would be multiplexed anyway, the failure of a single HTTP/2 connection would be worse than the failure of a single HTTP/1.1 connection (because it would fail multiple requests rather than just one), so I guess that it would not make that much of a difference with respect to a single HTTP/2 connection.
Google's QUIC protocol aims at resolving this issue since it is based on UDP and has built-in in support for connection migration (i.e. switching from WiFi to mobile network).

Shall I use WebSocket on ports other than 80?

Shall I use WebSocket on non-80 ports? Does it ruin the whole purpose of using existing web/HTTP infrastructures? And I think it no longer fits the name WebSocket on non-80 ports.
If I use WebSocket over other ports, why not just use TCP directly? Or is there any special benefits in the WebSocket protocol itself?
And since current WebSocket handshake is in the form of a HTTP UPGRADE request, does it mean I have to enable HTTP protocol on the port so that WebSocket handshake can be accomplished?
Shall I use WebSocket on non-80 ports? Does it ruin the whole purpose
of using existing web/HTTP infrastructures? And I think it no longer
fits the name WebSocket on non-80 ports.
You can run a webSocket server on any port that your host OS allows and that your client will be allowed to connect to.
However, there are a number of advantages to running it on port 80 (or 443).
Networking infrastructure is generally already deployed and open on port 80 for outbound connections from the places that clients live (like desktop computers, mobile devices, etc...) to the places that servers live (like data centers). So, new holes in the firewall or router configurations, etc... are usually not required in order to deploy a webSocket app on port 80. Configuration changes may be required to run on different ports. For example, many large corporate networks are very picky about what ports outbound connections can be made on and are configured only for certain standard and expected behaviors. Picking a non-standard port for a webSocket connection may not be allowed from some corporate networks. This is the BIG reason to use port 80 (maximum interoperability from private networks that have locked down configurations).
Many webSocket apps running from the browser wish to leverage existing security/login/auth infrastructure already being used on port 80 for the host web page. Using that exact same infrastructure to check authentication of a webSocket connection may be simpler if everything is on the same port.
Some server infrastructures for webSockets (such as socket.io in node.js) use a combined server infrastructure (single process, one listener) to support both HTTP requests and webSockets. This is simpler if both are on the same port.
If I use WebSocket over other ports, why not just use TCP directly? Or
is there any special benefits in the WebSocket protocol itself?
The webSocket protocol was originally defined to work from a browser to a server. There is no generic TCP access from a browser so if you want a persistent socket without custom browser add-ons, then a webSocket is what is offered. As compared to a plain TCP connection, the webSocket protocol offers the ability to leverage HTTP authentication and cookies, a standard way of doing app-level and end-to-end keep-alive ping/pong (TCP offers hop-level keep-alive, but not end-to-end), a built in framing protocol (you'd have to design your own packet formats in TCP) and a lot of libraries that support these higher level features. Basically, webSocket works at a higher level than TCP (using TCP under the covers) and offers more built-in features that most people find useful. For example, if using TCP, one of the first things you have to do is get or design a protocol (a means of expressing your data). This is already built-in with webSocket.
And since current WebSocket handshake is in the form of a HTTP UPGRADE
request, does it mean I have to enable HTTP protocol on the port so
that WebSocket handshake can be accomplished?
You MUST have an HTTP server running on the port that you wish to use webSocket on because all webSocket requests start with an HTTP request. It wouldn't have to be heavily featured HTTP server, but it does have to handle the initial HTTP request.
Yes - Use 443 (ie, the HTTPS port) instead.
There's little reason these days to use port 80 (HTTP) for anything other than a redirection to port 443 (HTTPS), as certification (via services like LetsEncrypt) are easy and free to set up.
The only possible exceptions to this rule are local development, and non-internet facing services.
Should I use a non-standard port?
I suspect this is the intent of your question. To this, I'd argue that doing so adds an unnecessary layer of complication with no obvious benefits. It doesn't add security, and it doesn't make anything easier.
But it does mean that specific firewall exceptions need to be made to host and connect to your websocket server. This means that people accessing your services from a corporate/school/locked down environment are probably not going to be able to use it, unless they can somehow convince management that it is mandatory. I doubt there are many good reasons to exclude your userbase in this way.
But there's nothing stopping you from doing it either...
In my opinion, yes you can. 80 is the default port, but you can change it to any as you like.

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