From a04d592d06ddf7f7add921f19ebc01bf78165dc8 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Mon, 21 Apr 2014 16:26:15 -0700 Subject: [PATCH] OWIN->K and K->OWIN support via Func. --- HttpAbstractions.sln | 37 +- .../ICanHasOwinEnvironment.cs | 9 + .../Microsoft.AspNet.Owin.kproj | 31 ++ src/Microsoft.AspNet.Owin/OwinConstants.cs | 175 +++++++++ src/Microsoft.AspNet.Owin/OwinEnvironment.cs | 269 +++++++++++++ src/Microsoft.AspNet.Owin/OwinExtensions.cs | 30 ++ .../OwinFeatureCollection.cs | 368 ++++++++++++++++++ .../OwinMiddlewareFactory.cs | 50 +++ src/Microsoft.AspNet.Owin/Project.json | 30 ++ .../Microsoft.AspNet.Owin.Tests.kproj | 27 ++ .../OwinEnvironmentTests.cs | 226 +++++++++++ .../OwinFeatureCollectionTests.cs | 47 +++ test/Microsoft.AspNet.Owin.Tests/Project.json | 26 ++ 13 files changed, 1324 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.AspNet.Owin/ICanHasOwinEnvironment.cs create mode 100644 src/Microsoft.AspNet.Owin/Microsoft.AspNet.Owin.kproj create mode 100644 src/Microsoft.AspNet.Owin/OwinConstants.cs create mode 100644 src/Microsoft.AspNet.Owin/OwinEnvironment.cs create mode 100644 src/Microsoft.AspNet.Owin/OwinExtensions.cs create mode 100644 src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs create mode 100644 src/Microsoft.AspNet.Owin/OwinMiddlewareFactory.cs create mode 100644 src/Microsoft.AspNet.Owin/Project.json create mode 100644 test/Microsoft.AspNet.Owin.Tests/Microsoft.AspNet.Owin.Tests.kproj create mode 100644 test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs create mode 100644 test/Microsoft.AspNet.Owin.Tests/OwinFeatureCollectionTests.cs create mode 100644 test/Microsoft.AspNet.Owin.Tests/Project.json diff --git a/HttpAbstractions.sln b/HttpAbstractions.sln index 83fd260c8c..c7f07227ab 100644 --- a/HttpAbstractions.sln +++ b/HttpAbstractions.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30327.0 +VisualStudioVersion = 12.0.30401.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A5A15F1C-885A-452A-A731-B0173DDBD913}" EndProject @@ -21,6 +21,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.FeatureMod EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Abstractions.Tests", "test\Microsoft.AspNet.Abstractions.Tests\Microsoft.AspNet.Abstractions.Tests.kproj", "{F16692B8-9F38-4DCA-A582-E43172B989C6}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Owin", "src\Microsoft.AspNet.Owin\Microsoft.AspNet.Owin.kproj", "{59BED991-F207-48ED-B24C-0A1D9C986C01}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Owin.Tests", "test\Microsoft.AspNet.Owin.Tests\Microsoft.AspNet.Owin.Tests.kproj", "{16219571-3268-4D12-8689-12B7163DBA13}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +36,7 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Debug|Any CPU.ActiveCfg = Debug|x86 + {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Debug|Any CPU.Build.0 = Debug|x86 {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Debug|Mixed Platforms.Build.0 = Debug|x86 {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Debug|x86.ActiveCfg = Debug|x86 @@ -42,6 +47,7 @@ Global {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Release|x86.ActiveCfg = Release|x86 {BCF0F967-8753-4438-BD07-AADCA9CE509A}.Release|x86.Build.0 = Release|x86 {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Debug|Any CPU.ActiveCfg = Debug|x86 + {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Debug|Any CPU.Build.0 = Debug|x86 {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Debug|Mixed Platforms.Build.0 = Debug|x86 {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Debug|x86.ActiveCfg = Debug|x86 @@ -52,6 +58,7 @@ Global {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Release|x86.ActiveCfg = Release|x86 {22071333-15BA-4D16-A1D5-4D5B1A83FBDD}.Release|x86.Build.0 = Release|x86 {D9128247-8F97-48B8-A863-F1F21A029FCE}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D9128247-8F97-48B8-A863-F1F21A029FCE}.Debug|Any CPU.Build.0 = Debug|x86 {D9128247-8F97-48B8-A863-F1F21A029FCE}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {D9128247-8F97-48B8-A863-F1F21A029FCE}.Debug|Mixed Platforms.Build.0 = Debug|x86 {D9128247-8F97-48B8-A863-F1F21A029FCE}.Debug|x86.ActiveCfg = Debug|x86 @@ -62,6 +69,7 @@ Global {D9128247-8F97-48B8-A863-F1F21A029FCE}.Release|x86.ActiveCfg = Release|x86 {D9128247-8F97-48B8-A863-F1F21A029FCE}.Release|x86.Build.0 = Release|x86 {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Debug|Any CPU.ActiveCfg = Debug|x86 + {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Debug|Any CPU.Build.0 = Debug|x86 {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Debug|Mixed Platforms.Build.0 = Debug|x86 {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Debug|x86.ActiveCfg = Debug|x86 @@ -72,6 +80,7 @@ Global {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Release|x86.ActiveCfg = Release|x86 {32A4C918-30EE-41DB-8E26-8A3BB88ED231}.Release|x86.Build.0 = Release|x86 {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Debug|Any CPU.ActiveCfg = Debug|x86 + {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Debug|Any CPU.Build.0 = Debug|x86 {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Debug|Mixed Platforms.Build.0 = Debug|x86 {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Debug|x86.ActiveCfg = Debug|x86 @@ -82,6 +91,7 @@ Global {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Release|x86.ActiveCfg = Release|x86 {AA99AF26-F7B1-4A6B-A922-5C25539F6391}.Release|x86.Build.0 = Release|x86 {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Debug|Any CPU.ActiveCfg = Debug|x86 + {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Debug|Any CPU.Build.0 = Debug|x86 {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Debug|Mixed Platforms.Build.0 = Debug|x86 {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Debug|x86.ActiveCfg = Debug|x86 @@ -92,6 +102,7 @@ Global {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Release|x86.ActiveCfg = Release|x86 {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7}.Release|x86.Build.0 = Release|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Debug|Any CPU.ActiveCfg = Debug|x86 + {F16692B8-9F38-4DCA-A582-E43172B989C6}.Debug|Any CPU.Build.0 = Debug|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Debug|Mixed Platforms.Build.0 = Debug|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Debug|x86.ActiveCfg = Debug|x86 @@ -101,6 +112,28 @@ Global {F16692B8-9F38-4DCA-A582-E43172B989C6}.Release|Mixed Platforms.Build.0 = Release|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Release|x86.ActiveCfg = Release|x86 {F16692B8-9F38-4DCA-A582-E43172B989C6}.Release|x86.Build.0 = Release|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|Any CPU.ActiveCfg = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|Any CPU.Build.0 = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|x86.ActiveCfg = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Debug|x86.Build.0 = Debug|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Release|Any CPU.ActiveCfg = Release|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Release|Mixed Platforms.Build.0 = Release|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Release|x86.ActiveCfg = Release|x86 + {59BED991-F207-48ED-B24C-0A1D9C986C01}.Release|x86.Build.0 = Release|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|Any CPU.ActiveCfg = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|Any CPU.Build.0 = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|x86.ActiveCfg = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Debug|x86.Build.0 = Debug|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Release|Any CPU.ActiveCfg = Release|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Release|Mixed Platforms.Build.0 = Release|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Release|x86.ActiveCfg = Release|x86 + {16219571-3268-4D12-8689-12B7163DBA13}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -113,5 +146,7 @@ Global {AA99AF26-F7B1-4A6B-A922-5C25539F6391} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} {C5D2BAE1-E182-48A0-AA74-1AF14B782BF7} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} {F16692B8-9F38-4DCA-A582-E43172B989C6} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} + {59BED991-F207-48ED-B24C-0A1D9C986C01} = {A5A15F1C-885A-452A-A731-B0173DDBD913} + {16219571-3268-4D12-8689-12B7163DBA13} = {F31FF137-390C-49BF-A3BD-7C6ED3597C21} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Owin/ICanHasOwinEnvironment.cs b/src/Microsoft.AspNet.Owin/ICanHasOwinEnvironment.cs new file mode 100644 index 0000000000..49218e1115 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/ICanHasOwinEnvironment.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Microsoft.AspNet.Owin +{ + public interface ICanHasOwinEnvironment + { + IDictionary Environment { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Owin/Microsoft.AspNet.Owin.kproj b/src/Microsoft.AspNet.Owin/Microsoft.AspNet.Owin.kproj new file mode 100644 index 0000000000..fb37b90152 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/Microsoft.AspNet.Owin.kproj @@ -0,0 +1,31 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 59bed991-f207-48ed-b24c-0a1d9c986c01 + Library + + + + + + + 2.0 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Owin/OwinConstants.cs b/src/Microsoft.AspNet.Owin/OwinConstants.cs new file mode 100644 index 0000000000..a54f04cccf --- /dev/null +++ b/src/Microsoft.AspNet.Owin/OwinConstants.cs @@ -0,0 +1,175 @@ +namespace Microsoft.AspNet.Owin +{ + internal static class OwinConstants + { + #region OWIN v1.0.0 - 3.2.1. Request Data + + // http://owin.org/spec/owin-1.0.0.html + + public const string RequestScheme = "owin.RequestScheme"; + public const string RequestMethod = "owin.RequestMethod"; + public const string RequestPathBase = "owin.RequestPathBase"; + public const string RequestPath = "owin.RequestPath"; + public const string RequestQueryString = "owin.RequestQueryString"; + public const string RequestProtocol = "owin.RequestProtocol"; + public const string RequestHeaders = "owin.RequestHeaders"; + public const string RequestBody = "owin.RequestBody"; + + #endregion + + #region OWIN v1.0.0 - 3.2.2. Response Data + + // http://owin.org/spec/owin-1.0.0.html + + public const string ResponseStatusCode = "owin.ResponseStatusCode"; + public const string ResponseReasonPhrase = "owin.ResponseReasonPhrase"; + public const string ResponseProtocol = "owin.ResponseProtocol"; + public const string ResponseHeaders = "owin.ResponseHeaders"; + public const string ResponseBody = "owin.ResponseBody"; + + #endregion + + #region OWIN v1.0.0 - 3.2.3. Other Data + + // http://owin.org/spec/owin-1.0.0.html + + public const string CallCancelled = "owin.CallCancelled"; + + public const string OwinVersion = "owin.Version"; + + #endregion + + #region OWIN Keys for IAppBuilder.Properties + + internal static class Builder + { + public const string AddSignatureConversion = "builder.AddSignatureConversion"; + public const string DefaultApp = "builder.DefaultApp"; + } + + #endregion + + #region OWIN Key Guidelines and Common Keys - 6. Common keys + + // http://owin.org/spec/CommonKeys.html + + internal static class CommonKeys + { + public const string ClientCertificate = "ssl.ClientCertificate"; + public const string LoadClientCertAsync = "ssl.LoadClientCertAsync"; + public const string RemoteIpAddress = "server.RemoteIpAddress"; + public const string RemotePort = "server.RemotePort"; + public const string LocalIpAddress = "server.LocalIpAddress"; + public const string LocalPort = "server.LocalPort"; + public const string IsLocal = "server.IsLocal"; + public const string TraceOutput = "host.TraceOutput"; + public const string Addresses = "host.Addresses"; + public const string AppName = "host.AppName"; + public const string Capabilities = "server.Capabilities"; + public const string OnSendingHeaders = "server.OnSendingHeaders"; + public const string OnAppDisposing = "host.OnAppDisposing"; + public const string Scheme = "scheme"; + public const string Host = "host"; + public const string Port = "port"; + public const string Path = "path"; + } + + #endregion + + #region SendFiles v0.3.0 + + // http://owin.org/extensions/owin-SendFile-Extension-v0.3.0.htm + + internal static class SendFiles + { + // 3.1. Startup + + public const string Version = "sendfile.Version"; + public const string Support = "sendfile.Support"; + public const string Concurrency = "sendfile.Concurrency"; + + // 3.2. Per Request + + public const string SendAsync = "sendfile.SendAsync"; + } + + #endregion + + #region Opaque v0.3.0 + + // http://owin.org/extensions/owin-OpaqueStream-Extension-v0.3.0.htm + + internal static class OpaqueConstants + { + // 3.1. Startup + + public const string Version = "opaque.Version"; + + // 3.2. Per Request + + public const string Upgrade = "opaque.Upgrade"; + + // 5. Consumption + + public const string Stream = "opaque.Stream"; + // public const string Version = "opaque.Version"; // redundant, declared above + public const string CallCancelled = "opaque.CallCancelled"; + } + + #endregion + + #region WebSocket v0.4.0 + + // http://owin.org/extensions/owin-OpaqueStream-Extension-v0.3.0.htm + + internal static class WebSocket + { + // 3.1. Startup + + public const string Version = "websocket.Version"; + + // 3.2. Per Request + + public const string Accept = "websocket.Accept"; + + // 4. Accept + + public const string SubProtocol = "websocket.SubProtocol"; + + // 5. Consumption + + public const string SendAsync = "websocket.SendAsync"; + public const string ReceiveAsync = "websocket.ReceiveAsync"; + public const string CloseAsync = "websocket.CloseAsync"; + // public const string Version = "websocket.Version"; // redundant, declared above + public const string CallCancelled = "websocket.CallCancelled"; + public const string ClientCloseStatus = "websocket.ClientCloseStatus"; + public const string ClientCloseDescription = "websocket.ClientCloseDescription"; + } + + #endregion + + #region Security v0.1.0 + + // http://owin.org/extensions/owin-Security-Extension-v0.1.0.htm + + internal static class Security + { + // 3.2. Per Request + + public const string User = "server.User"; + + public const string Authenticate = "security.Authenticate"; + + // 3.3. Response + + public const string SignIn = "security.SignIn"; + + public const string SignOut = "security.SignOut"; + + public const string Challenge = "security.Challenge"; + } + + #endregion + } +} diff --git a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs new file mode 100644 index 0000000000..157d98ba86 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.HttpFeature; + +namespace Microsoft.AspNet.Owin +{ + using SendFileFunc = Func; + + public class OwinEnvironment : IDictionary + { + private HttpContext _context; + private IDictionary _entries; + + public OwinEnvironment(HttpContext context) + { + _context = context; + _entries = new Dictionary() + { + { OwinConstants.RequestProtocol, new FeatureMap(feature => feature.Protocol, (feature, value) => feature.Protocol = Convert.ToString(value)) }, + { OwinConstants.RequestScheme, new FeatureMap(feature => feature.Scheme, (feature, value) => feature.Scheme = Convert.ToString(value)) }, + { OwinConstants.RequestMethod, new FeatureMap(feature => feature.Method, (feature, value) => feature.Method = Convert.ToString(value)) }, + { OwinConstants.RequestPathBase, new FeatureMap(feature => feature.PathBase, (feature, value) => feature.PathBase = Convert.ToString(value)) }, + { OwinConstants.RequestPath, new FeatureMap(feature => feature.Path, (feature, value) => feature.Path = Convert.ToString(value)) }, + { OwinConstants.RequestQueryString, new FeatureMap(feature => feature.QueryString, (feature, value) => feature.QueryString = Convert.ToString(value)) }, + { OwinConstants.RequestHeaders, new FeatureMap(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary)value) }, + { OwinConstants.RequestBody, new FeatureMap(feature => feature.Body, (feature, value) => feature.Body = (Stream)value) }, + + { OwinConstants.ResponseStatusCode, new FeatureMap(feature => feature.StatusCode, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) }, + { OwinConstants.ResponseReasonPhrase, new FeatureMap(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) }, + { OwinConstants.ResponseHeaders, new FeatureMap(feature => feature.Headers, (feature, value) => feature.Headers = (IDictionary)value) }, + { OwinConstants.ResponseBody, new FeatureMap(feature => feature.Body, (feature, value) => feature.Body = (Stream)value) }, + { OwinConstants.CommonKeys.OnSendingHeaders, new FeatureMap(feature => new Action, object>(feature.OnSendingHeaders)) }, + + { OwinConstants.CommonKeys.LocalPort, new FeatureMap(feature => feature.LocalPort.ToString(CultureInfo.InvariantCulture), + (feature, value) => feature.LocalPort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) }, + { OwinConstants.CommonKeys.RemotePort, new FeatureMap(feature => feature.RemotePort.ToString(CultureInfo.InvariantCulture), + (feature, value) => feature.RemotePort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) }, + + { OwinConstants.CommonKeys.LocalIpAddress, new FeatureMap(feature => feature.LocalIpAddress.ToString(), + (feature, value) => feature.LocalIpAddress = IPAddress.Parse(Convert.ToString(value))) }, + { OwinConstants.CommonKeys.RemoteIpAddress, new FeatureMap(feature => feature.RemoteIpAddress.ToString(), + (feature, value) => feature.RemoteIpAddress = IPAddress.Parse(Convert.ToString(value))) }, + + { OwinConstants.CommonKeys.IsLocal, new FeatureMap(feature => feature.IsLocal, (feature, value) => feature.IsLocal = Convert.ToBoolean(value)) }, + + { OwinConstants.SendFiles.SendAsync, new FeatureMap(feature => new SendFileFunc(feature.SendFileAsync)) }, + }; + + if (context.Request.IsSecure) + { + _entries.Add(OwinConstants.CommonKeys.ClientCertificate, new FeatureMap(feature => feature.ClientCertificate, + (feature, value) => feature.ClientCertificate = (X509Certificate)value)); + _entries.Add(OwinConstants.CommonKeys.LoadClientCertAsync, new FeatureMap(feature => new Func(feature.LoadAsync))); + } + + _context.Items[typeof(HttpContext).FullName] = _context; // Store for lookup when we transition back out of OWIN + } + + // Public in case there's a new/custom feature interface that needs to be added. + public IDictionary FeatureMaps + { + get { return _entries; } + } + + void IDictionary.Add(string key, object value) + { + if (_entries.ContainsKey(key)) + { + throw new InvalidOperationException("Key already present"); + } + _context.Items.Add(key, value); + } + + bool IDictionary.ContainsKey(string key) + { + return _entries.ContainsKey(key) || _context.Items.ContainsKey(key); + } + + ICollection IDictionary.Keys + { + get + { + return _entries.Keys.Concat(_context.Items.Keys.Select(key => Convert.ToString(key))).ToList(); + } + } + + bool IDictionary.Remove(string key) + { + if (_entries.Remove(key)) + { + return true; + } + return _context.Items.Remove(key); + } + + bool IDictionary.TryGetValue(string key, out object value) + { + FeatureMap entry; + if (_entries.TryGetValue(key, out entry)) + { + value = entry.Get(_context); + return true; + } + return _context.Items.TryGetValue(key, out value); + } + + ICollection IDictionary.Values + { + get { throw new NotImplementedException(); } + } + + object IDictionary.this[string key] + { + get + { + FeatureMap entry; + if (_entries.TryGetValue(key, out entry)) + { + return entry.Get(_context); + } + object value; + if (_context.Items.TryGetValue(key, out value)) + { + return value; + } + throw new KeyNotFoundException(key); + } + set + { + FeatureMap entry; + if (_entries.TryGetValue(key, out entry)) + { + if (entry.Setter == null) + { + _entries.Remove(key); + if (value != null) + { + _context.Items[key] = value; + } + } + else + { + entry.Setter(_context.GetFeature(entry.FeatureInterface), value); + } + } + else + { + if (value == null) + { + _context.Items.Remove(key); + } + else + { + _context.Items[key] = value; + } + } + } + } + + void ICollection>.Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void ICollection>.Clear() + { + _entries.Clear(); + _context.Items.Clear(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + int ICollection>.Count + { + get { return _entries.Count + _context.Items.Count; } + } + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + foreach (var entryPair in _entries) + { + yield return new KeyValuePair(entryPair.Key, entryPair.Value.Get(_context)); + } + foreach (var entryPair in _context.Items) + { + yield return new KeyValuePair(Convert.ToString(entryPair.Key), entryPair.Value); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public class FeatureMap + { + internal FeatureMap(Type featureInterface, Func getter) + : this(featureInterface, getter, null) + { + } + + internal FeatureMap(Type featureInterface, Func getter, Action setter) + { + FeatureInterface = featureInterface; + Getter = getter; + Setter = setter; + } + + internal Type FeatureInterface { get; set; } + internal Func Getter { get; set; } + internal Action Setter { get; set; } + + internal object Get(HttpContext context) + { + object featureInstance = context.GetFeature(FeatureInterface); + if (featureInstance == null) + { + return null; + } + return Getter(featureInstance); + } + + internal void Set(HttpContext context, object value) + { + Setter(context.GetFeature(FeatureInterface), value); + } + } + + public class FeatureMap : FeatureMap + { + internal FeatureMap(Func getter) + : base(typeof(T), feature => getter((T)feature)) + { + } + + internal FeatureMap(Func getter, Action setter) + : base(typeof(T), feature => getter((T)feature), (feature, value) => setter((T)feature, value)) + { + } + } + } +} diff --git a/src/Microsoft.AspNet.Owin/OwinExtensions.cs b/src/Microsoft.AspNet.Owin/OwinExtensions.cs new file mode 100644 index 0000000000..135973e654 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/OwinExtensions.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Owin; + +namespace Microsoft.AspNet +{ + using AppFunc = Func, Task>; + + public static class OwinExtensions + { + public static IBuilder UseOwinMiddleware(this IBuilder builder, Func middleware) + { + Func middleware1 = next1 => + { + AppFunc exitMiddlware = env => + { + return next1((HttpContext)env[typeof(HttpContext).FullName]); + }; + var app = middleware(exitMiddlware); + return httpContext => + { + return app.Invoke(new OwinEnvironment(httpContext)); + }; + }; + return builder.Use(middleware1); + } + } +} diff --git a/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs b/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs new file mode 100644 index 0000000000..8dc7f3422f --- /dev/null +++ b/src/Microsoft.AspNet.Owin/OwinFeatureCollection.cs @@ -0,0 +1,368 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.HttpFeature; +using Microsoft.AspNet.FeatureModel; + +namespace Microsoft.AspNet.Owin +{ + using SendFileFunc = Func; + + public class OwinFeatureCollection : + IFeatureCollection, + IHttpRequestInformation, + IHttpResponseInformation, + IHttpConnection, + IHttpSendFile, + IHttpTransportLayerSecurity, + ICanHasOwinEnvironment + { + public IDictionary Environment { get; set; } + + public OwinFeatureCollection(IDictionary environment) + { + Environment = environment; + } + + T Prop(string key) + { + object value; + if (Environment.TryGetValue(key, out value) && value is T) + { + return (T)value; + } + return default(T); + } + + void Prop(string key, object value) + { + Environment[key] = value; + } + + string IHttpRequestInformation.Protocol + { + get { return Prop(OwinConstants.RequestProtocol); } + set { Prop(OwinConstants.RequestProtocol, value); } + } + + string IHttpRequestInformation.Scheme + { + get { return Prop(OwinConstants.RequestScheme); } + set { Prop(OwinConstants.RequestScheme, value); } + } + + string IHttpRequestInformation.Method + { + get { return Prop(OwinConstants.RequestMethod); } + set { Prop(OwinConstants.RequestMethod, value); } + } + + string IHttpRequestInformation.PathBase + { + get { return Prop(OwinConstants.RequestPathBase); } + set { Prop(OwinConstants.RequestPathBase, value); } + } + + string IHttpRequestInformation.Path + { + get { return Prop(OwinConstants.RequestPath); } + set { Prop(OwinConstants.RequestPath, value); } + } + + string IHttpRequestInformation.QueryString + { + get { return Prop(OwinConstants.RequestQueryString); } + set { Prop(OwinConstants.RequestQueryString, value); } + } + + IDictionary IHttpRequestInformation.Headers + { + get { return Prop>(OwinConstants.RequestHeaders); } + set { Prop(OwinConstants.RequestHeaders, value); } + } + + Stream IHttpRequestInformation.Body + { + get { return Prop(OwinConstants.RequestBody); } + set { Prop(OwinConstants.RequestBody, value); } + } + + int IHttpResponseInformation.StatusCode + { + get { return Prop(OwinConstants.ResponseStatusCode); } + set { Prop(OwinConstants.ResponseStatusCode, value); } + } + + string IHttpResponseInformation.ReasonPhrase + { + get { return Prop(OwinConstants.ResponseReasonPhrase); } + set { Prop(OwinConstants.ResponseReasonPhrase, value); } + } + + IDictionary IHttpResponseInformation.Headers + { + get { return Prop>(OwinConstants.ResponseHeaders); } + set { Prop(OwinConstants.ResponseHeaders, value); } + } + + Stream IHttpResponseInformation.Body + { + get { return Prop(OwinConstants.ResponseBody); } + set { Prop(OwinConstants.ResponseBody, value); } + } + + void IHttpResponseInformation.OnSendingHeaders(Action callback, object state) + { + var register = Prop, object>>(OwinConstants.CommonKeys.OnSendingHeaders); + if (register == null) + { + throw new NotSupportedException(OwinConstants.CommonKeys.OnSendingHeaders); + } + register(callback, state); + } + + IPAddress IHttpConnection.RemoteIpAddress + { + get { return IPAddress.Parse(Prop(OwinConstants.CommonKeys.RemoteIpAddress)); } + set { Prop(OwinConstants.CommonKeys.RemoteIpAddress, value.ToString()); } + } + + IPAddress IHttpConnection.LocalIpAddress + { + get { return IPAddress.Parse(Prop(OwinConstants.CommonKeys.LocalIpAddress)); } + set { Prop(OwinConstants.CommonKeys.LocalIpAddress, value.ToString()); } + } + + int IHttpConnection.RemotePort + { + get { return int.Parse(Prop(OwinConstants.CommonKeys.RemotePort)); } + set { Prop(OwinConstants.CommonKeys.RemotePort, value.ToString(CultureInfo.InvariantCulture)); } + } + + int IHttpConnection.LocalPort + { + get { return int.Parse(Prop(OwinConstants.CommonKeys.LocalPort)); } + set { Prop(OwinConstants.CommonKeys.LocalPort, value.ToString(CultureInfo.InvariantCulture)); } + } + + bool IHttpConnection.IsLocal + { + get { return Prop(OwinConstants.CommonKeys.IsLocal); } + set { Prop(OwinConstants.CommonKeys.LocalPort, value); } + } + + private bool SupportsSendFile + { + get + { + object obj; + return Environment.TryGetValue(OwinConstants.SendFiles.SendAsync, out obj) && obj != null; + } + } + + Task IHttpSendFile.SendFileAsync(string path, long offset, long? length, CancellationToken cancellation) + { + object obj; + if (Environment.TryGetValue(OwinConstants.SendFiles.SendAsync, out obj)) + { + var func = (SendFileFunc)obj; + return func(path, offset, length, cancellation); + } + throw new NotSupportedException(OwinConstants.SendFiles.SendAsync); + } + + private bool SupportsClientCerts + { + get + { + object obj; + if (string.Equals("https", ((IHttpRequestInformation)this).Scheme, StringComparison.OrdinalIgnoreCase) + && (Environment.TryGetValue(OwinConstants.CommonKeys.LoadClientCertAsync, out obj) + || Environment.TryGetValue(OwinConstants.CommonKeys.ClientCertificate, out obj)) + && obj != null) + { + return true; + } + return false; + } + } + + X509Certificate IHttpTransportLayerSecurity.ClientCertificate + { + get { return Prop(OwinConstants.CommonKeys.ClientCertificate); } + set { Prop(OwinConstants.CommonKeys.ClientCertificate, value); } + } + + Task IHttpTransportLayerSecurity.LoadAsync() + { + throw new NotImplementedException(); + } + + public int Revision + { + get { return 0; } // Not modifiable + } + + public void Add(Type key, object value) + { + throw new NotSupportedException(); + } + + public bool ContainsKey(Type key) + { + // Does this type implement the requested interface? + if (key.GetTypeInfo().IsAssignableFrom(this.GetType().GetTypeInfo())) + { + // Check for conditional features + if (key == typeof(IHttpSendFile)) + { + return SupportsSendFile; + } + else if (key == typeof(IHttpTransportLayerSecurity)) + { + return SupportsClientCerts; + } + + // The rest of the features are always supported. + return true; + } + return false; + } + + public ICollection Keys + { + get + { + var keys = new List() + { + typeof(IHttpRequestInformation), + typeof(IHttpResponseInformation), + typeof(IHttpConnection), + typeof(ICanHasOwinEnvironment), + }; + if (SupportsSendFile) + { + keys.Add(typeof(IHttpSendFile)); + } + if (SupportsClientCerts) + { + keys.Add(typeof(IHttpTransportLayerSecurity)); + } + return keys; + } + } + + public bool Remove(Type key) + { + throw new NotSupportedException(); + } + + public bool TryGetValue(Type key, out object value) + { + if (ContainsKey(key)) + { + value = this; + return true; + } + value = null; + return false; + } + + public ICollection Values + { + get { throw new NotSupportedException(); } + } + + public object this[Type key] + { + get + { + object value; + if (TryGetValue(key, out value)) + { + return value; + } + throw new KeyNotFoundException(key.FullName); + } + set + { + throw new NotSupportedException(); + } + } + + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public void Clear() + { + throw new NotSupportedException(); + } + + public bool Contains(KeyValuePair item) + { + object result; + return TryGetValue(item.Key, out result) && result.Equals(item.Value); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) + { + throw new ArgumentNullException("array"); + } + if (arrayIndex < 0 || arrayIndex > array.Length) + { + throw new ArgumentOutOfRangeException("arrayIndex", arrayIndex, string.Empty); + } + var keys = Keys; + if (keys.Count > array.Length - arrayIndex) + { + throw new ArgumentException(); + } + + foreach (var key in keys) + { + array[arrayIndex++] = new KeyValuePair(key, this[key]); + } + } + + public int Count + { + get { return Keys.Count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); + } + + public IEnumerator> GetEnumerator() + { + return Keys.Select(type => new KeyValuePair(type, this[type])).GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Dispose() + { + } + } +} + diff --git a/src/Microsoft.AspNet.Owin/OwinMiddlewareFactory.cs b/src/Microsoft.AspNet.Owin/OwinMiddlewareFactory.cs new file mode 100644 index 0000000000..96bbaa48f5 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/OwinMiddlewareFactory.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.PipelineCore; + +namespace Microsoft.AspNet.Owin +{ + using AppFunc = Func, Task>; + + public static class OwinMiddlewareFactory + { + public static Func Create(Action configuration) + { + return Create(services: null, configuration: configuration); + } + + public static Func Create(IServiceProvider services, Action configuration) + { + var builder = new Builder(services); + configuration(builder); + + return Create(exit => + { + builder.Use(ignored => exit); + return builder.Build(); + }); + } + + public static Func Create(Func middleware) + { + return next => + { + var app = middleware(httpContext => + { + return next(httpContext.GetFeature().Environment); + }); + + return env => + { + return app.Invoke( + new DefaultHttpContext( + new FeatureCollection( + new OwinFeatureCollection(env)))); + }; + }; + } + } +} diff --git a/src/Microsoft.AspNet.Owin/Project.json b/src/Microsoft.AspNet.Owin/Project.json new file mode 100644 index 0000000000..4a7ce42ba7 --- /dev/null +++ b/src/Microsoft.AspNet.Owin/Project.json @@ -0,0 +1,30 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.Abstractions": "", + "Microsoft.AspNet.FeatureModel": "", + "Microsoft.AspNet.PipelineCore": "", + "Microsoft.AspNet.HttpFeature": "" + }, + "configurations": { + "net45": { }, + "k10": { + "dependencies": { + "System.Collections": "4.0.0.0", + "System.ComponentModel": "4.0.0.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.Runtime": "4.0.20.0", + "System.Runtime.Extensions": "4.0.10.0", + "System.Runtime.InteropServices": "4.0.20.0", + "System.Security.Claims": "0.1-alpha-*", + "System.Security.Cryptography.X509Certificates": "4.0.0.0", + "System.Security.Principal" : "4.0.0.0", + "System.Threading.Tasks": "4.0.10.0" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Owin.Tests/Microsoft.AspNet.Owin.Tests.kproj b/test/Microsoft.AspNet.Owin.Tests/Microsoft.AspNet.Owin.Tests.kproj new file mode 100644 index 0000000000..23a18cf7d4 --- /dev/null +++ b/test/Microsoft.AspNet.Owin.Tests/Microsoft.AspNet.Owin.Tests.kproj @@ -0,0 +1,27 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 16219571-3268-4d12-8689-12b7163dba13 + Library + + + + + + + 2.0 + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs new file mode 100644 index 0000000000..de9fc4a4a6 --- /dev/null +++ b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Abstractions.Security; +using Microsoft.AspNet.HttpFeature; +using Xunit; + +namespace Microsoft.AspNet.Owin +{ + public class OwinEnvironmentTests + { + private T Get(IDictionary environment, string key) + { + object value; + return environment.TryGetValue(key, out value) ? (T)value : default(T); + } + + [Fact] + public void OwinEnvironmentCanBeCreated() + { + MoqHttpContext context = new MoqHttpContext(); + context.Request.Method = "SomeMethod"; + IDictionary env = new OwinEnvironment(context); + + Assert.Equal("SomeMethod", Get(env, "owin.RequestMethod")); + env["owin.RequestMethod"] = "SomeOtherMethod"; + Assert.Equal("SomeOtherMethod", context.Request.Method); + } + + private class MoqHttpContext : HttpContext + { + private HttpRequest _request; + private IDictionary _items; + + public MoqHttpContext() + { + _request = new MoqHttpRequest(); + _items = new Dictionary(); + } + + public override HttpRequest Request + { + get { return _request; } + } + + public override HttpResponse Response + { + get { throw new NotImplementedException(); } + } + + public override ClaimsPrincipal User + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override IDictionary Items + { + get { return _items; } + } + + public override IServiceProvider ApplicationServices + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override IServiceProvider RequestServices + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override void Dispose() + { + throw new NotImplementedException(); + } + + public override object GetFeature(Type type) + { + return Request; + } + + public override void SetFeature(Type type, object instance) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetAuthenticationTypes() + { + throw new NotImplementedException(); + } + + public override IEnumerable Authenticate(IList authenticationTypes) + { + throw new NotImplementedException(); + } + + public override Task> AuthenticateAsync(IList authenticationTypes) + { + throw new NotImplementedException(); + } + } + + private class MoqHttpRequest : HttpRequest, IHttpRequestInformation + { + public override HttpContext HttpContext + { + get { throw new NotImplementedException(); } + } + + public override string Method + { + get; + set; + } + + public override string Scheme + { + get; + set; + } + + public override bool IsSecure + { + get { return false; } + } + + public override HostString Host + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override PathString PathBase + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override PathString Path + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override QueryString QueryString + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override IReadableStringCollection Query + { + get { throw new NotImplementedException(); } + } + + public override Task GetFormAsync() + { + throw new NotImplementedException(); + } + + public override string Protocol + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override IHeaderDictionary Headers + { + get { throw new NotImplementedException(); } + } + + public override IReadableStringCollection Cookies + { + get { throw new NotImplementedException(); } + } + + public override long? ContentLength + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override Stream Body + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override CancellationToken CallCanceled + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + string IHttpRequestInformation.PathBase + { + get; + set; + } + + string IHttpRequestInformation.Path + { + get; + set; + } + + string IHttpRequestInformation.QueryString + { + get; + set; + } + + IDictionary IHttpRequestInformation.Headers + { + get; + set; + } + } + } +} diff --git a/test/Microsoft.AspNet.Owin.Tests/OwinFeatureCollectionTests.cs b/test/Microsoft.AspNet.Owin.Tests/OwinFeatureCollectionTests.cs new file mode 100644 index 0000000000..d6476c0a8a --- /dev/null +++ b/test/Microsoft.AspNet.Owin.Tests/OwinFeatureCollectionTests.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.HttpFeature; +using Xunit; + +namespace Microsoft.AspNet.Owin +{ + public class OwinHttpEnvironmentTests + { + private T Get(IFeatureCollection features) + { + object value; + return features.TryGetValue(typeof(T), out value) ? (T)value : default(T); + } + + [Fact] + public void OwinHttpEnvironmentCanBeCreated() + { + var env = new Dictionary + { + {"owin.RequestMethod", "POST"} + }; + var features = new FeatureObject(new OwinFeatureCollection(env)); + + Assert.Equal(Get(features).Method, "POST"); + } + + [Fact] + public void ImplementedInterfacesAreEnumerated() + { + var env = new Dictionary + { + {"owin.RequestMethod", "POST"} + }; + var features = new FeatureObject(new OwinFeatureCollection(env)); + + var entries = features.ToArray(); + var keys = features.Keys.ToArray(); + var values = features.Values.ToArray(); + + Assert.Contains(typeof(IHttpRequestInformation), keys); + Assert.Contains(typeof(IHttpResponseInformation), keys); + } + } +} + diff --git a/test/Microsoft.AspNet.Owin.Tests/Project.json b/test/Microsoft.AspNet.Owin.Tests/Project.json new file mode 100644 index 0000000000..3ba09cb57f --- /dev/null +++ b/test/Microsoft.AspNet.Owin.Tests/Project.json @@ -0,0 +1,26 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.Owin": "", + "Microsoft.AspNet.HttpFeature": "", + "Microsoft.AspNet.Abstractions": "", + "Microsoft.AspNet.FeatureModel": "", + "Microsoft.AspNet.PipelineCore": "", + "Xunit.KRunner": "0.1-alpha-*", + "xunit.abstractions": "2.0.0-aspnet-*", + "xunit.assert": "2.0.0-aspnet-*", + "xunit.core": "2.0.0-aspnet-*", + "xunit.execution": "2.0.0-aspnet-*" + }, + "commands": { + "test": "Xunit.KRunner" + }, + "configurations": { + "net45": { + "dependencies": { + "System.Runtime": "", + "Shouldly": "1.1.1.1" + } + } + } +} \ No newline at end of file