dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.36k stars 4.74k forks source link

[API Proposal]: Determining the http protocol version chosen by ClientWebSocket #75353

Open Tratcher opened 2 years ago

Tratcher commented 2 years ago

Background and motivation

.NET 7 now supports WebSockets over HTTP/2. ClientWebSocket can negotiate with the server to choose HTTP/1.1 or HTTP/2 based on what's available. However, there's not an easy way to determine which protocol was used. HttpResponseMessage has the Version field to let you know this, but ClientWebSocket does not.

This information is mainly useful for testing, logging, diagnostics, etc., the WebSocket works the same either way.

API Proposal

namespace System.Net.WebSockets;

public class ClientWebSocket : WebSocket
{
    // Returns the Http version negotiated by ConnectAsync, or HttpVersion.Unknown if not available.
    public Version HttpVersion { get; }
}

API Usage

        var wsClient = new ClientWebSocket();
        wsClient.Options.HttpVersion = HttpVersion.Http20;
        wsClient.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
        wsClient.Options.CollectHttpResponseDetails = false; // Shouldn't be required
        var httpClient = new HttpClient();
        await wsClient.ConnectAsync(new Uri(url), httpClient, default);
        Console.WriteLine("Negotiated: " + wsClient.Version.ToString());

Alternative Designs

Workaround: Check if the HttpStatusCode returns 101 (HTTP/1.1) or 200 (HTTP/2). Note you need to enable CollectHttpResponseDetails for HttpStatusCode to be populated (weird).

Risks

No response

ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/ncl See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation .NET 7 now supports WebSockets over HTTP/2. ClientWebSocket can negotiate with the server to choose HTTP/1.1 or HTTP/2 based on what's available. However, there's not an easy way to determine which protocol was used. HttpResponseMessage has the Version field to let you know this, but ClientWebSocket does not. This information is mainly useful for testing, logging, diagnostics, etc., the WebSocket works the same either way. ### API Proposal ```csharp namespace System.Net.WebSockets; public class ClientWebSocket : WebSocket { // Returns the Http version negotiated by ConnectAsync, or HttpVersion.Unknown if not available. public Version HttpVersion { get; } } ``` ### API Usage ```csharp var wsClient = new ClientWebSocket(); wsClient.Options.HttpVersion = HttpVersion.Http20; wsClient.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrLower; wsClient.Options.CollectHttpResponseDetails = false; // Shouldn't be required var httpClient = new HttpClient(); await wsClient.ConnectAsync(new Uri(url), httpClient, default); Console.WriteLine("Negotiated: " + wsClient.Version.ToString()); ``` ### Alternative Designs Workaround: Check if the HttpStatusCode returns 101 (HTTP/1.1) or 200 (HTTP/2). Note you need to enable CollectHttpResponseDetails for HttpStatusCode to be populated (weird). ### Risks _No response_
Author: Tratcher
Assignees: -
Labels: `api-suggestion`, `area-System.Net`
Milestone: -
MihaZupan commented 2 years ago

Workaround: Check if the HttpStatusCode returns 101 (HTTP/1.1) or 200 (HTTP/2). Note you need to enable CollectHttpResponseDetails for HttpStatusCode to be populated (weird).

Some discussion around that: https://github.com/dotnet/runtime/pull/71757#discussion_r915907922. Version may be similar here.

I agree we should let you query Version even if CollectHttpResponseDetails isn't set (worst-case it's Unknown as you point out).

karelz commented 2 years ago

Triage: We could leverage the HttpVersion.Unknown when the info is not available (e.g. in Browser) or follow pattern of StatusCode. We need a decision, ideally in 8.0. Implementation afterwards will be simple.

wfurt commented 5 months ago

triage: not urgent, moving to future.