Skip to content

Commit a43284c

Browse files
authored
[Release/6.0-rc2] Fix and test HttpSys delegation (#36698)
* Out of proc delegation tests * Troubleshoot IsFeatureSupported * Fix test * Fix formatting * Seperate tests * Cleanup * Fix SLN
1 parent e0b60e8 commit a43284c

21 files changed

+340
-31
lines changed

AspNetCore.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.App.Co
16561656
EndProject
16571657
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}"
16581658
EndProject
1659+
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}"
1660+
EndProject
1661+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelegationSite", "src\Servers\HttpSys\test\testassets\DelegationSite\DelegationSite.csproj", "{14217994-9F24-4E14-87B6-58367ED0413B}"
1662+
EndProject
16591663
Global
16601664
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16611665
Debug|Any CPU = Debug|Any CPU
@@ -7907,6 +7911,30 @@ Global
79077911
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x64.Build.0 = Release|Any CPU
79087912
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x86.ActiveCfg = Release|Any CPU
79097913
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03}.Release|x86.Build.0 = Release|Any CPU
7914+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
7915+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
7916+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x64.ActiveCfg = Debug|Any CPU
7917+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x64.Build.0 = Debug|Any CPU
7918+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x86.ActiveCfg = Debug|Any CPU
7919+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Debug|x86.Build.0 = Debug|Any CPU
7920+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
7921+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|Any CPU.Build.0 = Release|Any CPU
7922+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x64.ActiveCfg = Release|Any CPU
7923+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x64.Build.0 = Release|Any CPU
7924+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x86.ActiveCfg = Release|Any CPU
7925+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1}.Release|x86.Build.0 = Release|Any CPU
7926+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
7927+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|Any CPU.Build.0 = Debug|Any CPU
7928+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x64.ActiveCfg = Debug|Any CPU
7929+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x64.Build.0 = Debug|Any CPU
7930+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x86.ActiveCfg = Debug|Any CPU
7931+
{14217994-9F24-4E14-87B6-58367ED0413B}.Debug|x86.Build.0 = Debug|Any CPU
7932+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|Any CPU.ActiveCfg = Release|Any CPU
7933+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|Any CPU.Build.0 = Release|Any CPU
7934+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x64.ActiveCfg = Release|Any CPU
7935+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x64.Build.0 = Release|Any CPU
7936+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x86.ActiveCfg = Release|Any CPU
7937+
{14217994-9F24-4E14-87B6-58367ED0413B}.Release|x86.Build.0 = Release|Any CPU
79107938
EndGlobalSection
79117939
GlobalSection(SolutionProperties) = preSolution
79127940
HideSolutionNode = FALSE
@@ -8727,6 +8755,8 @@ Global
87278755
{636B73F6-9EED-435D-9DA8-EB3C7FDFAB1C} = {23C56C5B-9D3F-4069-88C4-87ACCFE05DBC}
87288756
{78D9A5AF-6089-44F4-93AB-F7E96925E4FD} = {23C56C5B-9D3F-4069-88C4-87ACCFE05DBC}
87298757
{B739B8B6-94F5-4F05-9497-28A7C6EBBC03} = {12541C41-58FB-46F4-BA0F-0D56B5B45A18}
8758+
{37BDB2D5-EEEC-48C9-A7AD-5A2A73BD31B1} = {C3722C5D-E159-4AB3-AF60-769185B31B47}
8759+
{14217994-9F24-4E14-87B6-58367ED0413B} = {C3722C5D-E159-4AB3-AF60-769185B31B47}
87308760
EndGlobalSection
87318761
GlobalSection(ExtensibilityGlobals) = postSolution
87328762
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

