Use CoreFx managed WebSockets
This commit is contained in:
parent
484955f83e
commit
44910bbd59
|
|
@ -16,8 +16,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "HelloWorld", "samples\Hello
|
|||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SelfHostServer", "samples\SelfHostServer\SelfHostServer.xproj", "{1236F93A-AC5C-4A77-9477-C88F040151CA}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Net.WebSockets.Server", "src\Microsoft.Net.WebSockets.Server\Microsoft.Net.WebSockets.Server.xproj", "{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.WebListener.FunctionalTests", "test\Microsoft.AspNetCore.Server.WebListener.FunctionalTests\Microsoft.AspNetCore.Server.WebListener.FunctionalTests.xproj", "{4492FF4C-9032-411D-853F-46B01755E504}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.WebListener", "src\Microsoft.AspNetCore.Server.WebListener\Microsoft.AspNetCore.Server.WebListener.xproj", "{B9F45F9D-D206-47F0-8E5F-54CE2F0BDF92}"
|
||||
|
|
@ -83,16 +81,6 @@ Global
|
|||
{1236F93A-AC5C-4A77-9477-C88F040151CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{1236F93A-AC5C-4A77-9477-C88F040151CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{1236F93A-AC5C-4A77-9477-C88F040151CA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4492FF4C-9032-411D-853F-46B01755E504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4492FF4C-9032-411D-853F-46B01755E504}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4492FF4C-9032-411D-853F-46B01755E504}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -156,7 +144,6 @@ Global
|
|||
{3F5212AA-E287-49DD-8CEC-44BF0A2AC9A1} = {99D5E5F3-88F5-4CCF-8D8C-717C8925DF09}
|
||||
{6DAF3E6B-8E1B-4E6E-B9FE-7B1E5FDB7DB4} = {3A1E31E3-2794-4CA3-B8E2-253E96BDE514}
|
||||
{1236F93A-AC5C-4A77-9477-C88F040151CA} = {3A1E31E3-2794-4CA3-B8E2-253E96BDE514}
|
||||
{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB} = {99D5E5F3-88F5-4CCF-8D8C-717C8925DF09}
|
||||
{4492FF4C-9032-411D-853F-46B01755E504} = {E183C826-1360-4DFF-9994-F33CED5C8525}
|
||||
{B9F45F9D-D206-47F0-8E5F-54CE2F0BDF92} = {99D5E5F3-88F5-4CCF-8D8C-717C8925DF09}
|
||||
{DCB6E0B1-223D-44E6-8696-4767E5B6E6A1} = {E183C826-1360-4DFF-9994-F33CED5C8525}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ namespace HelloWorld
|
|||
// Response
|
||||
byte[] bytes = Encoding.ASCII.GetBytes("Hello World: " + DateTime.Now);
|
||||
|
||||
if (context.IsWebSocketRequest())
|
||||
if (context.IsWebSocketRequest)
|
||||
{
|
||||
Console.WriteLine("WebSocket");
|
||||
WebSocket webSocket = await context.AcceptWebSocketAsync();
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
"emitEntryPoint": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http.Server": "0.2.0-*",
|
||||
"Microsoft.Net.WebSockets.Server": "0.2.0-*"
|
||||
"Microsoft.Net.Http.Server": "0.2.0-*"
|
||||
},
|
||||
"commands": {
|
||||
"sample": "HelloWorld"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,9 +41,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
|||
ITlsTokenBindingFeature,
|
||||
IHttpBufferingFeature,
|
||||
IHttpRequestLifetimeFeature,
|
||||
#if WEBSOCKETS
|
||||
IHttpWebSocketFeature,
|
||||
#endif
|
||||
IHttpAuthenticationFeature,
|
||||
IHttpUpgradeFeature,
|
||||
IHttpRequestIdentifierFeature
|
||||
|
|
@ -436,14 +434,8 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
|||
{
|
||||
return _requestContext.UpgradeAsync();
|
||||
}
|
||||
#if WEBSOCKETS
|
||||
bool IHttpWebSocketFeature.IsWebSocketRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
return _requestContext.IsWebSocketRequest();
|
||||
}
|
||||
}
|
||||
|
||||
bool IHttpWebSocketFeature.IsWebSocketRequest => _requestContext.IsWebSocketRequest;
|
||||
|
||||
Task<WebSocket> IHttpWebSocketFeature.AcceptAsync(WebSocketAcceptContext context)
|
||||
{
|
||||
|
|
@ -455,7 +447,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
|||
}
|
||||
return _requestContext.AcceptWebSocketAsync(subProtocol);
|
||||
}
|
||||
#endif
|
||||
|
||||
ClaimsPrincipal IHttpAuthenticationFeature.User
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -38,9 +38,7 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
|||
{ typeof(IHttpBufferingFeature), _identityFunc },
|
||||
{ typeof(IHttpRequestLifetimeFeature), _identityFunc },
|
||||
{ typeof(IHttpUpgradeFeature), _identityFunc },
|
||||
#if WEBSOCKETS
|
||||
{ typeof(IHttpWebSocketFeature), _identityFunc },
|
||||
#endif
|
||||
{ typeof(IHttpAuthenticationFeature), _identityFunc },
|
||||
{ typeof(IHttpRequestIdentifierFeature), _identityFunc },
|
||||
{ typeof(RequestContext), ctx => ctx.RequestContext },
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ namespace Microsoft.Net.Http.Server
|
|||
|
||||
public HeaderCollection Headers { get; }
|
||||
|
||||
private UnsafeNclNativeMethods.HttpApi.HTTP_VERB KnownMethod { get; }
|
||||
internal UnsafeNclNativeMethods.HttpApi.HTTP_VERB KnownMethod { get; }
|
||||
|
||||
public bool IsHeadMethod => KnownMethod == UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security.Authentication.ExtendedProtection;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
|
|
@ -120,6 +121,155 @@ namespace Microsoft.Net.Http.Server
|
|||
return Task.FromResult<Stream>(opaqueStream);
|
||||
}
|
||||
|
||||
// Compare ValidateWebSocketRequest
|
||||
public bool IsWebSocketRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!WebSocketHelpers.AreWebSocketsSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsUpgradableRequest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Request.KnownMethod != UnsafeNclNativeMethods.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 != UnsafeNclNativeMethods.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 is 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketHelpers.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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.Net.Http.Server
|
||||
{
|
||||
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
|
||||
{
|
||||
// https://github.com/aspnet/WebListener/issues/215
|
||||
return true; // TODO: 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
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 { }
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# 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).
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
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)})";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,7 +10,9 @@
|
|||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk",
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
"CS1591",
|
||||
"CS1572",
|
||||
"CS1573"
|
||||
],
|
||||
"xmlDoc": true
|
||||
},
|
||||
|
|
@ -31,6 +33,7 @@
|
|||
"System.IO": "4.1.0-*",
|
||||
"System.IO.FileSystem": "4.0.1-*",
|
||||
"System.Net.Primitives": "4.0.11-*",
|
||||
"System.Net.WebSockets": "4.0.0-*",
|
||||
"System.Runtime.Extensions": "4.1.0-*",
|
||||
"System.Runtime.InteropServices": "4.1.0-*",
|
||||
"System.Security.Claims": "4.0.1-*",
|
||||
|
|
@ -38,7 +41,8 @@
|
|||
"System.Security.Principal.Windows": "4.0.0-*",
|
||||
"System.Text.Encoding.Extensions": "4.0.11-*",
|
||||
"System.Threading": "4.0.11-*",
|
||||
"System.Threading.Overlapped": "4.0.1-*"
|
||||
"System.Threading.Overlapped": "4.0.1-*",
|
||||
"System.Threading.Timer": "4.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
// this class contains known header names
|
||||
internal static class HttpKnownHeaderNames
|
||||
{
|
||||
public const string CacheControl = "Cache-Control";
|
||||
public const string Connection = "Connection";
|
||||
public const string Date = "Date";
|
||||
public const string KeepAlive = "Keep-Alive";
|
||||
public const string Pragma = "Pragma";
|
||||
public const string ProxyConnection = "Proxy-Connection";
|
||||
public const string Trailer = "Trailer";
|
||||
public const string TransferEncoding = "Transfer-Encoding";
|
||||
public const string Upgrade = "Upgrade";
|
||||
public const string Via = "Via";
|
||||
public const string Warning = "Warning";
|
||||
public const string ContentLength = "Content-Length";
|
||||
public const string ContentType = "Content-Type";
|
||||
public const string ContentDisposition = "Content-Disposition";
|
||||
public const string ContentEncoding = "Content-Encoding";
|
||||
public const string ContentLanguage = "Content-Language";
|
||||
public const string ContentLocation = "Content-Location";
|
||||
public const string ContentRange = "Content-Range";
|
||||
public const string Expires = "Expires";
|
||||
public const string LastModified = "Last-Modified";
|
||||
public const string Age = "Age";
|
||||
public const string Location = "Location";
|
||||
public const string ProxyAuthenticate = "Proxy-Authenticate";
|
||||
public const string RetryAfter = "Retry-After";
|
||||
public const string Server = "Server";
|
||||
public const string SetCookie = "Set-Cookie";
|
||||
public const string SetCookie2 = "Set-Cookie2";
|
||||
public const string Vary = "Vary";
|
||||
public const string WWWAuthenticate = "WWW-Authenticate";
|
||||
public const string Accept = "Accept";
|
||||
public const string AcceptCharset = "Accept-Charset";
|
||||
public const string AcceptEncoding = "Accept-Encoding";
|
||||
public const string AcceptLanguage = "Accept-Language";
|
||||
public const string Authorization = "Authorization";
|
||||
public const string Cookie = "Cookie";
|
||||
public const string Cookie2 = "Cookie2";
|
||||
public const string Expect = "Expect";
|
||||
public const string From = "From";
|
||||
public const string Host = "Host";
|
||||
public const string IfMatch = "If-Match";
|
||||
public const string IfModifiedSince = "If-Modified-Since";
|
||||
public const string IfNoneMatch = "If-None-Match";
|
||||
public const string IfRange = "If-Range";
|
||||
public const string IfUnmodifiedSince = "If-Unmodified-Since";
|
||||
public const string MaxForwards = "Max-Forwards";
|
||||
public const string ProxyAuthorization = "Proxy-Authorization";
|
||||
public const string Referer = "Referer";
|
||||
public const string Range = "Range";
|
||||
public const string UserAgent = "User-Agent";
|
||||
public const string ContentMD5 = "Content-MD5";
|
||||
public const string ETag = "ETag";
|
||||
public const string TE = "TE";
|
||||
public const string Allow = "Allow";
|
||||
public const string AcceptRanges = "Accept-Ranges";
|
||||
public const string P3P = "P3P";
|
||||
public const string XPoweredBy = "X-Powered-By";
|
||||
public const string XAspNetVersion = "X-AspNet-Version";
|
||||
public const string SecWebSocketKey = "Sec-WebSocket-Key";
|
||||
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
|
||||
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
|
||||
public const string Origin = "Origin";
|
||||
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
|
||||
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright from='1997' to='2001' company='Microsoft Corporation'>
|
||||
// Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
// Information Contained Herein is Proprietary and Confidential.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal sealed class SR
|
||||
{
|
||||
internal const string net_servicePointAddressNotSupportedInHostMode = "net_servicePointAddressNotSupportedInHostMode";
|
||||
internal const string net_Websockets_AlreadyOneOutstandingOperation = "net_Websockets_AlreadyOneOutstandingOperation";
|
||||
internal const string net_Websockets_WebSocketBaseFaulted = "net_Websockets_WebSocketBaseFaulted";
|
||||
internal const string net_WebSockets_NativeSendResponseHeaders = "net_WebSockets_NativeSendResponseHeaders";
|
||||
internal const string net_WebSockets_Generic = "net_WebSockets_Generic";
|
||||
internal const string net_WebSockets_NotAWebSocket_Generic = "net_WebSockets_NotAWebSocket_Generic";
|
||||
internal const string net_WebSockets_UnsupportedWebSocketVersion_Generic = "net_WebSockets_UnsupportedWebSocketVersion_Generic";
|
||||
internal const string net_WebSockets_HeaderError_Generic = "net_WebSockets_HeaderError_Generic";
|
||||
internal const string net_WebSockets_UnsupportedProtocol_Generic = "net_WebSockets_UnsupportedProtocol_Generic";
|
||||
internal const string net_WebSockets_UnsupportedPlatform = "net_WebSockets_UnsupportedPlatform";
|
||||
internal const string net_WebSockets_AcceptNotAWebSocket = "net_WebSockets_AcceptNotAWebSocket";
|
||||
internal const string net_WebSockets_AcceptUnsupportedWebSocketVersion = "net_WebSockets_AcceptUnsupportedWebSocketVersion";
|
||||
internal const string net_WebSockets_AcceptHeaderNotFound = "net_WebSockets_AcceptHeaderNotFound";
|
||||
internal const string net_WebSockets_AcceptUnsupportedProtocol = "net_WebSockets_AcceptUnsupportedProtocol";
|
||||
internal const string net_WebSockets_ClientAcceptingNoProtocols = "net_WebSockets_ClientAcceptingNoProtocols";
|
||||
internal const string net_WebSockets_ClientSecWebSocketProtocolsBlank = "net_WebSockets_ClientSecWebSocketProtocolsBlank";
|
||||
internal const string net_WebSockets_ArgumentOutOfRange_TooSmall = "net_WebSockets_ArgumentOutOfRange_TooSmall";
|
||||
internal const string net_WebSockets_ArgumentOutOfRange_InternalBuffer = "net_WebSockets_ArgumentOutOfRange_InternalBuffer";
|
||||
internal const string net_WebSockets_ArgumentOutOfRange_TooBig = "net_WebSockets_ArgumentOutOfRange_TooBig";
|
||||
internal const string net_WebSockets_InvalidState_Generic = "net_WebSockets_InvalidState_Generic";
|
||||
internal const string net_WebSockets_InvalidState_ClosedOrAborted = "net_WebSockets_InvalidState_ClosedOrAborted";
|
||||
internal const string net_WebSockets_InvalidState = "net_WebSockets_InvalidState";
|
||||
internal const string net_WebSockets_ReceiveAsyncDisallowedAfterCloseAsync = "net_WebSockets_ReceiveAsyncDisallowedAfterCloseAsync";
|
||||
internal const string net_WebSockets_InvalidMessageType = "net_WebSockets_InvalidMessageType";
|
||||
internal const string net_WebSockets_InvalidBufferType = "net_WebSockets_InvalidBufferType";
|
||||
internal const string net_WebSockets_InvalidMessageType_Generic = "net_WebSockets_InvalidMessageType_Generic";
|
||||
internal const string net_WebSockets_Argument_InvalidMessageType = "net_WebSockets_Argument_InvalidMessageType";
|
||||
internal const string net_WebSockets_ConnectionClosedPrematurely_Generic = "net_WebSockets_ConnectionClosedPrematurely_Generic";
|
||||
internal const string net_WebSockets_InvalidCharInProtocolString = "net_WebSockets_InvalidCharInProtocolString";
|
||||
internal const string net_WebSockets_InvalidEmptySubProtocol = "net_WebSockets_InvalidEmptySubProtocol";
|
||||
internal const string net_WebSockets_ReasonNotNull = "net_WebSockets_ReasonNotNull";
|
||||
internal const string net_WebSockets_InvalidCloseStatusCode = "net_WebSockets_InvalidCloseStatusCode";
|
||||
internal const string net_WebSockets_InvalidCloseStatusDescription = "net_WebSockets_InvalidCloseStatusDescription";
|
||||
internal const string net_WebSockets_Scheme = "net_WebSockets_Scheme";
|
||||
internal const string net_WebSockets_AlreadyStarted = "net_WebSockets_AlreadyStarted";
|
||||
internal const string net_WebSockets_Connect101Expected = "net_WebSockets_Connect101Expected";
|
||||
internal const string net_WebSockets_InvalidResponseHeader = "net_WebSockets_InvalidResponseHeader";
|
||||
internal const string net_WebSockets_NotConnected = "net_WebSockets_NotConnected";
|
||||
internal const string net_WebSockets_InvalidRegistration = "net_WebSockets_InvalidRegistration";
|
||||
internal const string net_WebSockets_NoDuplicateProtocol = "net_WebSockets_NoDuplicateProtocol";
|
||||
|
||||
internal const string NotReadableStream = "NotReadableStream";
|
||||
internal const string NotWriteableStream = "NotWriteableStream";
|
||||
|
||||
public static string GetString(string name, params object[] args)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public static string GetString(string name)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>e788aeae-2cb4-4bfa-8746-d0bb7e93a1bb</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
internal sealed class SafeLoadLibrary : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private const string KERNEL32 = "kernel32.dll";
|
||||
|
||||
public static readonly SafeLoadLibrary Zero = new SafeLoadLibrary(false);
|
||||
|
||||
private SafeLoadLibrary() : base(true)
|
||||
{
|
||||
}
|
||||
|
||||
private SafeLoadLibrary(bool ownsHandle) : base(ownsHandle)
|
||||
{
|
||||
}
|
||||
|
||||
public static unsafe SafeLoadLibrary LoadLibraryEx(string library)
|
||||
{
|
||||
SafeLoadLibrary result = UnsafeNativeMethods.SafeNetHandles.LoadLibraryExW(library, null, 0);
|
||||
if (result.IsInvalid)
|
||||
{
|
||||
result.SetHandleAsInvalid();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return UnsafeNativeMethods.SafeNetHandles.FreeLibrary(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
// This class is a wrapper for a WSPC (WebSocket protocol component) session. WebSocketCreateClientHandle and WebSocketCreateServerHandle return a PVOID and not a real handle
|
||||
// but we use a SafeHandle because it provides us the guarantee that WebSocketDeleteHandle will always get called.
|
||||
internal sealed class SafeWebSocketHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
internal SafeWebSocketHandle()
|
||||
: base(true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (this.IsInvalid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.WebSocketDeleteHandle(this.handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,875 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="UnsafeNativeMethods.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
internal static class UnsafeNativeMethods
|
||||
{
|
||||
#if NETSTANDARD1_3
|
||||
private const string api_ms_win_core_libraryloader_LIB = "api-ms-win-core-libraryloader-l1-1-0.dll";
|
||||
#else
|
||||
private const string KERNEL32 = "kernel32.dll";
|
||||
#endif
|
||||
private const string WEBSOCKET = "websocket.dll";
|
||||
|
||||
internal static class SafeNetHandles
|
||||
{
|
||||
#if NETSTANDARD1_3
|
||||
[DllImport(api_ms_win_core_libraryloader_LIB, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
#else
|
||||
[DllImport(KERNEL32, ExactSpelling = true, CharSet=CharSet.Unicode, SetLastError = true)]
|
||||
#endif
|
||||
internal static extern unsafe SafeLoadLibrary LoadLibraryExW([In] string lpwLibFileName, [In] void* hFile, [In] uint dwFlags);
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
[DllImport(api_ms_win_core_libraryloader_LIB, ExactSpelling = true, SetLastError = true)]
|
||||
#else
|
||||
[DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)]
|
||||
#endif
|
||||
internal static extern unsafe bool FreeLibrary([In] IntPtr hModule);
|
||||
}
|
||||
|
||||
internal static class WebSocketProtocolComponent
|
||||
{
|
||||
private static readonly string DllFileName;
|
||||
private static readonly string DummyWebsocketKeyBase64 = Convert.ToBase64String(new byte[16]);
|
||||
private static readonly SafeLoadLibrary WebSocketDllHandle;
|
||||
private static readonly string PrivateSupportedVersion;
|
||||
|
||||
private static readonly HttpHeader[] InitialClientRequestHeaders = new HttpHeader[]
|
||||
{
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Connection,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Connection.Length,
|
||||
Value = HttpKnownHeaderNames.Upgrade,
|
||||
ValueLength = (uint)HttpKnownHeaderNames.Upgrade.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Upgrade,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Upgrade.Length,
|
||||
Value = WebSocketHelpers.WebSocketUpgradeToken,
|
||||
ValueLength = (uint)WebSocketHelpers.WebSocketUpgradeToken.Length
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly HttpHeader[] ServerFakeRequestHeaders;
|
||||
|
||||
internal static class Errors
|
||||
{
|
||||
internal const int E_INVALID_OPERATION = unchecked((int)0x80000050);
|
||||
internal const int E_INVALID_PROTOCOL_OPERATION = unchecked((int)0x80000051);
|
||||
internal const int E_INVALID_PROTOCOL_FORMAT = unchecked((int)0x80000052);
|
||||
internal const int E_NUMERIC_OVERFLOW = unchecked((int)0x80000053);
|
||||
internal const int E_FAIL = unchecked((int)0x80004005);
|
||||
}
|
||||
|
||||
internal enum Action
|
||||
{
|
||||
NoAction = 0,
|
||||
SendToNetwork = 1,
|
||||
IndicateSendComplete = 2,
|
||||
ReceiveFromNetwork = 3,
|
||||
IndicateReceiveComplete = 4,
|
||||
}
|
||||
|
||||
internal enum BufferType : uint
|
||||
{
|
||||
None = 0x00000000,
|
||||
UTF8Message = 0x80000000,
|
||||
UTF8Fragment = 0x80000001,
|
||||
BinaryMessage = 0x80000002,
|
||||
BinaryFragment = 0x80000003,
|
||||
Close = 0x80000004,
|
||||
PingPong = 0x80000005,
|
||||
UnsolicitedPong = 0x80000006
|
||||
}
|
||||
|
||||
internal enum PropertyType
|
||||
{
|
||||
ReceiveBufferSize = 0,
|
||||
SendBufferSize = 1,
|
||||
DisableMasking = 2,
|
||||
AllocatedBuffer = 3,
|
||||
DisableUtf8Verification = 4,
|
||||
KeepAliveInterval = 5,
|
||||
}
|
||||
|
||||
internal enum ActionQueue
|
||||
{
|
||||
Send = 1,
|
||||
Receive = 2,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct Property
|
||||
{
|
||||
internal PropertyType Type;
|
||||
internal IntPtr PropertyData;
|
||||
internal uint PropertySize;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct Buffer
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
internal DataBuffer Data;
|
||||
[FieldOffset(0)]
|
||||
internal CloseBuffer CloseStatus;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct DataBuffer
|
||||
{
|
||||
internal IntPtr BufferData;
|
||||
internal uint BufferLength;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct CloseBuffer
|
||||
{
|
||||
internal IntPtr ReasonData;
|
||||
internal uint ReasonLength;
|
||||
internal ushort CloseStatus;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct HttpHeader
|
||||
{
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
internal string Name;
|
||||
internal uint NameLength;
|
||||
[MarshalAs(UnmanagedType.LPStr)]
|
||||
internal string Value;
|
||||
internal uint ValueLength;
|
||||
}
|
||||
|
||||
static WebSocketProtocolComponent()
|
||||
{
|
||||
#if NETSTANDARD1_3
|
||||
DllFileName = Path.Combine(Environment.GetEnvironmentVariable("SYSTEMROOT"), "System32", WEBSOCKET);
|
||||
#else
|
||||
DllFileName = Path.Combine(Environment.SystemDirectory, WEBSOCKET);
|
||||
#endif
|
||||
WebSocketDllHandle = SafeLoadLibrary.LoadLibraryEx(DllFileName);
|
||||
|
||||
if (!WebSocketDllHandle.IsInvalid)
|
||||
{
|
||||
PrivateSupportedVersion = GetSupportedVersion();
|
||||
|
||||
ServerFakeRequestHeaders = new HttpHeader[]
|
||||
{
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Connection,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Connection.Length,
|
||||
Value = HttpKnownHeaderNames.Upgrade,
|
||||
ValueLength = (uint)HttpKnownHeaderNames.Upgrade.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Upgrade,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Upgrade.Length,
|
||||
Value = WebSocketHelpers.WebSocketUpgradeToken,
|
||||
ValueLength = (uint)WebSocketHelpers.WebSocketUpgradeToken.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Host,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Host.Length,
|
||||
Value = string.Empty,
|
||||
ValueLength = 0
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.SecWebSocketVersion,
|
||||
NameLength = (uint)HttpKnownHeaderNames.SecWebSocketVersion.Length,
|
||||
Value = SupportedVersion,
|
||||
ValueLength = (uint)SupportedVersion.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.SecWebSocketKey,
|
||||
NameLength = (uint)HttpKnownHeaderNames.SecWebSocketKey.Length,
|
||||
Value = DummyWebsocketKeyBase64,
|
||||
ValueLength = (uint)DummyWebsocketKeyBase64.Length
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal static string SupportedVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WebSocketDllHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
return PrivateSupportedVersion;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return !WebSocketDllHandle.IsInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetSupportedVersion()
|
||||
{
|
||||
if (WebSocketDllHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
SafeWebSocketHandle webSocketHandle = null;
|
||||
try
|
||||
{
|
||||
int errorCode = WebSocketCreateClientHandle_Raw(null, 0, out webSocketHandle);
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
if (webSocketHandle == null ||
|
||||
webSocketHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
IntPtr additionalHeadersPtr;
|
||||
uint additionalHeaderCount;
|
||||
|
||||
errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
InitialClientRequestHeaders,
|
||||
(uint)InitialClientRequestHeaders.Length,
|
||||
out additionalHeadersPtr,
|
||||
out additionalHeaderCount);
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount);
|
||||
|
||||
string version = null;
|
||||
foreach (HttpHeader header in additionalHeaders)
|
||||
{
|
||||
if (string.Compare(header.Name,
|
||||
HttpKnownHeaderNames.SecWebSocketVersion,
|
||||
StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
version = header.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Contract.Assert(version != null, "'version' MUST NOT be NULL.");
|
||||
|
||||
return version;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (webSocketHandle != null)
|
||||
{
|
||||
webSocketHandle.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WebSocketCreateClientHandle(Property[] properties,
|
||||
out SafeWebSocketHandle webSocketHandle)
|
||||
{
|
||||
uint propertyCount = properties == null ? 0 : (uint)properties.Length;
|
||||
|
||||
if (WebSocketDllHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
int errorCode = WebSocketCreateClientHandle_Raw(properties, propertyCount, out webSocketHandle);
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
if (webSocketHandle == null ||
|
||||
webSocketHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
IntPtr additionalHeadersPtr;
|
||||
uint additionalHeaderCount;
|
||||
|
||||
// Currently the WSPC doesn't allow to initiate a data session
|
||||
// without also being involved in the http handshake
|
||||
// There is no information whatsoever, which is needed by the
|
||||
// WSPC for parsing WebSocket frames from the HTTP handshake
|
||||
// In the managed implementation the HTTP header handling
|
||||
// will be done using the managed HTTP stack and we will
|
||||
// just fake an HTTP handshake for the WSPC calling
|
||||
// WebSocketBeginClientHandshake and WebSocketEndClientHandshake
|
||||
// with statically defined dummy headers.
|
||||
errorCode = WebSocketBeginClientHandshake_Raw(webSocketHandle,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
InitialClientRequestHeaders,
|
||||
(uint)InitialClientRequestHeaders.Length,
|
||||
out additionalHeadersPtr,
|
||||
out additionalHeaderCount);
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
HttpHeader[] additionalHeaders = MarshalHttpHeaders(additionalHeadersPtr, (int)additionalHeaderCount);
|
||||
|
||||
string key = null;
|
||||
foreach (HttpHeader header in additionalHeaders)
|
||||
{
|
||||
if (string.Compare(header.Name,
|
||||
HttpKnownHeaderNames.SecWebSocketKey,
|
||||
StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
key = header.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Contract.Assert(key != null, "'key' MUST NOT be NULL.");
|
||||
|
||||
string acceptValue = WebSocketHelpers.GetSecWebSocketAcceptString(key);
|
||||
HttpHeader[] responseHeaders = new HttpHeader[]
|
||||
{
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Connection,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Connection.Length,
|
||||
Value = HttpKnownHeaderNames.Upgrade,
|
||||
ValueLength = (uint)HttpKnownHeaderNames.Upgrade.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.Upgrade,
|
||||
NameLength = (uint)HttpKnownHeaderNames.Upgrade.Length,
|
||||
Value = WebSocketHelpers.WebSocketUpgradeToken,
|
||||
ValueLength = (uint)WebSocketHelpers.WebSocketUpgradeToken.Length
|
||||
},
|
||||
new HttpHeader()
|
||||
{
|
||||
Name = HttpKnownHeaderNames.SecWebSocketAccept,
|
||||
NameLength = (uint)HttpKnownHeaderNames.SecWebSocketAccept.Length,
|
||||
Value = acceptValue,
|
||||
ValueLength = (uint)acceptValue.Length
|
||||
}
|
||||
};
|
||||
|
||||
errorCode = WebSocketEndClientHandshake_Raw(webSocketHandle,
|
||||
responseHeaders,
|
||||
(uint)responseHeaders.Length,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero);
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point.");
|
||||
}
|
||||
|
||||
internal static void WebSocketCreateServerHandle(Property[] properties,
|
||||
int propertyCount,
|
||||
out SafeWebSocketHandle webSocketHandle)
|
||||
{
|
||||
Contract.Assert(propertyCount >= 0, "'propertyCount' MUST NOT be negative.");
|
||||
Contract.Assert((properties == null && propertyCount == 0) ||
|
||||
(properties != null && propertyCount == properties.Length),
|
||||
"'propertyCount' MUST MATCH 'properties.Length'.");
|
||||
|
||||
if (WebSocketDllHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
int errorCode = WebSocketCreateServerHandle_Raw(properties, (uint)propertyCount, out webSocketHandle);
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
if (webSocketHandle == null ||
|
||||
webSocketHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
IntPtr responseHeadersPtr;
|
||||
uint responseHeaderCount;
|
||||
|
||||
// Currently the WSPC doesn't allow to initiate a data session
|
||||
// without also being involved in the http handshake
|
||||
// There is no information whatsoever, which is needed by the
|
||||
// WSPC for parsing WebSocket frames from the HTTP handshake
|
||||
// In the managed implementation the HTTP header handling
|
||||
// will be done using the managed HTTP stack and we will
|
||||
// just fake an HTTP handshake for the WSPC calling
|
||||
// WebSocketBeginServerHandshake and WebSocketEndServerHandshake
|
||||
// with statically defined dummy headers.
|
||||
errorCode = WebSocketBeginServerHandshake_Raw(webSocketHandle,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
ServerFakeRequestHeaders,
|
||||
(uint)ServerFakeRequestHeaders.Length,
|
||||
out responseHeadersPtr,
|
||||
out responseHeaderCount);
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
HttpHeader[] responseHeaders = MarshalHttpHeaders(responseHeadersPtr, (int)responseHeaderCount);
|
||||
errorCode = WebSocketEndServerHandshake_Raw(webSocketHandle);
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
Contract.Assert(webSocketHandle != null, "'webSocketHandle' MUST NOT be NULL at this point.");
|
||||
}
|
||||
|
||||
internal static void WebSocketAbortHandle(SafeHandle webSocketHandle)
|
||||
{
|
||||
Contract.Assert(webSocketHandle != null && !webSocketHandle.IsInvalid,
|
||||
"'webSocketHandle' MUST NOT be NULL or INVALID.");
|
||||
|
||||
WebSocketAbortHandle_Raw(webSocketHandle);
|
||||
|
||||
DrainActionQueue(webSocketHandle, ActionQueue.Send);
|
||||
DrainActionQueue(webSocketHandle, ActionQueue.Receive);
|
||||
}
|
||||
|
||||
internal static void WebSocketDeleteHandle(IntPtr webSocketPtr)
|
||||
{
|
||||
Contract.Assert(webSocketPtr != IntPtr.Zero, "'webSocketPtr' MUST NOT be IntPtr.Zero.");
|
||||
WebSocketDeleteHandle_Raw(webSocketPtr);
|
||||
}
|
||||
|
||||
internal static void WebSocketSend(WebSocketBase webSocket,
|
||||
BufferType bufferType,
|
||||
Buffer buffer)
|
||||
{
|
||||
Contract.Assert(webSocket != null,
|
||||
"'webSocket' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(webSocket.SessionHandle != null && !webSocket.SessionHandle.IsInvalid,
|
||||
"'webSocket.SessionHandle' MUST NOT be NULL or INVALID.");
|
||||
|
||||
ThrowIfSessionHandleClosed(webSocket);
|
||||
|
||||
int errorCode;
|
||||
try
|
||||
{
|
||||
errorCode = WebSocketSend_Raw(webSocket.SessionHandle, bufferType, ref buffer, IntPtr.Zero);
|
||||
}
|
||||
catch (ObjectDisposedException innerException)
|
||||
{
|
||||
throw ConvertObjectDisposedException(webSocket, innerException);
|
||||
}
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
}
|
||||
|
||||
internal static void WebSocketSendWithoutBody(WebSocketBase webSocket,
|
||||
BufferType bufferType)
|
||||
{
|
||||
Contract.Assert(webSocket != null,
|
||||
"'webSocket' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(webSocket.SessionHandle != null && !webSocket.SessionHandle.IsInvalid,
|
||||
"'webSocket.SessionHandle' MUST NOT be NULL or INVALID.");
|
||||
|
||||
ThrowIfSessionHandleClosed(webSocket);
|
||||
|
||||
int errorCode;
|
||||
try
|
||||
{
|
||||
errorCode = WebSocketSendWithoutBody_Raw(webSocket.SessionHandle, bufferType, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
catch (ObjectDisposedException innerException)
|
||||
{
|
||||
throw ConvertObjectDisposedException(webSocket, innerException);
|
||||
}
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
}
|
||||
|
||||
internal static void WebSocketReceive(WebSocketBase webSocket)
|
||||
{
|
||||
Contract.Assert(webSocket != null,
|
||||
"'webSocket' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(webSocket.SessionHandle != null && !webSocket.SessionHandle.IsInvalid,
|
||||
"'webSocket.SessionHandle' MUST NOT be NULL or INVALID.");
|
||||
|
||||
ThrowIfSessionHandleClosed(webSocket);
|
||||
|
||||
int errorCode;
|
||||
try
|
||||
{
|
||||
errorCode = WebSocketReceive_Raw(webSocket.SessionHandle, IntPtr.Zero, IntPtr.Zero);
|
||||
}
|
||||
catch (ObjectDisposedException innerException)
|
||||
{
|
||||
throw ConvertObjectDisposedException(webSocket, innerException);
|
||||
}
|
||||
|
||||
ThrowOnError(errorCode);
|
||||
}
|
||||
|
||||
internal static void WebSocketGetAction(WebSocketBase webSocket,
|
||||
ActionQueue actionQueue,
|
||||
Buffer[] dataBuffers,
|
||||
ref uint dataBufferCount,
|
||||
out Action action,
|
||||
out BufferType bufferType,
|
||||
out IntPtr actionContext)
|
||||
{
|
||||
Contract.Assert(webSocket != null,
|
||||
"'webSocket' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(webSocket.SessionHandle != null && !webSocket.SessionHandle.IsInvalid,
|
||||
"'webSocket.SessionHandle' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(dataBufferCount >= 0, "'dataBufferCount' MUST NOT be negative.");
|
||||
Contract.Assert((dataBuffers == null && dataBufferCount == 0) ||
|
||||
(dataBuffers != null && dataBufferCount == dataBuffers.Length),
|
||||
"'dataBufferCount' MUST MATCH 'dataBuffers.Length'.");
|
||||
|
||||
action = Action.NoAction;
|
||||
bufferType = BufferType.None;
|
||||
actionContext = IntPtr.Zero;
|
||||
|
||||
IntPtr dummy;
|
||||
ThrowIfSessionHandleClosed(webSocket);
|
||||
|
||||
int errorCode;
|
||||
try
|
||||
{
|
||||
errorCode = WebSocketGetAction_Raw(webSocket.SessionHandle,
|
||||
actionQueue,
|
||||
dataBuffers,
|
||||
ref dataBufferCount,
|
||||
out action,
|
||||
out bufferType,
|
||||
out dummy,
|
||||
out actionContext);
|
||||
}
|
||||
catch (ObjectDisposedException innerException)
|
||||
{
|
||||
throw ConvertObjectDisposedException(webSocket, innerException);
|
||||
}
|
||||
ThrowOnError(errorCode);
|
||||
|
||||
webSocket.ValidateNativeBuffers(action, bufferType, dataBuffers, dataBufferCount);
|
||||
|
||||
Contract.Assert(dataBufferCount >= 0);
|
||||
Contract.Assert((dataBufferCount == 0 && dataBuffers == null) ||
|
||||
(dataBufferCount <= dataBuffers.Length));
|
||||
}
|
||||
|
||||
internal static void WebSocketCompleteAction(WebSocketBase webSocket,
|
||||
IntPtr actionContext,
|
||||
int bytesTransferred)
|
||||
{
|
||||
Contract.Assert(webSocket != null,
|
||||
"'webSocket' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(webSocket.SessionHandle != null && !webSocket.SessionHandle.IsInvalid,
|
||||
"'webSocket.SessionHandle' MUST NOT be NULL or INVALID.");
|
||||
Contract.Assert(actionContext != IntPtr.Zero, "'actionContext' MUST NOT be IntPtr.Zero.");
|
||||
Contract.Assert(bytesTransferred >= 0, "'bytesTransferred' MUST NOT be negative.");
|
||||
|
||||
if (webSocket.SessionHandle.IsClosed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
WebSocketCompleteAction_Raw(webSocket.SessionHandle, actionContext, (uint)bytesTransferred);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal static TimeSpan WebSocketGetDefaultKeepAliveInterval()
|
||||
{
|
||||
uint result = 0;
|
||||
uint size = sizeof(uint);
|
||||
int errorCode = WebSocketGetGlobalProperty_Raw(PropertyType.KeepAliveInterval, ref result, ref size);
|
||||
if (!Succeeded(errorCode))
|
||||
{
|
||||
Contract.Assert(errorCode == 0, "errorCode: " + errorCode);
|
||||
return Timeout.InfiniteTimeSpan;
|
||||
}
|
||||
return TimeSpan.FromMilliseconds(result);
|
||||
}
|
||||
|
||||
private static void DrainActionQueue(SafeHandle webSocketHandle, ActionQueue actionQueue)
|
||||
{
|
||||
Contract.Assert(webSocketHandle != null && !webSocketHandle.IsInvalid,
|
||||
"'webSocketHandle' MUST NOT be NULL or INVALID.");
|
||||
|
||||
IntPtr actionContext;
|
||||
IntPtr dummy;
|
||||
Action action;
|
||||
BufferType bufferType;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Buffer[] dataBuffers = new Buffer[1];
|
||||
uint dataBufferCount = 1;
|
||||
int errorCode = WebSocketGetAction_Raw(webSocketHandle,
|
||||
actionQueue,
|
||||
dataBuffers,
|
||||
ref dataBufferCount,
|
||||
out action,
|
||||
out bufferType,
|
||||
out dummy,
|
||||
out actionContext);
|
||||
|
||||
if (!Succeeded(errorCode))
|
||||
{
|
||||
Contract.Assert(errorCode == 0, "'errorCode' MUST be 0.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == Action.NoAction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocketCompleteAction_Raw(webSocketHandle, actionContext, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void MarshalAndVerifyHttpHeader(IntPtr httpHeaderPtr,
|
||||
ref HttpHeader httpHeader)
|
||||
{
|
||||
Contract.Assert(httpHeaderPtr != IntPtr.Zero, "'currentHttpHeaderPtr' MUST NOT be IntPtr.Zero.");
|
||||
|
||||
IntPtr httpHeaderNamePtr = Marshal.ReadIntPtr(httpHeaderPtr);
|
||||
IntPtr lengthPtr = IntPtr.Add(httpHeaderPtr, IntPtr.Size);
|
||||
int length = Marshal.ReadInt32(lengthPtr);
|
||||
Contract.Assert(length >= 0, "'length' MUST NOT be negative.");
|
||||
|
||||
if (httpHeaderNamePtr != IntPtr.Zero)
|
||||
{
|
||||
httpHeader.Name = Marshal.PtrToStringAnsi(httpHeaderNamePtr, length);
|
||||
}
|
||||
|
||||
if ((httpHeader.Name == null && length != 0) ||
|
||||
(httpHeader.Name != null && length != httpHeader.Name.Length))
|
||||
{
|
||||
Contract.Assert(false, "The length of 'httpHeader.Name' MUST MATCH 'length'.");
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
// structure of HttpHeader:
|
||||
// Name = string*
|
||||
// NameLength = uint*
|
||||
// Value = string*
|
||||
// ValueLength = uint*
|
||||
// NOTE - All fields in the object are pointers to the actual value, hence the use of
|
||||
// n * IntPtr.Size to get to the correct place in the object.
|
||||
int valueOffset = 2 * IntPtr.Size;
|
||||
int lengthOffset = 3 * IntPtr.Size;
|
||||
|
||||
IntPtr httpHeaderValuePtr =
|
||||
Marshal.ReadIntPtr(IntPtr.Add(httpHeaderPtr, valueOffset));
|
||||
lengthPtr = IntPtr.Add(httpHeaderPtr, lengthOffset);
|
||||
length = Marshal.ReadInt32(lengthPtr);
|
||||
httpHeader.Value = Marshal.PtrToStringAnsi(httpHeaderValuePtr, (int)length);
|
||||
|
||||
if ((httpHeader.Value == null && length != 0) ||
|
||||
(httpHeader.Value != null && length != httpHeader.Value.Length))
|
||||
{
|
||||
Contract.Assert(false, "The length of 'httpHeader.Value' MUST MATCH 'length'.");
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpHeader[] MarshalHttpHeaders(IntPtr nativeHeadersPtr,
|
||||
int nativeHeaderCount)
|
||||
{
|
||||
Contract.Assert(nativeHeaderCount >= 0, "'nativeHeaderCount' MUST NOT be negative.");
|
||||
Contract.Assert(nativeHeadersPtr != IntPtr.Zero || nativeHeaderCount == 0,
|
||||
"'nativeHeaderCount' MUST be 0.");
|
||||
|
||||
HttpHeader[] httpHeaders = new HttpHeader[nativeHeaderCount];
|
||||
|
||||
// structure of HttpHeader:
|
||||
// Name = string*
|
||||
// NameLength = uint*
|
||||
// Value = string*
|
||||
// ValueLength = uint*
|
||||
// NOTE - All fields in the object are pointers to the actual value, hence the use of
|
||||
// 4 * IntPtr.Size to get to the next header.
|
||||
int httpHeaderStructSize = 4 * IntPtr.Size;
|
||||
|
||||
for (int i = 0; i < nativeHeaderCount; i++)
|
||||
{
|
||||
int offset = httpHeaderStructSize * i;
|
||||
IntPtr currentHttpHeaderPtr = IntPtr.Add(nativeHeadersPtr, offset);
|
||||
MarshalAndVerifyHttpHeader(currentHttpHeaderPtr, ref httpHeaders[i]);
|
||||
}
|
||||
|
||||
Contract.Assert(httpHeaders != null);
|
||||
Contract.Assert(httpHeaders.Length == nativeHeaderCount);
|
||||
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
public static bool Succeeded(int hr)
|
||||
{
|
||||
return (hr >= 0);
|
||||
}
|
||||
|
||||
private static void ThrowOnError(int errorCode)
|
||||
{
|
||||
if (Succeeded(errorCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new WebSocketException(errorCode);
|
||||
}
|
||||
|
||||
private static void ThrowIfSessionHandleClosed(WebSocketBase webSocket)
|
||||
{
|
||||
if (webSocket.SessionHandle.IsClosed)
|
||||
{
|
||||
throw new WebSocketException(WebSocketError.InvalidState,
|
||||
SR.GetString(SR.net_WebSockets_InvalidState_ClosedOrAborted, webSocket.GetType().FullName, webSocket.State));
|
||||
}
|
||||
}
|
||||
|
||||
private static WebSocketException ConvertObjectDisposedException(WebSocketBase webSocket, ObjectDisposedException innerException)
|
||||
{
|
||||
return new WebSocketException(WebSocketError.InvalidState,
|
||||
SR.GetString(SR.net_WebSockets_InvalidState_ClosedOrAborted, webSocket.GetType().FullName, webSocket.State),
|
||||
innerException);
|
||||
}
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketCreateClientHandle", ExactSpelling = true)]
|
||||
private static extern int WebSocketCreateClientHandle_Raw(
|
||||
[In]Property[] properties,
|
||||
[In] uint propertyCount,
|
||||
[Out] out SafeWebSocketHandle webSocketHandle);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketBeginClientHandshake", ExactSpelling = true)]
|
||||
private static extern int WebSocketBeginClientHandshake_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] IntPtr subProtocols,
|
||||
[In] uint subProtocolCount,
|
||||
[In] IntPtr extensions,
|
||||
[In] uint extensionCount,
|
||||
[In] HttpHeader[] initialHeaders,
|
||||
[In] uint initialHeaderCount,
|
||||
[Out] out IntPtr additionalHeadersPtr,
|
||||
[Out] out uint additionalHeaderCount);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketEndClientHandshake", ExactSpelling = true)]
|
||||
private static extern int WebSocketEndClientHandshake_Raw([In] SafeHandle webSocketHandle,
|
||||
[In] HttpHeader[] responseHeaders,
|
||||
[In] uint responseHeaderCount,
|
||||
[In, Out] IntPtr selectedExtensions,
|
||||
[In] IntPtr selectedExtensionCount,
|
||||
[In] IntPtr selectedSubProtocol);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketBeginServerHandshake", ExactSpelling = true)]
|
||||
private static extern int WebSocketBeginServerHandshake_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] IntPtr subProtocol,
|
||||
[In] IntPtr extensions,
|
||||
[In] uint extensionCount,
|
||||
[In] HttpHeader[] requestHeaders,
|
||||
[In] uint requestHeaderCount,
|
||||
[Out] out IntPtr responseHeadersPtr,
|
||||
[Out] out uint responseHeaderCount);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketEndServerHandshake", ExactSpelling = true)]
|
||||
private static extern int WebSocketEndServerHandshake_Raw([In] SafeHandle webSocketHandle);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketCreateServerHandle", ExactSpelling = true)]
|
||||
private static extern int WebSocketCreateServerHandle_Raw(
|
||||
[In]Property[] properties,
|
||||
[In] uint propertyCount,
|
||||
[Out] out SafeWebSocketHandle webSocketHandle);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketAbortHandle", ExactSpelling = true)]
|
||||
private static extern void WebSocketAbortHandle_Raw(
|
||||
[In] SafeHandle webSocketHandle);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketDeleteHandle", ExactSpelling = true)]
|
||||
private static extern void WebSocketDeleteHandle_Raw(
|
||||
[In] IntPtr webSocketHandle);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketSend", ExactSpelling = true)]
|
||||
private static extern int WebSocketSend_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] BufferType bufferType,
|
||||
[In] ref Buffer buffer,
|
||||
[In] IntPtr applicationContext);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketSend", ExactSpelling = true)]
|
||||
private static extern int WebSocketSendWithoutBody_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] BufferType bufferType,
|
||||
[In] IntPtr buffer,
|
||||
[In] IntPtr applicationContext);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketReceive", ExactSpelling = true)]
|
||||
private static extern int WebSocketReceive_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] IntPtr buffers,
|
||||
[In] IntPtr applicationContext);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketGetAction", ExactSpelling = true)]
|
||||
private static extern int WebSocketGetAction_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] ActionQueue actionQueue,
|
||||
[In, Out] Buffer[] dataBuffers,
|
||||
[In, Out] ref uint dataBufferCount,
|
||||
[Out] out Action action,
|
||||
[Out] out BufferType bufferType,
|
||||
[Out] out IntPtr applicationContext,
|
||||
[Out] out IntPtr actionContext);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketCompleteAction", ExactSpelling = true)]
|
||||
private static extern void WebSocketCompleteAction_Raw(
|
||||
[In] SafeHandle webSocketHandle,
|
||||
[In] IntPtr actionContext,
|
||||
[In] uint bytesTransferred);
|
||||
|
||||
[DllImport(WEBSOCKET, EntryPoint = "WebSocketGetGlobalProperty", ExactSpelling = true)]
|
||||
private static extern int WebSocketGetGlobalProperty_Raw(
|
||||
[In] PropertyType property,
|
||||
[In, Out] ref uint value,
|
||||
[In, Out] ref uint size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyMetadata("Serviceable", "True")]
|
||||
[assembly: NeutralResourcesLanguage("en-us")]
|
||||
[assembly: AssemblyCompany("Microsoft Corporation.")]
|
||||
[assembly: AssemblyCopyright("© Microsoft Corporation. All rights reserved.")]
|
||||
[assembly: AssemblyProduct("Microsoft ASP.NET Core")]
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ServerWebSocket.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
internal sealed class ServerWebSocket : WebSocketBase
|
||||
{
|
||||
private readonly SafeHandle _sessionHandle;
|
||||
private readonly UnsafeNativeMethods.WebSocketProtocolComponent.Property[] _properties;
|
||||
|
||||
public ServerWebSocket(Stream innerStream,
|
||||
string subProtocol,
|
||||
int receiveBufferSize,
|
||||
TimeSpan keepAliveInterval,
|
||||
ArraySegment<byte> internalBuffer)
|
||||
: base(innerStream, subProtocol, keepAliveInterval,
|
||||
WebSocketBuffer.CreateServerBuffer(internalBuffer, receiveBufferSize))
|
||||
{
|
||||
_properties = this.InternalBuffer.CreateProperties(false);
|
||||
_sessionHandle = this.CreateWebSocketHandle();
|
||||
|
||||
if (_sessionHandle == null || _sessionHandle.IsInvalid)
|
||||
{
|
||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
||||
}
|
||||
|
||||
StartKeepAliveTimer();
|
||||
}
|
||||
|
||||
internal override SafeHandle SessionHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Assert(_sessionHandle != null, "'m_SessionHandle MUST NOT be NULL.");
|
||||
return _sessionHandle;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
|
||||
Justification = "No arbitrary data controlled by PT code is leaking into native code.")]
|
||||
private SafeHandle CreateWebSocketHandle()
|
||||
{
|
||||
Contract.Assert(_properties != null, "'m_Properties' MUST NOT be NULL.");
|
||||
SafeWebSocketHandle sessionHandle;
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.WebSocketCreateServerHandle(_properties,
|
||||
_properties.Length,
|
||||
out sessionHandle);
|
||||
Contract.Assert(sessionHandle != null, "'sessionHandle MUST NOT be NULL.");
|
||||
|
||||
return sessionHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,711 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketBuffer.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.Net.WebSockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
// This class helps to abstract the internal WebSocket buffer, which is used to interact with the native WebSocket
|
||||
// protocol component (WSPC). It helps to shield the details of the layout and the involved pointer arithmetic.
|
||||
// The internal WebSocket buffer also contains a segment, which is used by the WebSocketBase class to buffer
|
||||
// payload (parsed by WSPC already) for the application, if the application requested fewer bytes than the
|
||||
// WSPC returned. The internal buffer is pinned for the whole lifetime if this class.
|
||||
// LAYOUT:
|
||||
// | Native buffer | PayloadReceiveBuffer | PropertyBuffer |
|
||||
// | RBS + SBS + 144 | RBS | PBS |
|
||||
// | Only WSPC may modify | Only WebSocketBase may modify |
|
||||
//
|
||||
// *RBS = ReceiveBufferSize, *SBS = SendBufferSize
|
||||
// *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes)
|
||||
public class WebSocketBuffer : IDisposable
|
||||
{
|
||||
private const int NativeOverheadBufferSize = 144;
|
||||
public const int MinSendBufferSize = 16;
|
||||
internal const int MinReceiveBufferSize = 256;
|
||||
internal const int MaxBufferSize = 64 * 1024;
|
||||
private static readonly int SizeOfUInt = Marshal.SizeOf<uint>();
|
||||
private static readonly int SizeOfBool = Marshal.SizeOf<bool>();
|
||||
private static readonly int PropertyBufferSize = (2 * SizeOfUInt) + SizeOfBool + IntPtr.Size;
|
||||
|
||||
private readonly int _ReceiveBufferSize;
|
||||
|
||||
// Indicates the range of the pinned byte[] that can be used by the WSPC (nativeBuffer + pinnedSendBuffer)
|
||||
private readonly long _StartAddress;
|
||||
private readonly long _EndAddress;
|
||||
private readonly GCHandle _GCHandle;
|
||||
private readonly ArraySegment<byte> _InternalBuffer;
|
||||
private readonly ArraySegment<byte> _NativeBuffer;
|
||||
private readonly ArraySegment<byte> _PayloadBuffer;
|
||||
private readonly ArraySegment<byte> _PropertyBuffer;
|
||||
private readonly int _SendBufferSize;
|
||||
private volatile int _PayloadOffset;
|
||||
private WebSocketReceiveResult _BufferedPayloadReceiveResult;
|
||||
private long _PinnedSendBufferStartAddress;
|
||||
private long _PinnedSendBufferEndAddress;
|
||||
private ArraySegment<byte> _PinnedSendBuffer;
|
||||
private GCHandle _PinnedSendBufferHandle;
|
||||
private int _StateWhenDisposing = int.MinValue;
|
||||
private int _SendBufferState;
|
||||
|
||||
private WebSocketBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
|
||||
{
|
||||
Contract.Assert(internalBuffer.Array != null, "'internalBuffer' MUST NOT be NULL.");
|
||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize >= MinSendBufferSize,
|
||||
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(receiveBufferSize <= MaxBufferSize,
|
||||
"'receiveBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize <= MaxBufferSize,
|
||||
"'sendBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
|
||||
_ReceiveBufferSize = receiveBufferSize;
|
||||
_SendBufferSize = sendBufferSize;
|
||||
_InternalBuffer = internalBuffer;
|
||||
_GCHandle = GCHandle.Alloc(internalBuffer.Array, GCHandleType.Pinned);
|
||||
// Size of the internal buffer owned exclusively by the WSPC.
|
||||
int nativeBufferSize = _ReceiveBufferSize + _SendBufferSize + NativeOverheadBufferSize;
|
||||
_StartAddress = Marshal.UnsafeAddrOfPinnedArrayElement(internalBuffer.Array, internalBuffer.Offset).ToInt64();
|
||||
_EndAddress = _StartAddress + nativeBufferSize;
|
||||
_NativeBuffer = new ArraySegment<byte>(internalBuffer.Array, internalBuffer.Offset, nativeBufferSize);
|
||||
_PayloadBuffer = new ArraySegment<byte>(internalBuffer.Array,
|
||||
_NativeBuffer.Offset + _NativeBuffer.Count,
|
||||
_ReceiveBufferSize);
|
||||
_PropertyBuffer = new ArraySegment<byte>(internalBuffer.Array,
|
||||
_PayloadBuffer.Offset + _PayloadBuffer.Count,
|
||||
PropertyBufferSize);
|
||||
_SendBufferState = SendBufferState.None;
|
||||
}
|
||||
|
||||
public int ReceiveBufferSize
|
||||
{
|
||||
get { return _ReceiveBufferSize; }
|
||||
}
|
||||
|
||||
public int SendBufferSize
|
||||
{
|
||||
get { return _SendBufferSize; }
|
||||
}
|
||||
|
||||
internal static WebSocketBuffer CreateClientBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
|
||||
{
|
||||
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, false),
|
||||
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
|
||||
|
||||
return new WebSocketBuffer(internalBuffer, receiveBufferSize, GetNativeSendBufferSize(sendBufferSize, false));
|
||||
}
|
||||
|
||||
internal static WebSocketBuffer CreateServerBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize)
|
||||
{
|
||||
int sendBufferSize = GetNativeSendBufferSize(MinSendBufferSize, true);
|
||||
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, true),
|
||||
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
|
||||
|
||||
return new WebSocketBuffer(internalBuffer, receiveBufferSize, sendBufferSize);
|
||||
}
|
||||
|
||||
public void Dispose(WebSocketState webSocketState)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _StateWhenDisposing, (int)webSocketState, int.MinValue) != int.MinValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.CleanUp();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(WebSocketState.None);
|
||||
}
|
||||
|
||||
internal UnsafeNativeMethods.WebSocketProtocolComponent.Property[] CreateProperties(bool useZeroMaskingKey)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
// serialize marshaled property values in the property segment of the internal buffer
|
||||
IntPtr internalBufferPtr = _GCHandle.AddrOfPinnedObject();
|
||||
int offset = _PropertyBuffer.Offset;
|
||||
Marshal.WriteInt32(internalBufferPtr, offset, _ReceiveBufferSize);
|
||||
offset += SizeOfUInt;
|
||||
Marshal.WriteInt32(internalBufferPtr, offset, _SendBufferSize);
|
||||
offset += SizeOfUInt;
|
||||
Marshal.WriteIntPtr(internalBufferPtr, offset, internalBufferPtr);
|
||||
offset += IntPtr.Size;
|
||||
Marshal.WriteInt32(internalBufferPtr, offset, useZeroMaskingKey ? (int)1 : (int)0);
|
||||
|
||||
int propertyCount = useZeroMaskingKey ? 4 : 3;
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.Property[] properties =
|
||||
new UnsafeNativeMethods.WebSocketProtocolComponent.Property[propertyCount];
|
||||
|
||||
// Calculate the pointers to the positions of the properties within the internal buffer
|
||||
offset = _PropertyBuffer.Offset;
|
||||
properties[0] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
|
||||
{
|
||||
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.ReceiveBufferSize,
|
||||
PropertySize = (uint)SizeOfUInt,
|
||||
PropertyData = IntPtr.Add(internalBufferPtr, offset)
|
||||
};
|
||||
offset += SizeOfUInt;
|
||||
|
||||
properties[1] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
|
||||
{
|
||||
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.SendBufferSize,
|
||||
PropertySize = (uint)SizeOfUInt,
|
||||
PropertyData = IntPtr.Add(internalBufferPtr, offset)
|
||||
};
|
||||
offset += SizeOfUInt;
|
||||
|
||||
properties[2] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
|
||||
{
|
||||
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.AllocatedBuffer,
|
||||
PropertySize = (uint)_NativeBuffer.Count,
|
||||
PropertyData = IntPtr.Add(internalBufferPtr, offset)
|
||||
};
|
||||
offset += IntPtr.Size;
|
||||
|
||||
if (useZeroMaskingKey)
|
||||
{
|
||||
properties[3] = new UnsafeNativeMethods.WebSocketProtocolComponent.Property()
|
||||
{
|
||||
Type = UnsafeNativeMethods.WebSocketProtocolComponent.PropertyType.DisableMasking,
|
||||
PropertySize = (uint)SizeOfBool,
|
||||
PropertyData = IntPtr.Add(internalBufferPtr, offset)
|
||||
};
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
internal void PinSendBuffer(ArraySegment<byte> payload, out bool bufferHasBeenPinned)
|
||||
{
|
||||
bufferHasBeenPinned = false;
|
||||
WebSocketHelpers.ValidateBuffer(payload.Array, payload.Offset, payload.Count);
|
||||
int previousState = Interlocked.Exchange(ref _SendBufferState, SendBufferState.SendPayloadSpecified);
|
||||
|
||||
if (previousState != SendBufferState.None)
|
||||
{
|
||||
Contract.Assert(false, "'m_SendBufferState' MUST BE 'None' at this point.");
|
||||
// Indicates a violation in the API contract that could indicate
|
||||
// memory corruption because the pinned sendbuffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
_PinnedSendBuffer = payload;
|
||||
_PinnedSendBufferHandle = GCHandle.Alloc(_PinnedSendBuffer.Array, GCHandleType.Pinned);
|
||||
bufferHasBeenPinned = true;
|
||||
_PinnedSendBufferStartAddress =
|
||||
Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array, _PinnedSendBuffer.Offset).ToInt64();
|
||||
_PinnedSendBufferEndAddress = _PinnedSendBufferStartAddress + _PinnedSendBuffer.Count;
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
internal IntPtr ConvertPinnedSendPayloadToNative(ArraySegment<byte> payload)
|
||||
{
|
||||
return ConvertPinnedSendPayloadToNative(payload.Array, payload.Offset, payload.Count);
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
internal IntPtr ConvertPinnedSendPayloadToNative(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (!IsPinnedSendPayloadBuffer(buffer, offset, count))
|
||||
{
|
||||
// Indicates a violation in the API contract that could indicate
|
||||
// memory corruption because the pinned sendbuffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array,
|
||||
_PinnedSendBuffer.Offset).ToInt64() == _PinnedSendBufferStartAddress,
|
||||
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
|
||||
|
||||
return new IntPtr(_PinnedSendBufferStartAddress + offset - _PinnedSendBuffer.Offset);
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
internal ArraySegment<byte> ConvertPinnedSendPayloadFromNative(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
|
||||
{
|
||||
if (!IsPinnedSendPayloadBuffer(buffer, bufferType))
|
||||
{
|
||||
// Indicates a violation in the API contract that could indicate
|
||||
// memory corruption because the pinned sendbuffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_PinnedSendBuffer.Array,
|
||||
_PinnedSendBuffer.Offset).ToInt64() == _PinnedSendBufferStartAddress,
|
||||
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
|
||||
|
||||
IntPtr bufferData;
|
||||
uint bufferSize;
|
||||
|
||||
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
|
||||
|
||||
int internalOffset = (int)(bufferData.ToInt64() - _PinnedSendBufferStartAddress);
|
||||
|
||||
return new ArraySegment<byte>(_PinnedSendBuffer.Array, _PinnedSendBuffer.Offset + internalOffset, (int)bufferSize);
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
private bool IsPinnedSendPayloadBuffer(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_SendBufferState != SendBufferState.SendPayloadSpecified)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return object.ReferenceEquals(buffer, _PinnedSendBuffer.Array) &&
|
||||
offset >= _PinnedSendBuffer.Offset &&
|
||||
offset + count <= _PinnedSendBuffer.Offset + _PinnedSendBuffer.Count;
|
||||
}
|
||||
|
||||
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
|
||||
private bool IsPinnedSendPayloadBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
|
||||
{
|
||||
if (_SendBufferState != SendBufferState.SendPayloadSpecified)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr bufferData;
|
||||
uint bufferSize;
|
||||
|
||||
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
|
||||
|
||||
long nativeBufferStartAddress = bufferData.ToInt64();
|
||||
long nativeBufferEndAddress = nativeBufferStartAddress + bufferSize;
|
||||
|
||||
return nativeBufferStartAddress >= _PinnedSendBufferStartAddress &&
|
||||
nativeBufferEndAddress >= _PinnedSendBufferStartAddress &&
|
||||
nativeBufferStartAddress <= _PinnedSendBufferEndAddress &&
|
||||
nativeBufferEndAddress <= _PinnedSendBufferEndAddress;
|
||||
}
|
||||
|
||||
// This method is only thread safe for races between Abort and at most 1 uncompleted send operation
|
||||
internal void ReleasePinnedSendBuffer()
|
||||
{
|
||||
int previousState = Interlocked.Exchange(ref _SendBufferState, SendBufferState.None);
|
||||
|
||||
if (previousState != SendBufferState.SendPayloadSpecified)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_PinnedSendBufferHandle.IsAllocated)
|
||||
{
|
||||
_PinnedSendBufferHandle.Free();
|
||||
}
|
||||
|
||||
_PinnedSendBuffer = WebSocketHelpers.EmptyPayload;
|
||||
}
|
||||
|
||||
internal void BufferPayload(ArraySegment<byte> payload,
|
||||
int unconsumedDataOffset,
|
||||
WebSocketMessageType messageType,
|
||||
bool endOfMessage)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
int bytesBuffered = payload.Count - unconsumedDataOffset;
|
||||
|
||||
Contract.Assert(_PayloadOffset == 0,
|
||||
"'m_PayloadOffset' MUST be '0' at this point.");
|
||||
Contract.Assert(_BufferedPayloadReceiveResult == null || _BufferedPayloadReceiveResult.Count == 0,
|
||||
"'m_BufferedPayloadReceiveResult.Count' MUST be '0' at this point.");
|
||||
|
||||
Buffer.BlockCopy(payload.Array,
|
||||
payload.Offset + unconsumedDataOffset,
|
||||
_PayloadBuffer.Array,
|
||||
_PayloadBuffer.Offset,
|
||||
bytesBuffered);
|
||||
|
||||
_BufferedPayloadReceiveResult =
|
||||
new WebSocketReceiveResult(bytesBuffered, messageType, endOfMessage);
|
||||
|
||||
this.ValidateBufferedPayload();
|
||||
}
|
||||
|
||||
internal bool ReceiveFromBufferedPayload(ArraySegment<byte> buffer, out WebSocketReceiveResult receiveResult)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ValidateBufferedPayload();
|
||||
|
||||
int bytesTransferred = Math.Min(buffer.Count, _BufferedPayloadReceiveResult.Count);
|
||||
receiveResult = WebSocketReceiveResultExtensions.DecrementAndClone(ref _BufferedPayloadReceiveResult, bytesTransferred);
|
||||
|
||||
Buffer.BlockCopy(_PayloadBuffer.Array,
|
||||
_PayloadBuffer.Offset + _PayloadOffset,
|
||||
buffer.Array,
|
||||
buffer.Offset,
|
||||
bytesTransferred);
|
||||
|
||||
bool morePayloadBuffered;
|
||||
if (_BufferedPayloadReceiveResult.Count == 0)
|
||||
{
|
||||
_PayloadOffset = 0;
|
||||
_BufferedPayloadReceiveResult = null;
|
||||
morePayloadBuffered = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_PayloadOffset += bytesTransferred;
|
||||
morePayloadBuffered = true;
|
||||
this.ValidateBufferedPayload();
|
||||
}
|
||||
|
||||
return morePayloadBuffered;
|
||||
}
|
||||
|
||||
internal ArraySegment<byte> ConvertNativeBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
IntPtr bufferData;
|
||||
uint bufferLength;
|
||||
|
||||
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferLength);
|
||||
|
||||
if (bufferData == IntPtr.Zero)
|
||||
{
|
||||
return WebSocketHelpers.EmptyPayload;
|
||||
}
|
||||
|
||||
if (this.IsNativeBuffer(bufferData, bufferLength))
|
||||
{
|
||||
return new ArraySegment<byte>(_InternalBuffer.Array,
|
||||
this.GetOffset(bufferData),
|
||||
(int)bufferLength);
|
||||
}
|
||||
|
||||
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
|
||||
// Indicates a violation in the contract with native Websocket.dll and could indicate
|
||||
// memory corruption because the internal buffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
internal void ConvertCloseBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
|
||||
out WebSocketCloseStatus closeStatus,
|
||||
out string reason)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
IntPtr bufferData;
|
||||
uint bufferLength;
|
||||
closeStatus = (WebSocketCloseStatus)buffer.CloseStatus.CloseStatus;
|
||||
|
||||
UnwrapWebSocketBuffer(buffer, UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.Close, out bufferData, out bufferLength);
|
||||
|
||||
if (bufferData == IntPtr.Zero)
|
||||
{
|
||||
reason = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
ArraySegment<byte> reasonBlob;
|
||||
if (this.IsNativeBuffer(bufferData, bufferLength))
|
||||
{
|
||||
reasonBlob = new ArraySegment<byte>(_InternalBuffer.Array,
|
||||
this.GetOffset(bufferData),
|
||||
(int)bufferLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
|
||||
// Indicates a violation in the contract with native Websocket.dll and could indicate
|
||||
// memory corruption because the internal buffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
// No need to wrap DecoderFallbackException for invalid UTF8 chacters, because
|
||||
// Encoding.UTF8 will not throw but replace invalid characters instead.
|
||||
reason = Encoding.UTF8.GetString(reasonBlob.Array, reasonBlob.Offset, reasonBlob.Count);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ValidateNativeBuffers(UnsafeNativeMethods.WebSocketProtocolComponent.Action action,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer[] dataBuffers,
|
||||
uint dataBufferCount)
|
||||
{
|
||||
Contract.Assert(dataBufferCount <= (uint)int.MaxValue,
|
||||
"'dataBufferCount' MUST NOT be bigger than Int32.MaxValue.");
|
||||
Contract.Assert(dataBuffers != null, "'dataBuffers' MUST NOT be NULL.");
|
||||
|
||||
ThrowIfDisposed();
|
||||
if (dataBufferCount > dataBuffers.Length)
|
||||
{
|
||||
Contract.Assert(false, "'dataBufferCount' MUST NOT be bigger than 'dataBuffers.Length'.");
|
||||
// Indicates a violation in the contract with native Websocket.dll and could indicate
|
||||
// memory corruption because the internal buffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
|
||||
int count = dataBuffers.Length;
|
||||
bool isSendActivity = action == UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateSendComplete ||
|
||||
action == UnsafeNativeMethods.WebSocketProtocolComponent.Action.SendToNetwork;
|
||||
|
||||
if (isSendActivity)
|
||||
{
|
||||
count = (int)dataBufferCount;
|
||||
}
|
||||
|
||||
bool nonZeroBufferFound = false;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.Buffer dataBuffer = dataBuffers[i];
|
||||
|
||||
IntPtr bufferData;
|
||||
uint bufferLength;
|
||||
UnwrapWebSocketBuffer(dataBuffer, bufferType, out bufferData, out bufferLength);
|
||||
|
||||
if (bufferData == IntPtr.Zero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nonZeroBufferFound = true;
|
||||
|
||||
bool isPinnedSendPayloadBuffer = IsPinnedSendPayloadBuffer(dataBuffer, bufferType);
|
||||
|
||||
if (bufferLength > GetMaxBufferSize())
|
||||
{
|
||||
if (!isSendActivity || !isPinnedSendPayloadBuffer)
|
||||
{
|
||||
Contract.Assert(false,
|
||||
"'dataBuffer.BufferLength' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
|
||||
// Indicates a violation in the contract with native Websocket.dll and could indicate
|
||||
// memory corruption because the internal buffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isPinnedSendPayloadBuffer && !IsNativeBuffer(bufferData, bufferLength))
|
||||
{
|
||||
Contract.Assert(false,
|
||||
"WebSocketGetAction MUST return a pointer within the pinned internal buffer.");
|
||||
// Indicates a violation in the contract with native Websocket.dll and could indicate
|
||||
// memory corruption because the internal buffer is shared between managed and native code
|
||||
throw new AccessViolationException();
|
||||
}
|
||||
}
|
||||
|
||||
if (!nonZeroBufferFound &&
|
||||
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.NoAction &&
|
||||
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateReceiveComplete &&
|
||||
action != UnsafeNativeMethods.WebSocketProtocolComponent.Action.IndicateSendComplete)
|
||||
{
|
||||
Contract.Assert(false, "At least one 'dataBuffer.Buffer' MUST NOT be NULL.");
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetNativeSendBufferSize(int sendBufferSize, bool isServerBuffer)
|
||||
{
|
||||
return isServerBuffer ? MinSendBufferSize : sendBufferSize;
|
||||
}
|
||||
|
||||
internal static void UnwrapWebSocketBuffer(UnsafeNativeMethods.WebSocketProtocolComponent.Buffer buffer,
|
||||
UnsafeNativeMethods.WebSocketProtocolComponent.BufferType bufferType,
|
||||
out IntPtr bufferData,
|
||||
out uint bufferLength)
|
||||
{
|
||||
bufferData = IntPtr.Zero;
|
||||
bufferLength = 0;
|
||||
|
||||
switch (bufferType)
|
||||
{
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.Close:
|
||||
bufferData = buffer.CloseStatus.ReasonData;
|
||||
bufferLength = buffer.CloseStatus.ReasonLength;
|
||||
break;
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.None:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.BinaryFragment:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.BinaryMessage:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UTF8Fragment:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UTF8Message:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.PingPong:
|
||||
case UnsafeNativeMethods.WebSocketProtocolComponent.BufferType.UnsolicitedPong:
|
||||
bufferData = buffer.Data.BufferData;
|
||||
bufferLength = buffer.Data.BufferLength;
|
||||
break;
|
||||
default:
|
||||
Contract.Assert(false,
|
||||
string.Format(CultureInfo.InvariantCulture,
|
||||
"BufferType '{0}' is invalid/unknown.",
|
||||
bufferType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
switch (_StateWhenDisposing)
|
||||
{
|
||||
case int.MinValue:
|
||||
return;
|
||||
case (int)WebSocketState.Closed:
|
||||
case (int)WebSocketState.Aborted:
|
||||
throw new WebSocketException(WebSocketError.InvalidState,
|
||||
SR.GetString(SR.net_WebSockets_InvalidState_ClosedOrAborted, typeof(WebSocketBase), _StateWhenDisposing));
|
||||
default:
|
||||
throw new ObjectDisposedException(GetType().FullName);
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG"), Conditional("CONTRACTS_FULL")]
|
||||
private void ValidateBufferedPayload()
|
||||
{
|
||||
Contract.Assert(_BufferedPayloadReceiveResult != null,
|
||||
"'m_BufferedPayloadReceiveResult' MUST NOT be NULL.");
|
||||
Contract.Assert(_BufferedPayloadReceiveResult.Count >= 0,
|
||||
"'m_BufferedPayloadReceiveResult.Count' MUST NOT be negative.");
|
||||
Contract.Assert(_PayloadOffset >= 0, "'m_PayloadOffset' MUST NOT be smaller than 0.");
|
||||
Contract.Assert(_PayloadOffset <= _PayloadBuffer.Count,
|
||||
"'m_PayloadOffset' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
|
||||
Contract.Assert(_PayloadOffset + _BufferedPayloadReceiveResult.Count <= _PayloadBuffer.Count,
|
||||
"'m_PayloadOffset + m_PayloadBytesBuffered' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
|
||||
}
|
||||
|
||||
private int GetOffset(IntPtr pBuffer)
|
||||
{
|
||||
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be IntPtr.Zero.");
|
||||
int offset = (int)(pBuffer.ToInt64() - _StartAddress + _InternalBuffer.Offset);
|
||||
|
||||
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
|
||||
return offset;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
private int GetMaxBufferSize()
|
||||
{
|
||||
return Math.Max(_ReceiveBufferSize, _SendBufferSize);
|
||||
}
|
||||
|
||||
internal bool IsInternalBuffer(byte[] buffer, int offset, int count)
|
||||
{
|
||||
Contract.Assert(buffer != null, "'buffer' MUST NOT be NULL.");
|
||||
Contract.Assert(_InternalBuffer.Array != null, "'m_InternalBuffer.Array' MUST NOT be NULL.");
|
||||
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
|
||||
Contract.Assert(count >= 0, "'count' MUST NOT be negative.");
|
||||
Contract.Assert(offset + count <= buffer.Length, "'offset + count' MUST NOT exceed 'buffer.Length'.");
|
||||
|
||||
return object.ReferenceEquals(buffer, _InternalBuffer.Array);
|
||||
}
|
||||
|
||||
internal IntPtr ToIntPtr(int offset)
|
||||
{
|
||||
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
|
||||
Contract.Assert(_StartAddress + offset <= _EndAddress, "'offset' is TOO BIG.");
|
||||
return new IntPtr(_StartAddress + offset);
|
||||
}
|
||||
|
||||
private bool IsNativeBuffer(IntPtr pBuffer, uint bufferSize)
|
||||
{
|
||||
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be NULL.");
|
||||
Contract.Assert(bufferSize <= GetMaxBufferSize(),
|
||||
"'bufferSize' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
|
||||
|
||||
long nativeBufferStartAddress = pBuffer.ToInt64();
|
||||
long nativeBufferEndAddress = bufferSize + nativeBufferStartAddress;
|
||||
|
||||
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(_InternalBuffer.Array, _InternalBuffer.Offset).ToInt64() == _StartAddress,
|
||||
"'m_InternalBuffer.Array' MUST be pinned for the whole lifetime of a WebSocket.");
|
||||
|
||||
if (nativeBufferStartAddress >= _StartAddress &&
|
||||
nativeBufferStartAddress <= _EndAddress &&
|
||||
nativeBufferEndAddress >= _StartAddress &&
|
||||
nativeBufferEndAddress <= _EndAddress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CleanUp()
|
||||
{
|
||||
if (_GCHandle.IsAllocated)
|
||||
{
|
||||
_GCHandle.Free();
|
||||
}
|
||||
|
||||
ReleasePinnedSendBuffer();
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
||||
{
|
||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize >= MinSendBufferSize,
|
||||
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
|
||||
int internalBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
|
||||
return new ArraySegment<byte>(new byte[internalBufferSize]);
|
||||
}
|
||||
|
||||
public static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
||||
{
|
||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize >= MinSendBufferSize,
|
||||
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
|
||||
int minBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
|
||||
if (count < minBufferSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("internalBuffer",
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_InternalBuffer, minBufferSize));
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetInternalBufferSize(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
||||
{
|
||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize >= MinSendBufferSize,
|
||||
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
|
||||
Contract.Assert(receiveBufferSize <= MaxBufferSize,
|
||||
"'receiveBufferSize' MUST be less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
Contract.Assert(sendBufferSize <= MaxBufferSize,
|
||||
"'sendBufferSize' MUST be at less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||
|
||||
int nativeSendBufferSize = GetNativeSendBufferSize(sendBufferSize, isServerBuffer);
|
||||
return (2 * receiveBufferSize) + nativeSendBufferSize + NativeOverheadBufferSize + PropertyBufferSize;
|
||||
}
|
||||
|
||||
private static class SendBufferState
|
||||
{
|
||||
public const int None = 0;
|
||||
public const int SendPayloadSpecified = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
public static class WebSocketConstants
|
||||
{
|
||||
public static string SupportedProtocolVersion = "13";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketError.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
internal enum WebSocketError
|
||||
{
|
||||
Success = 0,
|
||||
InvalidMessageType = 1,
|
||||
Faulted = 2,
|
||||
NativeError = 3,
|
||||
NotAWebSocket = 4,
|
||||
UnsupportedVersion = 5,
|
||||
UnsupportedProtocol = 6,
|
||||
HeaderError = 7,
|
||||
ConnectionClosedPrematurely = 8,
|
||||
InvalidState = 9
|
||||
}
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketException.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Net.WebSockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
#if !NETSTANDARD1_3
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
|
||||
#endif
|
||||
internal sealed class WebSocketException : Win32Exception
|
||||
{
|
||||
private WebSocketError _WebSocketErrorCode;
|
||||
|
||||
public WebSocketException()
|
||||
: this(Marshal.GetLastWin32Error())
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error)
|
||||
: this(error, GetErrorMessage(error))
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, string message) : base(message)
|
||||
{
|
||||
_WebSocketErrorCode = error;
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, Exception innerException)
|
||||
: this(error, GetErrorMessage(error), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
_WebSocketErrorCode = error;
|
||||
}
|
||||
|
||||
public WebSocketException(int nativeError)
|
||||
: base(nativeError)
|
||||
{
|
||||
_WebSocketErrorCode = !UnsafeNativeMethods.WebSocketProtocolComponent.Succeeded(nativeError) ? WebSocketError.NativeError : WebSocketError.Success;
|
||||
this.SetErrorCodeOnError(nativeError);
|
||||
}
|
||||
|
||||
public WebSocketException(int nativeError, string message)
|
||||
: base(nativeError, message)
|
||||
{
|
||||
_WebSocketErrorCode = !UnsafeNativeMethods.WebSocketProtocolComponent.Succeeded(nativeError) ? WebSocketError.NativeError : WebSocketError.Success;
|
||||
this.SetErrorCodeOnError(nativeError);
|
||||
}
|
||||
|
||||
public WebSocketException(int nativeError, Exception innerException)
|
||||
: base(SR.GetString(SR.net_WebSockets_Generic), innerException)
|
||||
{
|
||||
_WebSocketErrorCode = !UnsafeNativeMethods.WebSocketProtocolComponent.Succeeded(nativeError) ? WebSocketError.NativeError : WebSocketError.Success;
|
||||
this.SetErrorCodeOnError(nativeError);
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, int nativeError)
|
||||
: this(error, nativeError, GetErrorMessage(error))
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, int nativeError, string message)
|
||||
: base(message)
|
||||
{
|
||||
_WebSocketErrorCode = error;
|
||||
this.SetErrorCodeOnError(nativeError);
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, int nativeError, Exception innerException)
|
||||
: this(error, nativeError, GetErrorMessage(error), innerException)
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(WebSocketError error, int nativeError, string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
_WebSocketErrorCode = error;
|
||||
this.SetErrorCodeOnError(nativeError);
|
||||
}
|
||||
|
||||
public WebSocketException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public WebSocketException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
#if !NETSTANDARD1_3
|
||||
public override int ErrorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.NativeErrorCode;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public WebSocketError WebSocketErrorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _WebSocketErrorCode;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetErrorMessage(WebSocketError error)
|
||||
{
|
||||
// provide a canned message for the error type
|
||||
switch (error)
|
||||
{
|
||||
case WebSocketError.InvalidMessageType:
|
||||
return SR.GetString(SR.net_WebSockets_InvalidMessageType_Generic,
|
||||
typeof(WebSocket).Name + WebSocketBase.Methods.CloseAsync,
|
||||
typeof(WebSocket).Name + WebSocketBase.Methods.CloseOutputAsync);
|
||||
case WebSocketError.Faulted:
|
||||
return SR.GetString(SR.net_Websockets_WebSocketBaseFaulted);
|
||||
case WebSocketError.NotAWebSocket:
|
||||
return SR.GetString(SR.net_WebSockets_NotAWebSocket_Generic);
|
||||
case WebSocketError.UnsupportedVersion:
|
||||
return SR.GetString(SR.net_WebSockets_UnsupportedWebSocketVersion_Generic);
|
||||
case WebSocketError.UnsupportedProtocol:
|
||||
return SR.GetString(SR.net_WebSockets_UnsupportedProtocol_Generic);
|
||||
case WebSocketError.HeaderError:
|
||||
return SR.GetString(SR.net_WebSockets_HeaderError_Generic);
|
||||
case WebSocketError.ConnectionClosedPrematurely:
|
||||
return SR.GetString(SR.net_WebSockets_ConnectionClosedPrematurely_Generic);
|
||||
case WebSocketError.InvalidState:
|
||||
return SR.GetString(SR.net_WebSockets_InvalidState_Generic);
|
||||
default:
|
||||
return SR.GetString(SR.net_WebSockets_Generic);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the error code only if there is an error (i.e. nativeError >= 0). Otherwise the code blows up on deserialization
|
||||
// as the Exception..ctor() throws on setting HResult to 0. The default for HResult is -2147467259.
|
||||
private void SetErrorCodeOnError(int nativeError)
|
||||
{
|
||||
if (!UnsafeNativeMethods.WebSocketProtocolComponent.Succeeded(nativeError))
|
||||
{
|
||||
this.HResult = nativeError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketHelpers.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Net.WebSockets;
|
||||
|
||||
namespace Microsoft.Net.Http.Server
|
||||
{
|
||||
public static class WebSocketExtensions
|
||||
{
|
||||
public static bool IsWebSocketRequest(this RequestContext context)
|
||||
{
|
||||
if (!WebSocketHelpers.AreWebSocketsSupported)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!context.IsUpgradableRequest)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
|
||||
string connection = context.Request.Headers[HttpKnownHeaderNames.Connection];
|
||||
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upgrade: websocket
|
||||
string upgrade = context.Request.Headers[HttpKnownHeaderNames.Upgrade];
|
||||
if (!string.Equals(WebSocketHelpers.WebSocketUpgradeToken, upgrade, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sec-WebSocket-Version: 13
|
||||
string version = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
||||
if (!string.Equals(WebSocketConstants.SupportedProtocolVersion, version, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sec-WebSocket-Key: {base64string}
|
||||
string key = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
||||
if (!WebSocketHelpers.IsValidWebSocketKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare IsWebSocketRequest()
|
||||
private static void ValidateWebSocketRequest(RequestContext context)
|
||||
{
|
||||
if (!WebSocketHelpers.AreWebSocketsSupported)
|
||||
{
|
||||
throw new NotSupportedException("WebSockets are not supported on this platform.");
|
||||
}
|
||||
|
||||
if (!context.IsUpgradableRequest)
|
||||
{
|
||||
throw new InvalidOperationException("This request is not a valid upgrade request.");
|
||||
}
|
||||
|
||||
if (!string.Equals("GET", context.Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("This request is not a valid upgrade request; invalid verb: " + context.Request.Method);
|
||||
}
|
||||
|
||||
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
|
||||
string connection = context.Request.Headers[HttpKnownHeaderNames.Connection];
|
||||
if (connection == null || connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
|
||||
{
|
||||
throw new InvalidOperationException("The Connection header is invalid: " + connection);
|
||||
}
|
||||
|
||||
// Upgrade: websocket
|
||||
string upgrade = context.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
|
||||
string version = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
||||
if (!string.Equals(WebSocketConstants.SupportedProtocolVersion, version, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("The Sec-WebSocket-Version header is invalid or not supported: " + version);
|
||||
}
|
||||
|
||||
// Sec-WebSocket-Key: {base64string}
|
||||
string key = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
||||
if (!WebSocketHelpers.IsValidWebSocketKey(key))
|
||||
{
|
||||
throw new InvalidOperationException("The Sec-WebSocket-Key header is invalid: " + upgrade);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<WebSocket> AcceptWebSocketAsync(this RequestContext context)
|
||||
{
|
||||
return context.AcceptWebSocketAsync(null,
|
||||
WebSocketHelpers.DefaultReceiveBufferSize,
|
||||
WebSocketHelpers.DefaultKeepAliveInterval);
|
||||
}
|
||||
|
||||
public static Task<WebSocket> AcceptWebSocketAsync(this RequestContext context, string subProtocol)
|
||||
{
|
||||
return context.AcceptWebSocketAsync(subProtocol,
|
||||
WebSocketHelpers.DefaultReceiveBufferSize,
|
||||
WebSocketHelpers.DefaultKeepAliveInterval);
|
||||
}
|
||||
|
||||
public static Task<WebSocket> AcceptWebSocketAsync(this RequestContext context, string subProtocol, TimeSpan keepAliveInterval)
|
||||
{
|
||||
return context.AcceptWebSocketAsync(subProtocol,
|
||||
WebSocketHelpers.DefaultReceiveBufferSize,
|
||||
keepAliveInterval);
|
||||
}
|
||||
|
||||
public static Task<WebSocket> AcceptWebSocketAsync(
|
||||
this RequestContext context,
|
||||
string subProtocol,
|
||||
int receiveBufferSize,
|
||||
TimeSpan keepAliveInterval)
|
||||
{
|
||||
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
||||
|
||||
ArraySegment<byte> internalBuffer = WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
||||
return context.AcceptWebSocketAsync(subProtocol,
|
||||
receiveBufferSize,
|
||||
keepAliveInterval,
|
||||
internalBuffer);
|
||||
}
|
||||
|
||||
public static Task<WebSocket> AcceptWebSocketAsync(
|
||||
this RequestContext context,
|
||||
string subProtocol,
|
||||
int receiveBufferSize,
|
||||
TimeSpan keepAliveInterval,
|
||||
ArraySegment<byte> internalBuffer)
|
||||
{
|
||||
if (!context.IsUpgradableRequest)
|
||||
{
|
||||
throw new InvalidOperationException("This request is cannot be upgraded.");
|
||||
}
|
||||
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
||||
WebSocketHelpers.ValidateArraySegment<byte>(internalBuffer, "internalBuffer");
|
||||
WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
||||
|
||||
return AcceptWebSocketAsyncCore(context, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
|
||||
}
|
||||
|
||||
private static async Task<WebSocket> AcceptWebSocketAsyncCore(
|
||||
RequestContext context,
|
||||
string subProtocol,
|
||||
int receiveBufferSize,
|
||||
TimeSpan keepAliveInterval,
|
||||
ArraySegment<byte> internalBuffer)
|
||||
{
|
||||
ValidateWebSocketRequest(context);
|
||||
|
||||
var subProtocols = context.Request.Headers.GetValues(HttpKnownHeaderNames.SecWebSocketProtocol);
|
||||
bool shouldSendSecWebSocketProtocolHeader = WebSocketHelpers.ProcessWebSocketProtocolHeader(subProtocols, subProtocol);
|
||||
if (shouldSendSecWebSocketProtocolHeader)
|
||||
{
|
||||
context.Response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol] = subProtocol;
|
||||
}
|
||||
|
||||
// negotiate the websocket key return value
|
||||
string secWebSocketKey = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
||||
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
|
||||
|
||||
context.Response.Headers.Append(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
|
||||
context.Response.Headers.Append(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
|
||||
context.Response.Headers.Append(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
|
||||
|
||||
Stream opaqueStream = await context.UpgradeAsync();
|
||||
|
||||
return WebSocketHelpers.CreateServerWebSocket(
|
||||
opaqueStream,
|
||||
subProtocol,
|
||||
receiveBufferSize,
|
||||
keepAliveInterval,
|
||||
internalBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,418 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketHelpers.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
public static class WebSocketHelpers
|
||||
{
|
||||
internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
public const string WebSocketUpgradeToken = "websocket";
|
||||
public const int DefaultReceiveBufferSize = 16 * 1024;
|
||||
internal const int DefaultClientSendBufferSize = 16 * 1024;
|
||||
internal const int MaxControlFramePayloadLength = 123;
|
||||
|
||||
// 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();
|
||||
private static TimeSpan? _defaultKeepAliveInterval;
|
||||
|
||||
public static bool AreWebSocketsSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return UnsafeNativeMethods.WebSocketProtocolComponent.IsSupported;
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeSpan DefaultKeepAliveInterval
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_defaultKeepAliveInterval.HasValue)
|
||||
{
|
||||
if (AreWebSocketsSupported)
|
||||
{
|
||||
_defaultKeepAliveInterval = new TimeSpan?(UnsafeNativeMethods.WebSocketProtocolComponent.WebSocketGetDefaultKeepAliveInterval());
|
||||
}
|
||||
else
|
||||
{
|
||||
_defaultKeepAliveInterval = new TimeSpan?(Timeout.InfiniteTimeSpan);
|
||||
}
|
||||
}
|
||||
return _defaultKeepAliveInterval.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidWebSocketKey(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// TODO:
|
||||
// throw new NotImplementedException();
|
||||
return true;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5354:SHA1CannotBeUsed",
|
||||
Justification = "SHA1 used only for hashing purposes, not for crypto.")]
|
||||
public 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;
|
||||
}
|
||||
|
||||
public static WebSocket CreateServerWebSocket(Stream opaqueStream, string subProtocol, int receiveBufferSize, TimeSpan keepAliveInterval, ArraySegment<byte> internalBuffer)
|
||||
{
|
||||
return new ServerWebSocket(opaqueStream, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
|
||||
}
|
||||
|
||||
internal static string GetTraceMsgForParameters(int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
"offset: {0}, count: {1}, cancellationToken.CanBeCanceled: {2}",
|
||||
offset,
|
||||
count,
|
||||
cancellationToken.CanBeCanceled);
|
||||
}
|
||||
|
||||
// return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
|
||||
public static bool ProcessWebSocketProtocolHeader(IEnumerable<string> clientSecWebSocketProtocols, string subProtocol)
|
||||
{
|
||||
if (clientSecWebSocketProtocols == null || !clientSecWebSocketProtocols.Any())
|
||||
{
|
||||
// client hasn't specified any Sec-WebSocket-Protocol header
|
||||
if (subProtocol != null)
|
||||
{
|
||||
// If the server specified _anything_ this isn't valid.
|
||||
throw new WebSocketException(WebSocketError.UnsupportedProtocol,
|
||||
SR.GetString(SR.net_WebSockets_ClientAcceptingNoProtocols, 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,
|
||||
SR.GetString(SR.net_WebSockets_AcceptUnsupportedProtocol,
|
||||
string.Join(", ", clientSecWebSocketProtocols),
|
||||
subProtocol));
|
||||
}
|
||||
|
||||
internal static ConfiguredTaskAwaitable SuppressContextFlow(this Task task)
|
||||
{
|
||||
// We don't flow the synchronization context within WebSocket.xxxAsync - but the calling application
|
||||
// can decide whether the completion callback for the task returned from WebSocket.xxxAsync runs
|
||||
// under the caller's synchronization context.
|
||||
return task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static ConfiguredTaskAwaitable<T> SuppressContextFlow<T>(this Task<T> task)
|
||||
{
|
||||
// We don't flow the synchronization context within WebSocket.xxxAsync - but the calling application
|
||||
// can decide whether the completion callback for the task returned from WebSocket.xxxAsync runs
|
||||
// under the caller's synchronization context.
|
||||
return task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static bool IsStateTerminal(WebSocketState state)
|
||||
{
|
||||
return state == WebSocketState.Closed || state == WebSocketState.Aborted;
|
||||
}
|
||||
|
||||
internal static void ThrowOnInvalidState(WebSocketState state, params WebSocketState[] validStates)
|
||||
{
|
||||
string text = string.Empty;
|
||||
if (validStates != null && validStates.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < validStates.Length; i++)
|
||||
{
|
||||
WebSocketState webSocketState = validStates[i];
|
||||
if (state == webSocketState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
text = string.Join<WebSocketState>(", ", validStates);
|
||||
}
|
||||
throw new WebSocketException(SR.GetString("net_WebSockets_InvalidState", new object[]
|
||||
{
|
||||
state,
|
||||
text
|
||||
}));
|
||||
}
|
||||
|
||||
internal static void ValidateBuffer(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException("buffer");
|
||||
}
|
||||
|
||||
if (offset < 0 || offset > buffer.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("offset");
|
||||
}
|
||||
|
||||
if (count < 0 || count > (buffer.Length - offset))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("count");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateSubprotocol(string subProtocol)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(subProtocol))
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.net_WebSockets_InvalidEmptySubProtocol), "subProtocol");
|
||||
}
|
||||
|
||||
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(SR.GetString(SR.net_WebSockets_InvalidCharInProtocolString, subProtocol, invalidChar),
|
||||
"subProtocol");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, string statusDescription)
|
||||
{
|
||||
if (closeStatus == WebSocketCloseStatus.Empty && !string.IsNullOrEmpty(statusDescription))
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.net_WebSockets_ReasonNotNull,
|
||||
statusDescription,
|
||||
WebSocketCloseStatus.Empty),
|
||||
"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.GetString(SR.net_WebSockets_InvalidCloseStatusCode,
|
||||
closeStatusCode),
|
||||
"closeStatus");
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
if (!string.IsNullOrEmpty(statusDescription))
|
||||
{
|
||||
length = UTF8Encoding.UTF8.GetByteCount(statusDescription);
|
||||
}
|
||||
|
||||
if (length > WebSocketHelpers.MaxControlFramePayloadLength)
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.net_WebSockets_InvalidCloseStatusDescription,
|
||||
statusDescription,
|
||||
WebSocketHelpers.MaxControlFramePayloadLength),
|
||||
"statusDescription");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ValidateOptions(string subProtocol,
|
||||
int receiveBufferSize,
|
||||
int sendBufferSize,
|
||||
TimeSpan keepAliveInterval)
|
||||
{
|
||||
// We allow the subProtocol to be null. Validate if it is not null.
|
||||
if (subProtocol != null)
|
||||
{
|
||||
ValidateSubprotocol(subProtocol);
|
||||
}
|
||||
|
||||
ValidateBufferSizes(receiveBufferSize, sendBufferSize);
|
||||
|
||||
// -1
|
||||
if (keepAliveInterval < Timeout.InfiniteTimeSpan)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("keepAliveInterval", keepAliveInterval,
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, Timeout.InfiniteTimeSpan.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateBufferSizes(int receiveBufferSize, int sendBufferSize)
|
||||
{
|
||||
if (receiveBufferSize < WebSocketBuffer.MinReceiveBufferSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("receiveBufferSize", receiveBufferSize,
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, WebSocketBuffer.MinReceiveBufferSize));
|
||||
}
|
||||
|
||||
if (sendBufferSize < WebSocketBuffer.MinSendBufferSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sendBufferSize", sendBufferSize,
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_TooSmall, WebSocketBuffer.MinSendBufferSize));
|
||||
}
|
||||
|
||||
if (receiveBufferSize > WebSocketBuffer.MaxBufferSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("receiveBufferSize", receiveBufferSize,
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_TooBig,
|
||||
"receiveBufferSize",
|
||||
receiveBufferSize,
|
||||
WebSocketBuffer.MaxBufferSize));
|
||||
}
|
||||
|
||||
if (sendBufferSize > WebSocketBuffer.MaxBufferSize)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("sendBufferSize", sendBufferSize,
|
||||
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_TooBig,
|
||||
"sendBufferSize",
|
||||
sendBufferSize,
|
||||
WebSocketBuffer.MaxBufferSize));
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateInnerStream(Stream innerStream)
|
||||
{
|
||||
if (innerStream == null)
|
||||
{
|
||||
throw new ArgumentNullException("innerStream");
|
||||
}
|
||||
|
||||
if (!innerStream.CanRead)
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.NotReadableStream), "innerStream");
|
||||
}
|
||||
|
||||
if (!innerStream.CanWrite)
|
||||
{
|
||||
throw new ArgumentException(SR.GetString(SR.NotWriteableStream), "innerStream");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ThrowIfConnectionAborted(Stream connection, bool read)
|
||||
{
|
||||
if ((!read && !connection.CanWrite) ||
|
||||
(read && !connection.CanRead))
|
||||
{
|
||||
throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ThrowPlatformNotSupportedException_WSPC()
|
||||
{
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.net_WebSockets_UnsupportedPlatform));
|
||||
}
|
||||
|
||||
public static void ValidateArraySegment<T>(ArraySegment<T> arraySegment, string parameterName)
|
||||
{
|
||||
Contract.Requires(!string.IsNullOrEmpty(parameterName), "'parameterName' MUST NOT be NULL or string.Empty");
|
||||
|
||||
if (arraySegment.Array == null)
|
||||
{
|
||||
throw new ArgumentNullException(parameterName + ".Array");
|
||||
}
|
||||
|
||||
if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName + ".Offset");
|
||||
}
|
||||
if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(parameterName + ".Count");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebSocketReceiveResult.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Net.WebSockets;
|
||||
|
||||
namespace Microsoft.Net.WebSockets
|
||||
{
|
||||
internal static class WebSocketReceiveResultExtensions
|
||||
{
|
||||
internal static WebSocketReceiveResult DecrementAndClone(ref WebSocketReceiveResult original, int count)
|
||||
{
|
||||
Contract.Assert(count >= 0, "'count' MUST NOT be negative.");
|
||||
Contract.Assert(count <= original.Count, "'count' MUST NOT be bigger than 'this.Count'.");
|
||||
int remaining = original.Count - count;
|
||||
original = new WebSocketReceiveResult(remaining,
|
||||
original.MessageType,
|
||||
original.EndOfMessage,
|
||||
original.CloseStatus,
|
||||
original.CloseStatusDescription);
|
||||
return new WebSocketReceiveResult(count,
|
||||
original.MessageType,
|
||||
remaining == 0 && original.EndOfMessage,
|
||||
original.CloseStatus,
|
||||
original.CloseStatusDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
namespace Microsoft.Win32.SafeHandles
|
||||
{
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Class of safe handle which uses 0 or -1 as an invalid handle.
|
||||
[System.Security.SecurityCritical] // auto-generated_required
|
||||
internal abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
|
||||
{
|
||||
protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle)
|
||||
: base(IntPtr.Zero, ownsHandle)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
[System.Security.SecurityCritical]
|
||||
get { return handle == new IntPtr(0) || handle == new IntPtr(-1); }
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal class AccessViolationException : SystemException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ExternDll.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal static class ExternDll
|
||||
{
|
||||
public const string api_ms_win_core_localization_LIB = "api-ms-win-core-localization-l2-1-0.dll";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
/*=============================================================================
|
||||
**
|
||||
** Class: ExternalException
|
||||
**
|
||||
**
|
||||
** Purpose: Exception base class for all errors from Interop or Structured
|
||||
** Exception Handling code.
|
||||
**
|
||||
**
|
||||
=============================================================================*/
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
namespace System.Runtime.InteropServices
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
// Base exception for COM Interop errors &; Structured Exception Handler
|
||||
// exceptions.
|
||||
//
|
||||
internal class ExternalException : Exception
|
||||
{
|
||||
public ExternalException()
|
||||
{
|
||||
SetErrorCode(__HResults.E_FAIL);
|
||||
}
|
||||
|
||||
public ExternalException(String message)
|
||||
: base(message)
|
||||
{
|
||||
SetErrorCode(__HResults.E_FAIL);
|
||||
}
|
||||
|
||||
public ExternalException(String message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
SetErrorCode(__HResults.E_FAIL);
|
||||
}
|
||||
|
||||
public ExternalException(String message, int errorCode)
|
||||
: base(message)
|
||||
{
|
||||
SetErrorCode(errorCode);
|
||||
}
|
||||
|
||||
private void SetErrorCode(int errorCode)
|
||||
{
|
||||
HResult = ErrorCode;
|
||||
}
|
||||
|
||||
private static class __HResults
|
||||
{
|
||||
internal const int E_FAIL = unchecked((int)0x80004005);
|
||||
}
|
||||
|
||||
public virtual int ErrorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
return HResult;
|
||||
}
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
String message = Message;
|
||||
String s;
|
||||
String _className = GetType().ToString();
|
||||
s = _className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")";
|
||||
|
||||
if (!(String.IsNullOrEmpty(message)))
|
||||
{
|
||||
s = s + ": " + message;
|
||||
}
|
||||
|
||||
Exception _innerException = InnerException;
|
||||
|
||||
if (_innerException != null)
|
||||
{
|
||||
s = s + " ---> " + _innerException.ToString();
|
||||
}
|
||||
|
||||
|
||||
if (StackTrace != null)
|
||||
s += Environment.NewLine + StackTrace;
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="SafeNativeMethods.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal static class SafeNativeMethods
|
||||
{
|
||||
public const int
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100,
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200,
|
||||
FORMAT_MESSAGE_FROM_STRING = 0x00000400,
|
||||
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000,
|
||||
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
|
||||
|
||||
[DllImport(ExternDll.api_ms_win_core_localization_LIB, CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true, BestFitMapping = true)]
|
||||
public static unsafe extern int FormatMessage(int dwFlags, IntPtr lpSource_mustBeNull, uint dwMessageId,
|
||||
int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr[] arguments);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc.
|
||||
// All Rights Reserved
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
||||
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
||||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal class SystemException : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0-*",
|
||||
"description": "Implementation of WebSocket abstract base class. Used by WebListener.",
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http.Server": "0.2.0-*"
|
||||
},
|
||||
"buildOptions": {
|
||||
"allowUnsafe": true,
|
||||
"warningsAsErrors": true,
|
||||
"keyFile": "../../tools/Key.snk",
|
||||
"nowarn": [
|
||||
"CS1591"
|
||||
],
|
||||
"xmlDoc": true
|
||||
},
|
||||
"frameworks": {
|
||||
"net451": {},
|
||||
"netstandard1.3": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.0.11-*",
|
||||
"System.Linq": "4.1.0-*",
|
||||
"System.Net.WebSockets": "4.0.0-*",
|
||||
"System.Resources.ResourceManager": "4.0.1-*",
|
||||
"System.Runtime.Extensions": "4.1.0-*",
|
||||
"System.Security.Cryptography.Algorithms": "4.2.0-*",
|
||||
"System.Threading": "4.0.11-*",
|
||||
"System.Threading.Tasks": "4.0.11-*",
|
||||
"System.Threading.Timer": "4.0.1-*",
|
||||
"System.Threading.ThreadPool": "4.0.10-*"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
// NON-INFRINGEMENT.
|
||||
// See the Apache 2 License for the specific language governing
|
||||
// permissions and limitations under the License.
|
||||
#if WEBSOCKETS
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.WebSockets;
|
||||
|
|
@ -159,10 +159,9 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
|||
|
||||
private async Task<WebSocket> SendWebSocketRequestAsync(string address)
|
||||
{
|
||||
ClientWebSocket client = new ClientWebSocket();
|
||||
var client = new ClientWebSocket();
|
||||
await client.ConnectAsync(new Uri(address), CancellationToken.None);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -19,7 +19,8 @@
|
|||
},
|
||||
"System.Net.Http.WinHttpHandler": "4.0.0-*",
|
||||
"System.Net.Requests": "4.0.11-*",
|
||||
"System.Net.WebHeaderCollection": "4.0.1-*"
|
||||
"System.Net.WebHeaderCollection": "4.0.1-*",
|
||||
"System.Net.WebSockets.Client": "4.0.0"
|
||||
}
|
||||
},
|
||||
"net451": {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.Net.Http.Server
|
|||
Task<WebSocket> clientTask = SendWebSocketRequestAsync(ConvertToWebSocketAddress(address));
|
||||
|
||||
var context = await server.AcceptAsync();
|
||||
Assert.True(context.IsWebSocketRequest());
|
||||
Assert.True(context.IsWebSocketRequest);
|
||||
WebSocket serverWebSocket = await context.AcceptWebSocketAsync();
|
||||
WebSocket clientWebSocket = await clientTask;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
"dependencies": {
|
||||
"dotnet-test-xunit": "2.2.0-*",
|
||||
"Microsoft.Net.Http.Server": "0.2.0-*",
|
||||
"Microsoft.Net.WebSockets.Server": "0.2.0-*",
|
||||
"Microsoft.AspNetCore.Testing": "1.1.0-*",
|
||||
"xunit": "2.2.0-*"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue