Skip to content

[Release/6.0-rc2] Fix and test HttpSys delegation #36698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions AspNetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.App.Co
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.App.Analyzers.Test", "src\Framework\AspNetCoreAnalyzers\test\Microsoft.AspNetCore.App.Analyzers.Test.csproj", "{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.HttpSys.NonHelixTests", "src\Servers\HttpSys\test\NonHelixTests\Microsoft.AspNetCore.Server.HttpSys.NonHelixTests.csproj", "{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelegationSite", "src\Servers\HttpSys\test\testassets\DelegationSite\DelegationSite.csproj", "{14217994-9F24-4E14-87B6-58367ED0413B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -7907,6 +7911,30 @@ Global
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x64.Build.0 = Release|Any CPU
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x86.ActiveCfg = Release|Any CPU
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x86.Build.0 = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x64.ActiveCfg = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x64.Build.0 = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x86.ActiveCfg = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x86.Build.0 = Debug|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|Any CPU.Build.0 = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x64.ActiveCfg = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x64.Build.0 = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x86.ActiveCfg = Release|Any CPU
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x86.Build.0 = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x64.ActiveCfg = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x64.Build.0 = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x86.ActiveCfg = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x86.Build.0 = Debug|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|Any CPU.Build.0 = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x64.ActiveCfg = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x64.Build.0 = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x86.ActiveCfg = Release|Any CPU
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -8727,6 +8755,8 @@ Global
{636B73F6-9EED-435D-9DA8-EB3C7FDFAB1C} = {23C56C5B-9D3F-4069-88C4-87ACCFE05DBC}
{78D9A5AF-6089-44F4-93AB-F7E96925E4FD} = {23C56C5B-9D3F-4069-88C4-87ACCFE05DBC}
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03} = {12541C41-58FB-46F4-BA0F-0D56B5B45A18}
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1} = {C3722C5D-E159-4AB3-AF60-769185B31B47}
{14217994-9F24-4E14-87B6-58367ED0413B} = {C3722C5D-E159-4AB3-AF60-769185B31B47}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public override async Task<DeploymentResult> DeployAsync()
HostProcess.EnableRaisingEvents = true;
HostProcess.OutputDataReceived += (sender, dataArgs) =>
{
if (string.Equals(dataArgs.Data, ApplicationStartedMessage))
if (!string.IsNullOrEmpty(dataArgs.Data) && dataArgs.Data.Contains(ApplicationStartedMessage))
{
started.TrySetResult();
}
Expand Down
2 changes: 2 additions & 0 deletions src/Servers/HttpSys/HttpSysServer.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
"src\\Servers\\HttpSys\\samples\\TestClient\\TestClient.csproj",
"src\\Servers\\HttpSys\\src\\Microsoft.AspNetCore.Server.HttpSys.csproj",
"src\\Servers\\HttpSys\\test\\FunctionalTests\\Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj",
"src\\Servers\\HttpSys\\test\\NonHelixTests\\Microsoft.AspNetCore.Server.HttpSys.NonHelixTests.csproj",
"src\\Servers\\HttpSys\\test\\Tests\\Microsoft.AspNetCore.Server.HttpSys.Tests.csproj",
"src\\Servers\\HttpSys\\test\\testassets\\DelegationSite\\DelegationSite.csproj",
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",
"src\\Servers\\IIS\\IIS\\src\\Microsoft.AspNetCore.Server.IIS.csproj",
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
Expand Down
2 changes: 1 addition & 1 deletion src/Servers/HttpSys/src/MessagePump.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public MessagePump(IOptions<HttpSysOptions> options, ILoggerFactory loggerFactor
_serverAddresses = new ServerAddressesFeature();
Features.Set<IServerAddressesFeature>(_serverAddresses);

if (HttpApi.IsFeatureSupported(HttpApiTypes.HTTP_FEATURE_ID.HttpFeatureDelegateEx))
if (HttpApi.SupportsDelegation)
{
var delegationProperty = new ServerDelegationPropertyFeature(Listener.RequestQueue, _logger);
Features.Set<IServerDelegationFeature>(delegationProperty);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@
<Reference Include="Microsoft.Net.Http.Headers" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.AspNetCore.Server.HttpSys.FunctionalTests" />
<InternalsVisibleTo Include="Microsoft.AspNetCore.Server.HttpSys.NonHelixTests" />
</ItemGroup>

</Project>
10 changes: 5 additions & 5 deletions src/Servers/HttpSys/src/NativeInterop/HttpApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ internal static HTTP_API_VERSION ApiVersion
internal static bool SupportsTrailers { get; private set; }
[MemberNotNullWhen(true, nameof(HttpSetRequestProperty))]
internal static bool SupportsReset { get; private set; }
internal static bool SupportsDelegation { get; private set; }

static HttpApi()
{
Expand All @@ -136,18 +137,17 @@ private static void InitHttpApi(ushort majorVersion, ushort minorVersion)
version.HttpApiMajorVersion = majorVersion;
version.HttpApiMinorVersion = minorVersion;

var statusCode = HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null);
var statusCode = HttpInitialize(version, (uint)(HTTP_FLAGS.HTTP_INITIALIZE_SERVER | HTTP_FLAGS.HTTP_INITIALIZE_CONFIG), null);

supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;

if (supported)
{
HttpApiModule = SafeLibraryHandle.Open(HTTPAPI);
HttpSetRequestProperty = HttpApiModule.GetProcAddress<HttpSetRequestPropertyInvoker>("HttpSetRequestProperty", throwIfNotFound: false);

SupportsReset = HttpSetRequestProperty != null;
// Trailers support was added in the same release as Reset, but there's no method we can export to check it directly.
SupportsTrailers = SupportsReset;
SupportsTrailers = IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureResponseTrailers);
SupportsDelegation = IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureDelegateEx);
}
}

Expand All @@ -160,7 +160,7 @@ internal static bool Supported
}
}

