[CORS] Remove E2E functional tests (#13826)

* The tests are low value.
* The tests are flaky.
* The tests cover mostly browser behavior not spec compliance.
* It's an area that doesn't change often.
* We have unit tests to cover the casuistic.
This commit is contained in:
Javier Calvarro Nelson 2019-09-11 12:31:06 +02:00 committed by GitHub
parent 82a41f0dac
commit 2cae0cd451
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 0 additions and 4957 deletions

View File

@ -1,45 +0,0 @@
// 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.Text;
using Xunit.Sdk;
namespace FunctionalTests
{
public class Assert : Xunit.Assert
{
public static void Success(in ProcessResult processResult)
{
if (processResult.ExitCode != 0)
{
throw new ProcessAssertException(processResult);
}
}
private class ProcessAssertException : XunitException
{
public ProcessAssertException(in ProcessResult processResult)
{
Result = processResult;
}
public ProcessResult Result { get; }
public override string Message
{
get
{
var message = new StringBuilder();
message.Append(Result.ProcessStartInfo.FileName);
message.Append(" ");
message.Append(Result.ProcessStartInfo.Arguments);
message.Append($" exited with {Result.ExitCode}.");
message.AppendLine();
message.AppendLine();
message.Append(Result.Output);
return message.ToString();
}
}
}
}
}

View File

@ -1,34 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TestGroupName>Cors.FunctionalTests</TestGroupName>
<DefaultItemExcludes>$(DefaultItemExcludes);node_modules\**\*</DefaultItemExcludes>
<!-- Tests do not work on Helix or when bin/ directory is not in project directory due to undeclared dependency on test content. -->
<!-- https://github.com/aspnet/AspNetCore/issues/7990 -->
<BuildHelixPayload>false</BuildHelixPayload>
<BaseOutputPath />
<OutputPath />
</PropertyGroup>
<ItemGroup>
<!-- We don't need anything in this assembly, we just want to make sure it's built -->
<ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
<ProjectReference Include="..\testassets\TestOrigin\TestOrigin.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\testassets\TestDestination\TestDestination.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>
<ProjectReference Include="..\testassets\CorsMiddlewareWebSite\CorsMiddlewareWebSite.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Logging.Testing" />
</ItemGroup>
</Project>

View File

@ -1,16 +0,0 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.props))\Directory.Build.props" />
<!--
This project file is present so the CI will run `yarn install` on the package.json file.
The C# invokes node to start the tests.
-->
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsBuildable>false</IsBuildable>
<IsTestProject>false</IsTestProject>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Directory.Build.targets))\Directory.Build.targets" />
</Project>

View File

