This commit is contained in:
Chris R 2017-01-04 16:48:23 -08:00
Родитель 09c993e9a9
Коммит 691c69f1d2
11 изменённых файлов: 0 добавлений и 2195 удалений

Просмотреть файл

@ -1,44 +0,0 @@
param([string]$CoreFxRepoRoot)
$RepoRoot = Split-Path -Parent $PSScriptRoot
$FilesToCopy = @(
"src\System.Net.WebSockets.Client\src\System\Net\WebSockets\ManagedWebSocket.cs",
"src\Common\src\System\Net\WebSockets\WebSocketValidate.cs"
)
if(!$CoreFxRepoRoot) {
$CoreFxRepoRoot = "$RepoRoot\..\..\dotnet\corefx"
}
if(!(Test-Path $CoreFxRepoRoot)) {
throw "Could not find CoreFx repo at $CoreFxRepoRoot"
}
$CoreFxRepoRoot = Convert-Path $CoreFxRepoRoot
$DestinationRoot = "$RepoRoot\src\Microsoft.Net.Http.Server\fx\"
$FilesToCopy | foreach {
$Source = Join-Path $CoreFxRepoRoot $_
$Destination = Join-Path $DestinationRoot $_
$DestinationDir = Split-Path -Parent $Destination
if(!(Test-Path $Source)) {
Write-Warning "Can't find source file: $Source"
} else {
if(!(Test-Path $DestinationDir)) {
mkdir $DestinationDir | Out-Null
}
if(Test-Path $Destination) {
del $Destination
}
Write-Host "Copying $_"
$SourceCode = [IO.File]::ReadAllText($Source)
$SourceCode = $SourceCode.Replace("Task.FromException", "CompatHelpers.FromException")
$SourceCode = $SourceCode.Replace("Task.CompletedTask", "CompatHelpers.CompletedTask")
$SourceCode = $SourceCode.Replace("Array.Empty", "CompatHelpers.Empty")
$SourceCode = $SourceCode.Replace("nameof(ClientWebSocket)", "`"ClientWebSocket`"")
[IO.File]::WriteAllText($Destination, $SourceCode)
}
}

Просмотреть файл

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
@ -28,7 +27,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// ITlsTokenBindingFeature, TODO: https://github.com/aspnet/WebListener/issues/231
IHttpBufferingFeature,
IHttpRequestLifetimeFeature,
IHttpWebSocketFeature,
IHttpAuthenticationFeature,
IHttpUpgradeFeature,
IHttpRequestIdentifierFeature
@ -442,21 +440,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys
return await _requestContext.UpgradeAsync();
}
bool IHttpWebSocketFeature.IsWebSocketRequest => _requestContext.IsWebSocketRequest;
async Task<WebSocket> IHttpWebSocketFeature.AcceptAsync(WebSocketAcceptContext context)
{
// TODO: Advanced params
string subProtocol = null;
if (context != null)
{
subProtocol = context.SubProtocol;
}
await OnStart();
return await _requestContext.AcceptWebSocketAsync(subProtocol);
}
ClaimsPrincipal IHttpAuthenticationFeature.User
{
get { return _user; }

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Net.WebSockets;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Claims;
using System.Threading;
@ -110,155 +109,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys
return Task.FromResult<Stream>(opaqueStream);
}
// Compare ValidateWebSocketRequest
public bool IsWebSocketRequest
{
get
{
if (!WebSocketHelpers.AreWebSocketsSupported)
{
return false;
}
if (!IsUpgradableRequest)
{
return false;
}
if (Request.KnownMethod != HttpApi.HTTP_VERB.HttpVerbGET)
{
return false;
}
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
var connection = Request.Headers[HttpKnownHeaderNames.Connection].ToString();
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
{
return false;
}
// Upgrade: websocket
var upgrade = Request.Headers[HttpKnownHeaderNames.Upgrade];
if (!string.Equals(WebSocketHelpers.WebSocketUpgradeToken, upgrade, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Sec-WebSocket-Version: 13
var version = Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
if (!string.Equals(WebSocketHelpers.SupportedProtocolVersion, version, StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Sec-WebSocket-Key: {base64string}
var key = Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
if (!WebSocketHelpers.IsValidWebSocketKey(key))
{
return false;
}
return true;
}
}
// Compare IsWebSocketRequest()
private void ValidateWebSocketRequest()
{
if (!WebSocketHelpers.AreWebSocketsSupported)
{
throw new NotSupportedException("WebSockets are not supported on this platform.");
}
if (!IsUpgradableRequest)
{
throw new InvalidOperationException("This request is not a valid upgrade request.");
}
if (Request.KnownMethod != HttpApi.HTTP_VERB.HttpVerbGET)
{
throw new InvalidOperationException("This request is not a valid upgrade request; invalid verb: " + Request.Method);
}
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
var connection = Request.Headers[HttpKnownHeaderNames.Connection].ToString();
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
{
throw new InvalidOperationException("The Connection header is invalid: " + connection);
}
// Upgrade: websocket
var upgrade = Request.Headers[HttpKnownHeaderNames.Upgrade];
if (!string.Equals(WebSocketHelpers.WebSocketUpgradeToken, upgrade, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The Upgrade header is invalid: " + upgrade);
}
// Sec-WebSocket-Version: 13
var version = Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
if (!string.Equals(WebSocketHelpers.SupportedProtocolVersion, version, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The Sec-WebSocket-Version header is invalid or not supported: " + version);
}
// Sec-WebSocket-Key: {base64string}
var key = Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
if (!WebSocketHelpers.IsValidWebSocketKey(key))
{
throw new InvalidOperationException("The Sec-WebSocket-Key header is invalid: " + upgrade);
}
}
public Task<WebSocket> AcceptWebSocketAsync()
{
return AcceptWebSocketAsync(null, WebSocketHelpers.DefaultReceiveBufferSize, WebSocketHelpers.DefaultKeepAliveInterval);
}
public Task<WebSocket> AcceptWebSocketAsync(string subProtocol)
{
return AcceptWebSocketAsync(subProtocol, WebSocketHelpers.DefaultReceiveBufferSize, WebSocketHelpers.DefaultKeepAliveInterval);
}
public Task<WebSocket> AcceptWebSocketAsync(string subProtocol, TimeSpan keepAliveInterval)
{
return AcceptWebSocketAsync(subProtocol, WebSocketHelpers.DefaultReceiveBufferSize, keepAliveInterval);
}
public Task<WebSocket> AcceptWebSocketAsync(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval)
{
if (!IsUpgradableRequest)
{
throw new InvalidOperationException("This request cannot be upgraded.");
}
WebSocketHelpers.ValidateOptions(subProtocol, keepAliveInterval);
return AcceptWebSocketAsyncCore(subProtocol, receiveBufferSize, keepAliveInterval);
}
private async Task<WebSocket> AcceptWebSocketAsyncCore(string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval)
{
ValidateWebSocketRequest();
var subProtocols = Request.Headers.GetValues(HttpKnownHeaderNames.SecWebSocketProtocol);
var shouldSendSecWebSocketProtocolHeader = WebSocketHelpers.ProcessWebSocketProtocolHeader(subProtocols, subProtocol);
if (shouldSendSecWebSocketProtocolHeader)
{
Response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol] = subProtocol;
}
// negotiate the websocket key return value
var secWebSocketKey = Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
var secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
Response.Headers.Append(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
Response.Headers.Append(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
Response.Headers.Append(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
var opaqueStream = await UpgradeAsync();
return WebSocketHelpers.CreateServerWebSocket(opaqueStream, subProtocol, receiveBufferSize, keepAliveInterval);
}
// TODO: Public when needed
internal bool TryGetChannelBinding(ref ChannelBinding value)
{

Просмотреть файл

@ -1,172 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace Microsoft.AspNetCore.Server.HttpSys
{
internal static class WebSocketHelpers
{
internal static string SupportedProtocolVersion = "13";
internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
internal const string WebSocketUpgradeToken = "websocket";
internal const int DefaultReceiveBufferSize = 16 * 1024;
internal const int DefaultClientSendBufferSize = 16 * 1024;
internal const int MaxControlFramePayloadLength = 123;
internal static readonly TimeSpan DefaultKeepAliveInterval = TimeSpan.FromMinutes(2);
// RFC 6455 requests WebSocket clients to let the server initiate the TCP close to avoid that client sockets
// end up in TIME_WAIT-state
//
// After both sending and receiving a Close message, an endpoint considers the WebSocket connection closed and
// MUST close the underlying TCP connection. The server MUST close the underlying TCP connection immediately;
// the client SHOULD wait for the server to close the connection but MAY close the connection at any time after
// sending and receiving a Close message, e.g., if it has not received a TCP Close from the server in a
// reasonable time period.
internal const int ClientTcpCloseTimeout = 1000; // 1s
private const int CloseStatusCodeAbort = 1006;
private const int CloseStatusCodeFailedTLSHandshake = 1015;
private const int InvalidCloseStatusCodesFrom = 0;
private const int InvalidCloseStatusCodesTo = 999;
private const string Separators = "()<>@,;:\\\"/[]?={} ";
internal static readonly ArraySegment<byte> EmptyPayload = new ArraySegment<byte>(new byte[] { }, 0, 0);
private static readonly Random KeyGenerator = new Random();
internal static bool AreWebSocketsSupported
{
get
{
return ComNetOS.IsWin8orLater;
}
}
internal static bool IsValidWebSocketKey(string key)
{
if (string.IsNullOrWhiteSpace(key))
{
return false;
}
// TODO:
// throw new NotImplementedException();
return true;
}
internal static string GetSecWebSocketAcceptString(string secWebSocketKey)
{
string retVal;
// SHA1 used only for hashing purposes, not for crypto. Check here for FIPS compat.
using (SHA1 sha1 = SHA1.Create())
{
string acceptString = string.Concat(secWebSocketKey, WebSocketHelpers.SecWebSocketKeyGuid);
byte[] toHash = Encoding.UTF8.GetBytes(acceptString);
retVal = Convert.ToBase64String(sha1.ComputeHash(toHash));
}
return retVal;
}
internal static WebSocket CreateServerWebSocket(Stream opaqueStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval)
{
return ManagedWebSocket.CreateFromConnectedStream(opaqueStream, isServer: true, subprotocol: subProtocol,
keepAliveIntervalSeconds: (int)keepAliveInterval.TotalSeconds, receiveBufferSize: receiveBufferSize);
}
// return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
internal static bool ProcessWebSocketProtocolHeader(IEnumerable<string> clientSecWebSocketProtocols, string subProtocol)
{
if (clientSecWebSocketProtocols == null || !clientSecWebSocketProtocols.Any())
{
// client hasn't specified any Sec-WebSocket-Protocol header
if (!string.IsNullOrEmpty(subProtocol))
{
// If the server specified _anything_ this isn't valid.
throw new WebSocketException(WebSocketError.UnsupportedProtocol,
"The client did not specify a Sec-WebSocket-Protocol header. SubProtocol: " + subProtocol);
}
// Treat empty and null from the server as the same thing here, server should not send headers.
return false;
}
// here, we know the client specified something and it's non-empty.
if (string.IsNullOrEmpty(subProtocol))
{
// client specified some protocols, server specified 'null'. So server should send headers.
return false;
}
// here, we know that the client has specified something, it's not empty
// and the server has specified exactly one protocol
// client specified protocols, serverOptions has exactly 1 non-empty entry. Check that
// this exists in the list the client specified.
foreach (var currentRequestProtocol in clientSecWebSocketProtocols)
{
if (string.Compare(subProtocol, currentRequestProtocol, StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
}
throw new WebSocketException(WebSocketError.UnsupportedProtocol,
$"Unsupported protocol: {subProtocol}; Client supported protocols: {string.Join(", ", clientSecWebSocketProtocols)}");
}
internal static void ValidateSubprotocol(string subProtocol)
{
if (string.IsNullOrEmpty(subProtocol))
{
return;
}
char[] chars = subProtocol.ToCharArray();
string invalidChar = null;
int i = 0;
while (i < chars.Length)
{
char ch = chars[i];
if (ch < 0x21 || ch > 0x7e)
{
invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch);
break;
}
if (!char.IsLetterOrDigit(ch) &&
Separators.IndexOf(ch) >= 0)
{
invalidChar = ch.ToString();
break;
}
i++;
}
if (invalidChar != null)
{
throw new ArgumentException($"Invalid character '{invalidChar}' in the subProtocol '{subProtocol}'", nameof(subProtocol));
}
}
internal static void ValidateOptions(string subProtocol, TimeSpan keepAliveInterval)
{
ValidateSubprotocol(subProtocol);
// -1
if (keepAliveInterval < Timeout.InfiniteTimeSpan)
{
throw new ArgumentOutOfRangeException(nameof(keepAliveInterval), keepAliveInterval,
"The value must be greater than or equal too 0 seconds, or -1 second to disable.");
}
}
}
}

Просмотреть файл

@ -1,52 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
namespace System.Net.WebSockets
{
// Needed to support the WebSockets code from CoreFX.
internal static class CompatHelpers
{
internal static readonly Task CompletedTask;
static CompatHelpers()
{
var tcs = new TaskCompletionSource<object>();
tcs.SetResult(null);
CompletedTask = tcs.Task;
}
public static Task FromException(Exception ex)
{
#if NET451
return FromException<object>(ex);
#else
return Task.FromException(ex);
#endif
}
public static Task<T> FromException<T>(Exception ex)
{
#if NET451
var tcs = new TaskCompletionSource<T>();
tcs.SetException(ex);
return tcs.Task;
#else
return Task.FromException<T>(ex);
#endif
}
internal static T[] Empty<T>()
{
#if NET451
return new T[0];
#else
return Array.Empty<T>();
#endif
}
}
// This is just here to be used by a nameof in the CoreFX code.
//internal static class ClientWebSocket { }
}

Просмотреть файл

@ -1,5 +0,0 @@
# External Code
External code copied from CoreFX. Do not modify files in this directory, use the `scripts\UpdateCoreFxCore.ps1` script in the repo root.
This folder structure is designed to exactly mirror the structure in the CoreFX repo (hence the deep nesting).

Просмотреть файл

@ -1,27 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace System.Net.WebSockets
{
// Needed to support the WebSockets code from CoreFX.
internal static class SR
{
internal static readonly string net_Websockets_AlreadyOneOutstandingOperation = nameof(net_Websockets_AlreadyOneOutstandingOperation);
internal static readonly string net_WebSockets_Argument_InvalidMessageType = nameof(net_WebSockets_Argument_InvalidMessageType);
internal static readonly string net_WebSockets_InvalidCharInProtocolString = nameof(net_WebSockets_InvalidCharInProtocolString);
internal static readonly string net_WebSockets_InvalidCloseStatusCode = nameof(net_WebSockets_InvalidCloseStatusCode);
internal static readonly string net_WebSockets_InvalidCloseStatusDescription = nameof(net_WebSockets_InvalidCloseStatusDescription);
internal static readonly string net_WebSockets_InvalidEmptySubProtocol = nameof(net_WebSockets_InvalidEmptySubProtocol);
internal static readonly string net_WebSockets_InvalidState = nameof(net_WebSockets_InvalidState);
internal static readonly string net_WebSockets_InvalidState_ClosedOrAborted = nameof(net_WebSockets_InvalidState_ClosedOrAborted);
internal static readonly string net_WebSockets_ReasonNotNull = nameof(net_WebSockets_ReasonNotNull);
internal static readonly string net_WebSockets_UnsupportedPlatform = nameof(net_WebSockets_UnsupportedPlatform);
internal static string Format(string name, params object[] args) => $"TODO, RESX: {name}; ({string.Join(",", args)})";
}
}

Просмотреть файл

@ -1,132 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
using System.Text;
namespace System.Net.WebSockets
{
internal static class WebSocketValidate
{
internal const int MaxControlFramePayloadLength = 123;
private const int CloseStatusCodeAbort = 1006;
private const int CloseStatusCodeFailedTLSHandshake = 1015;
private const int InvalidCloseStatusCodesFrom = 0;
private const int InvalidCloseStatusCodesTo = 999;
private const string Separators = "()<>@,;:\\\"/[]?={} ";
internal static void ValidateSubprotocol(string subProtocol)
{
if (string.IsNullOrWhiteSpace(subProtocol))
{
throw new ArgumentException(SR.net_WebSockets_InvalidEmptySubProtocol, nameof(subProtocol));
}
string invalidChar = null;
int i = 0;
while (i < subProtocol.Length)
{
char ch = subProtocol[i];
if (ch < 0x21 || ch > 0x7e)
{
invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch);
break;
}
if (!char.IsLetterOrDigit(ch) &&
Separators.IndexOf(ch) >= 0)
{
invalidChar = ch.ToString();
break;
}
i++;
}
if (invalidChar != null)
{
throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCharInProtocolString, subProtocol, invalidChar), nameof(subProtocol));
}
}
internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, string statusDescription)
{
if (closeStatus == WebSocketCloseStatus.Empty && !string.IsNullOrEmpty(statusDescription))
{
throw new ArgumentException(SR.Format(SR.net_WebSockets_ReasonNotNull,
statusDescription,
WebSocketCloseStatus.Empty),
nameof(statusDescription));
}
int closeStatusCode = (int)closeStatus;
if ((closeStatusCode >= InvalidCloseStatusCodesFrom &&
closeStatusCode <= InvalidCloseStatusCodesTo) ||
closeStatusCode == CloseStatusCodeAbort ||
closeStatusCode == CloseStatusCodeFailedTLSHandshake)
{
// CloseStatus 1006 means Aborted - this will never appear on the wire and is reflected by calling WebSocket.Abort
throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusCode,
closeStatusCode),
nameof(closeStatus));
}
int length = 0;
if (!string.IsNullOrEmpty(statusDescription))
{
length = Encoding.UTF8.GetByteCount(statusDescription);
}
if (length > MaxControlFramePayloadLength)
{
throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusDescription,
statusDescription,
MaxControlFramePayloadLength),
nameof(statusDescription));
}
}
internal static void ThrowPlatformNotSupportedException()
{
throw new PlatformNotSupportedException(SR.net_WebSockets_UnsupportedPlatform);
}
internal static void ValidateArraySegment(ArraySegment<byte> arraySegment, string parameterName)
{
if (arraySegment.Array == null)
{
throw new ArgumentNullException(parameterName + ".Array");
}
}
internal static void ThrowIfInvalidState(WebSocketState currentState, bool isDisposed, WebSocketState[] validStates)
{
string validStatesText = string.Empty;
if (validStates != null && validStates.Length > 0)
{
foreach (WebSocketState validState in validStates)
{
if (currentState == validState)
{
// Ordering is important to maintain .NET 4.5 WebSocket implementation exception behavior.
if (isDisposed)
{
throw new ObjectDisposedException("ClientWebSocket");
}
return;
}
}
validStatesText = string.Join(", ", validStates);
}
throw new WebSocketException(
WebSocketError.InvalidState,
SR.Format(SR.net_WebSockets_InvalidState, currentState, validStatesText));
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,108 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.HttpSys.Listener
{
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)]
public class WebSocketTests
{
[ConditionalFact]
public async Task WebSocketAccept_AfterHeadersSent_Throws()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<HttpResponseMessage> clientTask = SendRequestAsync(address);
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
byte[] body = Encoding.UTF8.GetBytes("Hello World");
context.Response.Body.Write(body, 0, body.Length);
await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.AcceptWebSocketAsync());
context.Dispose();
HttpResponseMessage response = await clientTask;
Assert.Equal(200, (int)response.StatusCode);
Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
}
}
[ConditionalFact]
public async Task WebSocketAccept_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<WebSocket> clientTask = SendWebSocketRequestAsync(ConvertToWebSocketAddress(address));
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
Assert.True(context.IsUpgradableRequest);
WebSocket serverWebSocket = await context.AcceptWebSocketAsync();
WebSocket clientWebSocket = await clientTask;
serverWebSocket.Dispose();
clientWebSocket.Dispose();
}
}
[ConditionalFact]
public async Task WebSocketAccept_SendAndReceive_Success()
{
string address;
using (var server = Utilities.CreateHttpServer(out address))
{
Task<WebSocket> clientTask = SendWebSocketRequestAsync(ConvertToWebSocketAddress(address));
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
Assert.True(context.IsWebSocketRequest);
WebSocket serverWebSocket = await context.AcceptWebSocketAsync();
WebSocket clientWebSocket = await clientTask;
byte[] clientBuffer = new byte[] { 0x00, 0x01, 0xFF, 0x00, 0x00 };
await clientWebSocket.SendAsync(new ArraySegment<byte>(clientBuffer, 0, 3), WebSocketMessageType.Binary, true, CancellationToken.None);
byte[] serverBuffer = new byte[clientBuffer.Length];
var result = await serverWebSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer, 0, serverBuffer.Length), CancellationToken.None);
Assert.Equal(clientBuffer, serverBuffer);
await serverWebSocket.SendAsync(new ArraySegment<byte>(serverBuffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
byte[] clientEchoBuffer = new byte[clientBuffer.Length];
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(clientEchoBuffer), CancellationToken.None);
Assert.Equal(clientBuffer, clientEchoBuffer);
serverWebSocket.Dispose();
clientWebSocket.Dispose();
}
}
private string ConvertToWebSocketAddress(string address)
{
var builder = new UriBuilder(address);
builder.Scheme = "ws";
return builder.ToString();
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{
using (HttpClient client = new HttpClient())
{
return await client.GetAsync(uri);
}
}
private async Task<WebSocket> SendWebSocketRequestAsync(string address)
{
ClientWebSocket client = new ClientWebSocket();
await client.ConnectAsync(new Uri(address), CancellationToken.None);
return client;
}
}
}

Просмотреть файл

@ -1,182 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.HttpSys
{
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, WindowsVersions.Win2008R2)]
public class WebSocketTests
{
[ConditionalFact]
public async Task WebSocketTests_SupportKeys_Present()
{
string address;
using (Utilities.CreateHttpServer(out address, httpContext =>
{
try
{
var webSocketFeature = httpContext.Features.Get<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
}
catch (Exception ex)
{
return httpContext.Response.WriteAsync(ex.ToString());
}
return Task.FromResult(0);
}))
{
HttpResponseMessage response = await SendRequestAsync(address);
Assert.Equal(200, (int)response.StatusCode);
Assert.False(response.Headers.TransferEncodingChunked.HasValue, "Chunked");
Assert.Equal(0, response.Content.Headers.ContentLength);
Assert.Equal(string.Empty, response.Content.ReadAsStringAsync().Result);
}
}
[ConditionalFact]
public async Task WebSocketTests_AfterHeadersSent_Throws()
{
bool? upgradeThrew = null;
string address;
using (Utilities.CreateHttpServer(out address, async httpContext =>
{
await httpContext.Response.WriteAsync("Hello World");
try
{
var webSocketFeature = httpContext.Features.Get<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
await webSocketFeature.AcceptAsync(null);
upgradeThrew = false;
}
catch (InvalidOperationException)
{
upgradeThrew = true;
}
}))
{
HttpResponseMessage response = await SendRequestAsync(address);
Assert.Equal(200, (int)response.StatusCode);
Assert.True(upgradeThrew.Value);
}
}
[ConditionalFact]
public async Task WebSocketAccept_Success()
{
ManualResetEvent waitHandle = new ManualResetEvent(false);
bool? upgraded = null;
string address;
using (Utilities.CreateHttpServer(out address, async httpContext =>
{
var webSocketFeature = httpContext.Features.Get<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
Assert.True(webSocketFeature.IsWebSocketRequest);
await webSocketFeature.AcceptAsync(null);
upgraded = true;
waitHandle.Set();
}))
{
using (WebSocket clientWebSocket = await SendWebSocketRequestAsync(ConvertToWebSocketAddress(address)))
{
Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out");
Assert.True(upgraded.HasValue, "Upgraded not set");
Assert.True(upgraded.Value, "Upgrade failed");
}
}
}
[ConditionalFact]
public async Task WebSocketAccept_WithOnStarting_CallbackCalled()
{
var callbackCalled = false;
var waitHandle = new ManualResetEvent(false);
bool? upgraded = null;
string address;
using (Utilities.CreateHttpServer(out address, async httpContext =>
{
httpContext.Response.OnStarting(_ =>
{
callbackCalled = true;
return Task.FromResult(0);
}, null);
var webSocketFeature = httpContext.Features.Get<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
Assert.True(webSocketFeature.IsWebSocketRequest);
await webSocketFeature.AcceptAsync(null);
upgraded = true;
waitHandle.Set();
}))
{
using (WebSocket clientWebSocket = await SendWebSocketRequestAsync(ConvertToWebSocketAddress(address)))
{
Assert.True(waitHandle.WaitOne(TimeSpan.FromSeconds(1)), "Timed out");
Assert.True(upgraded.HasValue, "Upgraded not set");
Assert.True(upgraded.Value, "Upgrade failed");
Assert.True(callbackCalled, "Callback not called");
}
}
}
[ConditionalFact]
public async Task WebSocketAccept_SendAndReceive_Success()
{
byte[] clientBuffer = new byte[] { 0x00, 0x01, 0xFF, 0x00, 0x00 };
string address;
using (Utilities.CreateHttpServer(out address, async httpContext =>
{
var webSocketFeature = httpContext.Features.Get<IHttpWebSocketFeature>();
Assert.NotNull(webSocketFeature);
Assert.True(webSocketFeature.IsWebSocketRequest);
var serverWebSocket = await webSocketFeature.AcceptAsync(null);
byte[] serverBuffer = new byte[clientBuffer.Length];
var result = await serverWebSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer, 0, serverBuffer.Length), CancellationToken.None);
Assert.Equal(clientBuffer, serverBuffer);
await serverWebSocket.SendAsync(new ArraySegment<byte>(serverBuffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}))
{
using (WebSocket clientWebSocket = await SendWebSocketRequestAsync(ConvertToWebSocketAddress(address)))
{
await clientWebSocket.SendAsync(new ArraySegment<byte>(clientBuffer, 0, 3), WebSocketMessageType.Binary, true, CancellationToken.None);
byte[] clientEchoBuffer = new byte[clientBuffer.Length];
var result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(clientEchoBuffer), CancellationToken.None);
Assert.Equal(clientBuffer, clientEchoBuffer);
}
}
}
private string ConvertToWebSocketAddress(string address)
{
var builder = new UriBuilder(address);
builder.Scheme = "ws";
return builder.ToString();
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri)
{
using (HttpClient client = new HttpClient())
{
return await client.GetAsync(uri);
}
}
private async Task<WebSocket> SendWebSocketRequestAsync(string address)
{
var client = new ClientWebSocket();
await client.ConnectAsync(new Uri(address), CancellationToken.None);
return client;
}
}
}