internal static bool IsFeatureSupported(HTTP_FEATURE_ID feature)
private static bool IsFeatureSupported(HTTP_FEATURE_ID feature)
{
try
{
Expand Down
6 changes: 0 additions & 6 deletions src/Servers/HttpSys/src/Properties/AssemblyInfo.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/Servers/HttpSys/src/StandardFeatureCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static StandardFeatureCollection()
_featureFuncLookup[typeof(ITlsHandshakeFeature)] = ctx => ctx.GetTlsHandshakeFeature();
}

if (HttpApi.IsFeatureSupported(HttpApiTypes.HTTP_FEATURE_ID.HttpFeatureDelegateEx))
if (HttpApi.SupportsDelegation)
{
_featureFuncLookup[typeof(IHttpSysRequestDelegationFeature)] = _identityFunc;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes;

namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
{
Expand All @@ -17,14 +11,8 @@ public class DelegateSupportedConditionAttribute : Attribute, ITestCondition
private readonly bool _isSupported;
public DelegateSupportedConditionAttribute(bool isSupported) => _isSupported = isSupported;

private readonly Lazy<bool> _isDelegateSupported = new Lazy<bool>(CanDelegate);
public bool IsMet => (_isDelegateSupported.Value == _isSupported);
public bool IsMet => HttpApi.SupportsDelegation == _isSupported;

public string SkipReason => $"Http.Sys does {(_isSupported ? "not" : "")} support delegating requests";

private static bool CanDelegate()
{
return HttpApi.IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureDelegateEx);
}
}
}
3 changes: 3 additions & 0 deletions src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ public async Task DelegateAfterRequestBodyReadShouldThrow()
[DelegateSupportedCondition(false)]
public async Task DelegationFeaturesAreNull()
{
// Testing the DelegateSupportedCondition
Assert.True(Environment.OSVersion.Version < new Version(10, 0, 22000), "This should be supported on Win 11.");

using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
{
var delegateFeature = httpContext.Features.Get<IHttpSysRequestDelegationFeature>();
Expand Down
70 changes: 70 additions & 0 deletions src/Servers/HttpSys/test/NonHelixTests/DelegateOutOfProcTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net.Http;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing;
using Xunit.Abstractions;

namespace Microsoft.AspNetCore.Server.HttpSys.NonHelixTests
{
public class DelegateOutOfProcTests : LoggedTest
{
public DelegateOutOfProcTests(ITestOutputHelper output) : base(output) { }

[ConditionalFact]
[DelegateSupportedCondition(true)]
public async Task CanDelegateOutOfProcess()
{
using var _ = StartLog(out var loggerFactory);

var logger = loggerFactory.CreateLogger("CanDelegateOutOfProcess");

// https://github.com/dotnet/aspnetcore/issues/8247
#pragma warning disable 0618
var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("HttpSysServer"), "test", "testassets",
"DelegationSite");
#pragma warning restore 0618

var deploymentParameters = new DeploymentParameters(
applicationPath,
ServerType.HttpSys,
RuntimeFlavor.CoreClr,
RuntimeArchitecture.x64)
{
EnvironmentName = "Testing",
TargetFramework = Tfm.Default,
ApplicationType = ApplicationType.Portable,
PublishApplicationBeforeDeployment = true,
StatusMessagesEnabled = true
};

var queueName = Guid.NewGuid().ToString();
deploymentParameters.EnvironmentVariables["queue"] = queueName;

using var deployer = new SelfHostDeployer(deploymentParameters, loggerFactory);
var deploymentResult = await deployer.DeployAsync().DefaultTimeout();

// Make sure the deployment really worked
var responseString = await deploymentResult.HttpClient.GetStringAsync("").DefaultTimeout();
Assert.Equal("Hello from delegatee", responseString);

DelegationRule destination = default;
using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
{
var delegateFeature = httpContext.Features.Get<IHttpSysRequestDelegationFeature>();
delegateFeature.DelegateRequest(destination);
return Task.CompletedTask;
});

var delegationProperty = delegator.Features.Get<IServerDelegationFeature>();
using (destination = delegationProperty.CreateDelegationRule(queueName, deploymentResult.ApplicationBaseUri))
{
// Send a request to the delegator that gets transfered to the delegatee in the other process.
using var client = new HttpClient();
responseString = await client.GetStringAsync(delegatorAddress).DefaultTimeout();
Assert.Equal("Hello from delegatee", responseString);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Testing;

namespace Microsoft.AspNetCore.Server.HttpSys.NonHelixTests
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DelegateSupportedConditionAttribute : Attribute, ITestCondition
{
private readonly bool _isSupported;
public DelegateSupportedConditionAttribute(bool isSupported) => _isSupported = isSupported;

public bool IsMet => HttpApi.SupportsDelegation == _isSupported;

public string SkipReason => $"Http.Sys does {(_isSupported ? "not" : "")} support delegating requests";
}
}
36 changes: 36 additions & 0 deletions src/Servers/HttpSys/test/NonHelixTests/DummyApplication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;

namespace Microsoft.AspNetCore.Server.HttpSys
{
internal class DummyApplication : IHttpApplication<HttpContext>
{
private readonly RequestDelegate _requestDelegate;

public DummyApplication() : this(context => Task.CompletedTask) { }

public DummyApplication(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
}

public HttpContext CreateContext(IFeatureCollection contextFeatures)
{
return new DefaultHttpContext(contextFeatures);
}

public void DisposeContext(HttpContext httpContext, Exception exception)
{

}

public async Task ProcessRequestAsync(HttpContext httpContext)
{
await _requestDelegate(httpContext);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<TestGroupName>HttpSys.NonHelixTests</TestGroupName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<!-- 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/dotnet/aspnetcore/issues/8247 -->
<BuildHelixPayload>false</BuildHelixPayload>
<BaseOutputPath />
<OutputPath />
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Server.HttpSys" />
<Reference Include="Microsoft.Extensions.Hosting" />
</ItemGroup>

<PropertyGroup>
<!--Imitate IIS Express so we can use it's cert bindings-->
<PackageTags>214124cd-d05b-4309-9af9-9caa44b2b74a</PackageTags>
</PropertyGroup>

<ItemGroup>
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
<ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Testing;
using Xunit;

[assembly: OSSkipCondition(OperatingSystems.MacOSX)]
[assembly: OSSkipCondition(OperatingSystems.Linux)]
[assembly: CollectionBehavior(DisableTestParallelization = true)]
Loading