Merge remote-tracking branch 'CORS/rybrande/release21ToSrc' into rybrande/Mondo2.1

This commit is contained in:
Ryan Brandenburg 2018-11-21 15:17:00 -08:00
commit af2f57386e
67 changed files with 6158 additions and 0 deletions

41
src/CORS/.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
[Oo]bj/
[Bb]in/
TestResults/
.nuget/
*.sln.ide/
_ReSharper.*/
packages/
artifacts/
PublishProfiles/
.vs/
bower_components/
node_modules/
**/wwwroot/lib/
debugSettings.json
project.lock.json
*.user
*.suo
*.cache
*.docstates
_ReSharper.*
nuget.exe
*net45.csproj
*net451.csproj
*k10.csproj
*.psess
*.vsp
*.pidb
*.userprefs
*DS_Store
*.ncrunchsolution
*.*sdf
*.ipch
.settings
*.sln.ide
node_modules
**/[Cc]ompiler/[Rr]esources/**/*.js
*launchSettings.json
.build/
.testPublish/
.vscode
global.json

89
src/CORS/CORS.sln Normal file
View File

@ -0,0 +1,89 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26817.0
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{84FE6872-A610-4CEC-855F-A84CBF1F40FC}"
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F32074C7-087C-46CC-A913-422BFD2D6E0A}"
ProjectSection(SolutionItems) = preProject
test\Directory.Build.props = test\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cors", "src\Microsoft.AspNetCore.Cors\Microsoft.AspNetCore.Cors.csproj", "{41349FCD-D1C4-47A6-82D0-D16D00A8D59D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Cors.Test", "test\Microsoft.AspNetCore.Cors.Test\Microsoft.AspNetCore.Cors.Test.csproj", "{F05BE96F-F869-4408-A480-96935B4835EE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{538380BF-0D4C-4E30-8F41-E75C4B1C01FA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorsMiddlewareWebSite", "test\WebSites\CorsMiddlewareWebSite\CorsMiddlewareWebSite.csproj", "{B42D4844-FFF8-4EC2-88D1-3AE95234D9EB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{960E0703-A8A5-44DF-AA87-B7C614683B3C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleDestination", "samples\SampleDestination\SampleDestination.csproj", "{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleOrigin", "samples\SampleOrigin\SampleOrigin.csproj", "{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0CCC5C1B-F548-4A17-96F8-14C700093FA0}"
ProjectSection(SolutionItems) = preProject
.appveyor.yml = .appveyor.yml
.gitattributes = .gitattributes
.gitignore = .gitignore
.travis.yml = .travis.yml
build.cmd = build.cmd
build.ps1 = build.ps1
build.sh = build.sh
CONTRIBUTING.md = CONTRIBUTING.md
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
LICENSE.txt = LICENSE.txt
NuGet.config = NuGet.config
NuGetPackageVerifier.json = NuGetPackageVerifier.json
README.md = README.md
version.xml = version.xml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{41349FCD-D1C4-47A6-82D0-D16D00A8D59D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41349FCD-D1C4-47A6-82D0-D16D00A8D59D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41349FCD-D1C4-47A6-82D0-D16D00A8D59D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41349FCD-D1C4-47A6-82D0-D16D00A8D59D}.Release|Any CPU.Build.0 = Release|Any CPU
{F05BE96F-F869-4408-A480-96935B4835EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}.Release|Any CPU.Build.0 = Release|Any CPU
{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
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}
{F6675DC1-AA21-453B-89B6-DA425FB9C3A5} = {960E0703-A8A5-44DF-AA87-B7C614683B3C}
{99460370-AE5D-4DC9-8DBF-04DF66D6B21D} = {960E0703-A8A5-44DF-AA87-B7C614683B3C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F9ED9C53-44CD-4853-9621-D028B7B6A431}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,21 @@
<Project>
<Import
Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))\AspNetCoreSettings.props"
Condition=" '$(CI)' != 'true' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), AspNetCoreSettings.props))' != '' " />
<Import Project="version.props" />
<Import Project="build\dependencies.props" />
<Import Project="build\sources.props" />
<PropertyGroup>
<Product>Microsoft ASP.NET Core</Product>
<RepositoryUrl>https://github.com/aspnet/CORS</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)build\Key.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,7 @@
<Project>
<PropertyGroup>
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,7 @@
{
"Default": {
"rules": [
"DefaultCompositeRule"
]
}
}

9
src/CORS/README.md Normal file
View File

@ -0,0 +1,9 @@
CORS
===
AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/yi0m8evjtg107o12/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/CORS/branch/dev)
Travis: [![Travis](https://travis-ci.org/aspnet/CORS.svg?branch=dev)](https://travis-ci.org/aspnet/CORS)
CORS repository includes the core implementation for CORS policy, utilized by the CORS middleware and MVC.
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo.

BIN
src/CORS/build/Key.snk Normal file

Binary file not shown.

View File

@ -0,0 +1,10 @@
@Library('dotnet-ci') _
simpleNode('Ubuntu16.04', 'latest-or-auto-docker') {
stage ('Checking out source') {
checkout scm
}
stage ('Build') {
sh './build.sh --ci'
}
}

View File

@ -0,0 +1,10 @@
@Library('dotnet-ci') _
simpleNode('OSX10.12','latest') {
stage ('Checking out source') {
checkout scm
}
stage ('Build') {
sh './build.sh --ci'
}
}

View File

@ -0,0 +1,18 @@
import org.dotnet.ci.pipelines.Pipeline
def windowsPipeline = Pipeline.createPipeline(this, 'build/buildpipeline/windows.groovy')
def linuxPipeline = Pipeline.createPipeline(this, 'build/buildpipeline/linux.groovy')
def osxPipeline = Pipeline.createPipeline(this, 'build/buildpipeline/osx.groovy')
String configuration = 'Release'
def parameters = [
'Configuration': configuration
]
windowsPipeline.triggerPipelineOnEveryGithubPR("Windows ${configuration} x64 Build", parameters)
windowsPipeline.triggerPipelineOnGithubPush(parameters)
linuxPipeline.triggerPipelineOnEveryGithubPR("Ubuntu 16.04 ${configuration} Build", parameters)
linuxPipeline.triggerPipelineOnGithubPush(parameters)
osxPipeline.triggerPipelineOnEveryGithubPR("OSX 10.12 ${configuration} Build", parameters)
osxPipeline.triggerPipelineOnGithubPush(parameters)

View File

@ -0,0 +1,12 @@
@Library('dotnet-ci') _
// 'node' indicates to Jenkins that the enclosed block runs on a node that matches
// the label 'windows-with-vs'
simpleNode('Windows_NT','latest') {
stage ('Checking out source') {
checkout scm
}
stage ('Build') {
bat '.\\run.cmd -CI default-build'
}
}

View File

@ -0,0 +1,35 @@
<Project>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>
<!-- These package versions may be overridden or updated by automation. -->
<PropertyGroup Label="Package Versions: Auto">
<InternalAspNetCoreSdkPackageVersion>2.1.3-rtm-15802</InternalAspNetCoreSdkPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MoqPackageVersion>4.7.49</MoqPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>
<XunitAnalyzersPackageVersion>0.8.0</XunitAnalyzersPackageVersion>
<XunitPackageVersion>2.3.1</XunitPackageVersion>
<XunitRunnerVisualStudioPackageVersion>2.4.0-beta.1.build3945</XunitRunnerVisualStudioPackageVersion>
</PropertyGroup>
<!-- This may import a generated file which may override the variables above. -->
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
<!-- These are package versions that should not be overridden or updated by automation. -->
<PropertyGroup Label="Package Versions: Pinned">
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.1.1</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.1</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.2</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.1</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.1</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.1.1</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.1.1</MicrosoftExtensionsOptionsPackageVersion>
</PropertyGroup>
</Project>

15
src/CORS/build/repo.props Normal file
View File

@ -0,0 +1,15 @@
<Project>
<Import Project="dependencies.props" />
<PropertyGroup>
<!-- These properties are use by the automation that updates dependencies.props -->
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
<LineupPackageVersion>2.1.0-rc1-*</LineupPackageVersion>
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json</LineupPackageRestoreSource>
</PropertyGroup>
<ItemGroup>
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
<Project>
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
<PropertyGroup Label="RestoreSources">
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
$(RestoreSources);
https://dotnet.myget.org/F/dotnet-core/api/v3/index.json;
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
</RestoreSources>
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
$(RestoreSources);
https://api.nuget.org/v3/index.json;
</RestoreSources>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,38 @@
# CORS Sample
This sample consists of a request origin (SampleOrigin) and a request destination (SampleDestination). Both have different domain names, to simulate a CORS request.
## Modify Hosts File
To run this CORS sample, modify the hosts file to register the hostnames `destination.example.com` and `origin.example.com`.
### Windows:
Run a text editor (e.g. Notepad) as an Administrator. Open the hosts file on the path: "C:\Windows\System32\drivers\etc\hosts".
### Linux:
On a Terminal window, type "sudo nano /etc/hosts" and enter your admin password when prompted.
In the hosts file, add the following to the bottom of the file:
```
127.0.0.1 destination.example.com
127.0.0.1 origin.example.com
```
Save the file and close it. Then clear your browser history.
## Run the sample
The SampleOrigin application will use port 5001, and SampleDestination will use 5000. Please ensure there are no other processes using those ports before running the CORS sample.
* In a command prompt window, open the directory where you cloned the repository, and open the SampleDestination directory. Run the command: dotnet run
* Repeat the above step in the SampleOrigin directory
* Open a browser window and go to `http://origin.example.com:5001`
* Input a method and header to create a CORS request or use one of the example buttons to see CORS in action
As an example, apart from `GET`, `HEAD` and `POST` requests, `PUT` requests are allowed in the CORS policy on SampleDestination. Any others, like `DELETE`, `OPTIONS` etc. are not allowed and throw an error.
`Cache-Control` has been added as an allowed header to the sample. Any other headers are not allowed and throw an error. You may leave the header name and value blank.
To edit the policy, please see `app.UseCors()` method in the `Startup.cs` file of SampleDestination.
**If using Visual Studio to launch the request origin:**
Open Visual Studio and in the `launchSettings.json` file for the SampleOrigin project, change the `launchUrl` under SampleOrigin to `http://origin.example.com:5001`.
Using the dropdown near the Start button, choose SampleOrigin before pressing Start to ensure that it uses Kestrel and not IIS Express.

View File

@ -0,0 +1,25 @@
// 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.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://*:5000")
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureLogging(factory => factory.AddConsole())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Cors\Microsoft.AspNetCore.Cors.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,39 @@
// 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.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors(policy => policy
.WithOrigins("http://origin.example.com:5001")
.WithMethods("PUT")
.WithHeaders("Cache-Control"));
app.Run(async context =>
{
var responseHeaders = context.Response.Headers;
context.Response.ContentType = "text/plain";
foreach (var responseHeader in responseHeaders)
{
await context.Response.WriteAsync("\n" + responseHeader.Key + ": " + responseHeader.Value);
}
await context.Response.WriteAsync("\nStatus code of your request: " + context.Response.StatusCode.ToString());
});
}
}
}

View File

@ -0,0 +1,25 @@
// 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.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace SampleOrigin
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://*:5001")
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureLogging(factory => factory.AddConsole())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,29 @@
// 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.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleOrigin
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Run(context =>
{
var fileInfoProvider = env.WebRootFileProvider;
var fileInfo = fileInfoProvider.GetFileInfo("/Index.html");
context.Response.ContentType = "text/html";
return context.Response.SendFileAsync(fileInfo);
});
}
}
}

