Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
30da832
downgrade proposal for HttpClient
May 24, 2022
7b03bc8
HttpClient to handle ws over h2
Jun 7, 2022
e7b8332
ClientWebSocket to handle ws over h2
Jun 7, 2022
a5e991a
Add property for protocol header and remove it from known headers
Jun 17, 2022
264c802
Update src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestM…
Jun 18, 2022
08d1f8c
Address review feedback
Jun 18, 2022
71cc887
Rename HttpVersion and HttpVersionPolicy
Jun 20, 2022
8019a3c
Apply suggestions from code review
Jun 21, 2022
8ef8b4c
address review feedback
Jun 21, 2022
ac966a8
Merge branch 'main' into ws-h2-draft
Jun 23, 2022
f6de72a
address review feedback
Jun 24, 2022
9221483
address review feedback
Jun 28, 2022
fafc84b
Apply suggestions from code review
Jun 28, 2022
0d8f8f7
fix race condition on setting enable connect
Jun 29, 2022
9be5b95
inherit h2 read and writes streams
Jun 29, 2022
502b051
Apply suggestions from code review
Jun 30, 2022
e635439
fix H2PACK encoding issue
Jun 30, 2022
ad66857
add timeout for waiting settings task
Jun 30, 2022
c58d993
generalized settings received task
Jun 30, 2022
76c9e20
Update src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpH…
Jul 1, 2022
2467449
Fixing HttpStream tests
Jul 1, 2022
d9248a6
Apply suggestions from code review
Jul 4, 2022
baf52d4
address review feedback
Jul 4, 2022
afaf5be
Apply suggestions from code review
Jul 8, 2022
918c6a8
Address review feedback
Jul 8, 2022
9734075
Adapt test to ValueTask.FromException
Jul 8, 2022
efdcdba
Apply suggestions from code review
Jul 10, 2022
6f7b101
Adding connect tests
Jul 10, 2022
595642c
Apply suggestions from code review
Jul 10, 2022
e05db11
feedback + skip tests on browser
Jul 10, 2022
54d0b08
Merge branch 'main' into ws-h2-draft
Jul 10, 2022
b7f543b
Feedback + test for websocket stream
Jul 12, 2022
73968ec
Update src/libraries/System.Net.Http/src/Resources/Strings.resx
Jul 12, 2022
4dbe1c2
Address review feedback
Jul 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feedback + skip tests on browser
  • Loading branch information
Katya Sokolova committed Jul 10, 2022
commit e05db11d235350755d042fc8cd3612d148d68de7
2 changes: 1 addition & 1 deletion src/libraries/System.Net.Http/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,6 @@
<value>The HTTP/1.1 response chunk was too large.</value>
</data>
<data name="net_unsupported_extended_connect" xml:space="preserve">
<value>Extended CONNECT is not supported.</value>
<value>Failed to establish web socket connection over HTTP/2 because extended CONNECT is not supported. Try to downgrade the request version to HTTP/1.1</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public sealed class HttpRequestHeaders : HttpHeaders
private HttpGeneralHeaders? _generalHeaders;
private HttpHeaderValueCollection<NameValueWithParametersHeaderValue>? _expect;
private bool _expectContinueSet;
private string? _protocol;

#region Request Headers

Expand Down Expand Up @@ -159,7 +160,15 @@ public int? MaxForwards
set { SetOrRemoveParsedValue(KnownHeaders.MaxForwards.Descriptor, value); }
}

public string? Protocol { get; set; }
public string? Protocol
{
get => _protocol;
set
{
CheckContainsNewLine(value);
_protocol = value;
}
}

public AuthenticationHeaderValue? ProxyAuthorization
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ internal sealed partial class Http2Connection : HttpConnectionBase
// Equivalent to the bytes returned from HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewNameToAllocatedArray(":protocol")
private static ReadOnlySpan<byte> ProtocolLiteralHeaderBytes => new byte[] { 0x0, 0x9, 0x3a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c };

private static readonly TaskCompletionSourceWithCancellation<bool> s_settingsReceivedSingleton = CreateSuccessfullyCompletedTcs();

private TaskCompletionSourceWithCancellation<bool>? _initialSettingsReceived;

private readonly HttpConnectionPool _pool;
private readonly Stream _stream;

