From b9d7561bf9a4036b2ab809d5a2ab9208526b0758 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Thu, 12 Jun 2014 15:04:56 -0700 Subject: [PATCH] Enable WebSocket and Opaque features. --- src/Microsoft.AspNet.Http/HttpContext.cs | 12 ++++++ src/Microsoft.AspNet.Http/project.json | 1 + .../IHttpOpaqueUpgradeFeature.cs | 13 ++++++ .../IHttpWebSocketFeature.cs | 9 ++-- .../IWebSocketAcceptContext.cs | 13 ++++++ .../Microsoft.AspNet.HttpFeature.kproj | 4 +- src/Microsoft.AspNet.HttpFeature/project.json | 1 + .../DefaultHttpContext.cs | 43 ++++++++++++++++++- .../Infrastructure/Constants.cs | 1 + .../Microsoft.AspNet.PipelineCore.kproj | 3 +- .../WebSocketAcceptContext.cs | 12 ++++++ .../project.json | 1 + .../OwinEnvironmentTests.cs | 16 +++++++ 13 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.AspNet.HttpFeature/IHttpOpaqueUpgradeFeature.cs create mode 100644 src/Microsoft.AspNet.HttpFeature/IWebSocketAcceptContext.cs create mode 100644 src/Microsoft.AspNet.PipelineCore/WebSocketAcceptContext.cs diff --git a/src/Microsoft.AspNet.Http/HttpContext.cs b/src/Microsoft.AspNet.Http/HttpContext.cs index 7bad161454..7bd166d4da 100644 --- a/src/Microsoft.AspNet.Http/HttpContext.cs +++ b/src/Microsoft.AspNet.Http/HttpContext.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.WebSockets; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -27,6 +28,10 @@ namespace Microsoft.AspNet.Http public abstract CancellationToken OnRequestAborted { get; } + public abstract bool IsWebSocketRequest { get; } + + public abstract IList WebSocketRequestedProtocols { get; } + public abstract void Abort(); public abstract void Dispose(); @@ -60,5 +65,12 @@ namespace Microsoft.AspNet.Http } public abstract Task> AuthenticateAsync(IList authenticationTypes); + + public virtual Task AcceptWebSocketAsync() + { + return AcceptWebSocket(subProtocol: null); + } + + public abstract Task AcceptWebSocket(string subProtocol); } } diff --git a/src/Microsoft.AspNet.Http/project.json b/src/Microsoft.AspNet.Http/project.json index f626e3463a..11a017e2d5 100644 --- a/src/Microsoft.AspNet.Http/project.json +++ b/src/Microsoft.AspNet.Http/project.json @@ -5,6 +5,7 @@ "net45": {}, "k10": { "dependencies": { + "Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*", "System.Collections": "4.0.0.0", "System.ComponentModel": "4.0.0.0", "System.Diagnostics.Tools": "4.0.0.0", diff --git a/src/Microsoft.AspNet.HttpFeature/IHttpOpaqueUpgradeFeature.cs b/src/Microsoft.AspNet.HttpFeature/IHttpOpaqueUpgradeFeature.cs new file mode 100644 index 0000000000..f4fb2f4f0f --- /dev/null +++ b/src/Microsoft.AspNet.HttpFeature/IHttpOpaqueUpgradeFeature.cs @@ -0,0 +1,13 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Framework.Runtime; + +namespace Microsoft.AspNet.HttpFeature +{ + [AssemblyNeutral] + public interface IHttpOpaqueUpgradeFeature + { + bool IsUpgradableRequest { get; } + Task UpgradeAsync(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/IHttpWebSocketFeature.cs b/src/Microsoft.AspNet.HttpFeature/IHttpWebSocketFeature.cs index a04a584532..3d87e86bb4 100644 --- a/src/Microsoft.AspNet.HttpFeature/IHttpWebSocketFeature.cs +++ b/src/Microsoft.AspNet.HttpFeature/IHttpWebSocketFeature.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -#if NET45 using System.Net.WebSockets; using System.Threading.Tasks; using Microsoft.Framework.Runtime; @@ -11,8 +10,8 @@ namespace Microsoft.AspNet.HttpFeature [AssemblyNeutral] public interface IHttpWebSocketFeature { - bool IsWebSocketRequest { get; set; } - Task AcceptAsync(); + bool IsWebSocketRequest { get; } + + Task AcceptAsync(IWebSocketAcceptContext context); } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/IWebSocketAcceptContext.cs b/src/Microsoft.AspNet.HttpFeature/IWebSocketAcceptContext.cs new file mode 100644 index 0000000000..dbcc14c7ce --- /dev/null +++ b/src/Microsoft.AspNet.HttpFeature/IWebSocketAcceptContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.Framework.Runtime; + +namespace Microsoft.AspNet.HttpFeature +{ + [AssemblyNeutral] + public interface IWebSocketAcceptContext + { + string SubProtocol { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/Microsoft.AspNet.HttpFeature.kproj b/src/Microsoft.AspNet.HttpFeature/Microsoft.AspNet.HttpFeature.kproj index 38f5ea72e7..96237c3118 100644 --- a/src/Microsoft.AspNet.HttpFeature/Microsoft.AspNet.HttpFeature.kproj +++ b/src/Microsoft.AspNet.HttpFeature/Microsoft.AspNet.HttpFeature.kproj @@ -24,12 +24,14 @@ + + @@ -39,4 +41,4 @@ - + \ No newline at end of file diff --git a/src/Microsoft.AspNet.HttpFeature/project.json b/src/Microsoft.AspNet.HttpFeature/project.json index 7060443fce..2fa40105ad 100644 --- a/src/Microsoft.AspNet.HttpFeature/project.json +++ b/src/Microsoft.AspNet.HttpFeature/project.json @@ -4,6 +4,7 @@ "net45": {}, "k10": { "dependencies": { + "Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*", "System.IO": "4.0.0.0", "System.Net.Primitives": "4.0.10.0", "System.Runtime": "4.0.20.0", diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs index a5c559f112..4a8b059972 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpContext.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.WebSockets; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNet.Http; -using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Infrastructure; +using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.HttpFeature.Security; using Microsoft.AspNet.PipelineCore.Infrastructure; @@ -19,6 +21,8 @@ namespace Microsoft.AspNet.PipelineCore { public class DefaultHttpContext : HttpContext { + private static IList EmptyList = new List(); + private readonly HttpRequest _request; private readonly HttpResponse _response; @@ -26,6 +30,7 @@ namespace Microsoft.AspNet.PipelineCore private FeatureReference _serviceProviders; private FeatureReference _authentication; private FeatureReference _lifetime; + private FeatureReference _webSockets; private IFeatureCollection _features; public DefaultHttpContext(IFeatureCollection features) @@ -37,6 +42,8 @@ namespace Microsoft.AspNet.PipelineCore _items = FeatureReference.Default; _serviceProviders = FeatureReference.Default; _authentication = FeatureReference.Default; + _lifetime = FeatureReference.Default; + _webSockets = FeatureReference.Default; } IItemsFeature ItemsFeature @@ -59,6 +66,11 @@ namespace Microsoft.AspNet.PipelineCore get { return _lifetime.Fetch(_features); } } + private IHttpWebSocketFeature WebSocketFeature + { + get { return _webSockets.Fetch(_features); } + } + public override HttpRequest Request { get { return _request; } } public override HttpResponse Response { get { return _response; } } @@ -110,6 +122,23 @@ namespace Microsoft.AspNet.PipelineCore } } + public override bool IsWebSocketRequest + { + get + { + var webSocketFeature = WebSocketFeature; + return webSocketFeature != null && webSocketFeature.IsWebSocketRequest; + } + } + + public override IList WebSocketRequestedProtocols + { + get + { + return Request.Headers.GetValues(Constants.Headers.WebSocketSubProtocols) ?? EmptyList; + } + } + public override void Abort() { var lifetime = LifetimeFeature; @@ -196,5 +225,15 @@ namespace Microsoft.AspNet.PipelineCore return authenticateContext.Results; } + + public override Task AcceptWebSocket(string subProtocol) + { + var webSocketFeature = WebSocketFeature; + if (WebSocketFeature == null) + { + throw new NotSupportedException("WebSockets are not supported"); + } + return WebSocketFeature.AcceptAsync(new WebSocketAcceptContext() { SubProtocol = subProtocol } ); + } } } diff --git a/src/Microsoft.AspNet.PipelineCore/Infrastructure/Constants.cs b/src/Microsoft.AspNet.PipelineCore/Infrastructure/Constants.cs index e0cf75c268..1f440b0bf1 100644 --- a/src/Microsoft.AspNet.PipelineCore/Infrastructure/Constants.cs +++ b/src/Microsoft.AspNet.PipelineCore/Infrastructure/Constants.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Http.Infrastructure internal const string Cookie = "Cookie"; internal const string SetCookie = "Set-Cookie"; internal const string Expires = "Expires"; + internal const string WebSocketSubProtocols = "Sec-WebSocket-Protocol"; } } } diff --git a/src/Microsoft.AspNet.PipelineCore/Microsoft.AspNet.PipelineCore.kproj b/src/Microsoft.AspNet.PipelineCore/Microsoft.AspNet.PipelineCore.kproj index 957d775894..05dd7de5dd 100644 --- a/src/Microsoft.AspNet.PipelineCore/Microsoft.AspNet.PipelineCore.kproj +++ b/src/Microsoft.AspNet.PipelineCore/Microsoft.AspNet.PipelineCore.kproj @@ -51,6 +51,7 @@ + - + \ No newline at end of file diff --git a/src/Microsoft.AspNet.PipelineCore/WebSocketAcceptContext.cs b/src/Microsoft.AspNet.PipelineCore/WebSocketAcceptContext.cs new file mode 100644 index 0000000000..6e9a7ba0dd --- /dev/null +++ b/src/Microsoft.AspNet.PipelineCore/WebSocketAcceptContext.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.HttpFeature; + +namespace Microsoft.AspNet.PipelineCore +{ + public class WebSocketAcceptContext : IWebSocketAcceptContext + { + public virtual string SubProtocol { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.PipelineCore/project.json b/src/Microsoft.AspNet.PipelineCore/project.json index 3af3011dda..0c280000e8 100644 --- a/src/Microsoft.AspNet.PipelineCore/project.json +++ b/src/Microsoft.AspNet.PipelineCore/project.json @@ -10,6 +10,7 @@ "net45": {}, "k10": { "dependencies": { + "Microsoft.Net.WebSocketAbstractions": "0.1-alpha-*", "System.Collections": "4.0.0.0", "System.ComponentModel": "4.0.0.0", "System.Diagnostics.Debug": "4.0.10.0", diff --git a/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs index 5cd6230df6..87ae8aeb28 100644 --- a/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs +++ b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.WebSockets; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -113,10 +114,25 @@ namespace Microsoft.AspNet.Owin get { throw new NotImplementedException(); } } + public override bool IsWebSocketRequest + { + get { throw new NotImplementedException(); } + } + + public override IList WebSocketRequestedProtocols + { + get { throw new NotImplementedException(); } + } + public override void Abort() { throw new NotImplementedException(); } + + public override Task AcceptWebSocket(string subProtocol) + { + throw new NotImplementedException(); + } } private class MoqHttpRequest : HttpRequest, IHttpRequestFeature