src/Hosting/Server.IntegrationTesting/src/Deployers/SelfHostDeployer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public override async Task<DeploymentResult> DeployAsync()
150150
HostProcess.EnableRaisingEvents = true;
151151
HostProcess.OutputDataReceived += (sender, dataArgs) =>
152152
{
153-
if (string.Equals(dataArgs.Data, ApplicationStartedMessage))
153+
if (!string.IsNullOrEmpty(dataArgs.Data) && dataArgs.Data.Contains(ApplicationStartedMessage))
154154
{
155155
started.TrySetResult();
156156
}

src/Servers/HttpSys/HttpSysServer.slnf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
"src\\Servers\\HttpSys\\samples\\TestClient\\TestClient.csproj",
3232
"src\\Servers\\HttpSys\\src\\Microsoft.AspNetCore.Server.HttpSys.csproj",
3333
"src\\Servers\\HttpSys\\test\\FunctionalTests\\Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj",
34+
"src\\Servers\\HttpSys\\test\\NonHelixTests\\Microsoft.AspNetCore.Server.HttpSys.NonHelixTests.csproj",
3435
"src\\Servers\\HttpSys\\test\\Tests\\Microsoft.AspNetCore.Server.HttpSys.Tests.csproj",
36+
"src\\Servers\\HttpSys\\test\\testassets\\DelegationSite\\DelegationSite.csproj",
3537
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",
3638
"src\\Servers\\IIS\\IIS\\src\\Microsoft.AspNetCore.Server.IIS.csproj",
3739
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",

src/Servers/HttpSys/src/MessagePump.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public MessagePump(IOptions<HttpSysOptions> options, ILoggerFactory loggerFactor
5555
_serverAddresses = new ServerAddressesFeature();
5656
Features.Set<IServerAddressesFeature>(_serverAddresses);
5757

58-
if (HttpApi.IsFeatureSupported(HttpApiTypes.HTTP_FEATURE_ID.HttpFeatureDelegateEx))
58+
if (HttpApi.SupportsDelegation)
5959
{
6060
var delegationProperty = new ServerDelegationPropertyFeature(Listener.RequestQueue, _logger);
6161
Features.Set<IServerDelegationFeature>(delegationProperty);

src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,9 @@
2828
<Reference Include="Microsoft.Net.Http.Headers" />
2929
</ItemGroup>
3030

31+
<ItemGroup>
32+
<InternalsVisibleTo Include="Microsoft.AspNetCore.Server.HttpSys.FunctionalTests" />
33+
<InternalsVisibleTo Include="Microsoft.AspNetCore.Server.HttpSys.NonHelixTests" />
34+
</ItemGroup>
35+
3136
</Project>

src/Servers/HttpSys/src/NativeInterop/HttpApi.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ internal static HTTP_API_VERSION ApiVersion
125125
internal static bool SupportsTrailers { get; private set; }
126126
[MemberNotNullWhen(true, nameof(HttpSetRequestProperty))]
127127
internal static bool SupportsReset { get; private set; }
128+
internal static bool SupportsDelegation { get; private set; }
128129

129130
static HttpApi()
130131
{
@@ -136,18 +137,17 @@ private static void InitHttpApi(ushort majorVersion, ushort minorVersion)
136137
version.HttpApiMajorVersion = majorVersion;
137138
version.HttpApiMinorVersion = minorVersion;
138139

139-
var statusCode = HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null);
140+
var statusCode = HttpInitialize(version, (uint)(HTTP_FLAGS.HTTP_INITIALIZE_SERVER | HTTP_FLAGS.HTTP_INITIALIZE_CONFIG), null);
140141

141142
supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
142143

143144
if (supported)
144145
{
145146
HttpApiModule = SafeLibraryHandle.Open(HTTPAPI);
146147
HttpSetRequestProperty = HttpApiModule.GetProcAddress<HttpSetRequestPropertyInvoker>("HttpSetRequestProperty", throwIfNotFound: false);
147-
148148
SupportsReset = HttpSetRequestProperty != null;
149-
// Trailers support was added in the same release as Reset, but there's no method we can export to check it directly.
150-
SupportsTrailers = SupportsReset;
149+
SupportsTrailers = IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureResponseTrailers);
150+
SupportsDelegation = IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureDelegateEx);
151151
}
152152
}
153153

