Kestrel currently implements most of the WebTransport draft-02 specification, except for datagrams. Datagrams will be implemented at a later date. This document outlines how to use the functionality.
To help applications get started on implementing WebTransport, there is the WebTransportSampleApp
project. You can find it in src\Servers\Kestrel\samples\WebTransportSampleApp
. To use it simply run it from VS. This will launch the server and a terminal which will show logs that Kestrel prints. Now you should be able to connect to it from any client that implements the standard WebTransport draft02 specification.
Note: Once you run the WebTransportSampleApp, it will print the certificate hash that it is using for the SSL connection. You will need to copy and paste it into your client to make sure that both the server and the client use the same one.
The Chromium project has implemented a WebTransport client and can be accessed via their JS API from the Chrome or Edge DevTools console. A good sample app demoing how to use that API can be found here.
To setup a WebTransport connection, you will first you wil need to configure a host upon which you open a port. A very minimal example is shown below:
public class Program
{
public static void Main(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureWebHost(webHost =>
{
webHost.UseKestrel()
.ConfigureKestrel((context, options) =>
{
options.Listen([SOME IP ADDRESS], [SOME PORT], listenOptions =>
{
listenOptions.UseHttps([YOUR CERTIFICATE]);
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
});
})
.UseStartup<Startup>();
});
var host = hostBuilder.Build();
host.Run();
}
}
Note: As WebTransport uses HTTP/3, you must make sure to select the listenOptions.UseHttps
setting as well as set the listenOptions.UseHttps
to include HTTP/3.
Next, we defined the Startup
file. This file will setup the application layer logic that the server will use to accept and manage WebTransport sessions and streams.
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services) { }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var feature = context.Features.GetRequiredFeature<IHttpWebTransportFeature>();
if (feature.IsWebTransportRequest)
{
var session = await feature.AcceptAsync(CancellationToken.None);
// Do WebTransport stuff
}
else
{
await next(context);
}
});
}
}
The Configure
method is the main entry-point of your application logic. The app.Use
block is triggered every time there is a connection request. Once the request is a WebTransport request (which is defined by getting the IHttpWebTransportFeature
feature and then checking the IsWebTransportRequest
property), you will be able to accept WebTransport sessions and interact with the client.
This section highlights some of the most significant features of WebTransport that KEstrel implements. This is not an exhaustive list.
- Accept a WebTransport Session
var session = await feature.AcceptAsync(CancellationToken token);
This will await for the next incoming WebTransport session and return an instance of IWebTransportSession
when a connection is completed. A session must be created prior to any streams being created or any data is sent. Note that only clients can initiate a session, thus the server passively waits until one is received and cannot initiate it's own session. The cancellation token can be used to stop the operation.
- Accepting a WebTransport stream
var stream = await session.AcceptStreamAsync(CancellationToken token);
This will await for the next incoming WebTransport stream and return an instance of WebTransportStream
. Note that streams are buffered in order and so this call pops from the front of the queue of pending streams. However, if no streams are pending, it will block until it receives one. You can use the cancellation token to stop the operation.
Note: This method will return both bidirectional and unidirectional streams. They can be distinguished based on the stream.CanRead
and stream.CanWrite
properties.
- Opening a new WebTransport stream from the server
var stream = await session.OpenUnidirectionalStreamAsync(CancellationToken token);
This will attempt to open a new unidirectional stream from the server to the client and return an instance of WebTransportStream
. You can use the cancellation token to stop the operation.
- Sending data over a WebTransport stream
await stream.WriteAsync(ReadOnlyMemory<byte> bytes);
await stream.FlushAsync();
stream.WriteAsync
will write data to the stream but it will not automatically flush (i.e. send it to the client). Therefore, after the stream.WriteAsync
, you will need to call stream.FlushAsync
.
Note: You can only send data on streams that have stream.CanWrite
set as true
. Sending data on non-writable streams will throw an NotSupportedException
exception.
- Reading data from a WebTransport stream
var length = await stream.ReadAsync(Memory<byte> memory);
stream.ReadAsync
will read data from the stream and copy it into the provided memory
parameter. It will then return the number of bytes read.
Note: You can only read data from streams that have stream.CanRead
set as true
. Reading data on non-readable streams will throw an NotSupportedException
exception.
- Aborting a WebTransport session
session.Abort(int errorCode = 256);
Aborting a WebTransport session will result in severing the connection with the client and aborting all the streams. You can optionally specify an error code that will be passed down into the logs. The default value (256) represents no error.
- Aborting a WebTransport stream
stream.Abort(int errorCode = 256);
Aborting a WebTransport stream will result in abruptly stopping all data transmission and prevent further communication over this stream. The default value (256) represents no error.