Expand Down Expand Up @@ -176,6 +180,13 @@ static long TimeSpanToMs(TimeSpan value) {

private object SyncObject => _httpStreams;

internal TaskCompletionSourceWithCancellation<bool> InitialSettingsReceived =>
_initialSettingsReceived ??
Interlocked.CompareExchange(ref _initialSettingsReceived, new(), null) ??
_initialSettingsReceived;

internal bool IsConnectEnabled { get; private set; }

public async ValueTask SetupAsync()
{
try
Expand Down Expand Up @@ -1468,7 +1479,6 @@ private void WriteHeaders(HttpRequestMessage request, ref ArrayBuffer headerBuff
{
if (request.Headers.Protocol != null)
{
HttpHeaders.CheckContainsNewLine(request.Headers.Protocol);
WriteBytes(ProtocolLiteralHeaderBytes, ref headerBuffer);
Encoding? protocolEncoding = _pool.Settings._requestHeaderEncodingSelector?.Invoke(":protocol", request);
WriteLiteralHeaderValue(request.Headers.Protocol, protocolEncoding, ref headerBuffer);
Expand Down Expand Up @@ -1918,22 +1928,12 @@ private enum SettingId : ushort
EnableConnect = 0x8
}

private static readonly TaskCompletionSourceWithCancellation<bool> s_settingsReceivedSingleton = CreateSuccessfullyCompleted();

internal TaskCompletionSourceWithCancellation<bool> InitialSettingsReceived =>
_initialSettingsReceived ??
Interlocked.CompareExchange(ref _initialSettingsReceived, new(), null) ??
_initialSettingsReceived;

private TaskCompletionSourceWithCancellation<bool>? _initialSettingsReceived;

private static TaskCompletionSourceWithCancellation<bool> CreateSuccessfullyCompleted()
private static TaskCompletionSourceWithCancellation<bool> CreateSuccessfullyCompletedTcs()
{
var tcs = new TaskCompletionSourceWithCancellation<bool>();
tcs.TrySetResult(true);
return tcs;
}
internal bool IsConnectEnabled { get; private set; }

// Note that this is safe to be called concurrently by multiple threads.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,8 @@ public async ValueTask<HttpResponseMessage> SendWithVersionDetectionAndRetryAsyn
{
if (request.IsWebSocketH2Request())
{
if (await connection.InitialSettingsReceived.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false) && !connection.IsConnectEnabled)
await connection.InitialSettingsReceived.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false);
if (!connection.IsConnectEnabled)
{
HttpRequestException exception = new(SR.net_unsupported_extended_connect);
exception.Data["SETTINGS_ENABLE_CONNECT_PROTOCOL"] = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@
<data name="net_WebSockets_ArgumentOutOfRange_TooSmall" xml:space="preserve">
<value>The argument must be a value greater than {0}.</value>
</data>
<data name="net_WebSockets_Connect101Expected" xml:space="preserve">
<value>The server returned status code '{0}' when status code '101' was expected.</value>
</data>
<data name="net_WebSockets_Connect200Expected" xml:space="preserve">
<value>The server returned status code '{0}' when status code '200' was expected.</value>
<data name="net_WebSockets_ConnectStatusExpected" xml:space="preserve">
<value>The server returned status code '{0}' when status code '{1}' was expected.</value>
</data>
<data name="net_WebSockets_MissingResponseHeader" xml:space="preserve">
<value>The server's response was missing the required header '{0}'.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public async Task ConnectAsync(Uri uri, HttpMessageInvoker? invoker, Cancellatio

string? secValue = AddWebSocketHeaders(request, options);

// Issue the request. The response must be status code 101.
// Issue the request.
CancellationTokenSource? linkedCancellation;
CancellationTokenSource externalAndAbortCancellation;
if (cancellationToken.CanBeCanceled) // avoid allocating linked source if external token is not cancelable
Expand Down Expand Up @@ -355,7 +355,6 @@ static int ParseWindowBits(ReadOnlySpan<char> value)
else if (request.Version == HttpVersion.Version20)
{
request.Headers.Protocol = "websocket";
request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.Origin, request.Headers.Host);
}

request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.SecWebSocketVersion, "13");
Expand Down Expand Up @@ -413,7 +412,7 @@ private static void ValidateResponse(HttpResponseMessage response, string? secVa
{
if (response.StatusCode != HttpStatusCode.SwitchingProtocols)
{
throw new WebSocketException(WebSocketError.NotAWebSocket, SR.Format(SR.net_WebSockets_Connect101Expected, (int)response.StatusCode));
throw new WebSocketException(WebSocketError.NotAWebSocket, SR.Format(SR.net_WebSockets_ConnectStatusExpected, (int)response.StatusCode, (int)HttpStatusCode.SwitchingProtocols));
}

Debug.Assert(secValue != null);
Expand All @@ -427,7 +426,7 @@ private static void ValidateResponse(HttpResponseMessage response, string? secVa
{
if (response.StatusCode != HttpStatusCode.OK)
{
throw new WebSocketException(WebSocketError.NotAWebSocket, SR.Format(SR.net_WebSockets_Connect200Expected, (int)response.StatusCode));
throw new WebSocketException(WebSocketError.NotAWebSocket, SR.Format(SR.net_WebSockets_ConnectStatusExpected, (int)response.StatusCode, (int)HttpStatusCode.OK));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static IEnumerable<object[]> UnavailableWebSocketServers
{
server = System.Net.Test.Common.Configuration.Http.RemoteEchoServer;
var ub = new UriBuilder("ws", server.Host, server.Port, server.PathAndQuery);
exceptionMessage = ResourceHelper.GetExceptionMessage("net_WebSockets_Connect101Expected", (int) HttpStatusCode.OK);
exceptionMessage = ResourceHelper.GetExceptionMessage("net_WebSockets_ConnectStatusExpected", (int) HttpStatusCode.OK, (int) HttpStatusCode.SwitchingProtocols);

yield return new object[] { ub.Uri, exceptionMessage, WebSocketError.NotAWebSocket };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class ConnectTest_Http2 : ClientWebSocketTestBase
public ConnectTest_Http2(ITestOutputHelper output) : base(output) { }

[Fact]
[SkipOnPlatform(TestPlatforms.Browser, "WebSocket over HTTP/2 is not supported on Browser")]
public async Task ConnectAsync_VersionNotSupported_Throws()
{
await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
Expand All @@ -43,6 +44,7 @@ await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
}

[Fact]
[SkipOnPlatform(TestPlatforms.Browser, "WebSocket over HTTP/2 is not supported on Browser")]
public async Task ConnectAsync_VersionSupported_Success()
{
await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
Expand Down