If I’m understanding correctly, this could technically support TCP, but that hasn’t been explicitly considered so far?
If so, I think it would be great to make TCP something promised by the spec. Admittedly, we would likely need to place restrictions on the domains a page could access, and add permission prompts. Those could be bypassed in the case of QUIC. I think that shouldn’t put people off, however, as it would fill such a huge gap in the web today - email clients, database explorers and a whole host of other applications could be built. I know this has been generally something for extensions, and that there are concerns about DDOS attacks. However, I think this will give the web too much power to miss out on, and hence is a challenge we should embrace.
WebTransport is designed to be an API that can map onto different protocol, so it’s possible it could map onto TCP. However, there are some considerations:
TCP is already available via WebSocket. Victor (vvv) is planning on writing an implementation of the WebTransport API on top of WebSocket so that one could have a better API but with existing protocols (an API with multiple streams, albeit with head of line blocking).
For security reasons, a TCP-based WebTransport would likely be TCP+TLS, not just TLS.
Again, for security reasons, the server would need to opt-in to the client’s connection much like the QUIC WebTransport (QuicTransport) does, and like WebSocket does. This would require changes to the servers that you want to connect to which would be similar to supporting WebSockets (which could already do).
TCP would prevent many of the benefits of WebTransport: support for multiple streams and avoiding head-of-line-blocking. A protocol on top of TLS/WebSockets could provide for multiple streams (much like vvv plans to write, or HTTP/2 does), but you still have head-of-line blocking.
So, it could be done, but I’m not sure how much you’re gaining compared to what you can do now with WebSockets. On the other hand, it might be worth doing something for good fallback for networks on which UDP is blocked (and thus QUIC and HTTP/3 cannot work).
[BA] Not only would the API not provide gains over TCP, it could hurt. Head-of-line blocking can have very undesirable effects on the behavior of applications written to the API – loss on a TCP connection will block i/o on all streams sharing that connection. That would not happen if individual TCP connections were used. Overall, an application desiring fallback would do better to utilize legacy APIs (e.g. Websockets) if QUIC is unavailable, even if more code is required to do so.
I’ve been trying to reach out to people shipping games for the Web and gather their thoughts on this proposal. Jukka Jylänki (Unity) provided the following comments, which I found really helpful:
Pthatcher’s notes on the missing gaps (unordered datagrams, avoiding head-of-line blocking) are spot on. Something I’d like to highlight here is that WebRTC data channels did add UDP-like communication, but because of the complexity involved in implementing both a client and a server stack for WebRTC data channels, adoption has remained practically zero in games. Contrast this to WebSockets, which is quite lightweight in its definition, and much easier for a programmer to implement a client stack or a server stack to converse in WebSockets.
In order for any kind of new server-client protocol spec to be successful, it is not enough that there is a spec for the feature that browser/shell vendors implement as part of a tight spec+development iteration cycle, but the spec needs to be friendly towards independent implementors who need to ground up build a stack to talk the protocol. Back with WebRTC the browser implementations were initially riddled with quirks that were not well defined in the spec, and Chrome and Firefox had a long tail of bug bashing work stemming from confusion with unspecified scenarios, due to the complexity of the spec. So I would like to stress how important it is that a protocol spec of this kind is built up with simplicity and practicality in focus, keeping independent implementations in mind. Reading through https://pthatcherg.github.io/web-transport/ , it has user guide examples, and from there the transport spec leads to https://tools.ietf.org/html/draft-pauly-quic-datagram-02 which is a bit short treatise - so I am not sure how much work will currently be involved in implementing the protocol stack.
In summary, “implementor’s guide” is as important as “user’s guide”, or perhaps even more important, to guarantee adoption.
It is great to see efforts on this front progress - game networking with WebSockets is really painful, and game networking with WebRTC data channels is too messy to pull off.
I think the part about independent implementers matches with what we’ve been trying to achieve with QuicTransport design: minimizing the gap between basic QUIC and QUIC as useful for the web.
I’ve gave a brief presentation on Wednesday at the QUIC IETF interim meeting (slides). Some notes on the feedback that I got while at the meeting (pthatcher has his own notes, so he should feel free to correct me if I’m missing something):
Mark Nottingham noted that due to freeform nature of WebSockets, the service providers in traditional HTTP space (CDNs, load balancers, etc) have struggled to provide services for WebSockets, and this will likely be the case with this proposal too.
Roberto Peon said he’s very interested and “have been waiting for something like this for 8 years”, and provided some helpful comments about importance of scatter-gather and TTL that is transmitted across hops (something I should add to Http3Transport at some point).
Mirja Kühlewind pointed out that we should take a closer look at TAPS, since a lot of the design problems we have are also faced by TAPS.
Lucas Pardue mentioned that there might be projects at Cloudflare that would benefit from API like this.
David Schinazi said he’s interested in Http3Transport as a potential basis for MASQUE.
I feel like we do have a fair amount of interest. I’m also pretty convinced that there will be a fair share of smaller Web developers to whom I don’t have good means of reaching out to who would benefit from this proposal. At this point, is it possible for us to go ahead and move the draft to the WICG org?
Hey guys, Luke from Twitch here. I originally wrote a longer wall of text but I’ll keep it brief for now.
We’re messing around with WebRTC and definitely agree with the direction of this proposal. There are a lot of people (including ourselves) that are using WebRTC outside of video conferencing and would like to decouple the transport layer. WebRTC built on top of WebTransport would be amazing.
In our case, we want a way to transfer ordered but semi-reliable messages. This was not possible in browsers until WebRTC DataChannels.
DataChannels are great because the browser handles fragmentation, retransmitting individual packets, ordering packets into a message, dropping messages, and ordering any delivered messages into a stream. All you have to do is configure the stream and you get a function callback for each delivered message in the order they were sent.
With WebTransport unreliable streams, the browser performs fragmentation and orders packets into a message. We would have to make a stream per message and append some identifier to each message (in order mux other types of streams). If we determine a packet is lost after a timeout (NACK/SACK not possible), we need to retransmit the ENTIRE message (blocker: our messages are big). We would need to append a sequence number to each message, reordering them ourselves. This wouldn’t be acceptable.
With WebTransport datagrams, we would probably implement something similar to SCTP/QUIC on top. That’s fine to be honest. I’m not a fan of the overhead involved in implementing SCTP on top of UDP on top of QUIC on top of UDP.
So it sounds like what you’re looking for is message order to be taken care of, but still have option for unreliability. That’s a combo that isn’t well supported by QUIC streams since to get ordered delivery, you must put messages in one stream (not one message per stream), and that requires reliability.
But if you do the “one message per stream” approach, you can do the ordering yourself on top, as you mention, but I’d like to understand better why you don’t find that acceptable. In any “ordered unreliability” system, there is the big question of how long to wait for gaps in the order, and you’ll probably want to decide for yourself what kind of timeout you’re willing to live with rather than relying on the browser to decide for you, which means doing that yourself.
As far as reliability of streams: you don’t have to explicitly ask for retransmission of a stream, nor retransmit and entire stream/message. You can instead simply send the message reliably, and then if later you decide it’s past the point when you would want to retransmit, you could abort the stream.
If you built on top of datagrams, you’d be doing your own message packetization and reassembly, which is more work than doing the simple reordering of messages, but it’s not nearly as large as SCTP or QUIC is. It could be much more simple since you don’t have to worry about congestion control or encryption. The most complex part would probably be figuring out when to retransmit lost packets.
Yeah, message ordering would be nice but you’re right, ordered but non-reliable streams depend on the browser to determine when a message is lost. It’s possible today with data channels but it relies on heuristics that developers should instead control.
Like you mentioned, partially reliable messages could be implemented by calling Reset() on a stream. The decision won’t be optimal without access to congestion-control statistics like RTT, retransmits, packet loss. Also, when the reset is called on the receiver’s side, there’s a delay (based on RTT + packet loss) during which time the sender will keep retransmitting packets for naught.
I would drop the support for non-reliable streams. It’s not an elegant API to create a stream for each message and it really suffers from packet loss. With 1% uniform packet loss, a 1k byte message would arrive 99% of the time while a 100k byte message would arrive 36% of the time (0.99^100). You need some form of reliability when messages are split into multiple datagrams.
I would include a MessageTransport. It will make it much easier to port WebSocket code, even if it just adds delimiters on top of reliable streams. If you want unreliable messages, then it makes more sense to add them in the context of a MessageTransport API.
I don’t think I emphasized it enough but datagram support is huge and for the first time, gives developers the ability to fully control unreliable packet delivery. I can now build my own end-to-end protocol instead of being forced to use TCP and QUIC’s stream-based semantics. There’s definitely room for a message-based API that performs fragmentation and reordering but it’s not a critical component.
Many people want to drop unreliable streams, but others want to keep it :). You’re right that it doesn’t make sense for very large messages, but it can make sense for small messages that are 3-4 packets. And that’s even more true if you experience bursty loss rather than random loss. Of course, if you don’t want to use it and it stays, you don’t have to use it.
A MessageTransport can be implemented as a library on top of WebTransport fairly easily. I wrote an example of an unreliable WebSocket API on top of WebTransport here: https://github.com/w3c/webrtc-quic/blob/master/examples/quic-ws.js. But you could make a similar one with a simple framing protocol. I can write that into the example if you want.