View File

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
p {
font-size: 20px;
}
.button {
border: none;
color: white;
padding: 10px 15px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 5px 5px;
cursor: pointer;
}
.green {
background-color: #4CAF50;
}
.red {
background-color: indianred;
}
.gray {
background-color: gray;
}
</style>
<title>CORS Sample</title>
</head>
<body>
<script type="text/javascript">
// Make the CORS request.
function makeCORSRequest(method, headerName, headerValue) {
// Destination server with CORS enabled.
var url = 'http://destination.example.com:5000/';
var request = new XMLHttpRequest();
request.open(method, url, true);
if (headerName && headerValue) {
request.setRequestHeader(headerName, headerValue);
}
if (!request) {
alert('CORS not supported');
return;
}
// Response handlers.
request.onload = function () {
var text = request.responseText;
alert('Response from CORS ' + method + ' request to ' + url + ': ' + text);
};
request.onerror = function () {
alert('There was an error making the request for method ' + method);
};
request.send();
}
</script>
<p>CORS Sample</p>
Method: <input type="text" id="methodName" /><br /><br />
Header Name: <input type="text" id="headerName" /> Header Value: <input type="text" id="headerValue" /><br /><br />
<script>
document.getElementById('headerValue')
.addEventListener("keyup", function (event) {
event.preventDefault();
if (event.keyCode == 13) {
document.getElementById("CORS").click();
}
});
</script>
<button class="button gray" id="CORS" type="submit" onclick="makeCORSRequest(document.getElementById('methodName').value, document.getElementById('headerName').value, document.getElementById('headerValue').value);">Make a CORS Request</button><br /><br /><br /><br />
Method DELETE is not allowed:<button class="button red" id="InvalidMethodCORS" type="submit" onclick="makeCORSRequest('DELETE', 'Cache-Control', 'no-cache');">Invalid Method CORS Request</button>
Method PUT is allowed:<button class="button green" id="ValidMethodCORS" type="submit" onclick="makeCORSRequest('PUT', 'Cache-Control', 'no-cache');">Valid Method CORS Request</button><br /><br />
Header 'Max-Forwards' not supported:<button class="button red" id="InvalidHeaderCORS" type="submit" onclick="makeCORSRequest('PUT', 'Max-Forwards', '2');">Invalid Header CORS Request</button>
Header 'Cache-Control' is supported:<button class="button green" id="ValidHeaderCORS" type="submit" onclick="makeCORSRequest('PUT', 'Cache-Control', 'no-cache');">Valid Header CORS Request</button><br /><br />
</body>
</html>

View File

@ -0,0 +1,8 @@
<Project>
<Import Project="..\Directory.Build.props" />
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,59 @@
// 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 Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// Extension methods for setting up cross-origin resource sharing services in an <see cref="IServiceCollection" />.
/// </summary>
public static class CorsServiceCollectionExtensions
{
/// <summary>
/// Adds cross-origin resource sharing services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddCors(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddOptions();
services.TryAdd(ServiceDescriptor.Transient<ICorsService, CorsService>());
services.TryAdd(ServiceDescriptor.Transient<ICorsPolicyProvider, DefaultCorsPolicyProvider>());
return services;
}
/// <summary>
/// Adds cross-origin resource sharing services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="setupAction">An <see cref="Action{CorsOptions}"/> to configure the provided <see cref="CorsOptions"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddCors(this IServiceCollection services, Action<CorsOptions> setupAction)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (setupAction == null)
{
throw new ArgumentNullException(nameof(setupAction));
}
services.AddCors();
services.Configure(setupAction);
return services;
}
}
}

View File

@ -0,0 +1,14 @@
// 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 Microsoft.AspNetCore.Cors.Infrastructure;
namespace Microsoft.AspNetCore.Cors
{
/// <inheritdoc />
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class DisableCorsAttribute : Attribute, IDisableCorsAttribute
{
}
}

View File

@ -0,0 +1,34 @@
// 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 Microsoft.AspNetCore.Cors.Infrastructure;
namespace Microsoft.AspNetCore.Cors
{
/// <inheritdoc />
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class EnableCorsAttribute : Attribute, IEnableCorsAttribute
{
/// <summary>
/// Creates a new instance of the <see cref="EnableCorsAttribute"/> with the default policy
/// name defined by <see cref="CorsOptions.DefaultPolicyName"/>.
/// </summary>
public EnableCorsAttribute()
: this(policyName: null)
{
}
/// <summary>
/// Creates a new instance of the <see cref="EnableCorsAttribute"/> with the supplied policy name.
/// </summary>
/// <param name="policyName">The name of the policy to be applied.</param>
public EnableCorsAttribute(string policyName)
{
PolicyName = policyName;
}
/// <inheritdoc />
public string PolicyName { get; set; }
}
}

View File

@ -0,0 +1,95 @@
// 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.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// CORS-related constants.
/// </summary>
public static class CorsConstants
{
/// <summary>
/// The HTTP method for the CORS preflight request.
/// </summary>
public static readonly string PreflightHttpMethod = HttpMethods.Options;
/// <summary>
/// The Origin request header.
/// </summary>
public static readonly string Origin = HeaderNames.Origin;
/// <summary>
/// The value for the Access-Control-Allow-Origin response header to allow all origins.
/// </summary>
public static readonly string AnyOrigin = "*";
/// <summary>
/// The Access-Control-Request-Method request header.
/// </summary>
public static readonly string AccessControlRequestMethod = HeaderNames.AccessControlRequestMethod;
/// <summary>
/// The Access-Control-Request-Headers request header.
/// </summary>
public static readonly string AccessControlRequestHeaders = HeaderNames.AccessControlRequestHeaders;
/// <summary>
/// The Access-Control-Allow-Origin response header.
/// </summary>
public static readonly string AccessControlAllowOrigin = HeaderNames.AccessControlAllowOrigin;
/// <summary>
/// The Access-Control-Allow-Headers response header.
/// </summary>
public static readonly string AccessControlAllowHeaders = HeaderNames.AccessControlAllowHeaders;
/// <summary>
/// The Access-Control-Expose-Headers response header.
/// </summary>
public static readonly string AccessControlExposeHeaders = HeaderNames.AccessControlExposeHeaders;
/// <summary>
/// The Access-Control-Allow-Methods response header.
/// </summary>
public static readonly string AccessControlAllowMethods = HeaderNames.AccessControlAllowMethods;
/// <summary>
/// The Access-Control-Allow-Credentials response header.
/// </summary>
public static readonly string AccessControlAllowCredentials = HeaderNames.AccessControlAllowCredentials;
/// <summary>
/// The Access-Control-Max-Age response header.
/// </summary>
public static readonly string AccessControlMaxAge = HeaderNames.AccessControlMaxAge;
internal static readonly string[] SimpleRequestHeaders =
{
HeaderNames.Origin,
HeaderNames.Accept,
HeaderNames.AcceptLanguage,
HeaderNames.ContentLanguage,
};
internal static readonly string[] SimpleResponseHeaders =
{
HeaderNames.CacheControl,
HeaderNames.ContentLanguage,
HeaderNames.ContentType,
HeaderNames.Expires,
HeaderNames.LastModified,
HeaderNames.Pragma
};
internal static readonly string[] SimpleMethods =
{
HttpMethods.Get,
HttpMethods.Head,
HttpMethods.Post
};
}
}

View File

