Code Monkey home page Code Monkey logo

webtransport's Introduction

WebTransport

API that allows web applications to establish interactive, bidirectional, multiplexed network connections

It fills gaps in the web platform:

  • Lack of UDP-like networking API
  • Lack of WebSocket-like API without head-of-line blocking

It provides:

  • Reliable streams
  • Unreliable datagrams
  • Encryption and congestion control
  • An origin-based security model
  • Bindings for QUIC
  • Multiplexing with existing HTTP/3 connections
  • Flexible API that can be extended to other protocols, such as TCP fallback and p2p
  • Ability to change transport without changing application code

It's great for:

  • sending or receiving high-frequency, small messages that don't need to be reliable (like game state)
  • sending or receiving low-latency media
  • transferring files

See the explainer for more info.

See the proposed spec.

Samples

Basic Echo

WebCodecs Echo

webtransport's People

Contributors

aboba avatar aminamos avatar annevk avatar autokagami avatar bashi avatar chicoxyzzy avatar dontcallmedom avatar ekinnear avatar hst-m avatar jan-ivar avatar jianjunz avatar marcoscaceres avatar martinthomson avatar netizen-ais avatar nidhijaju avatar pthatcher avatar pthatcherg avatar ricea avatar slobo avatar steveanton avatar tidoust avatar vasilvv avatar wilaw avatar ylafon avatar yoavweiss avatar yutakahirano avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webtransport's Issues

Consider adding support for certificates not anchored to Web PKI

I've been talking to some folks who are currently using RTC Data Channels but are interested in using WebTransport. One of the use cases that came up is a situation where a website spins up an ephemeral VM and wants the user to talk to that VM directly. Provisioning a real browser-trusted certificate can be a lot of pain in this scenario. If we don't provide a way to deal with this, we'd regress over what the Data Channels currently provide.

We can do this with QuicTransport, since the API we designed is sufficiently low-level, i.e. we don't risk accidentally leaking the channel with altered trust model into random parts of the web ecosystem; nor do we send any sensitive data like cookies as the part of the handshake.

I've thought about this a bunch, and my current conclusion is that the best way to do this would be to essentially copy the WebRTC mechanism where you just specify the expected certificate hash. Here are some alternatives, and why I believe they are bad:

  • Allowing users to just ignore the certificate. This is bad in an obvious way: it encourages users to not do any authentication at all, steering them towards bad practices. It's also bad in a non-obvious way: if the users do decide to do their own authentication, they would have to bind the underlying cryptographic channel to it, which would require us to support TLS secret exporters.
  • Allowing users to have a custom certificate callback. This was my original instinct, however, on the reflection, I am not quite happy with it. My main concern is that QUIC connections can be intercepted by an enterprise MITM firewall, and the certificate in that firewall could contain potentially sensitive information.

QuicTransportBase.stop() in 'failed' state

If the QuicTransport is in the 'failed' state the transport is essentially closed, but when you call stop() it still sets the transport's state to closed and starts the closing procedure. I think step 2. needs to be changed to:

2. If transport's [[QuicTransportState]] is "closed" or "failed" then abort these steps.

Add SendStream to UnidirectionalStreamsTransport can be racy

  1. Let stream be the newly created SendStream object.
  1. Add stream to transport’s [[OutgoingStreams]] slot.
  1. Continue the following steps in the background.
  1. Create stream’s associated underlying transport.

If underlying transport is pooled Http3 connection. This can be racy. The spec checks if the max stream limit allowed creating new streams, than creats a new SendStream and than it adds it to the trabsport. Since the transport may be used by normal browsing the limit may be already reached again.

Meetings transparency

The ORTC CG is a community group. As such, I find it strange that there are closed (?) meetings that, as far as I can tell, are not publicly announced anywhere so other group members can participate if they wish.

Update based on WICG adoption of C/S specification

Now that the client/server specification is in WICG, several changes are needed:

  1. Updates to reflect the new editor's draft links, issues list, mailing list, etc.
  2. Boilerplate changes.
  3. Changes in references in other specs (e.g. P2P QUIC API).

Converge close() methods

WebSocketStream API defines the close method as follows:

void close(optional WebSocketCloseInfo closeInfo);

WebTransport defines close this way:

void close(WebTransportCloseInfo closeInfo);