@ -1,164 +0,0 @@
// 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.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Abstractions;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
namespace FunctionalTests
{
public class CorsMiddlewareFunctionalTests : LoggedTest
{
public CorsMiddlewareFunctionalTests(ITestOutputHelper output)
: base(output)
{
Output = output;
}
public ITestOutputHelper Output { get; }
[Flaky("https://github.com/aspnet/aspnetcore-internal/issues/2865", FlakyOn.All)]
[ConditionalTheory]
[OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Disabling this test on OSX until we have a resolution for https://github.com/aspnet/AspNetCore-Internal/issues/1619")]
[InlineData("Startup")]
[InlineData("StartupWithoutEndpointRouting")]
public async Task RunClientTests(string startup)
{
using (StartLog(out var loggerFactory))
using (var deploymentResult = await CreateDeployments(loggerFactory, startup))
{
ProcessStartInfo processStartInfo;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
processStartInfo = new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/c npm test --no-color --no-watchman",
};
}
else
{
processStartInfo = new ProcessStartInfo
{
FileName = "npm",
Arguments = "test --no-watchman",
};
}
// Disallow the test from downloading \ installing chromium.
processStartInfo.Environment["PUPPETEER_SKIP_CHROMIUM_DOWNLOAD"] = "true";
processStartInfo.Environment["DESTINATION_PORT"] = deploymentResult.DestinationResult.HttpClient.BaseAddress.Port.ToString();
processStartInfo.Environment["ORIGIN_PORT"] = deploymentResult.OriginResult.HttpClient.BaseAddress.Port.ToString();
processStartInfo.Environment["SECOND_ORIGIN_PORT"] = deploymentResult.SecondOriginResult.HttpClient.BaseAddress.Port.ToString();
// Act
var result = await ProcessManager.RunProcessAsync(processStartInfo, loggerFactory.CreateLogger("ProcessManager"));
// Assert
Assert.Success(result);
Assert.Contains("Test Suites: 1 passed, 1 total", result.Output);
}
}
private static async Task<CorsDeploymentResult> CreateDeployments(ILoggerFactory loggerFactory, string startup)
{
// https://github.com/aspnet/AspNetCore/issues/7990
#pragma warning disable 0618
var solutionPath = TestPathUtilities.GetSolutionRootDirectory("Middleware");
#pragma warning restore 0618
var configuration =
#if RELEASE
"Release";
#else
"Debug";
#endif
var originParameters = new DeploymentParameters
{
TargetFramework = "netcoreapp3.0",
RuntimeFlavor = RuntimeFlavor.CoreClr,
ServerType = ServerType.Kestrel,
ApplicationPath = Path.Combine(solutionPath, "CORS", "test", "testassets", "TestOrigin"),
PublishApplicationBeforeDeployment = false,
ApplicationType = ApplicationType.Portable,
Configuration = configuration,
};
var originFactory = ApplicationDeployerFactory.Create(originParameters, loggerFactory);
var originDeployment = await originFactory.DeployAsync();
var secondOriginFactory = ApplicationDeployerFactory.Create(originParameters, loggerFactory);
var secondOriginDeployment = await secondOriginFactory.DeployAsync();
var port = originDeployment.HttpClient.BaseAddress.Port;
var destinationParameters = new DeploymentParameters
{
TargetFramework = "netcoreapp3.0",
RuntimeFlavor = RuntimeFlavor.CoreClr,
ServerType = ServerType.Kestrel,
ApplicationPath = Path.Combine(solutionPath, "CORS", "test", "testassets", "TestDestination"),
PublishApplicationBeforeDeployment = false,
ApplicationType = ApplicationType.Portable,
Configuration = configuration,
EnvironmentVariables =
{
["CORS_STARTUP"] = startup,
["ORIGIN_PORT"] = port.ToString()
}
};
var destinationFactory = ApplicationDeployerFactory.Create(destinationParameters, loggerFactory);
var destinationDeployment = await destinationFactory.DeployAsync();
return new CorsDeploymentResult(originFactory, originDeployment, secondOriginFactory, secondOriginDeployment, destinationFactory, destinationDeployment);
}
private readonly struct CorsDeploymentResult : IDisposable
{
public CorsDeploymentResult(
ApplicationDeployer originDeployer,
DeploymentResult originResult,
ApplicationDeployer secondOriginDeployer,
DeploymentResult secondOriginResult,
ApplicationDeployer destinationDeployer,
DeploymentResult destinationResult)
{
OriginDeployer = originDeployer;
OriginResult = originResult;
SecondOriginDeployer = secondOriginDeployer;
SecondOriginResult = secondOriginResult;
DestinationDeployer = destinationDeployer;
DestinationResult = destinationResult;
}
public ApplicationDeployer OriginDeployer { get; }
public DeploymentResult OriginResult { get; }
public ApplicationDeployer SecondOriginDeployer { get; }
public DeploymentResult SecondOriginResult { get; }
public ApplicationDeployer DestinationDeployer { get; }
public DeploymentResult DestinationResult { get; }
public void Dispose()
{
OriginDeployer.Dispose();
SecondOriginDeployer.Dispose();
DestinationDeployer.Dispose();
}
}
}
}

View File