@ -0,0 +1,131 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// An ASP.NET middleware for handling CORS.
/// </summary>
public class CorsMiddleware
{
private readonly RequestDelegate _next;
private readonly ICorsService _corsService;
private readonly ICorsPolicyProvider _corsPolicyProvider;
private readonly CorsPolicy _policy;
private readonly string _corsPolicyName;
/// <summary>
/// Instantiates a new <see cref="CorsMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="corsService">An instance of <see cref="ICorsService"/>.</param>
/// <param name="policyProvider">A policy provider which can get an <see cref="CorsPolicy"/>.</param>
public CorsMiddleware(
RequestDelegate next,
ICorsService corsService,
ICorsPolicyProvider policyProvider)
: this(next, corsService, policyProvider, policyName: null)
{
}
/// <summary>
/// Instantiates a new <see cref="CorsMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="corsService">An instance of <see cref="ICorsService"/>.</param>
/// <param name="policyProvider">A policy provider which can get an <see cref="CorsPolicy"/>.</param>
/// <param name="policyName">An optional name of the policy to be fetched.</param>
public CorsMiddleware(
RequestDelegate next,
ICorsService corsService,
ICorsPolicyProvider policyProvider,
string policyName)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (corsService == null)
{
throw new ArgumentNullException(nameof(corsService));
}
if (policyProvider == null)
{
throw new ArgumentNullException(nameof(policyProvider));
}
_next = next;
_corsService = corsService;
_corsPolicyProvider = policyProvider;
_corsPolicyName = policyName;
}
/// <summary>
/// Instantiates a new <see cref="CorsMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="corsService">An instance of <see cref="ICorsService"/>.</param>
/// <param name="policy">An instance of the <see cref="CorsPolicy"/> which can be applied.</param>
public CorsMiddleware(
RequestDelegate next,
ICorsService corsService,
CorsPolicy policy)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (corsService == null)
{
throw new ArgumentNullException(nameof(corsService));
}
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
_next = next;
_corsService = corsService;
_policy = policy;
}
/// <inheritdoc />
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.ContainsKey(CorsConstants.Origin))
{
var corsPolicy = _policy ?? await _corsPolicyProvider?.GetPolicyAsync(context, _corsPolicyName);
if (corsPolicy != null)
{
var corsResult = _corsService.EvaluatePolicy(context, corsPolicy);
_corsService.ApplyResult(corsResult, context.Response);
var accessControlRequestMethod =
context.Request.Headers[CorsConstants.AccessControlRequestMethod];
if (string.Equals(
context.Request.Method,
CorsConstants.PreflightHttpMethod,
StringComparison.OrdinalIgnoreCase) &&
!StringValues.IsNullOrEmpty(accessControlRequestMethod))
{
// Since there is a policy which was identified,
// always respond to preflight requests.
context.Response.StatusCode = StatusCodes.Status204NoContent;
return;
}
}
}
await _next(context);
}
}
}

View File

@ -0,0 +1,70 @@
// 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 Microsoft.AspNetCore.Cors.Infrastructure;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// The <see cref="IApplicationBuilder"/> extensions for adding CORS middleware support.
/// </summary>
public static class CorsMiddlewareExtensions
{
/// <summary>
/// Adds a CORS middleware to your web application pipeline to allow cross domain requests.
/// </summary>
/// <param name="app">The IApplicationBuilder passed to your Configure method</param>
/// <returns>The original app parameter</returns>
public static IApplicationBuilder UseCors(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<CorsMiddleware>();
}
/// <summary>
/// Adds a CORS middleware to your web application pipeline to allow cross domain requests.
/// </summary>
/// <param name="app">The IApplicationBuilder passed to your Configure method</param>
/// <param name="policyName">The policy name of a configured policy.</param>
/// <returns>The original app parameter</returns>
public static IApplicationBuilder UseCors(this IApplicationBuilder app, string policyName)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<CorsMiddleware>(policyName);
}
/// <summary>
/// Adds a CORS middleware to your web application pipeline to allow cross domain requests.
/// </summary>
/// <param name="app">The IApplicationBuilder passed to your Configure method.</param>
/// <param name="configurePolicy">A delegate which can use a policy builder to build a policy.</param>
/// <returns>The original app parameter</returns>
public static IApplicationBuilder UseCors(
this IApplicationBuilder app,
Action<CorsPolicyBuilder> configurePolicy)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (configurePolicy == null)
{
throw new ArgumentNullException(nameof(configurePolicy));
}
var policyBuilder = new CorsPolicyBuilder();
configurePolicy(policyBuilder);
return app.UseMiddleware<CorsMiddleware>(policyBuilder.Build());
}
}
}

View File

@ -0,0 +1,119 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// Provides programmatic configuration for Cors.
/// </summary>
public class CorsOptions
{
private string _defaultPolicyName = "__DefaultCorsPolicy";
private IDictionary<string, CorsPolicy> PolicyMap { get; } = new Dictionary<string, CorsPolicy>();
public string DefaultPolicyName
{
get
{
return _defaultPolicyName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_defaultPolicyName = value;
}
}
/// <summary>
/// Adds a new policy and sets it as the default.
/// </summary>
/// <param name="policy">The <see cref="CorsPolicy"/> policy to be added.</param>
public void AddDefaultPolicy(CorsPolicy policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
AddPolicy(DefaultPolicyName, policy);
}
/// <summary>
/// Adds a new policy and sets it as the default.
/// </summary>
/// <param name="configurePolicy">A delegate which can use a policy builder to build a policy.</param>
public void AddDefaultPolicy(Action<CorsPolicyBuilder> configurePolicy)
{
if (configurePolicy == null)
{
throw new ArgumentNullException(nameof(configurePolicy));
}
AddPolicy(DefaultPolicyName, configurePolicy);
}
/// <summary>
/// Adds a new policy.
/// </summary>
/// <param name="name">The name of the policy.</param>
/// <param name="policy">The <see cref="CorsPolicy"/> policy to be added.</param>
public void AddPolicy(string name, CorsPolicy policy)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
PolicyMap[name] = policy;
}
/// <summary>
/// Adds a new policy.
/// </summary>
/// <param name="name">The name of the policy.</param>
/// <param name="configurePolicy">A delegate which can use a policy builder to build a policy.</param>
public void AddPolicy(string name, Action<CorsPolicyBuilder> configurePolicy)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (configurePolicy == null)
{
throw new ArgumentNullException(nameof(configurePolicy));
}
var policyBuilder = new CorsPolicyBuilder();
configurePolicy(policyBuilder);
PolicyMap[name] = policyBuilder.Build();
}
/// <summary>
/// Gets the policy based on the <paramref name="name"/>
/// </summary>
/// <param name="name">The name of the policy to lookup.</param>
/// <returns>The <see cref="CorsPolicy"/> if the policy was added.<c>null</c> otherwise.</returns>
public CorsPolicy GetPolicy(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;
}
}
}

View File

@ -0,0 +1,164 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// Defines the policy for Cross-Origin requests based on the CORS specifications.
/// </summary>
public class CorsPolicy
{
private TimeSpan? _preflightMaxAge;
/// <summary>
/// Default constructor for a CorsPolicy.
/// </summary>
public CorsPolicy()
{
IsOriginAllowed = DefaultIsOriginAllowed;
}
/// <summary>
/// Gets a value indicating if all headers are allowed.
/// </summary>
public bool AllowAnyHeader
{
get
{
if (Headers == null || Headers.Count != 1 || Headers.Count == 1 && Headers[0] != "*")
{
return false;
}
return true;
}
}
/// <summary>
/// Gets a value indicating if all methods are allowed.
/// </summary>
public bool AllowAnyMethod
{
get
{
if (Methods == null || Methods.Count != 1 || Methods.Count == 1 && Methods[0] != "*")
{
return false;
}
return true;
}
}
/// <summary>
/// Gets a value indicating if all origins are allowed.
/// </summary>
public bool AllowAnyOrigin
{
get
{
if (Origins == null || Origins.Count != 1 || Origins.Count == 1 && Origins[0] != "*")
{
return false;
}
return true;
}
}
/// <summary>
/// Gets or sets a function which evaluates whether an origin is allowed.
/// </summary>
public Func<string, bool> IsOriginAllowed { get; set; }
/// <summary>
/// Gets the headers that the resource might use and can be exposed.
/// </summary>
public IList<string> ExposedHeaders { get; } = new List<string>();
/// <summary>
/// Gets the headers that are supported by the resource.
/// </summary>
public IList<string> Headers { get; } = new List<string>();
/// <summary>
/// Gets the methods that are supported by the resource.
/// </summary>
public IList<string> Methods { get; } = new List<string>();
/// <summary>
/// Gets the origins that are allowed to access the resource.
/// </summary>
public IList<string> Origins { get; } = new List<string>();
/// <summary>
/// Gets or sets the <see cref="TimeSpan"/> for which the results of a preflight request can be cached.
/// </summary>
public TimeSpan? PreflightMaxAge
{
get
{
return _preflightMaxAge;
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(value), Resources.PreflightMaxAgeOutOfRange);
}
_preflightMaxAge = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether the resource supports user credentials in the request.
/// </summary>
public bool SupportsCredentials { get; set; }
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var builder = new StringBuilder();
builder.Append("AllowAnyHeader: ");
builder.Append(AllowAnyHeader);
builder.Append(", AllowAnyMethod: ");
builder.Append(AllowAnyMethod);
builder.Append(", AllowAnyOrigin: ");
builder.Append(AllowAnyOrigin);
builder.Append(", PreflightMaxAge: ");
builder.Append(PreflightMaxAge.HasValue ?
PreflightMaxAge.Value.TotalSeconds.ToString() : "null");
builder.Append(", SupportsCredentials: ");
builder.Append(SupportsCredentials);
builder.Append(", Origins: {");
builder.Append(string.Join(",", Origins));
builder.Append("}");
builder.Append(", Methods: {");
builder.Append(string.Join(",", Methods));
builder.Append("}");
builder.Append(", Headers: {");
builder.Append(string.Join(",", Headers));
builder.Append("}");
builder.Append(", ExposedHeaders: {");
builder.Append(string.Join(",", ExposedHeaders));
builder.Append("}");
return builder.ToString();
}
private bool DefaultIsOriginAllowed(string origin)
{
return Origins.Contains(origin, StringComparer.Ordinal);
}
}
}

View File