@@ -160,7 +160,7 @@ internal static bool Supported
160160
}
161161
}
162162

163-
internal static bool IsFeatureSupported(HTTP_FEATURE_ID feature)
163+
private static bool IsFeatureSupported(HTTP_FEATURE_ID feature)
164164
{
165165
try
166166
{

src/Servers/HttpSys/src/Properties/AssemblyInfo.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/Servers/HttpSys/src/StandardFeatureCollection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static StandardFeatureCollection()
4848
_featureFuncLookup[typeof(ITlsHandshakeFeature)] = ctx => ctx.GetTlsHandshakeFeature();
4949
}
5050

51-
if (HttpApi.IsFeatureSupported(HttpApiTypes.HTTP_FEATURE_ID.HttpFeatureDelegateEx))
51+
if (HttpApi.SupportsDelegation)
5252
{
5353
_featureFuncLookup[typeof(IHttpSysRequestDelegationFeature)] = _identityFunc;
5454
}
Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
94
using Microsoft.AspNetCore.Testing;
10-
using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes;
115

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

20-
private readonly Lazy<bool> _isDelegateSupported = new Lazy<bool>(CanDelegate);
21-
public bool IsMet => (_isDelegateSupported.Value == _isSupported);
14+
public bool IsMet => HttpApi.SupportsDelegation == _isSupported;
2215

2316
public string SkipReason => $"Http.Sys does {(_isSupported ? "not" : "")} support delegating requests";
24-
25-
private static bool CanDelegate()
26-
{
27-
return HttpApi.IsFeatureSupported(HTTP_FEATURE_ID.HttpFeatureDelegateEx);
28-
}
2917
}
3018
}

