Description
Background and motivation
Currently we are supporting WebSockets over HTTP/1.1 only. For WebSockets over HTTP/2 the protocol is quite different and described by RFC #8441. It is already supported by Chrome and there are an interest from YARP and ASP.NET partners. The main improvement is that supporting WebSockets over HTTP/2 will give advantage of multiplexing connection.
API Proposal
// EXISTING
class ClientWebSocket : WebSocket
{
// EXISTING
public Task ConnectAsync(Uri uri, CancellationToken cancellationToken);
// NEW
public Task ConnectAsync(Uri uri, HttpMessageInvoker invoker, CancellationToken cancellationToken);
}
// EXISTING
class ClientWebSocketOptions
{
// NEW
public System.Version HttpVersion { get { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
set { } };
public System.Net.Http.HttpVersionPolicy HttpVersionPolicy { get { throw null; }
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
set { } };
}
class HttpMethod : IEquatable<HttpMethod>
{
// EXISTING
// internal static HttpMethod Connect { get { throw null; } }
// NEW
public static HttpMethod Connect { get { throw null; } }
}
class HttpRequestHeaders : HttpHeaders
{
public string? Protocol { get { } set { } };
}
API Usage
var handler = new SocketsHttpHandler();
ClientWebSocket ws = new();
ws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
ws.Options.HttpVersion = HttpVersion.Version20;
ws.ConnectAsync(uri, new HttpMessageInvoker(handler), cancellationToken);
HttpRequestMessage request = new(HttpMethod.Connect, server.Address);
request.Headers.Protocol = "websocket";
Notes
-
HttpVersion
andHttpVersionPolicy
inClientWebSocketOptions
are similar toHttpClient
's property.ClientWebSocketOptions
for browser is a class with completely different properties, so the new properties don't affect it. -
Providing an external handler to
ClientWebSocket
is important because it enables advantage of HTTP/2 multiplexing by reusing the same handler for other ordinary HTTP/2 streams. Generic handlerHttpMessageInvoker
in ConnectAsync is a better alternative thanSocketsHttpHandler
. There is no need to check that it isSocketsHttpHandler
but it should be able to handle WebSocket requests and it is up to the user who provides it. -
The property for the
:protocol
header inHttpRequestHeaders
is required because we need to guarantee that pseudo headers are appeared before all regular headers based on spec.
Other alternatives considered, but rejected:
- Pass a low-level
SocketsHttpHandler
instead ofHttpMessageInvoker
. - Only one property
HttpVersion
if we do not require downgrade handling. In that case we rely on the version the user provides.
Risks
Adding new fields might increase memory usage.