@ -0,0 +1,225 @@
// 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.Linq;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// Exposes methods to build a policy.
/// </summary>
public class CorsPolicyBuilder
{
private readonly CorsPolicy _policy = new CorsPolicy();
/// <summary>
/// Creates a new instance of the <see cref="CorsPolicyBuilder"/>.
/// </summary>
/// <param name="origins">list of origins which can be added.</param>
public CorsPolicyBuilder(params string[] origins)
{
WithOrigins(origins);
}
/// <summary>
/// Creates a new instance of the <see cref="CorsPolicyBuilder"/>.
/// </summary>
/// <param name="policy">The policy which will be used to intialize the builder.</param>
public CorsPolicyBuilder(CorsPolicy policy)
{
Combine(policy);
}
/// <summary>
/// Adds the specified <paramref name="origins"/> to the policy.
/// </summary>
/// <param name="origins">The origins that are allowed.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder WithOrigins(params string[] origins)
{
foreach (var req in origins)
{
_policy.Origins.Add(req);
}
return this;
}
/// <summary>
/// Adds the specified <paramref name="headers"/> to the policy.
/// </summary>
/// <param name="headers">The headers which need to be allowed in the request.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder WithHeaders(params string[] headers)
{
foreach (var req in headers)
{
_policy.Headers.Add(req);
}
return this;
}
/// <summary>
/// Adds the specified <paramref name="exposedHeaders"/> to the policy.
/// </summary>
/// <param name="exposedHeaders">The headers which need to be exposed to the client.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder WithExposedHeaders(params string[] exposedHeaders)
{
foreach (var req in exposedHeaders)
{
_policy.ExposedHeaders.Add(req);
}
return this;
}
/// <summary>
/// Adds the specified <paramref name="methods"/> to the policy.
/// </summary>
/// <param name="methods">The methods which need to be added to the policy.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder WithMethods(params string[] methods)
{
foreach (var req in methods)
{
_policy.Methods.Add(req);
}
return this;
}
/// <summary>
/// Sets the policy to allow credentials.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder AllowCredentials()
{
_policy.SupportsCredentials = true;
return this;
}
/// <summary>
/// Sets the policy to not allow credentials.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder DisallowCredentials()
{
_policy.SupportsCredentials = false;
return this;
}
/// <summary>
/// Ensures that the policy allows any origin.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder AllowAnyOrigin()
{
_policy.Origins.Clear();
_policy.Origins.Add(CorsConstants.AnyOrigin);
return this;
}
/// <summary>
/// Ensures that the policy allows any method.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder AllowAnyMethod()
{
_policy.Methods.Clear();
_policy.Methods.Add("*");
return this;
}
/// <summary>
/// Ensures that the policy allows any header.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder AllowAnyHeader()
{
_policy.Headers.Clear();
_policy.Headers.Add("*");
return this;
}
/// <summary>
/// Sets the preflightMaxAge for the underlying policy.
/// </summary>
/// <param name="preflightMaxAge">A positive <see cref="TimeSpan"/> indicating the time a preflight
/// request can be cached.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder SetPreflightMaxAge(TimeSpan preflightMaxAge)
{
_policy.PreflightMaxAge = preflightMaxAge;
return this;
}
/// <summary>
/// Sets the specified <paramref name="isOriginAllowed"/> for the underlying policy.
/// </summary>
/// <param name="isOriginAllowed">The function used by the policy to evaluate if an origin is allowed.</param>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder SetIsOriginAllowed(Func<string, bool> isOriginAllowed)
{
_policy.IsOriginAllowed = isOriginAllowed;
return this;
}
/// <summary>
/// Sets the <see cref="CorsPolicy.IsOriginAllowed"/> property of the policy to be a function
/// that allows origins to match a configured wildcarded domain when evaluating if the
/// origin is allowed.
/// </summary>
/// <returns>The current policy builder.</returns>
public CorsPolicyBuilder SetIsOriginAllowedToAllowWildcardSubdomains()
{
_policy.IsOriginAllowed = _policy.IsOriginAnAllowedSubdomain;
return this;
}
/// <summary>
/// Builds a new <see cref="CorsPolicy"/> using the entries added.
/// </summary>
/// <returns>The constructed <see cref="CorsPolicy"/>.</returns>
public CorsPolicy Build()
{
return _policy;
}
/// <summary>
/// Combines the given <paramref name="policy"/> to the existing properties in the builder.
/// </summary>
/// <param name="policy">The policy which needs to be combined.</param>
/// <returns>The current policy builder.</returns>
private CorsPolicyBuilder Combine(CorsPolicy policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
WithOrigins(policy.Origins.ToArray());
WithHeaders(policy.Headers.ToArray());
WithExposedHeaders(policy.ExposedHeaders.ToArray());
WithMethods(policy.Methods.ToArray());
SetIsOriginAllowed(policy.IsOriginAllowed);
if (policy.PreflightMaxAge.HasValue)
{
SetPreflightMaxAge(policy.PreflightMaxAge.Value);
}
if (policy.SupportsCredentials)
{
AllowCredentials();
}
else
{
DisallowCredentials();
}
return this;
}
}
}

View File

@ -0,0 +1,36 @@
// 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.Linq;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
internal static class CorsPolicyExtensions
{
private const string _WildcardSubdomain = "*.";
public static bool IsOriginAnAllowedSubdomain(this CorsPolicy policy, string origin)
{
if (policy.Origins.Contains(origin))
{
return true;
}
if (Uri.TryCreate(origin, UriKind.Absolute, out var originUri))
{
return policy.Origins
.Where(o => o.Contains($"://{_WildcardSubdomain}"))
.Select(CreateDomainUri)
.Any(domain => UriHelpers.IsSubdomainOf(originUri, domain));
}
return false;
}
private static Uri CreateDomainUri(string origin)
{
return new Uri(origin.Replace(_WildcardSubdomain, string.Empty), UriKind.Absolute);
}
}
}

View File

@ -0,0 +1,94 @@
// 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.Collections.Generic;
using System.Text;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// Results returned by <see cref="ICorsService"/>.
/// </summary>
public class CorsResult
{
private TimeSpan? _preflightMaxAge;
/// <summary>
/// Gets or sets the allowed origin.
/// </summary>
public string AllowedOrigin { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the resource supports user credentials.
/// </summary>
public bool SupportsCredentials { get; set; }
/// <summary>
/// Gets the allowed methods.
/// </summary>
public IList<string> AllowedMethods { get; } = new List<string>();
/// <summary>
/// Gets the allowed headers.
/// </summary>
public IList<string> AllowedHeaders { get; } = new List<string>();
/// <summary>
/// Gets the allowed headers that can be exposed on the response.
/// </summary>
public IList<string> AllowedExposedHeaders { get; } = new List<string>();
/// <summary>
/// Gets or sets a value indicating if a 'Vary' header with the value 'Origin' is required.
/// </summary>
public bool VaryByOrigin { get; set; }
/// <summary>
/// Gets or sets the <see cref="TimeSpan"/> for which the results of a preflight request can be cached.
/// </summary>
public TimeSpan? PreflightMaxAge
{
get
{
return _preflightMaxAge;
}
set
{
if (value < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(value), Resources.PreflightMaxAgeOutOfRange);
}
_preflightMaxAge = value;
}
}
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var builder = new StringBuilder();
builder.Append("AllowCredentials: ");
builder.Append(SupportsCredentials);
builder.Append(", PreflightMaxAge: ");
builder.Append(PreflightMaxAge.HasValue ?
PreflightMaxAge.Value.TotalSeconds.ToString() : "null");
builder.Append(", AllowOrigin: ");
builder.Append(AllowedOrigin);
builder.Append(", AllowExposedHeaders: {");
builder.Append(string.Join(",", AllowedExposedHeaders));
builder.Append("}");
builder.Append(", AllowHeaders: {");
builder.Append(string.Join(",", AllowedHeaders));
builder.Append("}");
builder.Append(", AllowMethods: {");
builder.Append(string.Join(",", AllowedMethods));
builder.Append("}");
return builder.ToString();
}
}
}

View File