Can we converge these?

Request to add possibility to send messages within streams

Rationale

DatagramTransport only works for transports with a strict limitation of datagram's sizes. This doesn't work well for a revamp of e.g. RTCDataChannel and WebSocket which can transport messages of (virtually) arbitrary size. DatagramTransport also does not allow to send datagrams/messages within particular streams. Therefore, I'd like to propose a couple of changes (depends and leans on #46).

Proposal

Boring boilerplate first:

interface UnidirectionalMessageStreamsTransport {
    Promise<SendMessageStream> createSendMessageStream (optional SendMessageStreamParameters parameters);
    ReadableStream<ReceiveMessageStream> receiveMessageStreams ();
};

interface BidirectionalMessageStreamsTransport {
    Promise<BidirectionalMessageStream> createBidirectionalMessageStream ();
    ReadableStream<BidirectionalMessageStream> receiveBidirectionalMessageStreams ();
};
[Exposed=Window]
interface BidirectionalMessageStream : WebTransportStream {
};

BidirectionalMessageStream includes OutgoingMessageStream;
BidirectionalMessageStream includes IncomingMessageStream;
interface SendMessageStream : WebTransportStream {
};

SendMessageStream includes OutgoingMessageStream;

interface ReceiveMessageStream : WebTransportStream {
};

ReceiveMessageStream includes IncomingMessageStream;
[Exposed=Window]
interface mixin OutgoingMessageStream {
    readonly attribute WritableStream<OutgoingMessage> writable;
    readonly attribute Promise<StreamAbortInfo> writingAborted;
    void abortWriting (StreamAbortInfo abortInfo);
};

[Exposed=Window]
interface mixin IncomingMessageStream {
    readonly attribute ReadableStream<IncomingMessage> readable;
    readonly attribute Promise<StreamAbortInfo> readingAborted;
    void abortReading (StreamAbortInfo abortInfo);
};

The interesting part comes with the definition of OutgoingMessage and IncomingMessage:

[Exposed=Window]
interface OutgoingMessage : WritableStream<Uint8Array> {
};

[Exposed=Window]
interface IncomingStream : ReadableStream<Uint8Array> {
};

Note: Abort reading/writing a message does not necessarily mean aborting the stream, but depending on the implementation, it might also abort the stream.

An alternative would be to use the body mixin:

[Exposed=Window]
interface OutgoingMessage : Body {
    void abort (DOMString reason);
};

[Exposed=Window]
interface IncomingStream : Body {
    void cancel (DOMString reason);
};

Example Use Case: RTCDataChannelStream

Now, these interfaces could be used to improve RTCDataChannel in the following way:

partial interface RTCPeerConnection {
    Promise<RTCDataChannelStream> createDataChannelStream (USVString label, optional RTCDataChannelInit dataChannelDict);
    ReadableStream<RTCDataChannelStream> receiveDataChannelStream ();
}

[Exposed=Window]
interface RTCDataChannelStream {
    <relevant fields from RTCDataChannel here>
};

RTCDataChannelStream includes BidirectionalMessageStream;

I'd imagine this would work similarly well for WebSocket.

Discussion

Following this terminology, it would make sense to rename SendStream to SendByteStream, ReceiveStream to ReceiveByteStream, OutgoingStream to OutgoingByteStream, IncomingStream to IncomingByteStream, SendStreamParameters to SendByteStreamParameters, etc. and the various method names correspondingly. That being said, I think it makes sense to incorporate the type of stream into the interface in general to avoid confusion in a dynamically typed language (what kind of stream is this? a stream of bananas?), so this is IMO a good idea to implement regardless of this proposal.