@ -1,105 +0,0 @@
// 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.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace FunctionalTests
{
internal static class ProcessManager
{
private static readonly TimeSpan Timeout = TimeSpan.FromMinutes(3);
public static Task<ProcessResult> RunProcessAsync(ProcessStartInfo processStartInfo, ILogger logger)
{
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
var process = new Process()
{
StartInfo = processStartInfo,
EnableRaisingEvents = true,
};
var output = new StringBuilder();
var outputLock = new object();
process.ErrorDataReceived += Process_ErrorDataReceived;
process.OutputDataReceived += Process_OutputDataReceived;
logger.LogInformation($"Executing command '{process.StartInfo.FileName} {process.StartInfo.Arguments}'");
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
var timeoutTask = Task.Delay(Timeout).ContinueWith((t) =>
{
// Don't timeout during debug sessions
while (Debugger.IsAttached)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
if (process.HasExited)
{
// This will happen on success, the 'real' task has already completed so this value will
// never be visible.
return (ProcessResult)default;
}
// This is a timeout.
process.Kill();
throw new TimeoutException($"command '{process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {Timeout}.");
});
var waitTask = Task.Run(() =>
{
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
// this code used Process.Exited, which could result in us missing some output due to the ordering of
// events.
//
// See the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx
if (!process.WaitForExit(int.MaxValue))
{
// unreachable - the timeoutTask will kill the process before this happens.
throw new TimeoutException();
}
process.WaitForExit();
string outputString;
lock (outputLock)
{
outputString = output.ToString();
}
return new ProcessResult(processStartInfo, process.ExitCode, outputString);
});
return Task.WhenAny<ProcessResult>(waitTask, timeoutTask).Unwrap();
void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
logger.LogInformation(e.Data);
lock (outputLock)
{
output.AppendLine(e.Data);
}
}
void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
logger.LogInformation(e.Data);
lock (outputLock)
{
output.AppendLine(e.Data);
}
}
}
}
}

View File

@ -1,21 +0,0 @@
// 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.Diagnostics;
namespace FunctionalTests
{
public readonly struct ProcessResult
{
public ProcessResult(ProcessStartInfo processStartInfo, int exitCode, string output)
{
ProcessStartInfo = processStartInfo;
ExitCode = exitCode;
Output = output;
}
public ProcessStartInfo ProcessStartInfo { get; }
public int ExitCode { get; }
public string Output { get; }
}
}

View File

@ -1,12 +0,0 @@
{
"private": true,
"devDependencies": {
"jest": "^23.6.0",
"merge": "^1.2.1",
"puppeteer": "^1.15.0"
},
"dependencies": {},
"scripts": {
"test": "jest"
}
}

View File