@ -0,0 +1,313 @@
// 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.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Cors.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// Default implementation of <see cref="ICorsService"/>.
/// </summary>
public class CorsService : ICorsService
{
private readonly CorsOptions _options;
private readonly ILogger _logger;
/// <summary>
/// Creates a new instance of the <see cref="CorsService"/>.
/// </summary>
/// <param name="options">The option model representing <see cref="CorsOptions"/>.</param>
public CorsService(IOptions<CorsOptions> options)
: this(options, loggerFactory: null)
{
}
/// <summary>
/// Creates a new instance of the <see cref="CorsService"/>.
/// </summary>
/// <param name="options">The option model representing <see cref="CorsOptions"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public CorsService(IOptions<CorsOptions> options, ILoggerFactory loggerFactory)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_options = options.Value;
_logger = loggerFactory?.CreateLogger<CorsService>();
}
/// <summary>
/// Looks up a policy using the <paramref name="policyName"/> and then evaluates the policy using the passed in
/// <paramref name="context"/>.
/// </summary>
/// <param name="context"></param>
/// <param name="policyName"></param>
/// <returns>A <see cref="CorsResult"/> which contains the result of policy evaluation and can be
/// used by the caller to set appropriate response headers.</returns>
public CorsResult EvaluatePolicy(HttpContext context, string policyName)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var policy = _options.GetPolicy(policyName);
return EvaluatePolicy(context, policy);
}
/// <inheritdoc />
public CorsResult EvaluatePolicy(HttpContext context, CorsPolicy policy)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
var corsResult = new CorsResult();
var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];
if (string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase) &&
!StringValues.IsNullOrEmpty(accessControlRequestMethod))
{
_logger?.IsPreflightRequest();
EvaluatePreflightRequest(context, policy, corsResult);
}
else
{
EvaluateRequest(context, policy, corsResult);
}
return corsResult;
}
public virtual void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result)
{
var origin = context.Request.Headers[CorsConstants.Origin];
if (!IsOriginAllowed(policy, origin))
{
return;
}
AddOriginToResult(origin, policy, result);
result.SupportsCredentials = policy.SupportsCredentials;
AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders);
_logger?.PolicySuccess();
}
public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
{
var origin = context.Request.Headers[CorsConstants.Origin];
if (!IsOriginAllowed(policy, origin))
{
return;
}
var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];
if (StringValues.IsNullOrEmpty(accessControlRequestMethod))
{
return;
}
var requestHeaders =
context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestHeaders);
if (!policy.AllowAnyMethod)
{
var found = false;
for (var i = 0; i < policy.Methods.Count; i++)
{
var method = policy.Methods[i];
if (string.Equals(method, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase))
{
found = true;
break;
}
}
if (!found)
{
_logger?.PolicyFailure();
_logger?.AccessControlMethodNotAllowed(accessControlRequestMethod);
return;
}
}
if (!policy.AllowAnyHeader &&
requestHeaders != null)
{
foreach (var requestHeader in requestHeaders)
{
if (!CorsConstants.SimpleRequestHeaders.Contains(requestHeader, StringComparer.OrdinalIgnoreCase) &&
!policy.Headers.Contains(requestHeader, StringComparer.OrdinalIgnoreCase))
{
_logger?.PolicyFailure();
_logger?.RequestHeaderNotAllowed(requestHeader);
return;
}
}
}
AddOriginToResult(origin, policy, result);
result.SupportsCredentials = policy.SupportsCredentials;
result.PreflightMaxAge = policy.PreflightMaxAge;
result.AllowedMethods.Add(accessControlRequestMethod);
AddHeaderValues(result.AllowedHeaders, requestHeaders);
_logger?.PolicySuccess();
}
/// <inheritdoc />
public virtual void ApplyResult(CorsResult result, HttpResponse response)
{
if (result == null)
{
throw new ArgumentNullException(nameof(result));
}
if (response == null)
{
throw new ArgumentNullException(nameof(response));
}
var headers = response.Headers;
if (result.AllowedOrigin != null)
{
headers[CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin;
}
if (result.VaryByOrigin)
{
headers["Vary"] = "Origin";
}
if (result.SupportsCredentials)
{
headers[CorsConstants.AccessControlAllowCredentials] = "true";
}
if (result.AllowedMethods.Count > 0)
{
// Filter out simple methods
var nonSimpleAllowMethods = result.AllowedMethods
.Where(m =>
!CorsConstants.SimpleMethods.Contains(m, StringComparer.OrdinalIgnoreCase))
.ToArray();
if (nonSimpleAllowMethods.Length > 0)
{
headers.SetCommaSeparatedValues(
CorsConstants.AccessControlAllowMethods,
nonSimpleAllowMethods);
}
}
if (result.AllowedHeaders.Count > 0)
{
// Filter out simple request headers
var nonSimpleAllowRequestHeaders = result.AllowedHeaders
.Where(header =>
!CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase))
.ToArray();
if (nonSimpleAllowRequestHeaders.Length > 0)
{
headers.SetCommaSeparatedValues(
CorsConstants.AccessControlAllowHeaders,
nonSimpleAllowRequestHeaders);
}
}
if (result.AllowedExposedHeaders.Count > 0)
{
// Filter out simple response headers
var nonSimpleAllowResponseHeaders = result.AllowedExposedHeaders
.Where(header =>
!CorsConstants.SimpleResponseHeaders.Contains(header, StringComparer.OrdinalIgnoreCase))
.ToArray();
if (nonSimpleAllowResponseHeaders.Length > 0)
{
headers.SetCommaSeparatedValues(
CorsConstants.AccessControlExposeHeaders,
nonSimpleAllowResponseHeaders);
}
}
if (result.PreflightMaxAge.HasValue)
{
headers[CorsConstants.AccessControlMaxAge]
= result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture);
}
}
private void AddOriginToResult(string origin, CorsPolicy policy, CorsResult result)
{
if (policy.AllowAnyOrigin)
{
if (policy.SupportsCredentials)
{
result.AllowedOrigin = origin;
result.VaryByOrigin = true;
}
else
{
result.AllowedOrigin = CorsConstants.AnyOrigin;
}
}
else if (policy.IsOriginAllowed(origin))
{
result.AllowedOrigin = origin;
if(policy.Origins.Count > 1)
{
result.VaryByOrigin = true;
}
}
}
private static void AddHeaderValues(IList<string> target, IEnumerable<string> headerValues)
{
if (headerValues == null)
{
return;
}
foreach (var current in headerValues)
{
target.Add(current);
}
}
private bool IsOriginAllowed(CorsPolicy policy, StringValues origin)
{
if (StringValues.IsNullOrEmpty(origin))
{
_logger?.RequestDoesNotHaveOriginHeader();
return false;
}
_logger?.RequestHasOriginHeader(origin);
if (policy.AllowAnyOrigin || policy.IsOriginAllowed(origin))
{
return true;
}
_logger?.PolicyFailure();
_logger?.OriginNotAllowed(origin);
return false;
}
}
}

View File

@ -0,0 +1,36 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <inheritdoc />
public class DefaultCorsPolicyProvider : ICorsPolicyProvider
{
private readonly CorsOptions _options;
/// <summary>
/// Creates a new instance of <see cref="DefaultCorsPolicyProvider"/>.
/// </summary>
/// <param name="options">The options configured for the application.</param>
public DefaultCorsPolicyProvider(IOptions<CorsOptions> options)
{
_options = options.Value;
}
/// <inheritdoc />
public Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return Task.FromResult(_options.GetPolicy(policyName ?? _options.DefaultPolicyName));
}
}
}

View File

@ -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 System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// A type which can provide a <see cref="CorsPolicy"/> for a particular <see cref="HttpContext"/>.
/// </summary>
public interface ICorsPolicyProvider
{
/// <summary>
/// Gets a <see cref="CorsPolicy"/> from the given <paramref name="context"/>
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> associated with this call.</param>
/// <param name="policyName">An optional policy name to look for.</param>
/// <returns>A <see cref="CorsPolicy"/></returns>
Task<CorsPolicy> GetPolicyAsync(HttpContext context, string policyName);
}
}

View File

@ -0,0 +1,31 @@
// 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.AspNetCore.Http;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// A type which can evaluate a policy for a particular <see cref="HttpContext"/>.
/// </summary>
public interface ICorsService
{
/// <summary>
/// Evaluates the given <paramref name="policy"/> using the passed in <paramref name="context"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> associated with the call.</param>
/// <param name="policy">The <see cref="CorsPolicy"/> which needs to be evaluated.</param>
/// <returns>A <see cref="CorsResult"/> which contains the result of policy evaluation and can be
/// used by the caller to set appropriate response headers.</returns>
CorsResult EvaluatePolicy(HttpContext context, CorsPolicy policy);
/// <summary>
/// Adds CORS-specific response headers to the given <paramref name="response"/>.
/// </summary>
/// <param name="result">The <see cref="CorsResult"/> used to read the allowed values.</param>
/// <param name="response">The <see cref="HttpResponse"/> associated with the current call.</param>
void ApplyResult(CorsResult result, HttpResponse response);
}
}

View File

@ -0,0 +1,12 @@
// 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.
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// An interface which can be used to identify a type which provides metdata to disable cors for a resource.
/// </summary>
public interface IDisableCorsAttribute
{
}
}

View File

@ -0,0 +1,16 @@
// 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.
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
/// <summary>
/// An interface which can be used to identify a type which provides metadata needed for enabling CORS support.
/// </summary>
public interface IEnableCorsAttribute
{
/// <summary>
/// The name of the policy which needs to be applied.
/// </summary>
string PolicyName { get; set; }
}
}

View File

@ -0,0 +1,19 @@
// 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;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
internal static class UriHelpers
{
public static bool IsSubdomainOf(Uri subdomain, Uri domain)
{
return subdomain.IsAbsoluteUri
&& domain.IsAbsoluteUri
&& subdomain.Scheme == domain.Scheme
&& subdomain.Port == domain.Port
&& subdomain.Host.EndsWith($".{domain.Host}", StringComparison.Ordinal);
}
}
}

View File

