Expose Opaque and WebSockets.
This commit is contained in:
parent
1ef31a943a
commit
d1dab1665e
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 14
|
# Visual Studio 14
|
||||||
VisualStudioVersion = 14.0.21708.0
|
VisualStudioVersion = 14.0.21730.1
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "samples\TestClient\TestClient.csproj", "{8B828433-B333-4C19-96AE-00BFFF9D8841}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "samples\TestClient\TestClient.csproj", "{8B828433-B333-4C19-96AE-00BFFF9D8841}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -19,7 +19,7 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SelfHostServer", "samples\S
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.Windows", "src\Microsoft.AspNet.Security.Windows\Microsoft.AspNet.Security.Windows.kproj", "{EFC7538F-7AEB-4A3E-A1E6-6BDCCBD272BF}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Security.Windows", "src\Microsoft.AspNet.Security.Windows\Microsoft.AspNet.Security.Windows.kproj", "{EFC7538F-7AEB-4A3E-A1E6-6BDCCBD272BF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.WebSockets", "src\Microsoft.AspNet.WebSockets\Microsoft.AspNet.WebSockets.kproj", "{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Net.WebSockets", "src\Microsoft.Net.WebSockets\Microsoft.Net.WebSockets.kproj", "{E788AEAE-2CB4-4BFA-8746-D0BB7E93A1BB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Server.WebListener.FunctionalTests", "test\Microsoft.AspNet.Server.WebListener.FunctionalTests\Microsoft.AspNet.Server.WebListener.FunctionalTests.kproj", "{4492FF4C-9032-411D-853F-46B01755E504}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Server.WebListener.FunctionalTests", "test\Microsoft.AspNet.Server.WebListener.FunctionalTests\Microsoft.AspNet.Server.WebListener.FunctionalTests.kproj", "{4492FF4C-9032-411D-853F-46B01755E504}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,9 @@
|
||||||
// permissions and limitations under the License.
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.Net.Server;
|
using Microsoft.Net.Server;
|
||||||
|
|
||||||
namespace HelloWorld
|
namespace HelloWorld
|
||||||
|
|
@ -73,11 +75,23 @@ namespace HelloWorld
|
||||||
// Response
|
// Response
|
||||||
byte[] bytes = Encoding.ASCII.GetBytes("Hello World: " + DateTime.Now);
|
byte[] bytes = Encoding.ASCII.GetBytes("Hello World: " + DateTime.Now);
|
||||||
|
|
||||||
context.Response.ContentLength = bytes.Length;
|
if (context.IsWebSocketRequest)
|
||||||
context.Response.ContentType = "text/plain";
|
{
|
||||||
|
Console.WriteLine("WebSocket");
|
||||||
|
WebSocket webSocket = context.AcceptWebSocketAsync().Result;
|
||||||
|
webSocket.SendAsync(new ArraySegment<byte>(bytes, 0, bytes.Length), WebSocketMessageType.Text, true, CancellationToken.None).Wait();
|
||||||
|
webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Goodbye", CancellationToken.None).Wait();
|
||||||
|
webSocket.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello World");
|
||||||
|
context.Response.ContentLength = bytes.Length;
|
||||||
|
context.Response.ContentType = "text/plain";
|
||||||
|
|
||||||
context.Response.Body.Write(bytes, 0, bytes.Length);
|
context.Response.Body.Write(bytes, 0, bytes.Length);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
// See the Apache 2 License for the specific language governing
|
// See the Apache 2 License for the specific language governing
|
||||||
// permissions and limitations under the License.
|
// permissions and limitations under the License.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using Microsoft.AspNet;
|
using Microsoft.AspNet;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
|
@ -32,8 +36,20 @@ namespace SelfHostServer
|
||||||
|
|
||||||
app.Run(async context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
context.Response.ContentType = "text/plain";
|
if (context.IsWebSocketRequest)
|
||||||
await context.Response.WriteAsync("Hello world");
|
{
|
||||||
|
Console.WriteLine("WebSocket");
|
||||||
|
byte[] bytes = Encoding.ASCII.GetBytes("Hello World: " + DateTime.Now);
|
||||||
|
WebSocket webSocket = await context.AcceptWebSocketAsync();
|
||||||
|
await webSocket.SendAsync(new ArraySegment<byte>(bytes, 0, bytes.Length), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Goodbye", CancellationToken.None);
|
||||||
|
webSocket.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.ContentType = "text/plain";
|
||||||
|
await context.Response.WriteAsync("Hello world");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"Microsoft.AspNet.Server.WebListener": "",
|
"Microsoft.AspNet.Server.WebListener": "",
|
||||||
"Microsoft.Net.Server": ""
|
"Microsoft.Net.Server": ""
|
||||||
},
|
},
|
||||||
"commands": { "web": "Microsoft.AspNet.Hosting server.name=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001" },
|
"commands": { "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:8080" },
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"net45": {
|
"net45": {
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -28,7 +29,15 @@ using Microsoft.Net.Server;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.WebListener
|
namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
internal class FeatureContext : IHttpRequestFeature, IHttpConnectionFeature, IHttpResponseFeature, IHttpSendFileFeature, IHttpTransportLayerSecurityFeature, IHttpRequestLifetimeFeature
|
internal class FeatureContext :
|
||||||
|
IHttpRequestFeature,
|
||||||
|
IHttpConnectionFeature,
|
||||||
|
IHttpResponseFeature,
|
||||||
|
IHttpSendFileFeature,
|
||||||
|
IHttpTransportLayerSecurityFeature,
|
||||||
|
IHttpRequestLifetimeFeature,
|
||||||
|
IHttpWebSocketFeature,
|
||||||
|
IHttpOpaqueUpgradeFeature
|
||||||
{
|
{
|
||||||
private RequestContext _requestContext;
|
private RequestContext _requestContext;
|
||||||
private FeatureCollection _features;
|
private FeatureCollection _features;
|
||||||
|
|
@ -85,10 +94,13 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
_features.Add(typeof(IHttpSendFileFeature), this);
|
_features.Add(typeof(IHttpSendFileFeature), this);
|
||||||
_features.Add(typeof(IHttpRequestLifetimeFeature), this);
|
_features.Add(typeof(IHttpRequestLifetimeFeature), this);
|
||||||
|
|
||||||
|
// TODO: If Win8+
|
||||||
|
_features.Add(typeof(IHttpOpaqueUpgradeFeature), this);
|
||||||
|
_features.Add(typeof(IHttpWebSocketFeature), this);
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// _environment.CallCancelled = _cts.Token;
|
// _environment.CallCancelled = _cts.Token;
|
||||||
// _environment.User = _request.User;
|
// _environment.User = _request.User;
|
||||||
// Opaque/WebSockets
|
|
||||||
// Channel binding
|
// Channel binding
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -348,11 +360,16 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
set { Response.StatusCode = value; }
|
set { Response.StatusCode = value; }
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
#region IHttpSendFileFeature
|
||||||
|
|
||||||
Task IHttpSendFileFeature.SendFileAsync(string path, long offset, long? length, CancellationToken cancellation)
|
Task IHttpSendFileFeature.SendFileAsync(string path, long offset, long? length, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
return Response.SendFileAsync(path, offset, length, cancellation);
|
return Response.SendFileAsync(path, offset, length, cancellation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region IHttpRequestLifetimeFeature
|
||||||
|
|
||||||
public CancellationToken OnRequestAborted
|
public CancellationToken OnRequestAborted
|
||||||
{
|
{
|
||||||
get { return _requestContext.DisconnectToken; }
|
get { return _requestContext.DisconnectToken; }
|
||||||
|
|
@ -362,5 +379,46 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
{
|
{
|
||||||
_requestContext.Abort();
|
_requestContext.Abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region IHttpOpaqueUpgradeFeature
|
||||||
|
|
||||||
|
public bool IsUpgradableRequest
|
||||||
|
{
|
||||||
|
get { return _requestContext.IsUpgradableRequest; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Stream> UpgradeAsync()
|
||||||
|
{
|
||||||
|
if (!IsUpgradableRequest)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("This request cannot be upgraded.");
|
||||||
|
}
|
||||||
|
return _requestContext.UpgradeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
#region IHttpWebSocketFeature
|
||||||
|
|
||||||
|
public bool IsWebSocketRequest
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _requestContext.IsWebSocketRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<WebSocket> AcceptAsync(IWebSocketAcceptContext context)
|
||||||
|
{
|
||||||
|
// TODO: Advanced params
|
||||||
|
string subProtocol = null;
|
||||||
|
if (context != null)
|
||||||
|
{
|
||||||
|
subProtocol = context.SubProtocol;
|
||||||
|
}
|
||||||
|
return _requestContext.AcceptWebSocketAsync(subProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,6 @@ namespace Microsoft.AspNet.Server.WebListener
|
||||||
Interlocked.Increment(ref _outstandingRequests);
|
Interlocked.Increment(ref _outstandingRequests);
|
||||||
FeatureContext featureContext = new FeatureContext(requestContext);
|
FeatureContext featureContext = new FeatureContext(requestContext);
|
||||||
await _appFunc(featureContext.Features).SupressContext();
|
await _appFunc(featureContext.Features).SupressContext();
|
||||||
// TODO: WebSocket/Opaque upgrade - await requestContext.ProcessResponseAsync().SupressContext();
|
|
||||||
requestContext.Dispose();
|
requestContext.Dispose();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
"net45": {},
|
"net45": {},
|
||||||
"k10": {
|
"k10": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*",
|
||||||
"Microsoft.Win32.Primitives": "4.0.0.0",
|
"Microsoft.Win32.Primitives": "4.0.0.0",
|
||||||
"System.Collections": "4.0.0.0",
|
"System.Collections": "4.0.0.0",
|
||||||
"System.Collections.Concurrent": "4.0.0.0",
|
"System.Collections.Concurrent": "4.0.0.0",
|
||||||
|
|
@ -25,12 +26,14 @@
|
||||||
"System.Globalization": "4.0.10.0",
|
"System.Globalization": "4.0.10.0",
|
||||||
"System.IO": "4.0.0.0",
|
"System.IO": "4.0.0.0",
|
||||||
"System.Linq": "4.0.0.0",
|
"System.Linq": "4.0.0.0",
|
||||||
|
"System.Net.Primitives": "4.0.10.0",
|
||||||
"System.Reflection": "4.0.10.0",
|
"System.Reflection": "4.0.10.0",
|
||||||
"System.Resources.ResourceManager": "4.0.0.0",
|
"System.Resources.ResourceManager": "4.0.0.0",
|
||||||
"System.Runtime": "4.0.20.0",
|
"System.Runtime": "4.0.20.0",
|
||||||
"System.Runtime.Extensions": "4.0.10.0",
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
"System.Runtime.Handles": "4.0.0.0",
|
"System.Runtime.Handles": "4.0.0.0",
|
||||||
"System.Runtime.InteropServices": "4.0.20.0",
|
"System.Runtime.InteropServices": "4.0.20.0",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "4.0.0.0",
|
||||||
"System.Security.Principal": "4.0.0.0",
|
"System.Security.Principal": "4.0.0.0",
|
||||||
"System.Text.Encoding": "4.0.20.0",
|
"System.Text.Encoding": "4.0.20.0",
|
||||||
"System.Text.Encoding.Extensions": "4.0.10.0",
|
"System.Text.Encoding.Extensions": "4.0.10.0",
|
||||||
|
|
|
||||||
|
|
@ -1,36 +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.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Standard keys and values for use within the OWIN interfaces
|
|
||||||
/// </summary>
|
|
||||||
internal static class Constants
|
|
||||||
{
|
|
||||||
internal const string WebSocketAcceptKey = "websocket.Accept";
|
|
||||||
internal const string WebSocketSubProtocolKey = "websocket.SubProtocol";
|
|
||||||
internal const string WebSocketSendAsyncKey = "websocket.SendAsync";
|
|
||||||
internal const string WebSocketReceiveAyncKey = "websocket.ReceiveAsync";
|
|
||||||
internal const string WebSocketCloseAsyncKey = "websocket.CloseAsync";
|
|
||||||
internal const string WebSocketCallCancelledKey = "websocket.CallCancelled";
|
|
||||||
internal const string WebSocketVersionKey = "websocket.Version";
|
|
||||||
internal const string WebSocketVersion = "1.0";
|
|
||||||
internal const string WebSocketCloseStatusKey = "websocket.ClientCloseStatus";
|
|
||||||
internal const string WebSocketCloseDescriptionKey = "websocket.ClientCloseDescription";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +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="HttpListenerContext.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
namespace Microsoft.Net
|
|
||||||
{
|
|
||||||
using Microsoft.AspNet.WebSockets;
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
public sealed unsafe class HttpListenerContext {
|
|
||||||
private HttpListenerRequest m_Request;
|
|
||||||
|
|
||||||
public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol)
|
|
||||||
{
|
|
||||||
return this.AcceptWebSocketAsync(subProtocol,
|
|
||||||
WebSocketHelpers.DefaultReceiveBufferSize,
|
|
||||||
WebSocket.DefaultKeepAliveInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol, TimeSpan keepAliveInterval)
|
|
||||||
{
|
|
||||||
return this.AcceptWebSocketAsync(subProtocol,
|
|
||||||
WebSocketHelpers.DefaultReceiveBufferSize,
|
|
||||||
keepAliveInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol,
|
|
||||||
int receiveBufferSize,
|
|
||||||
TimeSpan keepAliveInterval)
|
|
||||||
{
|
|
||||||
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
|
||||||
|
|
||||||
ArraySegment<byte> internalBuffer = WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
|
||||||
return this.AcceptWebSocketAsync(subProtocol,
|
|
||||||
receiveBufferSize,
|
|
||||||
keepAliveInterval,
|
|
||||||
internalBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
||||||
public Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(string subProtocol,
|
|
||||||
int receiveBufferSize,
|
|
||||||
TimeSpan keepAliveInterval,
|
|
||||||
ArraySegment<byte> internalBuffer)
|
|
||||||
{
|
|
||||||
return WebSocketHelpers.AcceptWebSocketAsync(this,
|
|
||||||
subProtocol,
|
|
||||||
receiveBufferSize,
|
|
||||||
keepAliveInterval,
|
|
||||||
internalBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -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 file="HttpListenerRequest.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*
|
|
||||||
namespace Microsoft.Net
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
using System.Security.Principal;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Net;
|
|
||||||
using Microsoft.AspNet.WebSockets;
|
|
||||||
|
|
||||||
public sealed unsafe class HttpListenerRequest {
|
|
||||||
|
|
||||||
public bool IsWebSocketRequest
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!WebSocketProtocolComponent.IsSupported)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool foundConnectionUpgradeHeader = false;
|
|
||||||
if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection))
|
|
||||||
{
|
|
||||||
if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0)
|
|
||||||
{
|
|
||||||
foundConnectionUpgradeHeader = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundConnectionUpgradeHeader)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade))
|
|
||||||
{
|
|
||||||
if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -1,152 +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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
using WebSocketCloseAsync =
|
|
||||||
Func<int /* closeStatus */,
|
|
||||||
string /* closeDescription */,
|
|
||||||
CancellationToken /* cancel */,
|
|
||||||
Task>;
|
|
||||||
using WebSocketReceiveAsync =
|
|
||||||
Func<ArraySegment<byte> /* data */,
|
|
||||||
CancellationToken /* cancel */,
|
|
||||||
Task<Tuple<int /* messageType */,
|
|
||||||
bool /* endOfMessage */,
|
|
||||||
int /* count */>>>;
|
|
||||||
using WebSocketReceiveTuple =
|
|
||||||
Tuple<int /* messageType */,
|
|
||||||
bool /* endOfMessage */,
|
|
||||||
int /* count */>;
|
|
||||||
using WebSocketSendAsync =
|
|
||||||
Func<ArraySegment<byte> /* data */,
|
|
||||||
int /* messageType */,
|
|
||||||
bool /* endOfMessage */,
|
|
||||||
CancellationToken /* cancel */,
|
|
||||||
Task>;
|
|
||||||
|
|
||||||
internal class OwinWebSocketWrapper
|
|
||||||
{
|
|
||||||
private readonly WebSocket _webSocket;
|
|
||||||
private readonly IDictionary<string, object> _environment;
|
|
||||||
private readonly CancellationToken _cancellationToken;
|
|
||||||
|
|
||||||
internal OwinWebSocketWrapper(WebSocket webSocket, CancellationToken ct)
|
|
||||||
{
|
|
||||||
_webSocket = webSocket;
|
|
||||||
_cancellationToken = ct;
|
|
||||||
|
|
||||||
_environment = new Dictionary<string, object>();
|
|
||||||
_environment[Constants.WebSocketSendAsyncKey] = new WebSocketSendAsync(SendAsync);
|
|
||||||
_environment[Constants.WebSocketReceiveAyncKey] = new WebSocketReceiveAsync(ReceiveAsync);
|
|
||||||
_environment[Constants.WebSocketCloseAsyncKey] = new WebSocketCloseAsync(CloseAsync);
|
|
||||||
_environment[Constants.WebSocketCallCancelledKey] = ct;
|
|
||||||
_environment[Constants.WebSocketVersionKey] = Constants.WebSocketVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IDictionary<string, object> Environment
|
|
||||||
{
|
|
||||||
get { return _environment; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Task SendAsync(ArraySegment<byte> buffer, int messageType, bool endOfMessage, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
// Remap close messages to CloseAsync. System.Net.WebSockets.WebSocket.SendAsync does not allow close messages.
|
|
||||||
if (messageType == 0x8)
|
|
||||||
{
|
|
||||||
return RedirectSendToCloseAsync(buffer, cancel);
|
|
||||||
}
|
|
||||||
else if (messageType == 0x9 || messageType == 0xA)
|
|
||||||
{
|
|
||||||
// Ping & Pong, not allowed by the underlying APIs, silently discard.
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _webSocket.SendAsync(buffer, (WebSocketMessageType)messageType, endOfMessage, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<WebSocketReceiveTuple> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
WebSocketReceiveResult nativeResult = await _webSocket.ReceiveAsync(buffer, cancel);
|
|
||||||
|
|
||||||
if (nativeResult.MessageType == WebSocketMessageType.Close)
|
|
||||||
{
|
|
||||||
_environment[Constants.WebSocketCloseStatusKey] = (int)(nativeResult.CloseStatus ?? WebSocketCloseStatus.NormalClosure);
|
|
||||||
_environment[Constants.WebSocketCloseDescriptionKey] = nativeResult.CloseStatusDescription ?? string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new WebSocketReceiveTuple(
|
|
||||||
(int)nativeResult.MessageType,
|
|
||||||
nativeResult.EndOfMessage,
|
|
||||||
nativeResult.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Task CloseAsync(int status, string description, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
return _webSocket.CloseOutputAsync((WebSocketCloseStatus)status, description, cancel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task RedirectSendToCloseAsync(ArraySegment<byte> buffer, CancellationToken cancel)
|
|
||||||
{
|
|
||||||
if (buffer.Array == null || buffer.Count == 0)
|
|
||||||
{
|
|
||||||
return CloseAsync(1000, string.Empty, cancel);
|
|
||||||
}
|
|
||||||
else if (buffer.Count >= 2)
|
|
||||||
{
|
|
||||||
// Unpack the close message.
|
|
||||||
int statusCode =
|
|
||||||
(buffer.Array[buffer.Offset] << 8)
|
|
||||||
| buffer.Array[buffer.Offset + 1];
|
|
||||||
string description = Encoding.UTF8.GetString(buffer.Array, buffer.Offset + 2, buffer.Count - 2);
|
|
||||||
|
|
||||||
return CloseAsync(statusCode, description, cancel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("buffer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task CleanupAsync()
|
|
||||||
{
|
|
||||||
switch (_webSocket.State)
|
|
||||||
{
|
|
||||||
case WebSocketState.Closed: // Closed gracefully, no action needed.
|
|
||||||
case WebSocketState.Aborted: // Closed abortively, no action needed.
|
|
||||||
break;
|
|
||||||
case WebSocketState.CloseReceived:
|
|
||||||
// Echo what the client said, if anything.
|
|
||||||
await _webSocket.CloseAsync(_webSocket.CloseStatus ?? WebSocketCloseStatus.NormalClosure,
|
|
||||||
_webSocket.CloseStatusDescription ?? string.Empty, _cancellationToken);
|
|
||||||
break;
|
|
||||||
case WebSocketState.Open:
|
|
||||||
case WebSocketState.CloseSent: // No close received, abort so we don't have to drain the pipe.
|
|
||||||
_webSocket.Abort();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException("state", _webSocket.State, string.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,141 +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="WebSocket.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
public abstract class WebSocket : IDisposable
|
|
||||||
{
|
|
||||||
private static TimeSpan? defaultKeepAliveInterval;
|
|
||||||
|
|
||||||
public abstract WebSocketCloseStatus? CloseStatus { get; }
|
|
||||||
public abstract string CloseStatusDescription { get; }
|
|
||||||
public abstract string SubProtocol { get; }
|
|
||||||
public abstract WebSocketState State { get; }
|
|
||||||
|
|
||||||
public static TimeSpan DefaultKeepAliveInterval
|
|
||||||
{
|
|
||||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands",
|
|
||||||
Justification = "This is a harmless read-only operation")]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (defaultKeepAliveInterval == null)
|
|
||||||
{
|
|
||||||
if (UnsafeNativeMethods.WebSocketProtocolComponent.IsSupported)
|
|
||||||
{
|
|
||||||
defaultKeepAliveInterval = UnsafeNativeMethods.WebSocketProtocolComponent.WebSocketGetDefaultKeepAliveInterval();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
defaultKeepAliveInterval = Timeout.InfiniteTimeSpan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultKeepAliveInterval.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ArraySegment<byte> CreateClientBuffer(int receiveBufferSize, int sendBufferSize)
|
|
||||||
{
|
|
||||||
WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, sendBufferSize);
|
|
||||||
|
|
||||||
return WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, sendBufferSize, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ArraySegment<byte> CreateServerBuffer(int receiveBufferSize)
|
|
||||||
{
|
|
||||||
WebSocketHelpers.ValidateBufferSizes(receiveBufferSize, WebSocketBuffer.MinSendBufferSize);
|
|
||||||
|
|
||||||
return WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebSocket CreateServerWebSocket(Stream innerStream,
|
|
||||||
string subProtocol,
|
|
||||||
int receiveBufferSize,
|
|
||||||
TimeSpan keepAliveInterval,
|
|
||||||
ArraySegment<byte> internalBuffer)
|
|
||||||
{
|
|
||||||
if (!UnsafeNativeMethods.WebSocketProtocolComponent.IsSupported)
|
|
||||||
{
|
|
||||||
WebSocketHelpers.ThrowPlatformNotSupportedException_WSPC();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketHelpers.ValidateInnerStream(innerStream);
|
|
||||||
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
|
||||||
WebSocketHelpers.ValidateArraySegment<byte>(internalBuffer, "internalBuffer");
|
|
||||||
WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
|
||||||
|
|
||||||
return new ServerWebSocket(innerStream,
|
|
||||||
subProtocol,
|
|
||||||
receiveBufferSize,
|
|
||||||
keepAliveInterval,
|
|
||||||
internalBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Abort();
|
|
||||||
public abstract Task CloseAsync(WebSocketCloseStatus closeStatus,
|
|
||||||
string statusDescription,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
public abstract Task CloseOutputAsync(WebSocketCloseStatus closeStatus,
|
|
||||||
string statusDescription,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
[SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "This rule is outdated")]
|
|
||||||
public abstract void Dispose();
|
|
||||||
public abstract Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
public abstract Task SendAsync(ArraySegment<byte> buffer,
|
|
||||||
WebSocketMessageType messageType,
|
|
||||||
bool endOfMessage,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
protected static void ThrowOnInvalidState(WebSocketState state, params WebSocketState[] validStates)
|
|
||||||
{
|
|
||||||
string validStatesText = string.Empty;
|
|
||||||
|
|
||||||
if (validStates != null && validStates.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (WebSocketState currentState in validStates)
|
|
||||||
{
|
|
||||||
if (state == currentState)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validStatesText = string.Join(", ", validStates);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new WebSocketException(SR.GetString(SR.net_WebSockets_InvalidState, state, validStatesText));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static bool IsStateTerminal(WebSocketState state)
|
|
||||||
{
|
|
||||||
return state == WebSocketState.Closed ||
|
|
||||||
state == WebSocketState.Aborted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 file="WebSocketCloseStatus.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
[SuppressMessage("Microsoft.Design",
|
|
||||||
"CA1008:EnumsShouldHaveZeroValue",
|
|
||||||
Justification = "This enum is reflecting the IETF's WebSocket specification. " +
|
|
||||||
"'0' is a disallowed value for the close status code")]
|
|
||||||
public enum WebSocketCloseStatus
|
|
||||||
{
|
|
||||||
NormalClosure = 1000,
|
|
||||||
EndpointUnavailable = 1001,
|
|
||||||
ProtocolError = 1002,
|
|
||||||
InvalidMessageType = 1003,
|
|
||||||
Empty = 1005,
|
|
||||||
// AbnormalClosure = 1006, // 1006 is reserved and should never be used by user
|
|
||||||
InvalidPayloadData = 1007,
|
|
||||||
PolicyViolation = 1008,
|
|
||||||
MessageTooBig = 1009,
|
|
||||||
MandatoryExtension = 1010,
|
|
||||||
InternalServerError = 1011
|
|
||||||
// TLSHandshakeFailed = 1015, // 1015 is reserved and should never be used by user
|
|
||||||
|
|
||||||
// 0 - 999 Status codes in the range 0-999 are not used.
|
|
||||||
// 1000 - 1999 Status codes in the range 1000-1999 are reserved for definition by this protocol.
|
|
||||||
// 2000 - 2999 Status codes in the range 2000-2999 are reserved for use by extensions.
|
|
||||||
// 3000 - 3999 Status codes in the range 3000-3999 MAY be used by libraries and frameworks. The
|
|
||||||
// interpretation of these codes is undefined by this protocol. End applications MUST
|
|
||||||
// NOT use status codes in this range.
|
|
||||||
// 4000 - 4999 Status codes in the range 4000-4999 MAY be used by application code. The interpretaion
|
|
||||||
// of these codes is undefined by this protocol.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +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 NET45
|
|
||||||
using Microsoft.AspNet.WebSockets;
|
|
||||||
|
|
||||||
namespace Owin
|
|
||||||
{
|
|
||||||
public static class WebSocketExtensions
|
|
||||||
{
|
|
||||||
public static IAppBuilder UseWebSockets(this IAppBuilder app)
|
|
||||||
{
|
|
||||||
return app.Use<WebSocketMiddleware>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
|
|
@ -1,32 +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="WebSocketMessageType.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
public enum WebSocketMessageType
|
|
||||||
{
|
|
||||||
Text = 0x1,
|
|
||||||
Binary = 0x2,
|
|
||||||
Close = 0x8,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,185 +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.
|
|
||||||
/*
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Owin;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
using AppFunc = Func<IDictionary<string, object>, Task>;
|
|
||||||
using OpaqueUpgrade =
|
|
||||||
Action
|
|
||||||
<
|
|
||||||
IDictionary<string, object>, // Parameters
|
|
||||||
Func // OpaqueFunc callback
|
|
||||||
<
|
|
||||||
IDictionary<string, object>, // Opaque environment
|
|
||||||
Task // Complete
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
using WebSocketAccept =
|
|
||||||
Action
|
|
||||||
<
|
|
||||||
IDictionary<string, object>, // WebSocket Accept parameters
|
|
||||||
Func // WebSocketFunc callback
|
|
||||||
<
|
|
||||||
IDictionary<string, object>, // WebSocket environment
|
|
||||||
Task // Complete
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
using WebSocketFunc =
|
|
||||||
Func
|
|
||||||
<
|
|
||||||
IDictionary<string, object>, // WebSocket Environment
|
|
||||||
Task // Complete
|
|
||||||
>;
|
|
||||||
|
|
||||||
public class WebSocketMiddleware
|
|
||||||
{
|
|
||||||
private AppFunc _next;
|
|
||||||
|
|
||||||
public WebSocketMiddleware(AppFunc next)
|
|
||||||
{
|
|
||||||
_next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Invoke(IDictionary<string, object> environment)
|
|
||||||
{
|
|
||||||
IOwinContext context = new OwinContext(environment);
|
|
||||||
// Detect if an opaque upgrade is available, and if websocket upgrade headers are present.
|
|
||||||
// If so, add a websocket upgrade.
|
|
||||||
OpaqueUpgrade upgrade = context.Get<OpaqueUpgrade>("opaque.Upgrade");
|
|
||||||
if (upgrade != null)
|
|
||||||
{
|
|
||||||
// Headers and values:
|
|
||||||
// Connection: Upgrade
|
|
||||||
// Upgrade: WebSocket
|
|
||||||
// Sec-WebSocket-Version: (WebSocketProtocolComponent.SupportedVersion)
|
|
||||||
// Sec-WebSocket-Key: (hash, see WebSocketHelpers.GetSecWebSocketAcceptString)
|
|
||||||
// Sec-WebSocket-Protocol: (optional, list)
|
|
||||||
IList<string> connectionHeaders = context.Request.Headers.GetCommaSeparatedValues(HttpKnownHeaderNames.Connection); // "Upgrade, KeepAlive"
|
|
||||||
string upgradeHeader = context.Request.Headers[HttpKnownHeaderNames.Upgrade];
|
|
||||||
string versionHeader = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
|
||||||
string keyHeader = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
|
||||||
|
|
||||||
if (connectionHeaders != null && connectionHeaders.Count > 0
|
|
||||||
&& connectionHeaders.Contains(HttpKnownHeaderNames.Upgrade, StringComparer.OrdinalIgnoreCase)
|
|
||||||
&& string.Equals(upgradeHeader, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase)
|
|
||||||
&& string.Equals(versionHeader, UnsafeNativeMethods.WebSocketProtocolComponent.SupportedVersion, StringComparison.OrdinalIgnoreCase)
|
|
||||||
&& !string.IsNullOrWhiteSpace(keyHeader))
|
|
||||||
{
|
|
||||||
environment["websocket.Accept"] = new WebSocketAccept(new UpgradeHandshake(context, upgrade).AcceptWebSocket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _next(environment);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class UpgradeHandshake
|
|
||||||
{
|
|
||||||
private IOwinContext _context;
|
|
||||||
private OpaqueUpgrade _upgrade;
|
|
||||||
private WebSocketFunc _webSocketFunc;
|
|
||||||
|
|
||||||
private string _subProtocol;
|
|
||||||
private int _receiveBufferSize = WebSocketHelpers.DefaultReceiveBufferSize;
|
|
||||||
private TimeSpan _keepAliveInterval = WebSocket.DefaultKeepAliveInterval;
|
|
||||||
private ArraySegment<byte> _internalBuffer;
|
|
||||||
|
|
||||||
internal UpgradeHandshake(IOwinContext context, OpaqueUpgrade upgrade)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_upgrade = upgrade;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AcceptWebSocket(IDictionary<string, object> options, WebSocketFunc webSocketFunc)
|
|
||||||
{
|
|
||||||
_webSocketFunc = webSocketFunc;
|
|
||||||
|
|
||||||
// Get options
|
|
||||||
object temp;
|
|
||||||
if (options != null && options.TryGetValue("websocket.SubProtocol", out temp))
|
|
||||||
{
|
|
||||||
_subProtocol = temp as string;
|
|
||||||
}
|
|
||||||
if (options != null && options.TryGetValue("websocket.ReceiveBufferSize", out temp))
|
|
||||||
{
|
|
||||||
_receiveBufferSize = (int)temp;
|
|
||||||
}
|
|
||||||
if (options != null && options.TryGetValue("websocket.KeepAliveInterval", out temp))
|
|
||||||
{
|
|
||||||
_keepAliveInterval = (TimeSpan)temp;
|
|
||||||
}
|
|
||||||
if (options != null && options.TryGetValue("websocket.Buffer", out temp))
|
|
||||||
{
|
|
||||||
_internalBuffer = (ArraySegment<byte>)temp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_internalBuffer = WebSocketBuffer.CreateInternalBufferArraySegment(_receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set WebSocket upgrade response headers
|
|
||||||
|
|
||||||
string outgoingSecWebSocketProtocolString;
|
|
||||||
bool shouldSendSecWebSocketProtocolHeader =
|
|
||||||
WebSocketHelpers.ProcessWebSocketProtocolHeader(
|
|
||||||
_context.Request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
|
|
||||||
_subProtocol,
|
|
||||||
out outgoingSecWebSocketProtocolString);
|
|
||||||
|
|
||||||
if (shouldSendSecWebSocketProtocolHeader)
|
|
||||||
{
|
|
||||||
_context.Response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol] = outgoingSecWebSocketProtocolString;
|
|
||||||
}
|
|
||||||
|
|
||||||
string secWebSocketKey = _context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
|
||||||
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
|
|
||||||
|
|
||||||
_context.Response.Headers[HttpKnownHeaderNames.Connection] = HttpKnownHeaderNames.Upgrade;
|
|
||||||
_context.Response.Headers[HttpKnownHeaderNames.Upgrade] = WebSocketHelpers.WebSocketUpgradeToken;
|
|
||||||
_context.Response.Headers[HttpKnownHeaderNames.SecWebSocketAccept] = secWebSocketAccept;
|
|
||||||
|
|
||||||
_context.Response.StatusCode = 101; // Switching Protocols;
|
|
||||||
|
|
||||||
_upgrade(options, OpaqueCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task OpaqueCallback(IDictionary<string, object> opaqueEnv)
|
|
||||||
{
|
|
||||||
// Create WebSocket wrapper around the opaque env
|
|
||||||
WebSocket webSocket = CreateWebSocket(opaqueEnv);
|
|
||||||
OwinWebSocketWrapper wrapper = new OwinWebSocketWrapper(webSocket, (CancellationToken)opaqueEnv["opaque.CallCancelled"]);
|
|
||||||
await _webSocketFunc(wrapper.Environment);
|
|
||||||
// Close down the WebSocekt, gracefully if possible
|
|
||||||
await wrapper.CleanupAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebSocket CreateWebSocket(IDictionary<string, object> opaqueEnv)
|
|
||||||
{
|
|
||||||
Stream stream = (Stream)opaqueEnv["opaque.Stream"];
|
|
||||||
return new ServerWebSocket(stream, _subProtocol, _receiveBufferSize, _keepAliveInterval, _internalBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
@ -1,72 +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;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
public class WebSocketReceiveResult
|
|
||||||
{
|
|
||||||
public WebSocketReceiveResult(int count, WebSocketMessageType messageType, bool endOfMessage)
|
|
||||||
: this(count, messageType, endOfMessage, null, null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebSocketReceiveResult(int count,
|
|
||||||
WebSocketMessageType messageType,
|
|
||||||
bool endOfMessage,
|
|
||||||
WebSocketCloseStatus? closeStatus,
|
|
||||||
string closeStatusDescription)
|
|
||||||
{
|
|
||||||
if (count < 0)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException("count");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Count = count;
|
|
||||||
this.EndOfMessage = endOfMessage;
|
|
||||||
this.MessageType = messageType;
|
|
||||||
this.CloseStatus = closeStatus;
|
|
||||||
this.CloseStatusDescription = closeStatusDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count { get; private set; }
|
|
||||||
public bool EndOfMessage { get; private set; }
|
|
||||||
public WebSocketMessageType MessageType { get; private set; }
|
|
||||||
public WebSocketCloseStatus? CloseStatus { get; private set; }
|
|
||||||
public string CloseStatusDescription { get; private set; }
|
|
||||||
|
|
||||||
internal WebSocketReceiveResult Copy(int count)
|
|
||||||
{
|
|
||||||
Contract.Assert(count >= 0, "'count' MUST NOT be negative.");
|
|
||||||
Contract.Assert(count <= this.Count, "'count' MUST NOT be bigger than 'this.Count'.");
|
|
||||||
this.Count -= count;
|
|
||||||
return new WebSocketReceiveResult(count,
|
|
||||||
this.MessageType,
|
|
||||||
this.Count == 0 && this.EndOfMessage,
|
|
||||||
this.CloseStatus,
|
|
||||||
this.CloseStatusDescription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +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="WebSocketState.cs" company="Microsoft">
|
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// </copyright>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
|
||||||
{
|
|
||||||
public enum WebSocketState
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Connecting = 1,
|
|
||||||
Open = 2,
|
|
||||||
CloseSent = 3, // WebSocket close handshake started form local endpoint
|
|
||||||
CloseReceived = 4, // WebSocket close message received from remote endpoint. Waiting for app to call close
|
|
||||||
Closed = 5,
|
|
||||||
Aborted = 6,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.1-alpha-*",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.AspNet.Http" : "0.1-alpha-*",
|
|
||||||
"Microsoft.AspNet.HttpFeature" : "0.1-alpha-*"
|
|
||||||
},
|
|
||||||
"compilationOptions" : { "allowUnsafe": true },
|
|
||||||
"configurations": {
|
|
||||||
"net45" : {
|
|
||||||
"dependencies": {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -441,6 +441,17 @@ namespace Microsoft.Net.Server
|
||||||
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress);
|
return UnsafeNclNativeMethods.HttpApi.GetKnownVerb(RequestBuffer, OriginalBlobAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: We need an easier to user header collection that has this built in
|
||||||
|
internal string GetHeader(string headerName)
|
||||||
|
{
|
||||||
|
string[] values;
|
||||||
|
if (Headers.TryGetValue(headerName, out values))
|
||||||
|
{
|
||||||
|
return string.Join(", ", values);
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
// Populates the client certificate. The result may be null if there is no client cert.
|
// Populates the client certificate. The result may be null if there is no client cert.
|
||||||
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
|
// TODO: Does it make sense for this to be invoked multiple times (e.g. renegotiate)? Client and server code appear to
|
||||||
// enable this, but it's unclear what Http.Sys would do.
|
// enable this, but it's unclear what Http.Sys would do.
|
||||||
|
|
|
||||||
|
|
@ -24,23 +24,24 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
|
using Microsoft.Net.WebSockets;
|
||||||
|
|
||||||
namespace Microsoft.Net.Server
|
namespace Microsoft.Net.Server
|
||||||
{
|
{
|
||||||
using OpaqueFunc = Func<IDictionary<string, object>, Task>;
|
|
||||||
|
|
||||||
public sealed class RequestContext : IDisposable
|
public sealed class RequestContext : IDisposable
|
||||||
{
|
{
|
||||||
private WebListener _server;
|
private WebListener _server;
|
||||||
private Request _request;
|
private Request _request;
|
||||||
private Response _response;
|
private Response _response;
|
||||||
private NativeRequestContext _memoryBlob;
|
private NativeRequestContext _memoryBlob;
|
||||||
private OpaqueFunc _opaqueCallback;
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private CancellationTokenSource _requestAbortSource;
|
private CancellationTokenSource _requestAbortSource;
|
||||||
private CancellationToken? _disconnectToken;
|
private CancellationToken? _disconnectToken;
|
||||||
|
|
@ -128,17 +129,230 @@ namespace Microsoft.Net.Server
|
||||||
return Request.RequestId;
|
return Request.RequestId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public bool TryGetOpaqueUpgrade(ref Action<IDictionary<string, object>, OpaqueFunc> value)
|
public bool IsUpgradableRequest
|
||||||
{
|
{
|
||||||
if (_request.IsUpgradable)
|
get { return _request.IsUpgradable; }
|
||||||
{
|
|
||||||
value = OpaqueUpgrade;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<Stream> UpgradeAsync()
|
||||||
|
{
|
||||||
|
if (!IsUpgradableRequest || _response.SentHeaders)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the status code and reason phrase
|
||||||
|
Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
||||||
|
Response.ReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
||||||
|
|
||||||
|
Response.SendOpaqueUpgrade(); // TODO: Async
|
||||||
|
Request.SwitchToOpaqueMode();
|
||||||
|
Response.SwitchToOpaqueMode();
|
||||||
|
Stream opaqueStream = new OpaqueStream(Request.Body, Response.Body);
|
||||||
|
return Task.FromResult(opaqueStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsWebSocketRequest
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!WebSocketHelpers.AreWebSocketsSupported)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsUpgradableRequest)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals("GET", Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
|
||||||
|
string connection = Request.GetHeader(HttpKnownHeaderNames.Connection);
|
||||||
|
if (connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade: websocket
|
||||||
|
string upgrade = Request.GetHeader(HttpKnownHeaderNames.Upgrade);
|
||||||
|
if (!string.Equals(WebSocketHelpers.WebSocketUpgradeToken, upgrade, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sec-WebSocket-Version: 13
|
||||||
|
string version = Request.GetHeader(HttpKnownHeaderNames.SecWebSocketVersion);
|
||||||
|
if (!string.Equals(WebSocketConstants.SupportedProtocolVersion, version, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sec-WebSocket-Key: {base64string}
|
||||||
|
string key = Request.GetHeader(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 (!string.Equals("GET", Request.Method, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("This request is not a valid upgrade request; invalid verb: " + Request.Method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection: Upgrade (some odd clients send Upgrade,KeepAlive)
|
||||||
|
string connection = Request.GetHeader(HttpKnownHeaderNames.Connection);
|
||||||
|
if (connection.IndexOf(HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) < 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The Connection header is invalid: " + connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade: websocket
|
||||||
|
string upgrade = Request.GetHeader(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 = Request.GetHeader(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 = Request.GetHeader(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,
|
||||||
|
WebSocket.DefaultKeepAliveInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<WebSocket> AcceptWebSocketAsync(string subProtocol)
|
||||||
|
{
|
||||||
|
return AcceptWebSocketAsync(subProtocol,
|
||||||
|
WebSocketHelpers.DefaultReceiveBufferSize,
|
||||||
|
WebSocket.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)
|
||||||
|
{
|
||||||
|
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
||||||
|
|
||||||
|
ArraySegment<byte> internalBuffer = WebSocketBuffer.CreateInternalBufferArraySegment(receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
||||||
|
return this.AcceptWebSocketAsync(subProtocol,
|
||||||
|
receiveBufferSize,
|
||||||
|
keepAliveInterval,
|
||||||
|
internalBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<WebSocket> AcceptWebSocketAsync(
|
||||||
|
string subProtocol,
|
||||||
|
int receiveBufferSize,
|
||||||
|
TimeSpan keepAliveInterval,
|
||||||
|
ArraySegment<byte> internalBuffer)
|
||||||
|
{
|
||||||
|
if (!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(subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<WebSocket> AcceptWebSocketAsyncCore(
|
||||||
|
string subProtocol,
|
||||||
|
int receiveBufferSize,
|
||||||
|
TimeSpan keepAliveInterval,
|
||||||
|
ArraySegment<byte> internalBuffer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO: We need a better header collection API.
|
||||||
|
ValidateWebSocketRequest();
|
||||||
|
|
||||||
|
string subProtocols = string.Empty;
|
||||||
|
string[] values;
|
||||||
|
if (Request.Headers.TryGetValue(HttpKnownHeaderNames.SecWebSocketProtocol, out values))
|
||||||
|
{
|
||||||
|
subProtocols = string.Join(", ", values);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldSendSecWebSocketProtocolHeader = WebSocketHelpers.ProcessWebSocketProtocolHeader(subProtocols, subProtocol);
|
||||||
|
if (shouldSendSecWebSocketProtocolHeader)
|
||||||
|
{
|
||||||
|
Response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol] = new[] { subProtocol };
|
||||||
|
}
|
||||||
|
|
||||||
|
// negotiate the websocket key return value
|
||||||
|
string secWebSocketKey = Request.Headers[HttpKnownHeaderNames.SecWebSocketKey].First();
|
||||||
|
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
|
||||||
|
|
||||||
|
Response.Headers.Add(HttpKnownHeaderNames.Connection, new[] { HttpKnownHeaderNames.Upgrade });
|
||||||
|
Response.Headers.Add(HttpKnownHeaderNames.Upgrade, new[] { WebSocketHelpers.WebSocketUpgradeToken });
|
||||||
|
Response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, new[] { secWebSocketAccept });
|
||||||
|
|
||||||
|
Stream opaqueStream = await UpgradeAsync();
|
||||||
|
|
||||||
|
return WebSocketHelpers.CreateServerWebSocket(
|
||||||
|
opaqueStream,
|
||||||
|
subProtocol,
|
||||||
|
receiveBufferSize,
|
||||||
|
keepAliveInterval,
|
||||||
|
internalBuffer);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogHelper.LogException(Logger, "AcceptWebSocketAsync", ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
public bool TryGetChannelBinding(ref ChannelBinding value)
|
public bool TryGetChannelBinding(ref ChannelBinding value)
|
||||||
{
|
{
|
||||||
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
|
value = Server.GetChannelBinding(Request.ConnectionId, Request.IsSecureConnection);
|
||||||
|
|
@ -221,57 +435,5 @@ namespace Microsoft.Net.Server
|
||||||
// RequestQueueHandle may have been closed
|
// RequestQueueHandle may have been closed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
internal void OpaqueUpgrade(IDictionary<string, object> parameters, OpaqueFunc callback)
|
|
||||||
{
|
|
||||||
// Parameters are ignored for now
|
|
||||||
if (Response.SentHeaders)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
if (callback == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("callback");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the status code and reason phrase
|
|
||||||
Response.StatusCode = (int)HttpStatusCode.SwitchingProtocols;
|
|
||||||
Response.ReasonPhrase = HttpReasonPhrase.Get(HttpStatusCode.SwitchingProtocols);
|
|
||||||
|
|
||||||
// Store the callback and process it after the stack unwind.
|
|
||||||
_opaqueCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called after the AppFunc completes for any necessary post-processing.
|
|
||||||
internal unsafe Task ProcessResponseAsync()
|
|
||||||
{
|
|
||||||
// If an upgrade was requested, perform it
|
|
||||||
if (!Response.SentHeaders && _opaqueCallback != null
|
|
||||||
&& Response.StatusCode == (int)HttpStatusCode.SwitchingProtocols)
|
|
||||||
{
|
|
||||||
Response.SendOpaqueUpgrade();
|
|
||||||
|
|
||||||
IDictionary<string, object> opaqueEnv = CreateOpaqueEnvironment();
|
|
||||||
return _opaqueCallback(opaqueEnv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Helpers.CompletedTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object> CreateOpaqueEnvironment()
|
|
||||||
{
|
|
||||||
IDictionary<string, object> opaqueEnv = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
opaqueEnv[Constants.OpaqueVersionKey] = Constants.OpaqueVersion;
|
|
||||||
// TODO: Separate CT?
|
|
||||||
// opaqueEnv[Constants.OpaqueCallCancelledKey] = Environment.CallCancelled;
|
|
||||||
|
|
||||||
Request.SwitchToOpaqueMode();
|
|
||||||
Response.SwitchToOpaqueMode();
|
|
||||||
opaqueEnv[Constants.OpaqueStreamKey] = new OpaqueStream(Request.Body, Response.Body);
|
|
||||||
|
|
||||||
return opaqueEnv;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"version": "0.1-alpha-*",
|
"version": "0.1-alpha-*",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Framework.Logging": "0.1-alpha-*"
|
"Microsoft.Framework.Logging": "0.1-alpha-*",
|
||||||
|
"Microsoft.Net.WebSockets": ""
|
||||||
},
|
},
|
||||||
"compilationOptions": {
|
"compilationOptions": {
|
||||||
"allowUnsafe": true
|
"allowUnsafe": true
|
||||||
|
|
@ -10,6 +11,7 @@
|
||||||
"net45": {},
|
"net45": {},
|
||||||
"k10": {
|
"k10": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*",
|
||||||
"Microsoft.Win32.Primitives": "4.0.0.0",
|
"Microsoft.Win32.Primitives": "4.0.0.0",
|
||||||
"System.Collections": "4.0.0.0",
|
"System.Collections": "4.0.0.0",
|
||||||
"System.Collections.Concurrent": "4.0.0.0",
|
"System.Collections.Concurrent": "4.0.0.0",
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
// </copyright>
|
// </copyright>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
// this class contains known header names
|
// this class contains known header names
|
||||||
internal static class HttpKnownHeaderNames
|
internal static class HttpKnownHeaderNames
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
<Content Include="project.json" />
|
<Content Include="project.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Constants.cs" />
|
|
||||||
<Compile Include="fx\Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs" />
|
<Compile Include="fx\Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs" />
|
||||||
<Compile Include="fx\System\AccessViolationException.cs" />
|
<Compile Include="fx\System\AccessViolationException.cs" />
|
||||||
<Compile Include="fx\System\ComponentModel\Win32Exception.cs" />
|
<Compile Include="fx\System\ComponentModel\Win32Exception.cs" />
|
||||||
|
|
@ -31,29 +30,21 @@
|
||||||
<Compile Include="fx\System\SafeNativeMethods.cs" />
|
<Compile Include="fx\System\SafeNativeMethods.cs" />
|
||||||
<Compile Include="fx\System\SystemException.cs" />
|
<Compile Include="fx\System\SystemException.cs" />
|
||||||
<Compile Include="HttpKnownHeaderNames.cs" />
|
<Compile Include="HttpKnownHeaderNames.cs" />
|
||||||
<Compile Include="Legacy\HttpListenerContext.cs" />
|
|
||||||
<Compile Include="Legacy\HttpListenerRequest.cs" />
|
|
||||||
<Compile Include="Legacy\SR.cs" />
|
<Compile Include="Legacy\SR.cs" />
|
||||||
<Compile Include="Legacy\WebSocketHttpListenerDuplexStream.cs" />
|
<Compile Include="Legacy\WebSocketHttpListenerDuplexStream.cs" />
|
||||||
<Compile Include="NativeInterop\SafeLoadLibrary.cs" />
|
<Compile Include="NativeInterop\SafeLoadLibrary.cs" />
|
||||||
<Compile Include="NativeInterop\SafeNativeOverlapped.cs" />
|
<Compile Include="NativeInterop\SafeNativeOverlapped.cs" />
|
||||||
<Compile Include="NativeInterop\SafeWebSocketHandle.cs" />
|
<Compile Include="NativeInterop\SafeWebSocketHandle.cs" />
|
||||||
<Compile Include="NativeInterop\UnsafeNativeMethods.cs" />
|
<Compile Include="NativeInterop\UnsafeNativeMethods.cs" />
|
||||||
<Compile Include="OwinWebSocketWrapper.cs" />
|
|
||||||
<Compile Include="ServerWebSocket.cs" />
|
<Compile Include="ServerWebSocket.cs" />
|
||||||
<Compile Include="WebSocket.cs" />
|
|
||||||
<Compile Include="WebSocketBase.cs" />
|
<Compile Include="WebSocketBase.cs" />
|
||||||
<Compile Include="WebSocketBuffer.cs" />
|
<Compile Include="WebSocketBuffer.cs" />
|
||||||
<Compile Include="WebSocketCloseStatus.cs" />
|
<Compile Include="WebSocketConstants.cs" />
|
||||||
<Compile Include="WebSocketError.cs" />
|
<Compile Include="WebSocketError.cs" />
|
||||||
<Compile Include="WebSocketException.cs" />
|
<Compile Include="WebSocketException.cs" />
|
||||||
<Compile Include="WebSocketExtensions.cs" />
|
|
||||||
<Compile Include="WebSocketHelpers.cs" />
|
<Compile Include="WebSocketHelpers.cs" />
|
||||||
<Compile Include="WebSocketMessageType.cs" />
|
|
||||||
<Compile Include="WebSocketMiddleware.cs" />
|
<Compile Include="WebSocketMiddleware.cs" />
|
||||||
<Compile Include="WebSocketReceiveResult.cs" />
|
<Compile Include="WebSocketReceiveResultExtensions.cs" />
|
||||||
<Compile Include="WebSocketState.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal sealed class SafeLoadLibrary : SafeHandleZeroOrMinusOneIsInvalid
|
internal sealed class SafeLoadLibrary : SafeHandleZeroOrMinusOneIsInvalid
|
||||||
{
|
{
|
||||||
|
|
@ -25,7 +25,7 @@ using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal class SafeNativeOverlapped : SafeHandle
|
internal class SafeNativeOverlapped : SafeHandle
|
||||||
{
|
{
|
||||||
|
|
@ -21,10 +21,9 @@
|
||||||
// </copyright>
|
// </copyright>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.AspNet.WebSockets;
|
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
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
|
// 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.
|
// but we use a SafeHandle because it provides us the guarantee that WebSocketDeleteHandle will always get called.
|
||||||
|
|
@ -27,7 +27,7 @@ using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal static class UnsafeNativeMethods
|
internal static class UnsafeNativeMethods
|
||||||
{
|
{
|
||||||
|
|
@ -161,7 +161,11 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
|
|
||||||
static WebSocketProtocolComponent()
|
static WebSocketProtocolComponent()
|
||||||
{
|
{
|
||||||
|
#if NET45
|
||||||
DllFileName = Path.Combine(Environment.SystemDirectory, WEBSOCKET);
|
DllFileName = Path.Combine(Environment.SystemDirectory, WEBSOCKET);
|
||||||
|
#else
|
||||||
|
DllFileName = Path.Combine(Environment.GetEnvironmentVariable("SYSTEMROOT"), "System32", WEBSOCKET);
|
||||||
|
#endif
|
||||||
WebSocketDllHandle = SafeLoadLibrary.LoadLibraryEx(DllFileName);
|
WebSocketDllHandle = SafeLoadLibrary.LoadLibraryEx(DllFileName);
|
||||||
|
|
||||||
if (!WebSocketDllHandle.IsInvalid)
|
if (!WebSocketDllHandle.IsInvalid)
|
||||||
|
|
@ -27,7 +27,7 @@ using System.Diagnostics.Contracts;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal sealed class ServerWebSocket : WebSocketBase
|
internal sealed class ServerWebSocket : WebSocketBase
|
||||||
{
|
{
|
||||||
|
|
@ -29,13 +29,14 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal abstract class WebSocketBase : WebSocket, IDisposable
|
internal abstract class WebSocketBase : WebSocket, IDisposable
|
||||||
{
|
{
|
||||||
|
|
@ -233,7 +234,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
{
|
{
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseSent);
|
WebSocketHelpers.ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseSent);
|
||||||
|
|
||||||
bool ownsCancellationTokenSource = false;
|
bool ownsCancellationTokenSource = false;
|
||||||
CancellationToken linkedCancellationToken = CancellationToken.None;
|
CancellationToken linkedCancellationToken = CancellationToken.None;
|
||||||
|
|
@ -338,7 +339,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
{
|
{
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived);
|
WebSocketHelpers.ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived);
|
||||||
bool ownsCancellationTokenSource = false;
|
bool ownsCancellationTokenSource = false;
|
||||||
CancellationToken linkedCancellationToken = CancellationToken.None;
|
CancellationToken linkedCancellationToken = CancellationToken.None;
|
||||||
|
|
||||||
|
|
@ -468,13 +469,13 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
bool sessionHandleLockTaken = false;
|
bool sessionHandleLockTaken = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
|
TakeLocks(ref thisLockTaken, ref sessionHandleLockTaken);
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -548,7 +549,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -566,12 +567,12 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived);
|
WebSocketHelpers.ThrowOnInvalidState(State, WebSocketState.Open, WebSocketState.CloseReceived);
|
||||||
ownsCloseOutputCancellationTokenSource = _closeOutputOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
|
ownsCloseOutputCancellationTokenSource = _closeOutputOutstandingOperationHelper.TryStartOperation(cancellationToken, out linkedCancellationToken);
|
||||||
if (!ownsCloseOutputCancellationTokenSource)
|
if (!ownsCloseOutputCancellationTokenSource)
|
||||||
{
|
{
|
||||||
|
|
@ -676,7 +677,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
// returns TRUE if the caller should also call StartOnCloseCompleted
|
// returns TRUE if the caller should also call StartOnCloseCompleted
|
||||||
private bool OnCloseOutputCompleted()
|
private bool OnCloseOutputCompleted()
|
||||||
{
|
{
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -707,7 +708,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
{
|
{
|
||||||
Contract.Assert(thisLockTakenSnapshot, "'thisLockTakenSnapshot' MUST be 'true' at this point.");
|
Contract.Assert(thisLockTakenSnapshot, "'thisLockTakenSnapshot' MUST be 'true' at this point.");
|
||||||
|
|
||||||
if (IsStateTerminal(_state))
|
if (WebSocketHelpers.IsStateTerminal(_state))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -793,7 +794,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -806,12 +807,12 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ThrowIfPendingException();
|
ThrowIfPendingException();
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowOnInvalidState(State,
|
WebSocketHelpers.ThrowOnInvalidState(State,
|
||||||
WebSocketState.Open, WebSocketState.CloseReceived, WebSocketState.CloseSent);
|
WebSocketState.Open, WebSocketState.CloseReceived, WebSocketState.CloseSent);
|
||||||
|
|
||||||
Task closeOutputTask;
|
Task closeOutputTask;
|
||||||
|
|
@ -893,7 +894,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsStateTerminal(State))
|
if (WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -970,7 +971,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
Monitor.Enter(_thisLock, ref lockTaken);
|
Monitor.Enter(_thisLock, ref lockTaken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsStateTerminal(State))
|
if (!WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
bool ownsSendCancellationSource = false;
|
bool ownsSendCancellationSource = false;
|
||||||
try
|
try
|
||||||
|
|
@ -1055,7 +1056,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsStateTerminal(State))
|
if (!WebSocketHelpers.IsStateTerminal(State))
|
||||||
{
|
{
|
||||||
Abort();
|
Abort();
|
||||||
}
|
}
|
||||||
|
|
@ -1501,13 +1502,13 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
if (IsStateTerminal(State) || State == WebSocketState.CloseReceived)
|
if (WebSocketHelpers.IsStateTerminal(State) || State == WebSocketState.CloseReceived)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Monitor.Enter(_thisLock, ref thisLockTaken);
|
Monitor.Enter(_thisLock, ref thisLockTaken);
|
||||||
if (IsStateTerminal(State) || State == WebSocketState.CloseReceived)
|
if (WebSocketHelpers.IsStateTerminal(State) || State == WebSocketState.CloseReceived)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -25,11 +25,12 @@ using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
// This class helps to abstract the internal WebSocket buffer, which is used to interact with the native WebSocket
|
// 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.
|
// protocol component (WSPC). It helps to shield the details of the layout and the involved pointer arithmetic.
|
||||||
|
|
@ -43,10 +44,10 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
//
|
//
|
||||||
// *RBS = ReceiveBufferSize, *SBS = SendBufferSize
|
// *RBS = ReceiveBufferSize, *SBS = SendBufferSize
|
||||||
// *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes)
|
// *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes)
|
||||||
internal class WebSocketBuffer : IDisposable
|
public class WebSocketBuffer : IDisposable
|
||||||
{
|
{
|
||||||
private const int NativeOverheadBufferSize = 144;
|
private const int NativeOverheadBufferSize = 144;
|
||||||
internal const int MinSendBufferSize = 16;
|
public const int MinSendBufferSize = 16;
|
||||||
internal const int MinReceiveBufferSize = 256;
|
internal const int MinReceiveBufferSize = 256;
|
||||||
internal const int MaxBufferSize = 64 * 1024;
|
internal const int MaxBufferSize = 64 * 1024;
|
||||||
#if NET45
|
#if NET45
|
||||||
|
|
@ -362,7 +363,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
ValidateBufferedPayload();
|
ValidateBufferedPayload();
|
||||||
|
|
||||||
int bytesTransferred = Math.Min(buffer.Count, _BufferedPayloadReceiveResult.Count);
|
int bytesTransferred = Math.Min(buffer.Count, _BufferedPayloadReceiveResult.Count);
|
||||||
receiveResult = _BufferedPayloadReceiveResult.Copy(bytesTransferred);
|
receiveResult = WebSocketReceiveResultExtensions.DecrementAndClone(ref _BufferedPayloadReceiveResult, bytesTransferred);
|
||||||
|
|
||||||
Buffer.BlockCopy(_PayloadBuffer.Array,
|
Buffer.BlockCopy(_PayloadBuffer.Array,
|
||||||
_PayloadBuffer.Offset + _PayloadOffset,
|
_PayloadBuffer.Offset + _PayloadOffset,
|
||||||
|
|
@ -664,7 +665,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
ReleasePinnedSendBuffer();
|
ReleasePinnedSendBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ArraySegment<byte> CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
public static ArraySegment<byte> CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
||||||
{
|
{
|
||||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||||
|
|
@ -675,7 +676,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
return new ArraySegment<byte>(new byte[internalBufferSize]);
|
return new ArraySegment<byte>(new byte[internalBufferSize]);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
public static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
|
||||||
{
|
{
|
||||||
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
|
||||||
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.Net.WebSockets
|
||||||
|
{
|
||||||
|
public static class WebSocketConstants
|
||||||
|
{
|
||||||
|
public static string SupportedProtocolVersion = "13";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
// </copyright>
|
// </copyright>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
public enum WebSocketError
|
public enum WebSocketError
|
||||||
{
|
{
|
||||||
|
|
@ -23,9 +23,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
#if NET45
|
#if NET45
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
|
||||||
|
|
@ -26,21 +26,20 @@ using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Diagnostics.Contracts;
|
using System.Diagnostics.Contracts;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
#if NET45
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
#endif
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.WebSockets
|
namespace Microsoft.Net.WebSockets
|
||||||
{
|
{
|
||||||
internal static class WebSocketHelpers
|
public static class WebSocketHelpers
|
||||||
{
|
{
|
||||||
internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
internal const string WebSocketUpgradeToken = "websocket";
|
public const string WebSocketUpgradeToken = "websocket";
|
||||||
internal const int DefaultReceiveBufferSize = 16 * 1024;
|
public const int DefaultReceiveBufferSize = 16 * 1024;
|
||||||
internal const int DefaultClientSendBufferSize = 16 * 1024;
|
internal const int DefaultClientSendBufferSize = 16 * 1024;
|
||||||
internal const int MaxControlFramePayloadLength = 123;
|
internal const int MaxControlFramePayloadLength = 123;
|
||||||
|
|
||||||
|
|
@ -63,138 +62,30 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
internal static readonly ArraySegment<byte> EmptyPayload = new ArraySegment<byte>(new byte[] { }, 0, 0);
|
internal static readonly ArraySegment<byte> EmptyPayload = new ArraySegment<byte>(new byte[] { }, 0, 0);
|
||||||
private static readonly Random KeyGenerator = new Random();
|
private static readonly Random KeyGenerator = new Random();
|
||||||
|
|
||||||
/*
|
public static bool AreWebSocketsSupported
|
||||||
internal static Task<HttpListenerWebSocketContext> AcceptWebSocketAsync(HttpListenerContext context,
|
|
||||||
string subProtocol,
|
|
||||||
int receiveBufferSize,
|
|
||||||
TimeSpan keepAliveInterval,
|
|
||||||
ArraySegment<byte> internalBuffer)
|
|
||||||
{
|
{
|
||||||
WebSocketHelpers.ValidateOptions(subProtocol, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, keepAliveInterval);
|
get
|
||||||
WebSocketHelpers.ValidateArraySegment<byte>(internalBuffer, "internalBuffer");
|
{
|
||||||
WebSocketBuffer.Validate(internalBuffer.Count, receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
return UnsafeNativeMethods.WebSocketProtocolComponent.IsSupported;
|
||||||
|
}
|
||||||
return AcceptWebSocketAsyncCore(context, subProtocol, receiveBufferSize, keepAliveInterval, internalBuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<HttpListenerWebSocketContext> AcceptWebSocketAsyncCore(HttpListenerContext context,
|
public static bool IsValidWebSocketKey(string key)
|
||||||
string subProtocol,
|
|
||||||
int receiveBufferSize,
|
|
||||||
TimeSpan keepAliveInterval,
|
|
||||||
ArraySegment<byte> internalBuffer)
|
|
||||||
{
|
{
|
||||||
HttpListenerWebSocketContext webSocketContext = null;
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
/*if (Logging.On)
|
|
||||||
{
|
{
|
||||||
Logging.Enter(Logging.WebSockets, context, "AcceptWebSocketAsync", "");
|
return false;
|
||||||
}* /
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// get property will create a new response if one doesn't exist.
|
|
||||||
HttpListenerResponse response = context.Response;
|
|
||||||
HttpListenerRequest request = context.Request;
|
|
||||||
ValidateWebSocketHeaders(context);
|
|
||||||
|
|
||||||
string secWebSocketVersion = request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
|
||||||
|
|
||||||
// Optional for non-browser client
|
|
||||||
string origin = request.Headers[HttpKnownHeaderNames.Origin];
|
|
||||||
|
|
||||||
List<string> secWebSocketProtocols = new List<string>();
|
|
||||||
string outgoingSecWebSocketProtocolString;
|
|
||||||
bool shouldSendSecWebSocketProtocolHeader =
|
|
||||||
WebSocketHelpers.ProcessWebSocketProtocolHeader(
|
|
||||||
request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
|
|
||||||
subProtocol,
|
|
||||||
out outgoingSecWebSocketProtocolString);
|
|
||||||
|
|
||||||
if (shouldSendSecWebSocketProtocolHeader)
|
|
||||||
{
|
|
||||||
secWebSocketProtocols.Add(outgoingSecWebSocketProtocolString);
|
|
||||||
response.Headers.Add(HttpKnownHeaderNames.SecWebSocketProtocol,
|
|
||||||
outgoingSecWebSocketProtocolString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// negotiate the websocket key return value
|
|
||||||
string secWebSocketKey = request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
|
||||||
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
|
|
||||||
|
|
||||||
response.Headers.Add(HttpKnownHeaderNames.Connection, HttpKnownHeaderNames.Upgrade);
|
|
||||||
response.Headers.Add(HttpKnownHeaderNames.Upgrade, WebSocketHelpers.WebSocketUpgradeToken);
|
|
||||||
response.Headers.Add(HttpKnownHeaderNames.SecWebSocketAccept, secWebSocketAccept);
|
|
||||||
|
|
||||||
response.StatusCode = (int)HttpStatusCode.SwitchingProtocols; // HTTP 101
|
|
||||||
response.ComputeCoreHeaders();
|
|
||||||
ulong hresult = SendWebSocketHeaders(response);
|
|
||||||
if (hresult != 0)
|
|
||||||
{
|
|
||||||
throw new WebSocketException((int)hresult,
|
|
||||||
SR.GetString(SR.net_WebSockets_NativeSendResponseHeaders,
|
|
||||||
WebSocketHelpers.MethodNames.AcceptWebSocketAsync,
|
|
||||||
hresult));
|
|
||||||
}
|
|
||||||
|
|
||||||
await response.OutputStream.FlushAsync().SuppressContextFlow(); // TODO:??? FlushAsync was never implemented
|
|
||||||
|
|
||||||
HttpResponseStream responseStream = response.OutputStream as HttpResponseStream;
|
|
||||||
Contract.Assert(responseStream != null, "'responseStream' MUST be castable to System.Net.HttpResponseStream.");
|
|
||||||
((HttpResponseStream)response.OutputStream).SwitchToOpaqueMode();
|
|
||||||
HttpRequestStream requestStream = new HttpRequestStream(context);
|
|
||||||
requestStream.SwitchToOpaqueMode();
|
|
||||||
WebSocketHttpListenerDuplexStream webSocketStream =
|
|
||||||
new WebSocketHttpListenerDuplexStream(requestStream, responseStream, context);
|
|
||||||
WebSocket webSocket = WebSocket.CreateServerWebSocket(webSocketStream,
|
|
||||||
subProtocol,
|
|
||||||
receiveBufferSize,
|
|
||||||
keepAliveInterval,
|
|
||||||
internalBuffer);
|
|
||||||
|
|
||||||
webSocketContext = new HttpListenerWebSocketContext(
|
|
||||||
request.Url,
|
|
||||||
request.Headers,
|
|
||||||
request.Cookies,
|
|
||||||
context.User,
|
|
||||||
request.IsAuthenticated,
|
|
||||||
request.IsLocal,
|
|
||||||
request.IsSecureConnection,
|
|
||||||
origin,
|
|
||||||
secWebSocketProtocols.AsReadOnly(),
|
|
||||||
secWebSocketVersion,
|
|
||||||
secWebSocketKey,
|
|
||||||
webSocket);
|
|
||||||
|
|
||||||
if (Logging.On)
|
|
||||||
{
|
|
||||||
Logging.Associate(Logging.WebSockets, context, webSocketContext);
|
|
||||||
Logging.Associate(Logging.WebSockets, webSocketContext, webSocket);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
// TODO:
|
||||||
{
|
// throw new NotImplementedException();
|
||||||
if (Logging.On)
|
return true;
|
||||||
{
|
|
||||||
Logging.Exception(Logging.WebSockets, context, "AcceptWebSocketAsync", ex);
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (Logging.On)
|
|
||||||
{
|
|
||||||
Logging.Exit(Logging.WebSockets, context, "AcceptWebSocketAsync", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return webSocketContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
||||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5354:SHA1CannotBeUsed",
|
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5354:SHA1CannotBeUsed",
|
||||||
Justification = "SHA1 used only for hashing purposes, not for crypto.")]
|
Justification = "SHA1 used only for hashing purposes, not for crypto.")]
|
||||||
internal static string GetSecWebSocketAcceptString(string secWebSocketKey)
|
public static string GetSecWebSocketAcceptString(string secWebSocketKey)
|
||||||
{
|
{
|
||||||
string retVal;
|
string retVal;
|
||||||
#if NET45
|
|
||||||
// SHA1 used only for hashing purposes, not for crypto. Check here for FIPS compat.
|
// SHA1 used only for hashing purposes, not for crypto. Check here for FIPS compat.
|
||||||
using (SHA1 sha1 = SHA1.Create())
|
using (SHA1 sha1 = SHA1.Create())
|
||||||
{
|
{
|
||||||
|
|
@ -202,10 +93,14 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
byte[] toHash = Encoding.UTF8.GetBytes(acceptString);
|
byte[] toHash = Encoding.UTF8.GetBytes(acceptString);
|
||||||
retVal = Convert.ToBase64String(sha1.ComputeHash(toHash));
|
retVal = Convert.ToBase64String(sha1.ComputeHash(toHash));
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return retVal;
|
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)
|
internal static string GetTraceMsgForParameters(int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.InvariantCulture,
|
return string.Format(CultureInfo.InvariantCulture,
|
||||||
|
|
@ -216,11 +111,8 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
}
|
}
|
||||||
|
|
||||||
// return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
|
// return value here signifies if a Sec-WebSocket-Protocol header should be returned by the server.
|
||||||
internal static bool ProcessWebSocketProtocolHeader(string clientSecWebSocketProtocol,
|
public static bool ProcessWebSocketProtocolHeader(string clientSecWebSocketProtocol, string subProtocol)
|
||||||
string subProtocol,
|
|
||||||
out string acceptProtocol)
|
|
||||||
{
|
{
|
||||||
acceptProtocol = string.Empty;
|
|
||||||
if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
|
if (string.IsNullOrEmpty(clientSecWebSocketProtocol))
|
||||||
{
|
{
|
||||||
// client hasn't specified any Sec-WebSocket-Protocol header
|
// client hasn't specified any Sec-WebSocket-Protocol header
|
||||||
|
|
@ -236,10 +128,10 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
|
|
||||||
// here, we know the client specified something and it's non-empty.
|
// here, we know the client specified something and it's non-empty.
|
||||||
|
|
||||||
if (subProtocol == null)
|
if (string.IsNullOrEmpty(subProtocol))
|
||||||
{
|
{
|
||||||
// client specified some protocols, server specified 'null'. So server should send headers.
|
// client specified some protocols, server specified 'null'. So server should send headers.
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// here, we know that the client has specified something, it's not empty
|
// here, we know that the client has specified something, it's not empty
|
||||||
|
|
@ -247,14 +139,13 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
|
|
||||||
string[] requestProtocols = clientSecWebSocketProtocol.Split(new char[] { ',' },
|
string[] requestProtocols = clientSecWebSocketProtocol.Split(new char[] { ',' },
|
||||||
StringSplitOptions.RemoveEmptyEntries);
|
StringSplitOptions.RemoveEmptyEntries);
|
||||||
acceptProtocol = subProtocol;
|
|
||||||
|
|
||||||
// client specified protocols, serverOptions has exactly 1 non-empty entry. Check that
|
// client specified protocols, serverOptions has exactly 1 non-empty entry. Check that
|
||||||
// this exists in the list the client specified.
|
// this exists in the list the client specified.
|
||||||
for (int i = 0; i < requestProtocols.Length; i++)
|
for (int i = 0; i < requestProtocols.Length; i++)
|
||||||
{
|
{
|
||||||
string currentRequestProtocol = requestProtocols[i].Trim();
|
string currentRequestProtocol = requestProtocols[i].Trim();
|
||||||
if (string.Compare(acceptProtocol, currentRequestProtocol, StringComparison.OrdinalIgnoreCase) == 0)
|
if (string.Compare(subProtocol, currentRequestProtocol, StringComparison.OrdinalIgnoreCase) == 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +173,33 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
return task.ConfigureAwait(false);
|
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)
|
internal static void ValidateBuffer(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
|
|
@ -299,57 +217,6 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
throw new ArgumentOutOfRangeException("count");
|
throw new ArgumentOutOfRangeException("count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
private static unsafe ulong SendWebSocketHeaders(HttpListenerResponse response)
|
|
||||||
{
|
|
||||||
return response.SendHeaders(null, null,
|
|
||||||
UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_OPAQUE |
|
|
||||||
UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA |
|
|
||||||
UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
private static void ValidateWebSocketHeaders(HttpListenerContext context)
|
|
||||||
{
|
|
||||||
EnsureHttpSysSupportsWebSockets();
|
|
||||||
|
|
||||||
if (!context.Request.IsWebSocketRequest)
|
|
||||||
{
|
|
||||||
throw new WebSocketException(WebSocketError.NotAWebSocket,
|
|
||||||
SR.GetString(SR.net_WebSockets_AcceptNotAWebSocket,
|
|
||||||
WebSocketHelpers.MethodNames.ValidateWebSocketHeaders,
|
|
||||||
HttpKnownHeaderNames.Connection,
|
|
||||||
HttpKnownHeaderNames.Upgrade,
|
|
||||||
WebSocketHelpers.WebSocketUpgradeToken,
|
|
||||||
context.Request.Headers[HttpKnownHeaderNames.Upgrade]));
|
|
||||||
}
|
|
||||||
|
|
||||||
string secWebSocketVersion = context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
|
||||||
if (string.IsNullOrEmpty(secWebSocketVersion))
|
|
||||||
{
|
|
||||||
throw new WebSocketException(WebSocketError.HeaderError,
|
|
||||||
SR.GetString(SR.net_WebSockets_AcceptHeaderNotFound,
|
|
||||||
WebSocketHelpers.MethodNames.ValidateWebSocketHeaders,
|
|
||||||
HttpKnownHeaderNames.SecWebSocketVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Compare(secWebSocketVersion, WebSocketProtocolComponent.SupportedVersion, StringComparison.OrdinalIgnoreCase) != 0)
|
|
||||||
{
|
|
||||||
throw new WebSocketException(WebSocketError.UnsupportedVersion,
|
|
||||||
SR.GetString(SR.net_WebSockets_AcceptUnsupportedWebSocketVersion,
|
|
||||||
WebSocketHelpers.MethodNames.ValidateWebSocketHeaders,
|
|
||||||
secWebSocketVersion,
|
|
||||||
WebSocketProtocolComponent.SupportedVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey]))
|
|
||||||
{
|
|
||||||
throw new WebSocketException(WebSocketError.HeaderError,
|
|
||||||
SR.GetString(SR.net_WebSockets_AcceptHeaderNotFound,
|
|
||||||
WebSocketHelpers.MethodNames.ValidateWebSocketHeaders,
|
|
||||||
HttpKnownHeaderNames.SecWebSocketKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
internal static void ValidateSubprotocol(string subProtocol)
|
internal static void ValidateSubprotocol(string subProtocol)
|
||||||
{
|
{
|
||||||
|
|
@ -425,7 +292,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ValidateOptions(string subProtocol,
|
public static void ValidateOptions(string subProtocol,
|
||||||
int receiveBufferSize,
|
int receiveBufferSize,
|
||||||
int sendBufferSize,
|
int sendBufferSize,
|
||||||
TimeSpan keepAliveInterval)
|
TimeSpan keepAliveInterval)
|
||||||
|
|
@ -511,7 +378,7 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
throw new PlatformNotSupportedException(SR.GetString(SR.net_WebSockets_UnsupportedPlatform));
|
throw new PlatformNotSupportedException(SR.GetString(SR.net_WebSockets_UnsupportedPlatform));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ValidateArraySegment<T>(ArraySegment<T> arraySegment, string parameterName)
|
public static void ValidateArraySegment<T>(ArraySegment<T> arraySegment, string parameterName)
|
||||||
{
|
{
|
||||||
Contract.Requires(!string.IsNullOrEmpty(parameterName), "'parameterName' MUST NOT be NULL or string.Empty");
|
Contract.Requires(!string.IsNullOrEmpty(parameterName), "'parameterName' MUST NOT be NULL or string.Empty");
|
||||||
|
|
||||||
|
|
@ -529,11 +396,5 @@ namespace Microsoft.AspNet.WebSockets
|
||||||
throw new ArgumentOutOfRangeException(parameterName + ".Count");
|
throw new ArgumentOutOfRangeException(parameterName + ".Count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class MethodNames
|
|
||||||
{
|
|
||||||
internal const string AcceptWebSocketAsync = "AcceptWebSocketAsync";
|
|
||||||
internal const string ValidateWebSocketHeaders = "ValidateWebSocketHeaders";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
// 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.
|
||||||
|
/* TODO:
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.HttpFeature;
|
||||||
|
using Microsoft.Net.WebSockets;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.WebSockets
|
||||||
|
{
|
||||||
|
public class WebSocketMiddleware
|
||||||
|
{
|
||||||
|
private RequestDelegate _next;
|
||||||
|
|
||||||
|
public WebSocketMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
// Detect if an opaque upgrade is available, and if websocket upgrade headers are present.
|
||||||
|
// If so, add a websocket upgrade.
|
||||||
|
var upgradeFeature = context.GetFeature<IHttpOpaqueUpgradeFeature>();
|
||||||
|
if (upgradeFeature != null)
|
||||||
|
{
|
||||||
|
context.SetFeature<IHttpWebSocketFeature>(new UpgradeHandshake(context, upgradeFeature));
|
||||||
|
}
|
||||||
|
|
||||||
|
return _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpgradeHandshake : IHttpWebSocketFeature
|
||||||
|
{
|
||||||
|
private HttpContext _context;
|
||||||
|
private IHttpOpaqueUpgradeFeature _upgrade;
|
||||||
|
|
||||||
|
private string _subProtocol;
|
||||||
|
private int _receiveBufferSize = WebSocketHelpers.DefaultReceiveBufferSize;
|
||||||
|
private TimeSpan _keepAliveInterval = WebSocket.DefaultKeepAliveInterval;
|
||||||
|
private ArraySegment<byte>? _internalBuffer;
|
||||||
|
|
||||||
|
internal UpgradeHandshake(HttpContext context, IHttpOpaqueUpgradeFeature upgrade)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_upgrade = upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsWebSocketRequest
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Headers and values:
|
||||||
|
// Connection: Upgrade
|
||||||
|
// Upgrade: WebSocket
|
||||||
|
// Sec-WebSocket-Version: (WebSocketProtocolComponent.SupportedVersion)
|
||||||
|
// Sec-WebSocket-Key: (hash, see WebSocketHelpers.GetSecWebSocketAcceptString)
|
||||||
|
// Sec-WebSocket-Protocol: (optional, list)
|
||||||
|
IList<string> connectionHeaders = _context.Request.Headers.GetCommaSeparatedValues(HttpKnownHeaderNames.Connection); // "Upgrade, KeepAlive"
|
||||||
|
string upgradeHeader = _context.Request.Headers[HttpKnownHeaderNames.Upgrade];
|
||||||
|
string versionHeader = _context.Request.Headers[HttpKnownHeaderNames.SecWebSocketVersion];
|
||||||
|
string keyHeader = _context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
||||||
|
|
||||||
|
if (connectionHeaders != null && connectionHeaders.Count > 0
|
||||||
|
&& connectionHeaders.Contains(HttpKnownHeaderNames.Upgrade, StringComparer.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(upgradeHeader, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& string.Equals(versionHeader, UnsafeNativeMethods.WebSocketProtocolComponent.SupportedVersion, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.IsNullOrWhiteSpace(keyHeader))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<WebSocket> AcceptAsync(IWebSocketAcceptContext acceptContext)
|
||||||
|
{
|
||||||
|
// Get options
|
||||||
|
if (acceptContext != null)
|
||||||
|
{
|
||||||
|
_subProtocol = acceptContext.SubProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
var advancedAcceptContext = acceptContext as WebSocketAcceptContext;
|
||||||
|
if (advancedAcceptContext != null)
|
||||||
|
{
|
||||||
|
if (advancedAcceptContext.ReceiveBufferSize.HasValue)
|
||||||
|
{
|
||||||
|
_receiveBufferSize = advancedAcceptContext.ReceiveBufferSize.Value;
|
||||||
|
}
|
||||||
|
if (advancedAcceptContext.KeepAliveInterval.HasValue)
|
||||||
|
{
|
||||||
|
_keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value;
|
||||||
|
}
|
||||||
|
_internalBuffer = advancedAcceptContext.Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_internalBuffer.HasValue)
|
||||||
|
{
|
||||||
|
_internalBuffer = WebSocketBuffer.CreateInternalBufferArraySegment(_receiveBufferSize, WebSocketBuffer.MinSendBufferSize, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set WebSocket upgrade response headers
|
||||||
|
|
||||||
|
string outgoingSecWebSocketProtocolString;
|
||||||
|
bool shouldSendSecWebSocketProtocolHeader =
|
||||||
|
WebSocketHelpers.ProcessWebSocketProtocolHeader(
|
||||||
|
_context.Request.Headers[HttpKnownHeaderNames.SecWebSocketProtocol],
|
||||||
|
_subProtocol,
|
||||||
|
out outgoingSecWebSocketProtocolString);
|
||||||
|
|
||||||
|
if (shouldSendSecWebSocketProtocolHeader)
|
||||||
|
{
|
||||||
|
_context.Response.Headers[HttpKnownHeaderNames.SecWebSocketProtocol] = outgoingSecWebSocketProtocolString;
|
||||||
|
}
|
||||||
|
|
||||||
|
string secWebSocketKey = _context.Request.Headers[HttpKnownHeaderNames.SecWebSocketKey];
|
||||||
|
string secWebSocketAccept = WebSocketHelpers.GetSecWebSocketAcceptString(secWebSocketKey);
|
||||||
|
|
||||||
|
_context.Response.Headers[HttpKnownHeaderNames.Connection] = HttpKnownHeaderNames.Upgrade;
|
||||||
|
_context.Response.Headers[HttpKnownHeaderNames.Upgrade] = WebSocketHelpers.WebSocketUpgradeToken;
|
||||||
|
_context.Response.Headers[HttpKnownHeaderNames.SecWebSocketAccept] = secWebSocketAccept;
|
||||||
|
|
||||||
|
// 101 Switching Protocols;
|
||||||
|
Stream opaqueTransport = await _upgrade.UpgradeAsync();
|
||||||
|
return new ServerWebSocket(opaqueTransport, _subProtocol, _receiveBufferSize, _keepAliveInterval, _internalBuffer.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WebSocketAcceptContext : IWebSocketAcceptContext
|
||||||
|
{
|
||||||
|
public string SubProtocol { get; set; }
|
||||||
|
public int? ReceiveBufferSize { get; set; }
|
||||||
|
public TimeSpan? KeepAliveInterval { get; set; }
|
||||||
|
public ArraySegment<byte>? Buffer { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"version": "0.1-alpha-*",
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
|
"compilationOptions" : { "allowUnsafe": true },
|
||||||
|
"configurations": {
|
||||||
|
"net45" : { },
|
||||||
|
"k10" : {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*",
|
||||||
|
"Microsoft.Win32.Primitives": "4.0.0.0",
|
||||||
|
"System.Collections": "4.0.0.0",
|
||||||
|
"System.Collections.Concurrent": "4.0.0.0",
|
||||||
|
"System.Diagnostics.Contracts": "4.0.0.0",
|
||||||
|
"System.Diagnostics.Debug": "4.0.10.0",
|
||||||
|
"System.Diagnostics.Tools": "4.0.0.0",
|
||||||
|
"System.Globalization": "4.0.10.0",
|
||||||
|
"System.IO": "4.0.0.0",
|
||||||
|
"System.Linq": "4.0.0.0",
|
||||||
|
"System.Net.Primitives": "4.0.10.0",
|
||||||
|
"System.Reflection": "4.0.10.0",
|
||||||
|
"System.Resources.ResourceManager": "4.0.0.0",
|
||||||
|
"System.Runtime": "4.0.20.0",
|
||||||
|
"System.Runtime.Extensions": "4.0.10.0",
|
||||||
|
"System.Runtime.Handles": "4.0.0.0",
|
||||||
|
"System.Runtime.InteropServices": "4.0.20.0",
|
||||||
|
"System.Security.Cryptography.HashAlgorithms.SHA1": "4.0.0.0",
|
||||||
|
"System.Security.Principal": "4.0.0.0",
|
||||||
|
"System.Text.Encoding": "4.0.20.0",
|
||||||
|
"System.Text.Encoding.Extensions": "4.0.10.0",
|
||||||
|
"System.Threading": "4.0.0.0",
|
||||||
|
"System.Threading.Overlapped": "4.0.0.0",
|
||||||
|
"System.Threading.Tasks": "4.0.10.0",
|
||||||
|
"System.Threading.Timer": "4.0.0.0",
|
||||||
|
"System.Threading.ThreadPool": "4.0.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue