Skip to content

Commit af8d35d

Browse files
smudge202Tratcher
authored andcommitted
Merge Pass SubProtocols from TestHost WebSocketClient (#14666) (#14667)
1 parent 8e05fb0 commit af8d35d

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/Hosting/TestHost/src/WebSocketClient.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7+
using System.Linq;
78
using System.Net.WebSockets;
89
using System.Security.Cryptography;
910
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Microsoft.AspNetCore.Http;
1213
using Microsoft.AspNetCore.Http.Features;
14+
using Microsoft.Net.Http.Headers;
1315

1416
namespace Microsoft.AspNetCore.TestHost
1517
{
@@ -72,10 +74,15 @@ public async Task<WebSocket> ConnectAsync(Uri uri, CancellationToken cancellatio
7274
request.PathBase = _pathBase;
7375
}
7476
request.QueryString = QueryString.FromUriComponent(uri);
75-
request.Headers.Add("Connection", new string[] { "Upgrade" });
76-
request.Headers.Add("Upgrade", new string[] { "websocket" });
77-
request.Headers.Add("Sec-WebSocket-Version", new string[] { "13" });
78-
request.Headers.Add("Sec-WebSocket-Key", new string[] { CreateRequestKey() });
77+
request.Headers.Add(HeaderNames.Connection, new string[] { "Upgrade" });
78+
request.Headers.Add(HeaderNames.Upgrade, new string[] { "websocket" });
79+
request.Headers.Add(HeaderNames.SecWebSocketVersion, new string[] { "13" });
80+
request.Headers.Add(HeaderNames.SecWebSocketKey, new string[] { CreateRequestKey() });
81+
if (SubProtocols.Any())
82+
{
83+
request.Headers.Add(HeaderNames.SecWebSocketProtocol, SubProtocols.ToArray());
84+
}
85+
7986
request.Body = Stream.Null;
8087

8188
// WebSocket

src/Hosting/TestHost/test/TestClientTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.AspNetCore.Testing;
1717
using Microsoft.Extensions.DependencyInjection;
1818
using Microsoft.Extensions.Logging;
19+
using Microsoft.Net.Http.Headers;
1920
using Xunit;
2021

2122
namespace Microsoft.AspNetCore.TestHost
@@ -172,6 +173,7 @@ public async Task WebSocketWorks()
172173
{
173174
if (ctx.WebSockets.IsWebSocketRequest)
174175
{
176+
Assert.False(ctx.Request.Headers.ContainsKey(HeaderNames.SecWebSocketProtocol));
175177
var websocket = await ctx.WebSockets.AcceptWebSocketAsync();
176178
var receiveArray = new byte[1024];
177179
while (true)
@@ -232,6 +234,58 @@ public async Task WebSocketWorks()
232234
clientSocket.Dispose();
233235
}
234236

237+
[Fact]
238+
public async Task WebSocketSubProtocolsWorks()
239+
{
240+
// Arrange
241+
RequestDelegate appDelegate = async ctx =>
242+
{
243+
if (ctx.WebSockets.IsWebSocketRequest)
244+
{
245+
if (ctx.WebSockets.WebSocketRequestedProtocols.Contains("alpha") &&
246+
ctx.WebSockets.WebSocketRequestedProtocols.Contains("bravo"))
247+
{
248+
// according to rfc6455, the "server needs to include the same field and one of the selected subprotocol values"
249+
// however, this isn't enforced by either our server or client so it's possible to accept an arbitrary protocol.
250+
// Done here to demonstrate not "correct" behaviour, simply to show it's possible. Other clients may not allow this.
251+
var websocket = await ctx.WebSockets.AcceptWebSocketAsync("charlie");
252+
await websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Normal Closure", CancellationToken.None);
253+
}
254+
else
255+
{
256+
var subprotocols = ctx.WebSockets.WebSocketRequestedProtocols.Any()
257+
? string.Join(", ", ctx.WebSockets.WebSocketRequestedProtocols)
258+
: "<none>";
259+
var closeReason = "Unexpected subprotocols: " + subprotocols;
260+
var websocket = await ctx.WebSockets.AcceptWebSocketAsync();
261+
await websocket.CloseAsync(WebSocketCloseStatus.InternalServerError, closeReason, CancellationToken.None);
262+
}
263+
}
264+
};
265+
var builder = new WebHostBuilder()
266+
.Configure(app =>
267+
{
268+
app.Run(appDelegate);
269+
});
270+
var server = new TestServer(builder);
271+
272+
// Act
273+
var client = server.CreateWebSocketClient();
274+
client.SubProtocols.Add("alpha");
275+
client.SubProtocols.Add("bravo");
276+
var clientSocket = await client.ConnectAsync(new Uri("wss://localhost"), CancellationToken.None);
277+
var buffer = new byte[1024];
278+
var result = await clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
279+
280+
// Assert
281+
Assert.Equal(WebSocketMessageType.Close, result.MessageType);
282+
Assert.Equal("Normal Closure", result.CloseStatusDescription);
283+
Assert.Equal(WebSocketState.CloseReceived, clientSocket.State);
284+
Assert.Equal("charlie", clientSocket.SubProtocol);
285+
286+
clientSocket.Dispose();
287+
}
288+
235289
[ConditionalFact]
236290
public async Task WebSocketAcceptThrowsWhenCancelled()
237291
{

0 commit comments

Comments
 (0)