@ -0,0 +1,103 @@
// 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 Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Cors.Internal
{
internal static class CORSLoggerExtensions
{
private static readonly Action<ILogger, Exception> _isPreflightRequest;
private static readonly Action<ILogger, string, Exception> _requestHasOriginHeader;
private static readonly Action<ILogger, Exception> _requestDoesNotHaveOriginHeader;
private static readonly Action<ILogger, Exception> _policySuccess;
private static readonly Action<ILogger, Exception> _policyFailure;
private static readonly Action<ILogger, string, Exception> _originNotAllowed;
private static readonly Action<ILogger, string, Exception> _accessControlMethodNotAllowed;
private static readonly Action<ILogger, string, Exception> _requestHeaderNotAllowed;
static CORSLoggerExtensions()
{
_isPreflightRequest = LoggerMessage.Define(
LogLevel.Debug,
1,
"The request is a preflight request.");
_requestHasOriginHeader = LoggerMessage.Define<string>(
LogLevel.Debug,
2,
"The request has an origin header: '{origin}'.");
_requestDoesNotHaveOriginHeader = LoggerMessage.Define(
LogLevel.Debug,
3,
"The request does not have an origin header.");
_policySuccess = LoggerMessage.Define(
LogLevel.Information,
4,
"Policy execution successful.");
_policyFailure = LoggerMessage.Define(
LogLevel.Information,
5,
"Policy execution failed.");
_originNotAllowed = LoggerMessage.Define<string>(
LogLevel.Information,
6,
"Request origin {origin} does not have permission to access the resource.");
_accessControlMethodNotAllowed = LoggerMessage.Define<string>(
LogLevel.Information,
7,
"Request method {accessControlRequestMethod} not allowed in CORS policy.");
_requestHeaderNotAllowed = LoggerMessage.Define<string>(
LogLevel.Information,
8,
"Request header '{requestHeader}' not allowed in CORS policy.");
}
public static void IsPreflightRequest(this ILogger logger)
{
_isPreflightRequest(logger, null);
}
public static void RequestHasOriginHeader(this ILogger logger, string origin)
{
_requestHasOriginHeader(logger, origin, null);
}
public static void RequestDoesNotHaveOriginHeader(this ILogger logger)
{
_requestDoesNotHaveOriginHeader(logger, null);
}
public static void PolicySuccess(this ILogger logger)
{
_policySuccess(logger, null);
}
public static void PolicyFailure(this ILogger logger)
{
_policyFailure(logger, null);
}
public static void OriginNotAllowed(this ILogger logger, string origin)
{
_originNotAllowed(logger, origin, null);
}
public static void AccessControlMethodNotAllowed(this ILogger logger, string accessControlMethod)
{
_accessControlMethodNotAllowed(logger, accessControlMethod, null);
}
public static void RequestHeaderNotAllowed(this ILogger logger, string requestHeader)
{
_requestHeaderNotAllowed(logger, requestHeader, null);
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>CORS middleware and policy for ASP.NET Core to enable cross-origin resource sharing.
Commonly used types:
Microsoft.AspNetCore.Cors.DisableCorsAttribute
Microsoft.AspNetCore.Cors.EnableCorsAttribute</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;cors</PackageTags>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="$(MicrosoftAspNetCoreHttpExtensionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
// 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Cors.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

View File

@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Microsoft.AspNetCore.Cors {
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Cors.Resources", typeof(Resources).GetTypeInfo().Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to PreflightMaxAge must be greater than or equal to 0..
/// </summary>
internal static string PreflightMaxAgeOutOfRange {
get {
return ResourceManager.GetString("PreflightMaxAgeOutOfRange", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="PreflightMaxAgeOutOfRange" xml:space="preserve">
<value>PreflightMaxAge must be greater than or equal to 0.</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
<Project>
<Import Project="..\Directory.Build.props" />
<PropertyGroup>
<DeveloperBuildTestTfms>netcoreapp2.1</DeveloperBuildTestTfms>
<StandardTestTfms>$(DeveloperBuildTestTfms)</StandardTestTfms>
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' ">$(StandardTestTfms);netcoreapp2.0</StandardTestTfms>
<StandardTestTfms Condition=" '$(DeveloperBuild)' != 'true' AND '$(OS)' == 'Windows_NT' ">$(StandardTestTfms);net461</StandardTestTfms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.Sdk" PrivateAssets="All" Version="$(InternalAspNetCoreSdkPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -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.AspNetCore.Cors.Infrastructure
{
public class CorsMiddlewareFunctionalTests : IClassFixture<CorsTestFixture<CorsMiddlewareWebSite.Startup>>
{
public CorsMiddlewareFunctionalTests(CorsTestFixture<CorsMiddlewareWebSite.Startup> 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);
}
}
}

View File

@ -0,0 +1,353 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsMiddlewareTests
{
[Theory]
[InlineData("PuT")]
[InlineData("PUT")]
public async Task CorsRequest_MatchesPolicy_OnCaseInsensitiveAccessControlRequestMethod(string accessControlRequestMethod)
{
// Arrange
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors(builder =>
builder.WithOrigins("http://localhost:5001")
.WithMethods("PUT"));
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services => services.AddCors());
using (var server = new TestServer(hostBuilder))
{
// Act
// Actual request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5001")
.SendAsync(accessControlRequestMethod);
// Assert
response.EnsureSuccessStatusCode();
Assert.Single(response.Headers);
Assert.Equal("Cross origin response", await response.Content.ReadAsStringAsync());
Assert.Equal("http://localhost:5001", response.Headers.GetValues(CorsConstants.AccessControlAllowOrigin).FirstOrDefault());
}
}
[Fact]
public async Task CorsRequest_MatchPolicy_SetsResponseHeaders()
{
// Arrange
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors(builder =>
builder.WithOrigins("http://localhost:5001")
.WithMethods("PUT")
.WithHeaders("Header1")
.WithExposedHeaders("AllowedHeader"));
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services => services.AddCors());
using (var server = new TestServer(hostBuilder))
{
// Act
// Actual request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5001")
.SendAsync("PUT");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.Equal("Cross origin response", await response.Content.ReadAsStringAsync());
Assert.Equal("http://localhost:5001", response.Headers.GetValues(CorsConstants.AccessControlAllowOrigin).FirstOrDefault());
Assert.Equal("AllowedHeader", response.Headers.GetValues(CorsConstants.AccessControlExposeHeaders).FirstOrDefault());
}
}
[Theory]
[InlineData("OpTions")]
[InlineData("OPTIONS")]
public async Task PreFlight_MatchesPolicy_OnCaseInsensitiveOptionsMethod(string preflightMethod)
{
// Arrange
var policy = new CorsPolicy();
policy.Origins.Add("http://localhost:5001");
policy.Methods.Add("PUT");
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors("customPolicy");
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services =>
{
services.AddCors(options =>
{
options.AddPolicy("customPolicy", policy);
});
});
using (var server = new TestServer(hostBuilder))
{
// Act
// Preflight request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5001")
.SendAsync(preflightMethod);
// Assert
response.EnsureSuccessStatusCode();
Assert.Single(response.Headers);
Assert.Equal("http://localhost:5001", response.Headers.GetValues(CorsConstants.AccessControlAllowOrigin).FirstOrDefault());
}
}
[Fact]
public async Task PreFlight_MatchesPolicy_SetsResponseHeaders()
{
// Arrange
var policy = new CorsPolicy();
policy.Origins.Add("http://localhost:5001");
policy.Methods.Add("PUT");
policy.Headers.Add("Header1");
policy.ExposedHeaders.Add("AllowedHeader");
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors("customPolicy");
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services =>
{
services.AddCors(options =>
{
options.AddPolicy("customPolicy", policy);
});
});
using (var server = new TestServer(hostBuilder))
{
// Act
// Preflight request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5001")
.AddHeader(CorsConstants.AccessControlRequestMethod, "PUT")
.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.Equal("http://localhost:5001", response.Headers.GetValues(CorsConstants.AccessControlAllowOrigin).FirstOrDefault());
Assert.Equal("PUT", response.Headers.GetValues(CorsConstants.AccessControlAllowMethods).FirstOrDefault());
}
}
[Fact]
public async Task PreFlightRequest_DoesNotMatchPolicy_DoesNotSetHeaders()
{
// Arrange
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors(builder =>
builder.WithOrigins("http://localhost:5001")
.WithMethods("PUT")
.WithHeaders("Header1")
.WithExposedHeaders("AllowedHeader"));
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services => services.AddCors());
using (var server = new TestServer(hostBuilder))
{
// Act
// Preflight request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5002")
.AddHeader(CorsConstants.AccessControlRequestMethod, "PUT")
.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
Assert.Empty(response.Headers);
}
}
[Fact]
public async Task CorsRequest_DoesNotMatchPolicy_DoesNotSetHeaders()
{
// Arrange
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors(builder =>
builder.WithOrigins("http://localhost:5001")
.WithMethods("PUT")
.WithHeaders("Header1")
.WithExposedHeaders("AllowedHeader"));
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services => services.AddCors());
using (var server = new TestServer(hostBuilder))
{
// Act
// Actual request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5002")
.SendAsync("PUT");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(response.Headers);
}
}
[Fact]
public async Task Uses_PolicyProvider_AsFallback()
{
// Arrange
var corsService = Mock.Of<ICorsService>();
var mockProvider = new Mock<ICorsPolicyProvider>();
mockProvider.Setup(o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
.Returns(Task.FromResult<CorsPolicy>(null))
.Verifiable();
var middleware = new CorsMiddleware(
Mock.Of<RequestDelegate>(),
corsService,
mockProvider.Object,
policyName: null);
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add(CorsConstants.Origin, new[] { "http://example.com" });
// Act
await middleware.Invoke(httpContext);
// Assert
mockProvider.Verify(
o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()),
Times.Once);
}
[Fact]
public async Task DoesNotSetHeaders_ForNoPolicy()
{
// Arrange
var corsService = Mock.Of<ICorsService>();
var mockProvider = new Mock<ICorsPolicyProvider>();
mockProvider.Setup(o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
.Returns(Task.FromResult<CorsPolicy>(null))
.Verifiable();
var middleware = new CorsMiddleware(
Mock.Of<RequestDelegate>(),
corsService,
mockProvider.Object,
policyName: null);
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add(CorsConstants.Origin, new[] { "http://example.com" });
// Act
await middleware.Invoke(httpContext);
// Assert
Assert.Equal(200, httpContext.Response.StatusCode);
Assert.Empty(httpContext.Response.Headers);
mockProvider.Verify(
o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()),
Times.Once);
}
[Fact]
public async Task PreFlight_MatchesDefaultPolicy_SetsResponseHeaders()
{
// Arrange
var hostBuilder = new WebHostBuilder()
.Configure(app =>
{
app.UseCors();
app.Run(async context =>
{
await context.Response.WriteAsync("Cross origin response");
});
})
.ConfigureServices(services =>
{
services.AddCors(options =>
{
options.AddDefaultPolicy(policyBuilder =>
{
policyBuilder
.WithOrigins("http://localhost:5001")
.WithMethods("PUT")
.WithHeaders("Header1")
.WithExposedHeaders("AllowedHeader")
.Build();
});
options.AddPolicy("policy2", policyBuilder =>
{
policyBuilder
.WithOrigins("http://localhost:5002")
.Build();
});
});
});
using (var server = new TestServer(hostBuilder))
{
// Act
// Preflight request.
var response = await server.CreateRequest("/")
.AddHeader(CorsConstants.Origin, "http://localhost:5001")
.AddHeader(CorsConstants.AccessControlRequestMethod, "PUT")
.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal(2, response.Headers.Count());
Assert.Equal("http://localhost:5001", response.Headers.GetValues(CorsConstants.AccessControlAllowOrigin).FirstOrDefault());
Assert.Equal("PUT", response.Headers.GetValues(CorsConstants.AccessControlAllowMethods).FirstOrDefault());
}
}
}
}

View File

@ -0,0 +1,67 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsOptionsTest
{
[Fact]
public void AddDefaultPolicy_SetsDefaultPolicyName()
{
// Arrange
var corsOptions = new CorsOptions();
var expectedPolicy = new CorsPolicy();
// Act
corsOptions.AddPolicy("policy1", new CorsPolicy());
corsOptions.AddDefaultPolicy(expectedPolicy);
corsOptions.AddPolicy("policy3", new CorsPolicy());
// Assert
var actualPolicy = corsOptions.GetPolicy(corsOptions.DefaultPolicyName);
Assert.Same(expectedPolicy, actualPolicy);
}
[Fact]
public void AddDefaultPolicy_OverridesDefaultPolicyName()
{
// Arrange
var corsOptions = new CorsOptions();
var expectedPolicy = new CorsPolicy();
// Act
corsOptions.AddDefaultPolicy(new CorsPolicy());
corsOptions.AddDefaultPolicy(expectedPolicy);
// Assert
var actualPolicy = corsOptions.GetPolicy(corsOptions.DefaultPolicyName);
Assert.Same(expectedPolicy, actualPolicy);
}
[Fact]
public void AddDefaultPolicy_UsingPolicyBuilder_SetsDefaultPolicyName()
{
// Arrange
var corsOptions = new CorsOptions();
CorsPolicy expectedPolicy = null;
// Act
corsOptions.AddPolicy("policy1", policyBuilder =>
{
policyBuilder.AllowAnyOrigin().Build();
});
corsOptions.AddDefaultPolicy(policyBuilder =>
{
expectedPolicy = policyBuilder.AllowAnyOrigin().Build();
});
corsOptions.AddPolicy("policy3", new CorsPolicy());
// Assert
var actualPolicy = corsOptions.GetPolicy(corsOptions.DefaultPolicyName);
Assert.Same(expectedPolicy, actualPolicy);
}
}
}

