diff --git a/.gitignore b/.gitignore index 8a9bbc3a43..329a307cda 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ nuget.exe *.sln.ide node_modules **/[Cc]ompiler/[Rr]esources/**/*.js +*launchSettings.json diff --git a/CORS.sln b/CORS.sln index 0d3ab28c1b..a0d8647301 100644 --- a/CORS.sln +++ b/CORS.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{84FE6872-A610-4CEC-855F-A84CBF1F40FC}" EndProject @@ -16,6 +16,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Cors", "sr EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Cors.Test", "test\Microsoft.AspNet.Cors.Test\Microsoft.AspNet.Cors.Test.xproj", "{F05BE96F-F869-4408-A480-96935B4835EE}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{538380BF-0D4C-4E30-8F41-E75C4B1C01FA}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CorsMiddlewareWebSite", "test\WebSites\CorsMiddlewareWebSite\CorsMiddlewareWebSite.xproj", "{B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,6 +34,10 @@ Global {F05BE96F-F869-4408-A480-96935B4835EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {F05BE96F-F869-4408-A480-96935B4835EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F05BE96F-F869-4408-A480-96935B4835EE}.Release|Any CPU.Build.0 = Release|Any CPU + {B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -37,5 +45,7 @@ Global GlobalSection(NestedProjects) = preSolution {41349FCD-D1C4-47A6-82D0-D16D00A8D59D} = {84FE6872-A610-4CEC-855F-A84CBF1F40FC} {F05BE96F-F869-4408-A480-96935B4835EE} = {F32074C7-087C-46CC-A913-422BFD2D6E0A} + {538380BF-0D4C-4E30-8F41-E75C4B1C01FA} = {F32074C7-087C-46CC-A913-422BFD2D6E0A} + {B42D4844-FFF8-4EC2-88D1-3AE95234D9EB} = {538380BF-0D4C-4E30-8F41-E75C4B1C01FA} EndGlobalSection EndGlobal diff --git a/global.json b/global.json index ec2e704f70..262a7f2b59 100644 --- a/global.json +++ b/global.json @@ -1,3 +1,3 @@ { - "projects": ["src", "test"] + "projects": ["src", "test/WebSites"] } diff --git a/src/Microsoft.AspNet.Cors/CorsMiddleware.cs b/src/Microsoft.AspNet.Cors/CorsMiddleware.cs index 7779eb2291..f8d3042277 100644 --- a/src/Microsoft.AspNet.Cors/CorsMiddleware.cs +++ b/src/Microsoft.AspNet.Cors/CorsMiddleware.cs @@ -3,7 +3,6 @@ using System; using System.Threading.Tasks; -using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.Extensions.Primitives; diff --git a/test/Microsoft.AspNet.Cors.Test/CorsMiddlewareFunctionalTest.cs b/test/Microsoft.AspNet.Cors.Test/CorsMiddlewareFunctionalTest.cs new file mode 100644 index 0000000000..6be83fed21 --- /dev/null +++ b/test/Microsoft.AspNet.Cors.Test/CorsMiddlewareFunctionalTest.cs @@ -0,0 +1,98 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Cors.Infrastructure +{ + public class CorsMiddlewareFunctionalTests : IClassFixture> + { + public CorsMiddlewareFunctionalTests(CorsTestFixture fixture) + { + Client = fixture.Client; + } + + public HttpClient Client { get; } + + [Theory] + [InlineData("GET")] + [InlineData("HEAD")] + [InlineData("POST")] + public async Task ResourceWithSimpleRequestPolicy_Allows_SimpleRequests(string method) + { + // Arrange + var path = "/CorsMiddleware/EC6AA70D-BA3E-4B71-A87F-18625ADDB2BD"; + var origin = "http://example.com"; + var request = new HttpRequestMessage(new HttpMethod(method), path); + request.Headers.Add(CorsConstants.Origin, origin); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal(path, content); + var responseHeaders = response.Headers; + var header = Assert.Single(response.Headers); + Assert.Equal(CorsConstants.AccessControlAllowOrigin, header.Key); + Assert.Equal(new[] { "http://example.com" }, header.Value.ToArray()); + } + + [Theory] + [InlineData("GET")] + [InlineData("HEAD")] + [InlineData("POST")] + [InlineData("PUT")] + public async Task PolicyFailed_Disallows_PreFlightRequest(string method) + { + // Arrange + var path = "/CorsMiddleware/9B8BB9C6-5BF2-4255-A636-DCB450D51AAE"; + var request = new HttpRequestMessage(new HttpMethod(CorsConstants.PreflightHttpMethod), path); + + // Adding a custom header makes it a non-simple request. + request.Headers.Add(CorsConstants.Origin, "http://example.com"); + request.Headers.Add(CorsConstants.AccessControlRequestMethod, method); + request.Headers.Add(CorsConstants.AccessControlRequestHeaders, "Custom"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + // Middleware applied the policy and since that did not pass, there were no access control headers. + Assert.Equal(HttpStatusCode.NoContent, response.StatusCode); + Assert.Empty(response.Headers); + + // It should short circuit and hence no result. + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal(string.Empty, content); + } + + [Fact] + public async Task PolicyFailed_Allows_ActualRequest_WithMissingResponseHeaders() + { + // Arrange + var path = "/CorsMiddleware/1E6C6F4D-1E1C-450E-8BD0-73DBF089A78F"; + var request = new HttpRequestMessage(HttpMethod.Put, path); + + // Adding a custom header makes it a non simple request. + request.Headers.Add(CorsConstants.Origin, "http://example2.com"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + // Middleware applied the policy and since that did not pass, there were no access control headers. + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Empty(response.Headers); + + // It still has executed the action. + var content = await response.Content.ReadAsStringAsync(); + Assert.Equal(path, content); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Cors.Test/CorsTestFixtureOfT.cs b/test/Microsoft.AspNet.Cors.Test/CorsTestFixtureOfT.cs new file mode 100644 index 0000000000..f3c11d50b0 --- /dev/null +++ b/test/Microsoft.AspNet.Cors.Test/CorsTestFixtureOfT.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Http; +using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.TestHost; + +namespace Microsoft.AspNet.Cors.Infrastructure +{ + public class CorsTestFixture : IDisposable + where TStartup : class + { + private readonly TestServer _server; + + public CorsTestFixture() + { + var builder = new WebHostBuilder().UseStartup(); + _server = new TestServer(builder); + + Client = _server.CreateClient(); + Client.BaseAddress = new Uri("http://localhost"); + } + + public HttpClient Client { get; } + + public void Dispose() + { + Client.Dispose(); + _server.Dispose(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Cors.Test/Microsoft.AspNet.Cors.Test.xproj b/test/Microsoft.AspNet.Cors.Test/Microsoft.AspNet.Cors.Test.xproj index cb9268f4ad..3dc6401fe9 100644 --- a/test/Microsoft.AspNet.Cors.Test/Microsoft.AspNet.Cors.Test.xproj +++ b/test/Microsoft.AspNet.Cors.Test/Microsoft.AspNet.Cors.Test.xproj @@ -13,5 +13,8 @@ 2.0 + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Cors.Test/project.json b/test/Microsoft.AspNet.Cors.Test/project.json index 28524e36ec..e444740695 100644 --- a/test/Microsoft.AspNet.Cors.Test/project.json +++ b/test/Microsoft.AspNet.Cors.Test/project.json @@ -1,8 +1,10 @@ -{ +{ "version": "1.0.0-*", "dependencies": { + "CorsMiddlewareWebSite": "1.0.0-*", "Microsoft.AspNet.Cors": "6.0.0-*", "Microsoft.AspNet.TestHost": "1.0.0-*", + "Microsoft.Extensions.Logging.Testing": "1.0.0-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "commands": { diff --git a/test/WebSites/CorsMiddlewareWebSite/CorsMiddlewareWebSite.xproj b/test/WebSites/CorsMiddlewareWebSite/CorsMiddlewareWebSite.xproj new file mode 100644 index 0000000000..7e7d95f9c7 --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/CorsMiddlewareWebSite.xproj @@ -0,0 +1,18 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + b42d4844-fff8-4ec2-88d1-3ae95234d9eb + ..\..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 41642 + + + \ No newline at end of file diff --git a/test/WebSites/CorsMiddlewareWebSite/EchoMiddleware.cs b/test/WebSites/CorsMiddlewareWebSite/EchoMiddleware.cs new file mode 100644 index 0000000000..fbc07dacde --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/EchoMiddleware.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; + +namespace CorsMiddlewareWebSite +{ + public class EchoMiddleware + { + /// + /// Instantiates a new . + /// + /// The next middleware in the pipeline. + public EchoMiddleware(RequestDelegate next) + { + } + + /// + /// Echo the request's path in the response. Does not invoke later middleware in the pipeline. + /// + /// The of the current request. + /// A that completes when writing to the response is done. + public Task Invoke(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Response.ContentType = "text/plain; charset=utf-8"; + var path = context.Request.PathBase + context.Request.Path + context.Request.QueryString; + return context.Response.WriteAsync(path, Encoding.UTF8); + } + } +} \ No newline at end of file diff --git a/test/WebSites/CorsMiddlewareWebSite/Startup.cs b/test/WebSites/CorsMiddlewareWebSite/Startup.cs new file mode 100644 index 0000000000..43008f4719 --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/Startup.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace CorsMiddlewareWebSite +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddCors(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseCors(policy => policy.WithOrigins("http://example.com")); + app.UseMiddleware(); + } + } +} \ No newline at end of file diff --git a/test/WebSites/CorsMiddlewareWebSite/project.json b/test/WebSites/CorsMiddlewareWebSite/project.json new file mode 100644 index 0000000000..b6c30ac9fa --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/project.json @@ -0,0 +1,15 @@ +{ + "commands": { + "web": "Microsoft.AspNet.Server.Kestrel", + "weblistener": "Microsoft.AspNet.Server.WebListener" + }, + "dependencies": { + "Microsoft.AspNet.Cors": "6.0.0-*", + "Microsoft.AspNet.Server.Kestrel": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*" + }, + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +} \ No newline at end of file diff --git a/test/WebSites/CorsMiddlewareWebSite/readme.md b/test/WebSites/CorsMiddlewareWebSite/readme.md new file mode 100644 index 0000000000..d7f8b28106 --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/readme.md @@ -0,0 +1,4 @@ +CorsMiddlewareWebSite +=== + +This web site illustrates how to use CorsMiddleware to apply a policy for entire application. diff --git a/test/WebSites/CorsMiddlewareWebSite/wwwroot/web.config b/test/WebSites/CorsMiddlewareWebSite/wwwroot/web.config new file mode 100644 index 0000000000..8485f6719f --- /dev/null +++ b/test/WebSites/CorsMiddlewareWebSite/wwwroot/web.config @@ -0,0 +1,9 @@ + + + + + + + + +