From 2ae3a24a16dfd8df48edb029ac4b62ab0b68abc0 Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Wed, 18 Jun 2014 15:50:14 -0700 Subject: [PATCH] Remove CallCancelled property. Fix Owin query string. Add Owin user. Add Owin tests. --- src/Microsoft.AspNet.Http/HttpRequest.cs | 7 - src/Microsoft.AspNet.Owin/OwinEnvironment.cs | 39 ++- .../DefaultHttpRequest.cs | 13 - .../OwinEnvironmentTests.cs | 315 +++++++----------- 4 files changed, 155 insertions(+), 219 deletions(-) diff --git a/src/Microsoft.AspNet.Http/HttpRequest.cs b/src/Microsoft.AspNet.Http/HttpRequest.cs index a59ae78ab6..9a09b01a65 100644 --- a/src/Microsoft.AspNet.Http/HttpRequest.cs +++ b/src/Microsoft.AspNet.Http/HttpRequest.cs @@ -120,12 +120,5 @@ namespace Microsoft.AspNet.Http /// /// The owin.RequestBody Stream. public abstract Stream Body { get; set; } - - /// - /// Gets or sets the cancellation token for the request. - /// - /// The cancellation token for the request. - public abstract CancellationToken CallCanceled { get; set; } - } } diff --git a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs index c624e1a8c0..38cb35443c 100644 --- a/src/Microsoft.AspNet.Owin/OwinEnvironment.cs +++ b/src/Microsoft.AspNet.Owin/OwinEnvironment.cs @@ -8,11 +8,14 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Security.Claims; using System.Security.Cryptography.X509Certificates; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.HttpFeature; +using Microsoft.AspNet.HttpFeature.Security; namespace Microsoft.AspNet.Owin { @@ -28,12 +31,14 @@ namespace Microsoft.AspNet.Owin _context = context; _entries = new Dictionary() { + { OwinConstants.CallCancelled, new FeatureMap(feature => feature.OnRequestAborted) }, { 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.RequestQueryString, new FeatureMap(feature => RemoveQuestionMark(feature.QueryString), + (feature, value) => feature.QueryString = AddQuestionMark(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) }, @@ -56,6 +61,8 @@ namespace Microsoft.AspNet.Owin { 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)) }, + + { OwinConstants.Security.User, new FeatureMap(feature => feature.User, (feature, value) => feature.User = MakeClaimsPrincipal((IPrincipal)value)) }, }; if (context.Request.IsSecure) @@ -222,6 +229,36 @@ namespace Microsoft.AspNet.Owin throw new NotImplementedException(); } + private string RemoveQuestionMark(string queryString) + { + if (!string.IsNullOrEmpty(queryString)) + { + if (queryString[0] == '?') + { + return queryString.Substring(1); + } + } + return queryString; + } + + private string AddQuestionMark(string queryString) + { + if (!string.IsNullOrEmpty(queryString)) + { + return '?' + queryString; + } + return queryString; + } + + private ClaimsPrincipal MakeClaimsPrincipal(IPrincipal principal) + { + if (principal is ClaimsPrincipal) + { + return principal as ClaimsPrincipal; + } + return new ClaimsPrincipal(principal); + } + public class FeatureMap { public FeatureMap(Type featureInterface, Func getter) diff --git a/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs b/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs index 09106220e0..e99c7e0b02 100644 --- a/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs +++ b/src/Microsoft.AspNet.PipelineCore/DefaultHttpRequest.cs @@ -149,18 +149,5 @@ namespace Microsoft.AspNet.PipelineCore { get { return RequestCookiesFeature.Cookies; } } - - public override System.Threading.CancellationToken CallCanceled - { - get - { - // TODO: Which feature exposes this? - return CancellationToken.None; - } - set - { - throw new NotImplementedException(); - } - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs index 44f182db49..ea603bc501 100644 --- a/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs +++ b/test/Microsoft.AspNet.Owin.Tests/OwinEnvironmentTests.cs @@ -4,13 +4,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Net.WebSockets; +using System.Linq; using System.Security.Claims; using System.Threading; -using System.Threading.Tasks; +using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.HttpFeature; +using Microsoft.AspNet.HttpFeature.Security; +using Microsoft.AspNet.PipelineCore; using Xunit; namespace Microsoft.AspNet.Owin @@ -26,230 +27,148 @@ namespace Microsoft.AspNet.Owin [Fact] public void OwinEnvironmentCanBeCreated() { - MoqHttpContext context = new MoqHttpContext(); + HttpContext context = CreateContext(); context.Request.Method = "SomeMethod"; + context.User = new ClaimsPrincipal(new ClaimsIdentity("Foo")); + context.Request.Body = Stream.Null; + context.Request.Headers["CustomRequestHeader"] = "CustomRequestValue"; + context.Request.Path = new PathString("/path"); + context.Request.PathBase = new PathString("/pathBase"); + context.Request.Protocol = "http/1.0"; + context.Request.QueryString = new QueryString("?key=value"); + context.Request.Scheme = "http"; + context.Response.Body = Stream.Null; + context.Response.Headers["CustomResponseHeader"] = "CustomResponseValue"; + context.Response.StatusCode = 201; + + IDictionary env = new OwinEnvironment(context); + Assert.Equal("SomeMethod", Get(env, "owin.RequestMethod")); + Assert.Equal("Foo", Get(env, "server.User").Identity.AuthenticationType); + Assert.Same(Stream.Null, Get(env, "owin.RequestBody")); + var requestHeaders = Get>(env, "owin.RequestHeaders"); + Assert.NotNull(requestHeaders); + Assert.Equal("CustomRequestValue", requestHeaders["CustomRequestHeader"].First()); + Assert.Equal("/path", Get(env, "owin.RequestPath")); + Assert.Equal("/pathBase", Get(env, "owin.RequestPathBase")); + Assert.Equal("http/1.0", Get(env, "owin.RequestProtocol")); + Assert.Equal("key=value", Get(env, "owin.RequestQueryString")); + Assert.Equal("http", Get(env, "owin.RequestScheme")); + + Assert.Same(Stream.Null, Get(env, "owin.ResponseBody")); + var responseHeaders = Get>(env, "owin.ResponseHeaders"); + Assert.NotNull(responseHeaders); + Assert.Equal("CustomResponseValue", responseHeaders["CustomResponseHeader"].First()); + Assert.Equal(201, Get(env, "owin.ResponseStatusCode")); + } + + [Fact] + public void OwinEnvironmentCanBeModified() + { + HttpContext context = CreateContext(); IDictionary env = new OwinEnvironment(context); - Assert.Equal("SomeMethod", Get(env, "owin.RequestMethod")); - env["owin.RequestMethod"] = "SomeOtherMethod"; - Assert.Equal("SomeOtherMethod", context.Request.Method); + env["owin.RequestMethod"] = "SomeMethod"; + env["server.User"] = new ClaimsPrincipal(new ClaimsIdentity("Foo")); + env["owin.RequestBody"] = Stream.Null; + var requestHeaders = Get>(env, "owin.RequestHeaders"); + Assert.NotNull(requestHeaders); + requestHeaders["CustomRequestHeader"] = new[] { "CustomRequestValue" }; + env["owin.RequestPath"] = "/path"; + env["owin.RequestPathBase"] = "/pathBase"; + env["owin.RequestProtocol"] = "http/1.0"; + env["owin.RequestQueryString"] = "key=value"; + env["owin.RequestScheme"] = "http"; + env["owin.ResponseBody"] = Stream.Null; + var responseHeaders = Get>(env, "owin.ResponseHeaders"); + Assert.NotNull(responseHeaders); + responseHeaders["CustomResponseHeader"] = new[] { "CustomResponseValue" }; + env["owin.ResponseStatusCode"] = 201; + + Assert.Equal("SomeMethod", context.Request.Method); + Assert.Equal("Foo", context.User.Identity.AuthenticationType); + Assert.Same(Stream.Null, context.Request.Body); + Assert.Equal("CustomRequestValue", context.Request.Headers["CustomRequestHeader"]); + Assert.Equal("/path", context.Request.Path.Value); + Assert.Equal("/pathBase", context.Request.PathBase.Value); + Assert.Equal("http/1.0", context.Request.Protocol); + Assert.Equal("?key=value", context.Request.QueryString.Value); + Assert.Equal("http", context.Request.Scheme); + + Assert.Same(Stream.Null, context.Response.Body); + Assert.Equal("CustomResponseValue", context.Response.Headers["CustomResponseHeader"]); + Assert.Equal(201, context.Response.StatusCode); } - private class MoqHttpContext : HttpContext + private HttpContext CreateContext() { - private HttpRequest _request; - private IDictionary _items; + var features = new FeatureCollection(); + features.Add(typeof(IHttpRequestFeature), new MoqHttpRequestFeature()); + features.Add(typeof(IHttpResponseFeature), new MoqHttpResponseFeature()); + features.Add(typeof(IHttpAuthenticationFeature), new MoqHttpAuthenticationFeature()); + features.Add(typeof(IHttpRequestLifetimeFeature), new MoqHttpRequestLifetimeFeature()); + return new DefaultHttpContext(features); + } - public MoqHttpContext() + private class MoqHttpRequestFeature : IHttpRequestFeature + { + public MoqHttpRequestFeature() { - _request = new MoqHttpRequest(); - _items = new Dictionary(); + Headers = new Dictionary(); } - public override HttpRequest Request + public string Method { get; set; } + + public string Scheme { get; set; } + + public string Protocol { get; set; } + + public Stream Body { get; set; } + + public string PathBase { get; set; } + + public string Path { get; set; } + + public string QueryString { get; set; } + + public IDictionary Headers { get; set; } + } + + private class MoqHttpResponseFeature : IHttpResponseFeature + { + public MoqHttpResponseFeature() { - get { return _request; } + Headers = new Dictionary(); } - public override HttpResponse Response - { - get { throw new NotImplementedException(); } - } + public Stream Body { get; set; } - public override ClaimsPrincipal User - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } + public int StatusCode { get; set; } - public override IDictionary Items - { - get { return _items; } - } + public string ReasonPhrase { get; set; } - public override IServiceProvider ApplicationServices - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } + public IDictionary Headers { get; set; } - 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(); - } - - public override CancellationToken OnRequestAborted - { - 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 AcceptWebSocketAsync(string subProtocol) + public void OnSendingHeaders(Action callback, object state) { throw new NotImplementedException(); } } - private class MoqHttpRequest : HttpRequest, IHttpRequestFeature + private class MoqHttpAuthenticationFeature : IHttpAuthenticationFeature { - public override HttpContext HttpContext - { - get { throw new NotImplementedException(); } - } + public ClaimsPrincipal User { get; set; } - public override string Method - { - get; - set; - } + public IAuthenticationHandler Handler { get; set; } + } - public override string Scheme - { - get; - set; - } + private class MoqHttpRequestLifetimeFeature : IHttpRequestLifetimeFeature + { + public CancellationToken OnRequestAborted { get; private 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() + public void Abort() { 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 IHttpRequestFeature.PathBase - { - get; - set; - } - - string IHttpRequestFeature.Path - { - get; - set; - } - - string IHttpRequestFeature.QueryString - { - get; - set; - } - - IDictionary IHttpRequestFeature.Headers - { - get; - set; - } } } }