View File

@ -0,0 +1,292 @@
// 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.Collections.Generic;
using System.Linq;
using Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsPolicyBuilderTests
{
[Fact]
public void Constructor_WithPolicy_AddsTheGivenPolicy()
{
// Arrange
Func<string, bool> isOriginAllowed = origin => true;
var originalPolicy = new CorsPolicy();
originalPolicy.Origins.Add("http://existing.com");
originalPolicy.Headers.Add("Existing");
originalPolicy.Methods.Add("GET");
originalPolicy.ExposedHeaders.Add("ExistingExposed");
originalPolicy.SupportsCredentials = true;
originalPolicy.PreflightMaxAge = TimeSpan.FromSeconds(12);
originalPolicy.IsOriginAllowed = isOriginAllowed;
// Act
var builder = new CorsPolicyBuilder(originalPolicy);
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyHeader);
Assert.False(corsPolicy.AllowAnyMethod);
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.True(corsPolicy.SupportsCredentials);
Assert.NotSame(originalPolicy.Headers, corsPolicy.Headers);
Assert.Equal(originalPolicy.Headers, corsPolicy.Headers);
Assert.NotSame(originalPolicy.Methods, corsPolicy.Methods);
Assert.Equal(originalPolicy.Methods, corsPolicy.Methods);
Assert.NotSame(originalPolicy.Origins, corsPolicy.Origins);
Assert.Equal(originalPolicy.Origins, corsPolicy.Origins);
Assert.NotSame(originalPolicy.ExposedHeaders, corsPolicy.ExposedHeaders);
Assert.Equal(originalPolicy.ExposedHeaders, corsPolicy.ExposedHeaders);
Assert.Equal(TimeSpan.FromSeconds(12), corsPolicy.PreflightMaxAge);
Assert.Same(originalPolicy.IsOriginAllowed, corsPolicy.IsOriginAllowed);
}
[Fact]
public void ConstructorWithPolicy_HavingNullPreflightMaxAge_AddsTheGivenPolicy()
{
// Arrange
var originalPolicy = new CorsPolicy();
originalPolicy.Origins.Add("http://existing.com");
// Act
var builder = new CorsPolicyBuilder(originalPolicy);
// Assert
var corsPolicy = builder.Build();
Assert.Null(corsPolicy.PreflightMaxAge);
Assert.False(corsPolicy.AllowAnyHeader);
Assert.False(corsPolicy.AllowAnyMethod);
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.NotSame(originalPolicy.Origins, corsPolicy.Origins);
Assert.Equal(originalPolicy.Origins, corsPolicy.Origins);
Assert.Empty(corsPolicy.Headers);
Assert.Empty(corsPolicy.Methods);
Assert.Empty(corsPolicy.ExposedHeaders);
}
[Fact]
public void Constructor_WithNoOrigin()
{
// Arrange & Act
var builder = new CorsPolicyBuilder();
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyHeader);
Assert.False(corsPolicy.AllowAnyMethod);
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.False(corsPolicy.SupportsCredentials);
Assert.Empty(corsPolicy.ExposedHeaders);
Assert.Empty(corsPolicy.Headers);
Assert.Empty(corsPolicy.Methods);
Assert.Empty(corsPolicy.Origins);
Assert.Null(corsPolicy.PreflightMaxAge);
}
[Theory]
[InlineData("")]
[InlineData("http://example.com,http://example2.com")]
public void Constructor_WithParamsOrigin_InitializesOrigin(string origin)
{
// Arrange
var origins = origin.Split(',');
// Act
var builder = new CorsPolicyBuilder(origins);
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyHeader);
Assert.False(corsPolicy.AllowAnyMethod);
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.False(corsPolicy.SupportsCredentials);
Assert.Empty(corsPolicy.ExposedHeaders);
Assert.Empty(corsPolicy.Headers);
Assert.Empty(corsPolicy.Methods);
Assert.Equal(origins.ToList(), corsPolicy.Origins);
Assert.Null(corsPolicy.PreflightMaxAge);
}
[Fact]
public void WithOrigins_AddsOrigins()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.WithOrigins("http://example.com", "http://example2.com");
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.Equal(new List<string>() { "http://example.com", "http://example2.com" }, corsPolicy.Origins);
}
[Fact]
public void AllowAnyOrigin_AllowsAny()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.AllowAnyOrigin();
// Assert
var corsPolicy = builder.Build();
Assert.True(corsPolicy.AllowAnyOrigin);
Assert.Equal(new List<string>() { "*" }, corsPolicy.Origins);
}
[Fact]
public void SetIsOriginAllowed_AddsIsOriginAllowed()
{
// Arrange
var builder = new CorsPolicyBuilder();
Func<string, bool> isOriginAllowed = origin => true;
// Act
builder.SetIsOriginAllowed(isOriginAllowed);
// Assert
var corsPolicy = builder.Build();
Assert.Same(corsPolicy.IsOriginAllowed, isOriginAllowed);
}
[Fact]
public void SetIsOriginAllowedToAllowWildcardSubdomains_AllowsWildcardSubdomains()
{
// Arrange
var builder = new CorsPolicyBuilder("http://*.example.com");
// Act
builder.SetIsOriginAllowedToAllowWildcardSubdomains();
// Assert
var corsPolicy = builder.Build();
Assert.True(corsPolicy.IsOriginAllowed("http://test.example.com"));
}
[Fact]
public void WithMethods_AddsMethods()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.WithMethods("PUT", "GET");
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.Equal(new List<string>() { "PUT", "GET" }, corsPolicy.Methods);
}
[Fact]
public void AllowAnyMethod_AllowsAny()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.AllowAnyMethod();
// Assert
var corsPolicy = builder.Build();
Assert.True(corsPolicy.AllowAnyMethod);
Assert.Equal(new List<string>() { "*" }, corsPolicy.Methods);
}
[Fact]
public void WithHeaders_AddsHeaders()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.WithHeaders("example1", "example2");
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.AllowAnyHeader);
Assert.Equal(new List<string>() { "example1", "example2" }, corsPolicy.Headers);
}
[Fact]
public void AllowAnyHeaders_AllowsAny()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.AllowAnyHeader();
// Assert
var corsPolicy = builder.Build();
Assert.True(corsPolicy.AllowAnyHeader);
Assert.Equal(new List<string>() { "*" }, corsPolicy.Headers);
}
[Fact]
public void WithExposedHeaders_AddsExposedHeaders()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.WithExposedHeaders("exposed1", "exposed2");
// Assert
var corsPolicy = builder.Build();
Assert.Equal(new List<string>() { "exposed1", "exposed2" }, corsPolicy.ExposedHeaders);
}
[Fact]
public void SetPreFlightMaxAge_SetsThePreFlightAge()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.SetPreflightMaxAge(TimeSpan.FromSeconds(12));
// Assert
var corsPolicy = builder.Build();
Assert.Equal(TimeSpan.FromSeconds(12), corsPolicy.PreflightMaxAge);
}
[Fact]
public void AllowCredential_SetsSupportsCredentials_ToTrue()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.AllowCredentials();
// Assert
var corsPolicy = builder.Build();
Assert.True(corsPolicy.SupportsCredentials);
}
[Fact]
public void DisallowCredential_SetsSupportsCredentials_ToFalse()
{
// Arrange
var builder = new CorsPolicyBuilder();
// Act
builder.DisallowCredentials();
// Assert
var corsPolicy = builder.Build();
Assert.False(corsPolicy.SupportsCredentials);
}
}
}

View File

@ -0,0 +1,85 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public sealed class CorsPolicyExtensionsTest
{
[Fact]
public void IsOriginAnAllowedSubdomain_ReturnsTrueIfPolicyContainsOrigin()
{
// Arrange
const string origin = "http://sub.domain";
var policy = new CorsPolicy();
policy.Origins.Add(origin);
// Act
var actual = policy.IsOriginAnAllowedSubdomain(origin);
// Assert
Assert.True(actual);
}
[Theory]
[InlineData(null)]
[InlineData("null")]
[InlineData("http://")]
[InlineData("http://*")]
[InlineData("http://.domain")]
[InlineData("http://.domain/hello")]
public void IsOriginAnAllowedSubdomain_ReturnsFalseIfOriginIsMalformedUri(string malformedOrigin)
{
// Arrange
var policy = new CorsPolicy();
policy.Origins.Add("http://*.domain");
// Act
var actual = policy.IsOriginAnAllowedSubdomain(malformedOrigin);
// Assert
Assert.False(actual);
}
[Theory]
[InlineData("http://sub.domain", "http://*.domain")]
[InlineData("http://sub.sub.domain", "http://*.domain")]
[InlineData("http://sub.sub.domain", "http://*.sub.domain")]
[InlineData("http://sub.domain:4567", "http://*.domain:4567")]
public void IsOriginAnAllowedSubdomain_ReturnsTrue_WhenASubdomain(string origin, string allowedOrigin)
{
// Arrange
var policy = new CorsPolicy();
policy.Origins.Add(allowedOrigin);
// Act
var isAllowed = policy.IsOriginAnAllowedSubdomain(origin);
// Assert
Assert.True(isAllowed);
}
[Theory]
[InlineData("http://domain", "http://*.domain")]
[InlineData("http://sub.domain", "http://domain")]
[InlineData("http://sub.domain:1234", "http://*.domain:5678")]
[InlineData("http://sub.domain", "http://domain.*")]
[InlineData("http://sub.sub.domain", "http://sub.*.domain")]
[InlineData("http://sub.domain.hacker", "http://*.domain")]
[InlineData("https://sub.domain", "http://*.domain")]
public void IsOriginAnAllowedSubdomain_ReturnsFalse_WhenNotASubdomain(string origin, string allowedOrigin)
{
// Arrange
var policy = new CorsPolicy();
policy.Origins.Add(allowedOrigin);
// Act
var isAllowed = policy.IsOriginAnAllowedSubdomain(origin);
// Assert
Assert.False(isAllowed);
}
}
}