Using the body mixin for OutgoingMessage/IncomingMessage would be a more flexible API but might introduce more complexity, too. I also do not particularly like that it's a ReadableStream for both roles (when reading and when writing) and I don't really know if that would be an ergonomic approach for sending (at least when it comes to terminology, it's awkward). I'm also concerned that it would make piping harder. All of these concerns should be discussed before going with the latter suggestion.

Thoughts? I realise these changes and additions are quite substantial... if you think there's an easier way to achieve the same, let's discuss it.

/cc @ricea

Stats API Requirements

While discussing the stats PR, we began to discuss requirements for a stats API. I'd like to continue and document those requirements here so we can make sure we design the proper stats API.

A comment from @vr000m:

At the high level,

  1. I do not want to traverse getStats to figure out the relationship, if that was explicit somewhere then the getStats could be called on the individual objects. That would avoid get back a mega object.
  2. want to getStats through the lifecycle of the object.

I think one thing Varun called out is that he doesn't want stats appearing or disappearing without a stats API being aware. A stats library needs to be aware of all stats. For example a stream being created/destroyed. A few quick ideas doing this for QUIC stream stats (or any other lower level object):

  • exposing the stream stats in higher level WebTransport object (similar to ORTC model)
  • events new stats with an object creation
  • stats returned by WebTransport object includes current streams so you can pull stats from them as well

Datagram support in Streams

The examples in PR #16 have some issues with datagram boundaries.

Example:
datagramWritableStream.getWriter().write(encodedGameState);
....
reader.read();

Is the expectation that reader.read() will preserve the datagram boundaries provided in write(encodedGameState)?

Better error for when we can't send a datagram?

When we can't send a datagram due to congestion control, we reject a promise with an InvalidStateError. But that InvalidStateError doesn't give the app developer much info. Can we do better?

Partial Reliability

There are situations intermediate between the "reliable transport" and "no retransmissions" modes specified in the current document. As an example, a real-time communications application might want to support maxPacketLifetime or maxRetransmissions, so that real-time data would not be delivered after it had become stale. As an example, the rtx-time parameter in RTX could be emulated over QUIC via maxPacketLifetime applied to a QUIC stream.

There are ongoing discussions about how to support partial reliability in QUIC. Proposals include:

  1. DATAGRAM : https://tools.ietf.org/html/draft-pauly-quic-datagram
  2. Partially reliable streams: https://tools.ietf.org/html/draft-lubashev-quic-partial-reliability
  3. send message, and then RST_STREAM (if maxPacketLifetime or maxRetransmissions is exceeded):
    quicwg/ops-drafts#15

However, from an API point of view, it doesn't appear difficult to extend createStream() for partial reliability:

partial dictionary RTCQuicStreamParameters {
unsigned short maxPacketLifetime ;
unsigned short maxRetransmissions;
};

createSendStream and the max stream limit

What should happen In step 3 of createSendStream() if underlying transport cannot make a new stream due to the max stream limit. According to step 3 it will return a new resolved promise.
It should wait for max stream limit.

Using WHATWG streams for stream creation

The current explainer now uses WHATWG streams for datagrams and for the reading/writing of data for a given uindirectional streams and bidirectional streams. It also uses a stream of streams for receiving streams (so backpressure could be applied if one did not receive a large number of incoming streams fast enough).

But there is one place we don't use streams: creating streams (either unidirectional or bidirectional).

Some options:

A. Don't use streams of streams for creating streams and just apply backpressure to the streams. So while you could have tons of streams created, writes on them all get backpressure. Presumably an app would know not to have too many pending streams (that are receiving back pressure). This would leave the API a big asymmetric between send and receive (making it harder to pipe a stream of messages into a stream-per-message approach), but it works well for both unidirectional and bidirectional streams.

B. Use a stream of streams for creation of unidirectional send streams where you pass in a ReadableStream that gets is used to write to the send stream. This has the oddity that you're using a read stream to write, but one can trivially make a ReadableStream out of a byte array, so this makes it easy to do a "stream-per-message" approach. I don't see how it would work for bidirectional streams, but a "stream-per-message" approach will likely only want unidirectional streams anyway.

C. Don't use streams of streams for creating streams, but apply backpressure on creation using the fact that creating streams is async. Just don't resolve the promise until stream creation is unblocked. This seems like a natural thing to do. Like A, it's not ideal for stream-per-message approaches, but I believe one could implement a B on top of C with some JS.

Right now, I'm inclined toward doing both C and maybe B. C seems to work well for most use cases but B seems to offer more symmetry and also is better for stream-per-message approaches. And even though I think you could do it with JS on top, there might be performance and convenience reasons to make it part of the API.

Stats for congestion control and bandwidth estimation

One of the use cases we are interested in is media streaming; when streaming media, the application can often decide to change the amount of data it sends based on how much bandwidth it expects to have available. Since all of the transports we define are congestion-controlled, we already naturally have to make some form of a guess regarding how much data the path can handle (even though it can be as rudimentary as CWND/RTT).

We should provide an API that lets the underlying transport library expose this kind of data to the Web application. My intuitive idea would be estimateBytesAvailable(time), or even estimateBytesAvailable(time, p), for models that accept the target probability of not oversending (for fancier models that accept such parameter, e.g. Sprout).

cc @keithw, who is an expert on this topic and might have a much better idea of how this API should look.

Document interaction with workers

(from TAG review)

We currently do not mention workers in our spec, which I believe implies you're not allowed to open WebTransport sessions in worker threads. I don't believe we have any strong reasons not to allow that.

Consider removing disableRetransmissions

I believe we've discussed this before in person. The feature, as it currently is in spec, is somewhat underspecified and we never actually heard from anyone about wanting to use that feature as-is. Perhaps a good alternative would be export enough stats that the developer could make a good informed decision about cancellation themselves.

There is no interface to represent message-based transports

DatagramTransport only works for transports with a strict limitation of datagram's sizes. This doesn't work well for a revamp of e.g. RTCDataChannel and WebSocket which can transport messages of (virtually) arbitrary size.

I'd like to propose a MessageTransport API (depends and leans on #46):

interface mixin MessageTransport {
    WritableStream<WritableStream<Uint8Array>> sendMessages ();
    ReadableStream<ReadableStream<Uint8Array>> receiveDatagrams ();
};

An alternative would be to use the body mixin:

interface mixin MessageTransport {
    WritableStream<Body> sendMessages ();
    ReadableStream<Body> receiveDatagrams ();
};

The body mixin would be a more flexible API but probably introduce more complexity, too. I also do not particularly like that it's a ReadableStream for both roles (when reading and when writing) and I don't really know if that would be an ergonomic approach for sending (at least when it comes to terminology, it's awkward). I'm also concerned that it would make piping harder. All of these concerns should be discussed before going with the latter suggestion.

/cc @ricea

Beta testing availability?

Hi there, I understand the spec is still in somewhat early stages, I wonder if there's already plans on actual implementation inside any browser's beta channels? I'd love to play around with it as soon as it's available.

The web needs this. Thanks ❤️

Editor meeting details

We'd like to have a place for public discussion, so we should post a link to our biweekly meeting in the README.

CSP policies for WebTransport?

Currently, data exchange capabilities that are exposed to the Web are restricted through CSP connect-src, which restricts which origins can be connected to through WebSockets, XHR, etc.

  1. Will every WebTransport destination have an origin that can be verified by the UA (through Web PKI or some other means)?
  2. If the answer to "1" is yes, will WebTransport use connect-src or some other CSP policy mechanism?
  3. If the answer to "1" is no, how will application authors be able to enforce a WebTransport connection policy in their application?

Is streamId needed?

streamId is exposed on WebTransportStream but there are no APIs that consume it. As such, it doesn't appear to have a legitimate purpose. Unnecessarily exposing information to JavaScript can lead to security and privacy issues, so I propose removing it.

Convergence with WebSocketStream

WebSocketStream API defines close() and WebSocketCloseInfo as follows:

void close(optional WebSocketCloseInfo closeInfo);

dictionary WebSocketCloseInfo {
  [Clamp] unsigned short code;
  USVString reason = "";
};

WebTransport defines close() and WebTransportCloseInfo this way:

void close(WebTransportCloseInfo closeInfo);

dictionary WebTransportCloseInfo {
    unsigned short errorCode = 0;
    DOMString      reason = "";
};

Can we converge these?

How do we identify these things?

The current specs assume that URIs are the identifier. QUIC transport proposes a new scheme. However that presumes certain things about the transport features and granularity of the identification. For instance, the current design assumes exclusive access to the entire connection when used with QUIC transport.

A new scheme also has an unfortunate consequence in terms of how we integrate with Fetch and related things like CSP. WebSockets minted a URI scheme, but the ultimate decision was to erase that and replace it with the equivalent http:// or https:// prefix. This is likely to need a similar sort of allowance.

More seriously, we have an identifier for "something", but we don't really know what that thing is. We need to understand how all of the things in the protocol might be identified. Whether that includes details of the protocol stack is something we will have to understand.

BidirectionalStreamsTransport receiveBidirectionalStreams error

In https://wicg.github.io/web-transport/#%23bidirectional-streams-transport-methods section receiveBidirectionalStreams()

It says:

For each unidirectional stream received, create a corresponding IncomingStream and insert it into [[ReceivedStreams]]. As data is received over the unidirectional stream, insert that data into the corresponding IncomingStream. When the remote side closes or aborts the stream, close or abort the corresponding IncomingStream.

I think this is an error, it should read bidirectional streams, but I also not sure if it means we should also add add the stream as part of the receivedStreams. I would think that's not a good idea, but I'm not really sure (I'm pretty new to the spec). Thanks!

Exposing when a datagram is acked is a footgun

Now that we've added datagram support, Victor had a good point: an app can't rely on it. It's possible, for example, for a packet to be acked, but then dropped by the receiving browser because the JS can't keep up. Then you think it's acked, but it's not been processed. So you have to do app-level acking/feedback anyway and shouldn't trust the ack info.

They had a similar issue with HTTP3's QPACK which couldn't rely on QUIC ACKs (for streams) because there was a timing issue between the sender thinking something had been received an when the receiving application actually processed it.

Is there a scenario in which the ack info is useful and isn't a foot gun in this way?

Spec appears to potentially allow violation of the same-origin policy, cross-protocol attacks, etc.

WebSockets, limited though it is, was carefully designed to avoid violations of the browser same-origin policy via a handshake an optional use of CORS, and through the distinctive handshake and same-origin requirement, avoid risk of use for cross-protocol attacks. The same holds true for Fetch used with bidirectional streams.

This spec, on the face of it, appears to allow connecting to an arbitrary host:post (for the QUIC transport) or arbitrary URL (for the HTTP3 transport).

Same-origin policy applies protection not only to resources protected by stateful credentials like cookies, but also to resources protected solely by virtue of network position, such as via firewalls.

Furthermore, with this spec, cross-protocol attacks against any QUIC-based protocol may be possible. In addition, by opening up relatively "raw" messaging, the spec may allow for HTTP interactions that are deliberately not exposed through Fetch API for security reasons.

The Security Considerations section does not explain how these attacks are avoided, or alternately, why it is ok that they are allowed. Nor does the explainer give any indication that these types of attacks were considered as part of the threat model.

This seems like a potentially fatal problem with this spec as written, unless I am misreading or missing something. While the use cases are compelling, solving them while addressing security issues in a satisfactory way is the key problem for work in this space.

QuicTransportBase is not subclassable by user code

QuicTransportBase cannot be subclassed from JavaScript because it has no constructor. It's usually considered bad practice in the web platform to have classes that are not subclassable.

Since it only provides one method in addition to the mixins it uses, it probably is not needed at all. Any variants could just use the same mixins.

Forward error correction?

One of the lacking features of WebRTC data channels is that there is no native FEC under the hood, forcing developers to use JS or Web Assembly based alternatives. If FEC could be enabled and negotiated by both sides, this would allow for high-performance real-time data applications on more unreliable networks without retransmission.

"resolve" or "fulfill" a promise

Ah, promises.... your terminology is so confusing. Should we say "resolve" a promise or "fulfill" a promise?

Reading these docs:

https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md
https://stackoverflow.com/questions/29268569/what-is-the-correct-terminology-for-javascript-promises

may leave you tending toward "fulfill".

But a sampling of recent web specs shows almost all of them using only "resolve":

https://www.w3.org/TR/2018/WD-wasm-web-api-1-20180904/
https://www.w3.org/TR/2018/CR-webrtc-stats-20180703/
https://www.w3.org/TR/2018/WD-clipboard-apis-20181108/
https://www.w3.org/TR/2018/WD-push-api-20181026/
https://www.w3.org/TR/2018/WD-screen-orientation-20181012/

But at least one is using the word "fulfilment":

https://www.w3.org/TR/2018/WD-web-animations-1-20181011/

And WebRTC uses "resolve" (~20 times) and "fulfillment" (~10 times) and "fulfill" once:

https://www.w3.org/TR/2018/CR-webrtc-20180927/

Perhaps the right words are "resolve" and "fulfillment"?

Interaction with tooling

I was thinking about it, and there's a bunch of tooling on the web that assumes you have a URL for your resource (for example, so that an extension could filter those). Do we want to define URL for WebTransport? Do we want it to be something we expose in the API or something we only use in tooling? If a single server is reachable via multiple transports, do we want them to have same URL, or just one URL per transport?

Probably should be discussed once we move to WICG, filing here so we don't forget.

WHATWG streams

I've experimented with WHATWG streams for a few weeks. In my little demo it works pretty good now (apart from a few nits here and there that should be resolvable) and makes a much more composable API. Once more people start utilising WHATWG streams, piping from one stream to another or applying transformations can be made without much of a hassle. It also brings the advantage that data can be fed from the user application into the QUIC stream buffer directly without requiring to copy it.

I had to pause working on this as I need to work on my master thesis now. However, I don't want to hold back this idea any further and show you my current results. In case anyone else wants to jump in, feel free! Otherwise, I'll hopefully come back to this once I have some spare time.

Feedback welcome!

Spec

I've updated the spec but it's still work in progress. While methods and attributes of RTCQuicStream have been updated for WHATWG streams, general description and specification of the construction of a readable and a writable stream instance is still missing. However, you can have a look at the code if you want to see how I've written it for the demo.

The spec changes can be found here and on GitHub.

Note: This was originally created for the ORTC Spec, so my spec changes may not be entirely accurate for this spec here.

Demo

The demo can be started here and the code can be found here.

Datagram vs stream & relative stream prioritization

Currently, we specify no particular order in which stream data and datagrams should go.

The way the current scheme works in Google's implementation is roughly:

  1. A timer or a received packet causes the connection to become writable.
  2. The connection notifies the application so that it can send its datagrams.
  3. The connection checks the send buffers for the open streams and sends stream data.

This works only if all operations are synchronous, and we can't make step 2 synchronous since the JS callback would be asynchronous. Thus, we need to come up with a way to provide an opening for the JS to send datagrams while there's always stream data available to send.

A "version 0" approach would be to add fixed size buffer of datagram that gets always drained before stream data. That works, but if the buffer is too small, it can hurt performance, and if it's too big, this can lead to overbuffering.

One possible improvement would be to dynamically size the datagram buffer based on the congestion control window or some other criteria. One could possibly let the user set that buffer size manually. There is still, however, an implicit race between the JS application adding datagrams into the buffer and the network process deciding to send pending stream data instead.

Another idea I had is that the user can "reserve" a portion of congestion window for datagrams and leave the rest for stream data. That would look roughly like this: a user asks for a "token" that reserves a portion of the congestion window aside for a datagram. When the user sends the datagram, they return that token, since the reserved portion of the window is now used by the newly sent datagram.

Unused Internal Slots

Nothing uses ReceivedDatagrams & ReceiveDatagramsPromise internal slots, but we reference them in both constructors. We should either remove them in the constructor or mention how they are used in the DatagramTransport Mixin.

Same goes for a ReadyToSendPromise internal slot.

Ambiguity in CORS-like mechanism in the handshake

https://wicg.github.io/web-transport/#protocol-security says: “All transports must allow the server to filter connections based on the origin of the resource originating the transport session.” but this doesn’t seem to be quite what is specified in https://tools.ietf.org/html/draft-vvv-webtransport-quic-00 which says "QuicTransport uses a QUIC transport parameter to provide the user agent with an origin whitelist. The origin is not sent explicitly, as TLS ClientHello messages are sent in cleartext; instead, the server provides the user agent with a whitelist of origins that are allowed to connect to it."

The QuicTransport spec thus imposes two serious limits:

  1. The server MUST know, a priori, all hosts to which it may want to allow to communicate with it. There's no possibility of implementing a blocklist, or a sub-origin wildcarding scheme (e.g. *.example.com)
  2. The server has no trustworthy means of knowing the origin of a connection, unless it advertises only a single origin in web_accepted_origins.

Coalesce small writes

Imagine an application has a lot of streams open, and writes 100 bytes into each of them in a loop. Ideally, we would want them to be in the same packet when possible. However, with current interpretation of the text, they would all result in a new write call into QUIC, and thus each stream write would get its own packet.

My current intuition is that we should bundle all writes within the scope of the current microtask. I am not sure that this is a well-defined operation, so we might have to just queue a task instead.

Support for PSK mode?

I've just learned about TLS 1.3 PSK mode, which sounds like it could be very useful.

Basically, instead of the app having to provide a remote certificate fingerprint and send a local certificate fingerprint to the remote side, the app could provide a pre-shared secret on both ends. That could probably simplify the use of the API in p2p cases, and it could provide an easy extra layer of authentication (such as the app having the user provide the pre-shared secret to authenticate the its endpoint to the other endpoint).

Would this be worth pursuing? Could we trust the web app with providing a secure PSK (if you just provide "1234" every time, it doesn't do much good).

Use WHATWG Streams

This was one of the first issues that we had in webrtc-quic. I believe we're currently at the point where we all agree we should do this, and it's mostly blocked on writing the actual pull request.

References to QUIC in reusable interfaces

UnidirectionalStreamsTransport.createSendStream and BidirectionalStreamsTransport.createBidirectionalStream refer to QUIC regarding the maximum amount of streams but shouldn't.

HTTP/3 Transport and Alt-Svc

During the WEBTRANS BoF at IETF Singapore 106 I made a comment at the microphone about the relationship between WebTransport over HTTP/3 and Alt-Svc. I was asked to open an issue here.

Simple Problem
The following are requirements from https://tools.ietf.org/html/draft-vvv-webtransport-http3-01#section-4.1:

  • In order to indicate support for Http3Transport, both the client and
    server MUST send an empty "http3_transport_support" transport
    parameter. Endpoints MUST NOT use any Http3Transport-related
    functionality unless the parameter has been negotiated.

  • If "http3_transport_support" is negotiated, support for the QUIC
    DATAGRAM extension MUST be negotiated.

For any given HTTP/3 connection with a value of "h3-draft" that meets the following above criteria, what happens if an Alt-Svc advert on that connection incorrectly causes a client to attempt a new connection that cannot meet the same requirements? I think marking the alternative as broken is probably good enough.

Broader problem
An HTTP/3 connection that coalesces connections might trip up if some of the traffic to origin is intended to be Http3Transport and some is not. An alternative for a non-Http3Transport-using authority might cause a complete changeover to fail.

HTTP/2 defines the 421 status code that can be used to mitigate such problems but it doesn't apply to all possible error cases (only the one where extended CONNECT fails).

I think in reality, an Http3Transport server will likely want to be smart about the Alt-Svc values it produces.

Broader broader problem
How does a client discover an Http3Transport service?

We're also talking about putting Alt-Svc in the DNS. How does a client learn of the availability of Http3Transport providing server based on its name, should it even be able to?

Pooled connections?

I've recently heard interest in allowing the client/server QuicTransport to use the same QUIC connection(s) as the ones that are used for HTTP, perhaps even the one the page has used to load.

It might work something like this:

let quic = QuicTransport.getOrCreateWithPooledConnection(hostname, port);

If there's already a QUIC connection (either QuicTransport or HTTP) to that (hostname, port), then use the same QUIC connection for the QuicTransport (and if such a QuicTransport already exists, just return the same one). If not, create one. If created, it can be used by future HTTP traffic as well.

You may realize this has one big problem: how do you differentiate between HTTP streams and non-HTTP streams?

Here are some options:

A. Support datagrams. It's not as useful has having streams, but it's still useful.

B. Support unidirectional streams with the new 1-byte HTTP stream type (see https://tools.ietf.org/html/draft-ietf-quic-http-17#section-3.1). We can pick a value (only 2 out of 256 are allocated so far) and use that to indicate which streams are which. It eats up a byte in each stream, but we can hide that from the client API, and it's still pretty useful.

C. Support bidirectional streams created by the server. HTTP doesn't use them, so we could assume they aren't HTTP. It runs a foul of the text which says 'clients MUST omit or specify a value of zero for the QUIC transport parameter "initial_max_bidi_streams"' but maybe we can work around that.

D. Some combination of A+B+C, potentially having everything work except client-initiated bidirectional streams.

E. Add an extension to QUIC stream frames that add a "stream group ID" allowing many protocols to use all the stream IDs but within its own group. Kind of like many QuicTransports within one QUIC connection. Could also be useful for running separate HTTP sessions to different hostnames which could resolve to the same host and QUIC connection.

Currently, I'm inclined toward A + B and just saying that we don't support bidirectional streams with pooled connections. E would be nice, but it's probably too late for it to happen.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.