src/Servers/HttpSys/test/FunctionalTests/DelegateTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ public async Task DelegateAfterRequestBodyReadShouldThrow()
148148
[DelegateSupportedCondition(false)]
149149
public async Task DelegationFeaturesAreNull()
150150
{
151+
// Testing the DelegateSupportedCondition
152+
Assert.True(Environment.OSVersion.Version < new Version(10, 0, 22000), "This should be supported on Win 11.");
153+
151154
using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
152155
{
153156
var delegateFeature = httpContext.Features.Get<IHttpSysRequestDelegationFeature>();
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Net.Http;
5+
using Microsoft.AspNetCore.Server.IntegrationTesting;
6+
using Microsoft.AspNetCore.Testing;
7+
using Xunit.Abstractions;
8+
9+
namespace Microsoft.AspNetCore.Server.HttpSys.NonHelixTests
10+
{
11+
public class DelegateOutOfProcTests : LoggedTest
12+
{
13+
public DelegateOutOfProcTests(ITestOutputHelper output) : base(output) { }
14+
15+
[ConditionalFact]
16+
[DelegateSupportedCondition(true)]
17+
public async Task CanDelegateOutOfProcess()
18+
{
19+
using var _ = StartLog(out var loggerFactory);
20+
21+
var logger = loggerFactory.CreateLogger("CanDelegateOutOfProcess");
22+
23+
// https://github.com/dotnet/aspnetcore/issues/8247
24+
#pragma warning disable 0618
25+
var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("HttpSysServer"), "test", "testassets",
26+
"DelegationSite");
27+
#pragma warning restore 0618
28+
29+
var deploymentParameters = new DeploymentParameters(
30+
applicationPath,
31+
ServerType.HttpSys,
32+
RuntimeFlavor.CoreClr,
33+
RuntimeArchitecture.x64)
34+
{
35+
EnvironmentName = "Testing",
36+
TargetFramework = Tfm.Default,
37+
ApplicationType = ApplicationType.Portable,
38+
PublishApplicationBeforeDeployment = true,
39+
StatusMessagesEnabled = true
40+
};
41+
42+
var queueName = Guid.NewGuid().ToString();
43+
deploymentParameters.EnvironmentVariables["queue"] = queueName;
44+
45+
using var deployer = new SelfHostDeployer(deploymentParameters, loggerFactory);
46+
var deploymentResult = await deployer.DeployAsync().DefaultTimeout();
47+
48+
// Make sure the deployment really worked
49+
var responseString = await deploymentResult.HttpClient.GetStringAsync("").DefaultTimeout();
50+
Assert.Equal("Hello from delegatee", responseString);
51+
52+
DelegationRule destination = default;
53+
using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
54+
{
55+
var delegateFeature = httpContext.Features.Get<IHttpSysRequestDelegationFeature>();
56+
delegateFeature.DelegateRequest(destination);
57+
return Task.CompletedTask;
58+
});
59+
60+
var delegationProperty = delegator.Features.Get<IServerDelegationFeature>();
61+
using (destination = delegationProperty.CreateDelegationRule(queueName, deploymentResult.ApplicationBaseUri))
62+
{
63+
// Send a request to the delegator that gets transfered to the delegatee in the other process.
64+
using var client = new HttpClient();
65+
responseString = await client.GetStringAsync(delegatorAddress).DefaultTimeout();
66+
Assert.Equal("Hello from delegatee", responseString);
67+
}
68+
}
69+
}
70+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Testing;
5+
6+
namespace Microsoft.AspNetCore.Server.HttpSys.NonHelixTests
7+
{
8+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
9+
public class DelegateSupportedConditionAttribute : Attribute, ITestCondition
10+
{
11+
private readonly bool _isSupported;
12+
public DelegateSupportedConditionAttribute(bool isSupported) => _isSupported = isSupported;
13+
14+
public bool IsMet => HttpApi.SupportsDelegation == _isSupported;
15+
16+
public string SkipReason => $"Http.Sys does {(_isSupported ? "not" : "")} support delegating requests";
17+
}
18+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Hosting.Server;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.AspNetCore.Http.Features;
7+
8+
namespace Microsoft.AspNetCore.Server.HttpSys
9+
{
10+
internal class DummyApplication : IHttpApplication<HttpContext>
11+
{
12+
private readonly RequestDelegate _requestDelegate;
13+
14+
public DummyApplication() : this(context => Task.CompletedTask) { }
15+
16+
public DummyApplication(RequestDelegate requestDelegate)
17+
{
18+
_requestDelegate = requestDelegate;
19+
}
20+
21+
public HttpContext CreateContext(IFeatureCollection contextFeatures)
22+
{
23+
return new DefaultHttpContext(contextFeatures);
24+
}
25+
26+
public void DisposeContext(HttpContext httpContext, Exception exception)
27+
{
28+
29+
}
30+
31+
public async Task ProcessRequestAsync(HttpContext httpContext)
32+
{
33+
await _requestDelegate(httpContext);
34+
}
35+
}
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
5+
<TestGroupName>HttpSys.NonHelixTests</TestGroupName>
6+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
7+
8+
<!-- Tests do not work on Helix or when bin/ directory is not in project directory due to undeclared dependency on test content. -->
9+
<!-- https://github.com/dotnet/aspnetcore/issues/8247 -->
10+
<BuildHelixPayload>false</BuildHelixPayload>
11+
<BaseOutputPath />
12+
<OutputPath />
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<Reference Include="Microsoft.AspNetCore.Server.HttpSys" />
17+
<Reference Include="Microsoft.Extensions.Hosting" />
18+
</ItemGroup>
19+
20+
<PropertyGroup>
21+
<!--Imitate IIS Express so we can use it's cert bindings-->
22+
<PackageTags>214124cd-d05b-4309-9af9-9caa44b2b74a</PackageTags>
23+
</PropertyGroup>
24+
25+
<ItemGroup>
26+
<Content Include="testroot\**\*" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
27+
<ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
28+
</ItemGroup>
29+
30+
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.AspNetCore.Testing;
5+
using Xunit;
6+
7+
[assembly: OSSkipCondition(OperatingSystems.MacOSX)]
8+
[assembly: OSSkipCondition(OperatingSystems.Linux)]
9+
[assembly: CollectionBehavior(DisableTestParallelization = true)]

0 commit comments

Comments
 (0)