View File

@ -0,0 +1,74 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsPolicyTest
{
[Fact]
public void Default_Constructor()
{
// Arrange & Act
var corsPolicy = new CorsPolicy();
// Assert
Assert.False(corsPolicy.AllowAnyHeader);
Assert.False(corsPolicy.AllowAnyMethod);
Assert.False(corsPolicy.AllowAnyOrigin);
Assert.False(corsPolicy.SupportsCredentials);
Assert.Empty(corsPolicy.ExposedHeaders);
Assert.Empty(corsPolicy.Headers);
Assert.Empty(corsPolicy.Methods);
Assert.Empty(corsPolicy.Origins);
Assert.Null(corsPolicy.PreflightMaxAge);
Assert.NotNull(corsPolicy.IsOriginAllowed);
}
[Fact]
public void SettingNegativePreflightMaxAge_Throws()
{
// Arrange
var policy = new CorsPolicy();
// Act
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
{
policy.PreflightMaxAge = TimeSpan.FromSeconds(-12);
});
// Assert
Assert.Equal(
$"PreflightMaxAge must be greater than or equal to 0.{Environment.NewLine}Parameter name: value",
exception.Message);
}
[Fact]
public void ToString_ReturnsThePropertyValues()
{
// Arrange
var corsPolicy = new CorsPolicy
{
PreflightMaxAge = TimeSpan.FromSeconds(12),
SupportsCredentials = true
};
corsPolicy.Headers.Add("foo");
corsPolicy.Headers.Add("bar");
corsPolicy.Origins.Add("http://example.com");
corsPolicy.Origins.Add("http://example.org");
corsPolicy.Methods.Add("GET");
// Act
var policyString = corsPolicy.ToString();
// Assert
Assert.Equal(
@"AllowAnyHeader: False, AllowAnyMethod: False, AllowAnyOrigin: False, PreflightMaxAge: 12,"+
" SupportsCredentials: True, Origins: {http://example.com,http://example.org}, Methods: {GET},"+
" Headers: {foo,bar}, ExposedHeaders: {}",
policyString);
}
}
}

View File

@ -0,0 +1,69 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsResultTest
{
[Fact]
public void Default_Constructor()
{
// Arrange & Act
var result = new CorsResult();
// Assert
Assert.Empty(result.AllowedHeaders);
Assert.Empty(result.AllowedExposedHeaders);
Assert.Empty(result.AllowedMethods);
Assert.False(result.SupportsCredentials);
Assert.Null(result.AllowedOrigin);
Assert.Null(result.PreflightMaxAge);
}
[Fact]
public void SettingNegativePreflightMaxAge_Throws()
{
// Arrange
var result = new CorsResult();
// Act
var exception = Assert.Throws<ArgumentOutOfRangeException>(() =>
{
result.PreflightMaxAge = TimeSpan.FromSeconds(-1);
});
// Assert
Assert.Equal(
$"PreflightMaxAge must be greater than or equal to 0.{Environment.NewLine}Parameter name: value",
exception.Message);
}
[Fact]
public void ToString_ReturnsThePropertyValues()
{
// Arrange
var corsResult = new CorsResult
{
SupportsCredentials = true,
PreflightMaxAge = TimeSpan.FromSeconds(30),
AllowedOrigin = "*"
};
corsResult.AllowedExposedHeaders.Add("foo");
corsResult.AllowedHeaders.Add("bar");
corsResult.AllowedHeaders.Add("baz");
corsResult.AllowedMethods.Add("GET");
// Act
var result = corsResult.ToString();
// Assert
Assert.Equal(
@"AllowCredentials: True, PreflightMaxAge: 30, AllowOrigin: *," +
" AllowExposedHeaders: {foo}, AllowHeaders: {bar,baz}, AllowMethods: {GET}",
result);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class CorsTestFixture<TStartup> : IDisposable
where TStartup : class
{
private readonly TestServer _server;
public CorsTestFixture()
{
var builder = new WebHostBuilder().UseStartup<TStartup>();
_server = new TestServer(builder);
Client = _server.CreateClient();
Client.BaseAddress = new Uri("http://localhost");
}
public HttpClient Client { get; }
public void Dispose()
{
Client.Dispose();
_server.Dispose();
}
}
}

View File

@ -0,0 +1,56 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class DefaultPolicyProviderTests
{
[Fact]
public async Task UsesTheDefaultPolicyName()
{
// Arrange
var options = new CorsOptions();
var policy = new CorsPolicy();
options.AddPolicy(options.DefaultPolicyName, policy);
var corsOptions = new TestCorsOptions
{
Value = options
};
var policyProvider = new DefaultCorsPolicyProvider(corsOptions);
// Act
var actualPolicy = await policyProvider.GetPolicyAsync(new DefaultHttpContext(), policyName: null);
// Assert
Assert.Same(policy, actualPolicy);
}
[Theory]
[InlineData("")]
[InlineData("policyName")]
public async Task GetsNamedPolicy(string policyName)
{
// Arrange
var options = new CorsOptions();
var policy = new CorsPolicy();
options.AddPolicy(policyName, policy);
var corsOptions = new TestCorsOptions
{
Value = options
};
var policyProvider = new DefaultCorsPolicyProvider(corsOptions);
// Act
var actualPolicy = await policyProvider.GetPolicyAsync(new DefaultHttpContext(), policyName);
// Assert
Assert.Same(policy, actualPolicy);
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\WebSites\CorsMiddlewareWebSite\CorsMiddlewareWebSite.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Cors\Microsoft.AspNetCore.Cors.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="Moq" Version="$(MoqPackageVersion)" />
<PackageReference Include="xunit.analyzers" Version="$(XunitAnalyzersPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualstudioPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,12 @@
// 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.Extensions.Options;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public class TestCorsOptions : IOptions<CorsOptions>
{
public CorsOptions Value { get; set; }
}
}

View File

@ -0,0 +1,66 @@
// 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.Collections.Generic;
using Xunit;
namespace Microsoft.AspNetCore.Cors.Infrastructure
{
public sealed class UriHelpersTests
{
[Theory]
[MemberData(nameof(IsSubdomainOfTestData))]
public void TestIsSubdomainOf(Uri subdomain, Uri domain)
{
// Act
bool isSubdomain = UriHelpers.IsSubdomainOf(subdomain, domain);
// Assert
Assert.True(isSubdomain);
}
[Theory]
[MemberData(nameof(IsNotSubdomainOfTestData))]
public void TestIsSubdomainOf_ReturnsFalse_WhenNotSubdomain(Uri subdomain, Uri domain)
{
// Act
bool isSubdomain = UriHelpers.IsSubdomainOf(subdomain, domain);
// Assert
Assert.False(isSubdomain);
}
public static IEnumerable<object[]> IsSubdomainOfTestData
{
get
{
return new[]
{
new object[] {new Uri("http://sub.domain"), new Uri("http://domain")},
new object[] {new Uri("https://sub.domain"), new Uri("https://domain")},
new object[] {new Uri("https://sub.domain:5678"), new Uri("https://domain:5678")},
new object[] {new Uri("http://sub.sub.domain"), new Uri("http://domain")},
new object[] {new Uri("http://sub.sub.domain"), new Uri("http://sub.domain")}
};
}
}
public static IEnumerable<object[]> IsNotSubdomainOfTestData
{
get
{
return new[]
{
new object[] {new Uri("http://subdomain"), new Uri("http://domain")},
new object[] {new Uri("https://sub.domain"), new Uri("http://domain")},
new object[] {new Uri("https://sub.domain:1234"), new Uri("https://domain:5678")},
new object[] {new Uri("http://domain.tld"), new Uri("http://domain")},
new object[] {new Uri("http://sub.domain.tld"), new Uri("http://domain")},
new object[] {new Uri("/relativeUri", UriKind.Relative), new Uri("http://domain")},
new object[] {new Uri("http://sub.domain"), new Uri("/relative", UriKind.Relative)}
};
}
}
}
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.Cors\Microsoft.AspNetCore.Cors.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -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.AspNetCore.Http;
namespace CorsMiddlewareWebSite
{
public class EchoMiddleware
{
/// <summary>
/// Instantiates a new <see cref="EchoMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
public EchoMiddleware(RequestDelegate next)
{
}
/// <summary>
/// Echo the request's path in the response. Does not invoke later middleware in the pipeline.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> of the current request.</param>
/// <returns>A <see cref="Task"/> that completes when writing to the response is done.</returns>
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);
}
}
}

View File

@ -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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
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<EchoMiddleware>();
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@ -0,0 +1,4 @@
CorsMiddlewareWebSite
===
This web site illustrates how to use CorsMiddleware to apply a policy for entire application.

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>

12
src/CORS/version.props Normal file
View File

@ -0,0 +1,12 @@
<Project>
<PropertyGroup>
<VersionPrefix>2.1.1</VersionPrefix>
<VersionSuffix>rtm</VersionSuffix>
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
<BuildNumber Condition="'$(BuildNumber)' == ''">t000</BuildNumber>
<FeatureBranchVersionPrefix Condition="'$(FeatureBranchVersionPrefix)' == ''">a-</FeatureBranchVersionPrefix>
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(FeatureBranchVersionSuffix)' != ''">$(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-'))</VersionSuffix>
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
</PropertyGroup>
</Project>