#283 remove direct WebSocket support
This commit is contained in:
Родитель
09c993e9a9
Коммит
691c69f1d2
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче