Use CoreFx managed WebSockets

This commit is contained in:
Chris R 2016-08-03 09:48:52 -07:00
parent 484955f83e
commit 44910bbd59
43 changed files with 1927 additions and 7035 deletions

View File

@ -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}

View File

@ -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();

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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

View File

@ -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 },

View File

@ -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;

View File

@ -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)
{

View File

@ -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.");
}
}
}
}

View File

@ -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 { }
}

View File

@ -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).

View File

@ -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)})";
}
}

View File

@ -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));
}
}
}

View File

@ -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"
}
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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")]

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -1,9 +0,0 @@
using System;
namespace Microsoft.Net.WebSockets
{
public static class WebSocketConstants
{
public static string SupportedProtocolVersion = "13";
}
}

View File

@ -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
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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");
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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-*"
}
}
}
}

View File

@ -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
}

View File

@ -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": {

View File

@ -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;

View File

@ -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-*"
},