@ -1,310 +0,0 @@
const puppeteer = require('puppeteer');
const os = require("os");
const hostname = os.hostname();
const originPortVar = process.env.ORIGIN_PORT;
const destinationPortVar = process.env.DESTINATION_PORT;
const secondOriginPortVar = process.env.SECOND_ORIGIN_PORT;
const corsServerPath = `http://${hostname}:${destinationPortVar}`;
// e.g., npm test --debug
// In debug mode we show the editor, slow down operations, and increase the timeout for each test
let debug = process.env.npm_config_debug || false;
jest.setTimeout(debug ? 60000 : 30000);
let browser;
let error;
beforeAll(async () => {
const options = debug ?
{ headless: false, slowMo: 100 } :
{ args: ['--no-sandbox'] };
const label = 'Launch puppeteer ';
console.log('Begin launching puppeteer');
console.time(label);
try {
browser = await puppeteer.launch(options);
} catch (ex) {
error = ex;
}
console.timeEnd(label);
});
afterAll(async () => {
if (browser) {
await browser.close();
}
});
describe('Browser is initialized', () => {
// Workaround for https://github.com/jasmine/jasmine/issues/1533.
// Jasmine will not report errors from beforeAll and instead fail all the tests that
// expect the browser to be available. This test allows us to ensure the setup was successful
// and if unsuccessful report the error
test('no errors on launch', () => {
expect(error).toBeUndefined();
expect(browser).toBeDefined();
});
});
describe('CORS allowed origin tests ', () => {
const testPagePath = `http://${hostname}:${originPortVar}/`;
let page;
beforeAll(async () => {
page = await browser.newPage();
await page.goto(testPagePath);
});
test('allows simple GET requests', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'GET', mode: 'cors' };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('allows simple PUT requests when any method is allowed', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'PUT', mode: 'cors' };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
// This one is weird - although the server performs a preflight request and receives a Access-Control-Allow-Methods: PUT,
// the browser happily ignores the disallowed POST method.
test('allows POST requests when not explicitly allowed', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-header-method`;
const options = {
method: 'POST',
mode: 'cors',
body: JSON.stringify({ hello: 'world' }),
headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' })
};
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('allows header to be sent when allowed', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-header-method`;
const options = { method: 'PUT', mode: 'cors', headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' }) };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('does not allow disallowed HTTP Methods', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-header-method`;
const options = { method: 'DELETE', mode: 'cors', headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' }) };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('does not allow disallowed header', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-header-method`;
const options = { method: 'PUT', mode: 'cors', headers: new Headers({ "X-Not-Test": "value", 'Content-Type': 'application/json' }) };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('does not allow fetch with credentials in non-Preflighted request', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'POST', mode: 'cors', credentials: 'include' };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('does not allow fetch with credentials in Preflighted request', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'PUT', mode: 'cors', credentials: 'include' };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('allows request with credentials', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-credentials`;
const options = { method: 'GET', mode: 'cors', credentials: 'include' };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('allows Preflighted request with credentials', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-credentials`;
const options = {
method: 'PUT', mode: 'cors', credentials: 'include', headers: new Headers({
'X-Custom-Header': 'X-Custom-Value'
})
};
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('disallows accessing header when not included in exposed-header', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/exposed-header`;
const options = { method: 'GET', mode: 'cors' };
const response = await fetch(url, options);
try {
return response.headers.get('x-disallowedheader');
} catch (e) {
return null;
}
}, corsServerPath);
expect(result).toBeNull();
});
test('allows accessing header when included in exposed-header', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/exposed-header`;
const options = { method: 'GET', mode: 'cors' };
const response = await fetch(url, options);
try {
return response.headers.get('x-allowedheader');
} catch (e) {
return e;
}
}, corsServerPath);
expect(result).toBe("Test-Value");
});
});
describe('CORS disallowed origin tests ', () => {
const testPagePath = `http://${hostname}:${secondOriginPortVar}/`;
let page;
beforeAll(async () => {
page = await browser.newPage();
await page.goto(testPagePath);
});
test('allow opaque requests without CORS', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'GET', mode: 'no-cors' };
// The request will succeed, but we get an opaque filtered response (https://fetch.spec.whatwg.org/#concept-filtered-response).
const response = await fetch(url, options);
return response.type;
}, corsServerPath);
expect(result).toBe("opaque");
});
test('does not allow requests when origin is disallowed', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'GET', mode: 'cors' };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('does not allow preflight requests when origin is disallowed', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-origin`;
const options = { method: 'PUT', mode: 'cors' };
return await fetch(url, options);
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
test('allow requests to any origin endpoint', async () => {
const result = await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-all`;
const options = { method: 'PUT', mode: 'cors' };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
expect(result).toBe(200);
});
test('does not allow requests to any origin endpoint with credentials', async () => {
expect.assertions(1);
try {
await page.evaluate(async (corsServerPath) => {
const url = `${corsServerPath}/allow-all`;
const options = { method: 'PUT', mode: 'cors', headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' }), credentials: 'include' };
const response = await fetch(url, options);
return response.status;
}, corsServerPath);
} catch (e) {
expect(e).toBeDefined();
}
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
// 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.Diagnostics;
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class Program
{
public static void Main(string[] args)
{
using (var host = WebHost.CreateDefaultBuilder(args)
.UseUrls("http://+:0")
.UseStartup(GetStartupType())
.Build())
{
host.Run();
}
}
private static Type GetStartupType()
{
var startup = Environment.GetEnvironmentVariable("CORS_STARTUP");
if (startup == null)
{
return typeof(Startup);
}
else
{
switch (startup)
{
case "Startup":
return typeof(Startup);
case "StartupWithoutEndpointRouting":
return typeof(StartupWithoutEndpointRouting);
}
}
throw new InvalidOperationException("Could not resolve the startup type. Unexpected CORS_STARTUP environment variable.");
}
}
}

View File

@ -1,93 +0,0 @@
// 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.Diagnostics;
using System.Net;
using System.Text;
using System.Threading.Tasks;
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
{
private static readonly string DefaultAllowedOrigin = $"http://{Dns.GetHostName()}:{Environment.GetEnvironmentVariable("ORIGIN_PORT")}";
private readonly ILogger<Startup> _logger;
public Startup(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<Startup>();
_logger.LogInformation($"Setting up CORS middleware to allow clients on {DefaultAllowedOrigin}");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowOrigin", policy => policy
.WithOrigins(DefaultAllowedOrigin)
.AllowAnyMethod()
.AllowAnyHeader());
options.AddPolicy("AllowHeaderMethod", policy => policy
.WithOrigins(DefaultAllowedOrigin)
.WithHeaders("X-Test", "Content-Type")
.WithMethods("PUT"));
options.AddPolicy("AllowCredentials", policy => policy
.WithOrigins(DefaultAllowedOrigin)
.AllowAnyHeader()
.WithMethods("GET", "PUT")
.AllowCredentials());
options.AddPolicy("ExposedHeader", policy => policy
.WithOrigins(DefaultAllowedOrigin)
.WithExposedHeaders("X-AllowedHeader", "Content-Length"));
options.AddPolicy("AllowAll", policy => policy
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddRouting();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.Map("/allow-origin", HandleRequest).RequireCors("AllowOrigin");
endpoints.Map("/allow-header-method", HandleRequest).RequireCors("AllowHeaderMethod");
endpoints.Map("/allow-credentials", HandleRequest).RequireCors("AllowCredentials");
endpoints.Map("/exposed-header", HandleRequest).RequireCors("ExposedHeader");
endpoints.Map("/allow-all", HandleRequest).RequireCors("AllowAll");
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
private Task HandleRequest(HttpContext context)
{
var content = Encoding.UTF8.GetBytes("Hello world");
context.Response.Headers["X-AllowedHeader"] = "Test-Value";
context.Response.Headers["X-DisallowedHeader"] = "Test-Value";
context.Response.ContentType = "text/plain; charset=utf-8";
context.Response.ContentLength = content.Length;
return context.Response.Body.WriteAsync(content, 0, content.Length);
}
}
}

View File

@ -1,88 +0,0 @@
// 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;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class StartupWithoutEndpointRouting
{
private static readonly string DefaultAllowedOrigin = $"http://{Dns.GetHostName()}:{Environment.GetEnvironmentVariable("ORIGIN_PORT")}";
private readonly ILogger<StartupWithoutEndpointRouting> _logger;
public StartupWithoutEndpointRouting(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<StartupWithoutEndpointRouting>();
_logger.LogInformation($"Setting up CORS middleware to allow clients on {DefaultAllowedOrigin}");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
public void Configure(IApplicationBuilder app)
{
app.Map("/allow-origin", innerBuilder =>
{
innerBuilder.UseCors(policy => policy
.WithOrigins(DefaultAllowedOrigin)
.AllowAnyMethod()
.AllowAnyHeader());
innerBuilder.UseMiddleware<SampleMiddleware>();
});
app.Map("/allow-header-method", innerBuilder =>
{
innerBuilder.UseCors(policy => policy
.WithOrigins(DefaultAllowedOrigin)
.WithHeaders("X-Test", "Content-Type")
.WithMethods("PUT"));
innerBuilder.UseMiddleware<SampleMiddleware>();
});
app.Map("/allow-credentials", innerBuilder =>
{
innerBuilder.UseCors(policy => policy
.WithOrigins(DefaultAllowedOrigin)
.AllowAnyHeader()
.WithMethods("GET", "PUT")
.AllowCredentials());
innerBuilder.UseMiddleware<SampleMiddleware>();
});
app.Map("/exposed-header", innerBuilder =>
{
innerBuilder.UseCors(policy => policy
.WithOrigins(DefaultAllowedOrigin)
.WithExposedHeaders("X-AllowedHeader", "Content-Length"));
innerBuilder.UseMiddleware<SampleMiddleware>();
});
app.Map("/allow-all", innerBuilder =>
{
innerBuilder.UseCors(policy => policy
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
innerBuilder.UseMiddleware<SampleMiddleware>();
});
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
}
}

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Cors" />
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>
</Project>

View File

@ -1,31 +0,0 @@
// 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.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace SampleDestination
{
public class SampleMiddleware
{
private readonly RequestDelegate _next;
public SampleMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
var content = Encoding.UTF8.GetBytes("Hello world");
context.Response.Headers["X-AllowedHeader"] = "Test-Value";
context.Response.Headers["X-DisallowedHeader"] = "Test-Value";
context.Response.ContentType = "text/plain; charset=utf-8";
context.Response.ContentLength = content.Length;
return context.Response.Body.WriteAsync(content, 0, content.Length);
}
}
}

View File

@ -1,22 +0,0 @@
// 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.Diagnostics;
using System.IO;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace SampleOrigin
{
public class Program
{
public static void Main(string[] args)
{
using (var host = WebHost.CreateDefaultBuilder(args).UseUrls("http://+:0").UseStartup<Startup>().Build())
{
host.Run();
}
}
}
}

View File

@ -1,24 +0,0 @@
// 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.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace SampleOrigin
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
}
}

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
</ItemGroup>
</Project>

View File

@ -1,2 +0,0 @@
<h1>Welcome to the CORS test suite. Please wait...</h1>
<a href="test.htm">Click here for the browser test suite.</a>

View File

@ -1,135 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
.success {
background-color: #4CAF50;
}
</style>
<title>CORS Sample</title>
</head>
<body>
<p>CORS Sample</p>
<table>
<thead>
<tr>
<th>Scenario</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr id="scenarios"></tr>
</tbody>
</table>
<script type="text/javascript">
(function () {
function _fetch(options) {
const url = `http://${location.hostname}:9000/${options.testPath}`;
options.cors = 'cors';
return fetch(url, options);
}
function assertSuccess(options) {
return _fetch(options).then(s => true).catch(s => false);
}
function assertFailure(options) {
return _fetch(options).then(s => false).catch(s => true);
}
const scenarios = {
"Simple GET Request": assertSuccess({
testPath: 'allow-origin',
method: 'GET'
}),
"Simple PUT Request": assertSuccess({
testPath: 'allow-origin',
method: 'PUT'
}),
"Disallowed DELETE Method": assertFailure({
testPath: 'allow-header-method',
method: 'DELETE'
}),
"Allowed Header": assertSuccess({
testPath: 'allow-header-method',
method: 'PUT',
headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' })
}),
// This one is weird - although the server performs a preflight request and receives a Access-Control-Allow-Methods: PUT,
// the browser happily ignores the disallowed POST method
"Allowed Header Disallowed POST": assertSuccess({
testPath: 'allow-header-method',
method: 'POST',
headers: new Headers({ "X-Test": "value", 'Content-Type': 'application/json' })
}),
"Disallowed Header": assertFailure({
testPath: 'allow-header-method',
method: 'POST',
headers: new Headers({ "Api-Key": "some_key", 'Content-Type': 'application/json' })
}),
"Disallowed Credentials": assertFailure({
testPath: 'allow-origin',
method: 'POST',
credentials: 'include'
}),
"Disallowed Credentials with preflight": assertFailure({
testPath: 'allow-origin',
method: 'PUT',
credentials: 'include'
}),
"Allowed Credentials": assertSuccess({
testPath: 'allow-credentials',
method: 'GET',
credentials: 'include'
}),
"Allowed Credentials with preflight": assertSuccess({
testPath: 'allow-credentials',
method: 'PUT',
credentials: 'include'
}),
"Disallowed exposed header": _fetch({
testPath: 'exposed-header',
method: 'GET'
}).then(response => !response.headers.has("x-disallowedheader")),
"Allowed exposed header": _fetch({
testPath: 'exposed-header',
method: 'GET'
}).then(response => response.headers.has("x-allowedheader"))
};
const scenariosElement = document.querySelector('#scenarios');
Object.keys(scenarios).map(scenario => {
const row = document.createElement('tr');
row.setAttribute("class", "scenario");
scenariosElement.appendChild(row);
let resultHtml = '<span class="waiting"></span>';
const setHtml = () => row.innerHTML = `<td>${scenario}</td><td>${resultHtml}</td>`;
setHtml();
return scenarios[scenario].then(result => {
resultHtml = result ?
'<span class="success"></span>' :
'<span class="failed"><span>';
setHtml();
});
});
})();
</script>
</body>
</html>

View File

@ -243,8 +243,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Crypto
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "..\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CORS.FunctionalTests", "CORS\test\FunctionalTests\CORS.FunctionalTests.csproj", "{E025D98E-BD85-474A-98A9-E7F44F392F8E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration", "..\Servers\IIS\IISIntegration\src\Microsoft.AspNetCore.Server.IISIntegration.csproj", "{47B6636D-09A3-47AE-9303-9F5D15EEE9D8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NodeServices", "NodeServices", "{17B409B3-7EC6-49D8-847E-CFAA319E01B5}"
@ -289,10 +287,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Author
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks", "ConcurrencyLimiter\perf\Microbenchmarks\Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj", "{737B26B4-CFC6-4B44-9070-DD36334E85B3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestDestination", "CORS\test\testassets\TestDestination\TestDestination.csproj", "{DFEB537A-2D35-4C62-8A13-42798DF66A80}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestOrigin", "CORS\test\testassets\TestOrigin\TestOrigin.csproj", "{E0521105-3A7B-480B-B962-0BFC2838D919}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore", "..\DefaultBuilder\src\Microsoft.AspNetCore.csproj", "{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "..\Servers\IIS\IntegrationTesting.IIS\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{92E11EBB-759E-4DA8-AB61-A9977D9F97D0}"
@ -1377,18 +1371,6 @@ Global
{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}.Release|x64.Build.0 = Release|Any CPU
{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}.Release|x86.ActiveCfg = Release|Any CPU
{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}.Release|x86.Build.0 = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|x64.ActiveCfg = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|x64.Build.0 = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Debug|x86.Build.0 = Debug|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|Any CPU.Build.0 = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|x64.ActiveCfg = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|x64.Build.0 = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|x86.ActiveCfg = Release|Any CPU
{E025D98E-BD85-474A-98A9-E7F44F392F8E}.Release|x86.Build.0 = Release|Any CPU
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -1581,30 +1563,6 @@ Global
{737B26B4-CFC6-4B44-9070-DD36334E85B3}.Release|x64.Build.0 = Release|Any CPU
{737B26B4-CFC6-4B44-9070-DD36334E85B3}.Release|x86.ActiveCfg = Release|Any CPU
{737B26B4-CFC6-4B44-9070-DD36334E85B3}.Release|x86.Build.0 = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|x64.ActiveCfg = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|x64.Build.0 = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|x86.ActiveCfg = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Debug|x86.Build.0 = Debug|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|Any CPU.Build.0 = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|x64.ActiveCfg = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|x64.Build.0 = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|x86.ActiveCfg = Release|Any CPU
{DFEB537A-2D35-4C62-8A13-42798DF66A80}.Release|x86.Build.0 = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|x64.ActiveCfg = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|x64.Build.0 = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|x86.ActiveCfg = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Debug|x86.Build.0 = Debug|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|Any CPU.Build.0 = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|x64.ActiveCfg = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|x64.Build.0 = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|x86.ActiveCfg = Release|Any CPU
{E0521105-3A7B-480B-B962-0BFC2838D919}.Release|x86.Build.0 = Release|Any CPU
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -1746,7 +1704,6 @@ Global
{3AD5B221-C718-4F14-883A-4345DC90CF9C} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{227030D6-99AD-4C6A-AE70-1333BCBE8705} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{7343B4E4-C5A2-49E2-B431-4D1E6A26E424} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{E025D98E-BD85-474A-98A9-E7F44F392F8E} = {4967DE1B-FEC2-4C2B-8F7F-6262D67C9434}
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{C801B6A3-906F-406F-BA25-EAE0B4BCE5C9} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
{40951683-DBC4-437A-BBAB-2FA7147E11EA} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
@ -1765,8 +1722,6 @@ Global
{7E2EA6E2-31FE-418A-9AE4-955A4C708AE7} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{CDDD7C43-5BEB-4E3E-8A59-FCDC83C9FBCF} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{737B26B4-CFC6-4B44-9070-DD36334E85B3} = {8C9AA8A2-9D1F-4450-9F8D-56BAB6F3D343}
{DFEB537A-2D35-4C62-8A13-42798DF66A80} = {BD7B3AD8-0BA6-405F-8CF6-24B9464D4B5B}
{E0521105-3A7B-480B-B962-0BFC2838D919} = {BD7B3AD8-0BA6-405F-8CF6-24B9464D4B5B}
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}