[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:
parent
82a41f0dac
commit
2cae0cd451
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"private": true,
|
|
||||||
"devDependencies": {
|
|
||||||
"jest": "^23.6.0",
|
|
||||||
"merge": "^1.2.1",
|
|
||||||
"puppeteer": "^1.15.0"
|
|
||||||
},
|
|
||||||
"dependencies": {},
|
|
||||||
"scripts": {
|
|
||||||
"test": "jest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
@ -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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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!");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -243,8 +243,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Crypto
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "..\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.DataProtection.Abstractions", "..\DataProtection\Abstractions\src\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}"
|
||||||
EndProject
|
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}"
|
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
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NodeServices", "NodeServices", "{17B409B3-7EC6-49D8-847E-CFAA319E01B5}"
|
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
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks", "ConcurrencyLimiter\perf\Microbenchmarks\Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj", "{737B26B4-CFC6-4B44-9070-DD36334E85B3}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks", "ConcurrencyLimiter\perf\Microbenchmarks\Microsoft.AspNetCore.ConcurrencyLimiter.Microbenchmarks.csproj", "{737B26B4-CFC6-4B44-9070-DD36334E85B3}"
|
||||||
EndProject
|
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}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore", "..\DefaultBuilder\src\Microsoft.AspNetCore.csproj", "{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}"
|
||||||
EndProject
|
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}"
|
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|x64.Build.0 = Release|Any CPU
|
||||||
{7343B4E4-C5A2-49E2-B431-4D1E6A26E424}.Release|x86.ActiveCfg = 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
|
{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.ActiveCfg = Debug|Any CPU
|
||||||
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8}.Debug|Any CPU.Build.0 = 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
|
{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|x64.Build.0 = Release|Any CPU
|
||||||
{737B26B4-CFC6-4B44-9070-DD36334E85B3}.Release|x86.ActiveCfg = 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
|
{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.ActiveCfg = Debug|Any CPU
|
||||||
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075}.Debug|Any CPU.Build.0 = 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
|
{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}
|
{3AD5B221-C718-4F14-883A-4345DC90CF9C} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
|
||||||
{227030D6-99AD-4C6A-AE70-1333BCBE8705} = {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}
|
{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}
|
{47B6636D-09A3-47AE-9303-9F5D15EEE9D8} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
|
||||||
{C801B6A3-906F-406F-BA25-EAE0B4BCE5C9} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
|
{C801B6A3-906F-406F-BA25-EAE0B4BCE5C9} = {17B409B3-7EC6-49D8-847E-CFAA319E01B5}
|
||||||
{40951683-DBC4-437A-BBAB-2FA7147E11EA} = {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}
|
{7E2EA6E2-31FE-418A-9AE4-955A4C708AE7} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
|
||||||
{CDDD7C43-5BEB-4E3E-8A59-FCDC83C9FBCF} = {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}
|
{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}
|
{46B4FE62-06A1-4D54-B3E8-D8B4B3560075} = {ACA6DDB9-7592-47CE-A740-D15BF307E9E0}
|
||||||
{92E11EBB-759E-4DA8-AB61-A9977D9F97D0} = {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}
|
{D0CB733B-4CE8-4F6C-BBB9-548EA1A96966} = {D6FA4ABE-E685-4EDD-8B06-D8777E76B472}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue