IIS HTTP/2 Response Trailers (#24120)

This commit is contained in:
Justin Kotalik 2020-08-06 09:21:51 -07:00 committed by GitHub
parent ed5034eb8e
commit c924af3bd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 673 additions and 10 deletions

View File

@ -1457,6 +1457,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteropWebsite", "src\Grpc\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wasm.Performance.ConsoleHost", "src\Components\benchmarkapps\Wasm.Performance\ConsoleHost\Wasm.Performance.ConsoleHost.csproj", "{E9408723-E6A9-4715-B906-3B25B0238ABA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "src\Servers\IIS\IIS\test\testassets\InProcessWebSite\InProcessWebSite.csproj", "{8DA61885-B95E-4BA1-A752-C79B6597FC44}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ANCM", "ANCM", "{D62AF49B-F9FE-4794-AC39-A473FF13CA81}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AspNetCore", "src\Servers\IIS\AspNetCoreModuleV2\AspNetCore\AspNetCore.vcxproj", "{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonLib", "src\Servers\IIS\AspNetCoreModuleV2\CommonLib\CommonLib.vcxproj", "{55494E58-E061-4C4C-A0A8-837008E72F85}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonLibTests", "src\Servers\IIS\AspNetCoreModuleV2\CommonLibTests\CommonLibTests.vcxproj", "{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "src\Servers\IIS\AspNetCoreModuleV2\gtest\gtest.vcxproj", "{CAC1267B-8778-4257-AAC6-CAF481723B01}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\Servers\IIS\AspNetCoreModuleV2\IISLib\IISLib.vcxproj", "{09D9D1D6-2951-4E14-BC35-76A23CF9391A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InProcessRequestHandler", "src\Servers\IIS\AspNetCoreModuleV2\InProcessRequestHandler\InProcessRequestHandler.vcxproj", "{D57EA297-6DC2-4BC0-8C91-334863327863}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OutOfProcessRequestHandler", "src\Servers\IIS\AspNetCoreModuleV2\OutOfProcessRequestHandler\OutOfProcessRequestHandler.vcxproj", "{7F87406C-A3C8-4139-A68D-E4C344294A67}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RequestHandlerLib", "src\Servers\IIS\AspNetCoreModuleV2\RequestHandlerLib\RequestHandlerLib.vcxproj", "{1533E271-F61B-441B-8B74-59FB61DF0552}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ANCMSymbols", "src\Servers\IIS\AspNetCoreModuleV2\Symbols\Microsoft.AspNetCore.ANCMSymbols.csproj", "{7E268085-1046-4362-80CB-2977FF826DCA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -6903,6 +6925,124 @@ Global
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x64.Build.0 = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x86.ActiveCfg = Release|Any CPU
{E9408723-E6A9-4715-B906-3B25B0238ABA}.Release|x86.Build.0 = Release|Any CPU
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Debug|Any CPU.ActiveCfg = Debug|x86
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Debug|x64.ActiveCfg = Debug|x64
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Debug|x64.Build.0 = Debug|x64
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Debug|x86.ActiveCfg = Debug|x86
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Debug|x86.Build.0 = Debug|x86
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Release|Any CPU.ActiveCfg = Release|x86
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Release|x64.ActiveCfg = Release|x64
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Release|x64.Build.0 = Release|x64
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Release|x86.ActiveCfg = Release|x86
{8DA61885-B95E-4BA1-A752-C79B6597FC44}.Release|x86.Build.0 = Release|x86
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|Any CPU.ActiveCfg = Debug|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x64.ActiveCfg = Debug|x64
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x64.Build.0 = Debug|x64
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x86.ActiveCfg = Debug|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x86.Build.0 = Debug|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Debug|x86.Deploy.0 = Debug|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|Any CPU.ActiveCfg = Release|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x64.ActiveCfg = Release|x64
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x64.Build.0 = Release|x64
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x86.ActiveCfg = Release|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x86.Build.0 = Release|Win32
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B}.Release|x86.Deploy.0 = Release|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|Any CPU.ActiveCfg = Debug|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x64.ActiveCfg = Debug|x64
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x64.Build.0 = Debug|x64
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x86.ActiveCfg = Debug|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x86.Build.0 = Debug|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Debug|x86.Deploy.0 = Debug|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|Any CPU.ActiveCfg = Release|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x64.ActiveCfg = Release|x64
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x64.Build.0 = Release|x64
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x86.ActiveCfg = Release|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x86.Build.0 = Release|Win32
{55494E58-E061-4C4C-A0A8-837008E72F85}.Release|x86.Deploy.0 = Release|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x64.ActiveCfg = Debug|x64
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x64.Build.0 = Debug|x64
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x86.ActiveCfg = Debug|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x86.Build.0 = Debug|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Debug|x86.Deploy.0 = Debug|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|Any CPU.ActiveCfg = Release|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x64.ActiveCfg = Release|x64
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x64.Build.0 = Release|x64
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x86.ActiveCfg = Release|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x86.Build.0 = Release|Win32
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1}.Release|x86.Deploy.0 = Release|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x64.ActiveCfg = Debug|x64
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x64.Build.0 = Debug|x64
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x86.ActiveCfg = Debug|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x86.Build.0 = Debug|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Debug|x86.Deploy.0 = Debug|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|Any CPU.ActiveCfg = Release|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.ActiveCfg = Release|x64
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x64.Build.0 = Release|x64
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x86.ActiveCfg = Release|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x86.Build.0 = Release|Win32
{CAC1267B-8778-4257-AAC6-CAF481723B01}.Release|x86.Deploy.0 = Release|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|Any CPU.ActiveCfg = Debug|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x64.ActiveCfg = Debug|x64
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x64.Build.0 = Debug|x64
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x86.ActiveCfg = Debug|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x86.Build.0 = Debug|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Debug|x86.Deploy.0 = Debug|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|Any CPU.ActiveCfg = Release|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x64.ActiveCfg = Release|x64
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x64.Build.0 = Release|x64
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x86.ActiveCfg = Release|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x86.Build.0 = Release|Win32
{09D9D1D6-2951-4E14-BC35-76A23CF9391A}.Release|x86.Deploy.0 = Release|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|Any CPU.ActiveCfg = Debug|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x64.ActiveCfg = Debug|x64
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x64.Build.0 = Debug|x64
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x86.ActiveCfg = Debug|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x86.Build.0 = Debug|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Debug|x86.Deploy.0 = Debug|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|Any CPU.ActiveCfg = Release|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x64.ActiveCfg = Release|x64
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x64.Build.0 = Release|x64
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x86.ActiveCfg = Release|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x86.Build.0 = Release|Win32
{D57EA297-6DC2-4BC0-8C91-334863327863}.Release|x86.Deploy.0 = Release|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|Any CPU.ActiveCfg = Debug|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x64.ActiveCfg = Debug|x64
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x64.Build.0 = Debug|x64
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x86.ActiveCfg = Debug|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x86.Build.0 = Debug|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Debug|x86.Deploy.0 = Debug|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|Any CPU.ActiveCfg = Release|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x64.ActiveCfg = Release|x64
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x64.Build.0 = Release|x64
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.ActiveCfg = Release|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.Build.0 = Release|Win32
{7F87406C-A3C8-4139-A68D-E4C344294A67}.Release|x86.Deploy.0 = Release|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|Any CPU.ActiveCfg = Debug|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x64.ActiveCfg = Debug|x64
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x64.Build.0 = Debug|x64
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x86.ActiveCfg = Debug|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x86.Build.0 = Debug|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Debug|x86.Deploy.0 = Debug|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|Any CPU.ActiveCfg = Release|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x64.ActiveCfg = Release|x64
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x64.Build.0 = Release|x64
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x86.ActiveCfg = Release|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x86.Build.0 = Release|Win32
{1533E271-F61B-441B-8B74-59FB61DF0552}.Release|x86.Deploy.0 = Release|Win32
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|x64.ActiveCfg = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|x64.Build.0 = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|x86.ActiveCfg = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Debug|x86.Build.0 = Debug|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|Any CPU.Build.0 = Release|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|x64.ActiveCfg = Release|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|x64.Build.0 = Release|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|x86.ActiveCfg = Release|Any CPU
{7E268085-1046-4362-80CB-2977FF826DCA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -7633,6 +7773,17 @@ Global
{C3A0F425-669F-46A8-893F-CF449A6DAE56} = {00B2DD87-7E2A-4460-BE1B-5E18B1062B7F}
{19189670-E206-471D-94F8-7D3D545E5020} = {00B2DD87-7E2A-4460-BE1B-5E18B1062B7F}
{E9408723-E6A9-4715-B906-3B25B0238ABA} = {6276A9A0-791B-49C1-AD8F-50AC47CDC196}
{8DA61885-B95E-4BA1-A752-C79B6597FC44} = {41BB7BA4-AC08-4E9A-83EA-6D587A5B951C}
{D62AF49B-F9FE-4794-AC39-A473FF13CA81} = {D67E977E-75DF-41EE-8315-6DBF5C2B7357}
{EC82302F-D2F0-4727-99D1-EABC0DD9DC3B} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{55494E58-E061-4C4C-A0A8-837008E72F85} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{1EAC8125-1765-4E2D-8CBE-56DC98A1C8C1} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{CAC1267B-8778-4257-AAC6-CAF481723B01} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{09D9D1D6-2951-4E14-BC35-76A23CF9391A} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{D57EA297-6DC2-4BC0-8C91-334863327863} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{7F87406C-A3C8-4139-A68D-E4C344294A67} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{1533E271-F61B-441B-8B74-59FB61DF0552} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
{7E268085-1046-4362-80CB-2977FF826DCA} = {D62AF49B-F9FE-4794-AC39-A473FF13CA81}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

View File

@ -10,6 +10,50 @@ extern bool g_fInProcessApplicationCreated;
extern std::string g_errorPageContent;
extern IHttpServer* g_pHttpServer;
//
// Add support for certain HTTP/2.0 features like trailing headers
// and GOAWAY or RST_STREAM frames.
//
class __declspec(uuid("1a2acc57-cae2-4f28-b4ab-00c8f96b12ec"))
IHttpResponse4 : public IHttpResponse3
{
public:
virtual
HRESULT
DeleteTrailer(
_In_ PCSTR pszHeaderName
) = 0;
virtual
PCSTR
GetTrailer(
_In_ PCSTR pszHeaderName,
_Out_ USHORT* pcchHeaderValue = NULL
) const = 0;
virtual
VOID
ResetStream(
_In_ ULONG errorCode
) = 0;
virtual
VOID
SetNeedGoAway(
VOID
) = 0;
virtual
HRESULT
SetTrailer(
_In_ PCSTR pszHeaderName,
_In_ PCSTR pszHeaderValue,
_In_ USHORT cchHeaderValue,
_In_ BOOL fReplace
) = 0;
};
//
// Initialization export
//
@ -517,4 +561,32 @@ http_set_startup_error_page_content(_In_ byte* errorPageContent, int length)
memcpy(&g_errorPageContent[0], errorPageContent, length);
}
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
HRESULT
http_has_response4(
_In_ IN_PROCESS_HANDLER* pInProcessHandler,
_Out_ BOOL* supportsTrailers
)
{
IHttpResponse4* pHttpResponse;
HRESULT hr = HttpGetExtendedInterface(g_pHttpServer, pInProcessHandler->QueryHttpContext()->GetResponse(), &pHttpResponse);
*supportsTrailers = SUCCEEDED(hr);
return 0;
}
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
HRESULT
http_response_set_trailer(
_In_ IN_PROCESS_HANDLER* pInProcessHandler,
_In_ PCSTR pszHeaderName,
_In_ PCSTR pszHeaderValue,
_In_ USHORT usHeaderValueLength,
_In_ BOOL fReplace)
{
// always unknown
IHttpResponse4* pHttpResponse = (IHttpResponse4*)pInProcessHandler->QueryHttpContext()->GetResponse();
return pHttpResponse->SetTrailer(pszHeaderName, pszHeaderValue, usHeaderValueLength, fReplace);
}
// End of export

View File

@ -32,7 +32,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
IServerVariablesFeature,
ITlsConnectionFeature,
IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature
IHttpMaxRequestBodySizeFeature,
IHttpResponseTrailersFeature
{
// NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation,
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
@ -376,6 +377,23 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
}
}
internal IHttpResponseTrailersFeature GetResponseTrailersFeature()
{
// Check version is above 2.
if (HttpVersion >= System.Net.HttpVersion.Version20 && NativeMethods.HttpSupportTrailer(_pInProcessHandler))
{
return this;
}
return null;
}
IHeaderDictionary IHttpResponseTrailersFeature.Trailers
{
get => ResponseTrailers ??= HttpResponseTrailers;
set => ResponseTrailers = value;
}
void IHttpResponseBodyFeature.DisableBuffering()
{
NativeMethods.HttpDisableBuffering(_pInProcessHandler);

View File

@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private static readonly Type IISHttpContextType = typeof(IISHttpContext);
private static readonly Type IServerVariablesFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
private static readonly Type IHttpMaxRequestBodySizeFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
private static readonly Type IHttpResponseTrailersFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature);
private object _currentIHttpRequestFeature;
private object _currentIHttpResponseFeature;
@ -48,6 +49,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private object _currentIHttpBodyControlFeature;
private object _currentIServerVariablesFeature;
private object _currentIHttpMaxRequestBodySizeFeature;
private object _currentIHttpResponseTrailersFeature;
private void Initialize()
{
@ -63,6 +65,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_currentIServerVariablesFeature = this;
_currentIHttpMaxRequestBodySizeFeature = this;
_currentITlsConnectionFeature = this;
_currentIHttpResponseTrailersFeature = GetResponseTrailersFeature();
}
internal object FastFeatureGet(Type key)
@ -147,6 +150,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
{
return _currentIHttpMaxRequestBodySizeFeature;
}
if (key == IHttpResponseTrailersFeature)
{
return _currentIHttpResponseTrailersFeature;
}
return ExtraFeatureGet(key);
}
@ -249,6 +256,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
{
_currentIHttpMaxRequestBodySizeFeature = feature;
}
if (key == IHttpResponseTrailersFeature)
{
_currentIHttpResponseTrailersFeature = feature;
}
if (key == IISHttpContextType)
{
throw new InvalidOperationException("Cannot set IISHttpContext in feature collection");
@ -334,6 +345,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
{
yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeature, _currentIHttpMaxRequestBodySizeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
}
if (_currentIHttpResponseTrailersFeature != null)
{
yield return new KeyValuePair<Type, object>(IHttpResponseTrailersFeature, _currentIHttpResponseTrailersFeature as global::Microsoft.AspNetCore.Http.Features.IHttpResponseTrailersFeature);
}
if (MaybeExtra != null)
{

View File

@ -169,6 +169,12 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
// if request is done no need to flush, http.sys would do it for us
if (result.IsCompleted)
{
// When is the reader completed? Is it always after the request pipeline exits? Or can CompleteAsync make it complete early?
if (HasTrailers)
{
SetResponseTrailers();
}
break;
}

View File

@ -67,6 +67,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
protected Pipe _bodyInputPipe;
protected OutputProducer _bodyOutput;
private HeaderCollection _trailers;
private const string NtlmString = "NTLM";
private const string NegotiateString = "Negotiate";
private const string BasicString = "Basic";
@ -114,7 +116,11 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
public IHeaderDictionary RequestHeaders { get; set; }
public IHeaderDictionary ResponseHeaders { get; set; }
public IHeaderDictionary ResponseTrailers { get; set; }
private HeaderCollection HttpResponseHeaders { get; set; }
private HeaderCollection HttpResponseTrailers => _trailers ??= new HeaderCollection(checkTrailers: true);
internal bool HasTrailers => _trailers?.Count > 0;
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
private bool HasStartedConsumingRequestBody { get; set; }
@ -418,6 +424,42 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
}
}
public unsafe void SetResponseTrailers()
{
HttpResponseTrailers.IsReadOnly = true;
foreach (var headerPair in HttpResponseTrailers)
{
var headerValues = headerPair.Value;
if (headerValues.Count == 0)
{
continue;
}
var headerNameBytes = Encoding.ASCII.GetBytes(headerPair.Key);
fixed (byte* pHeaderName = headerNameBytes)
{
var isFirst = true;
for (var i = 0; i < headerValues.Count; i++)
{
var headerValue = headerValues[i];
if (string.IsNullOrEmpty(headerValue))
{
continue;
}
var headerValueBytes = Encoding.UTF8.GetBytes(headerValue);
fixed (byte* pHeaderValue = headerValueBytes)
{
NativeMethods.HttpResponseSetTrailer(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, replace: isFirst);
}
isFirst = false;
}
}
}
}
public abstract Task<bool> ProcessRequestAsync();
public void OnStarting(Func<object, Task> callback, object state)

View File

@ -141,6 +141,11 @@ namespace Microsoft.AspNetCore.Server.IIS
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_response_set_unknown_header(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_has_response4(IntPtr pInProcessHandler, out bool isResponse4);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_response_set_trailer(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool replace);
[DllImport(AspNetCoreModuleDll)]
private static extern unsafe int http_response_set_known_header(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace);
@ -308,6 +313,18 @@ namespace Microsoft.AspNetCore.Server.IIS
}
}
internal static unsafe void HttpResponseSetTrailer(IntPtr pInProcessHandler, byte* pHeaderName, byte* pHeaderValue, ushort length, bool replace)
{
Validate(http_response_set_trailer(pInProcessHandler, pHeaderName, pHeaderValue, length, false));
}
internal static unsafe bool HttpSupportTrailer(IntPtr pInProcessHandler)
{
bool supportsTrailers;
Validate(http_has_response4(pInProcessHandler, out supportsTrailers));
return supportsTrailers;
}
private static void Validate(int hr)
{
if (hr != HR_OK)

View File

@ -10,22 +10,22 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
public class FixtureLoggedTest : LoggedTest
{
private readonly IISTestSiteFixture _fixture;
protected IISTestSiteFixture Fixture { get; set; }
public FixtureLoggedTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
Fixture = fixture;
}
public override void Initialize(TestContext context, MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
{
base.Initialize(context, methodInfo, testMethodArguments, testOutputHelper);
_fixture.Attach(this);
Fixture.Attach(this);
}
public override void Dispose()
{
_fixture.Detach(this);
Fixture.Detach(this);
base.Dispose();
}
}

View File

@ -0,0 +1,199 @@
// 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.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing;
using Microsoft.Net.Http.Headers;
using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
[Collection(PublishedSitesCollection.Name)]
public class ResponseTrailersTests : IISFunctionalTestBase
{
private const string WindowsVersionForTrailers = "10.0.20180";
public ResponseTrailersTests(PublishedSitesFixture fixture) : base(fixture)
{
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_HTTP2_TrailersAvailable()
{
var version = System.Environment.OSVersion.Version;
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_HTTP2_TrailersAvailable");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.Empty(response.TrailingHeaders);
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_HTTP1_TrailersNotAvailable()
{
var deploymentParameters = Fixture.GetBaseDeploymentParameters(hostingModel: IntegrationTesting.HostingModel.OutOfProcess);
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_HTTP1_TrailersNotAvailable");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version11, response.Version);
Assert.Empty(response.TrailingHeaders);
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_ProhibitedTrailers_Blocked()
{
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_ProhibitedTrailers_Blocked");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.Empty(response.TrailingHeaders);
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_NoBody_TrailersSent()
{
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_NoBody_TrailersSent");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal("TrailerValue", response.TrailingHeaders.GetValues("TrailerName").Single());
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_WithBody_TrailersSent()
{
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_WithBody_TrailersSent");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.Equal("Hello World", await response.Content.ReadAsStringAsync());
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_WithContentLengthBody_TrailersSent()
{
var body = "Hello World";
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_WithContentLengthBody_TrailersSent");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.Equal(body, await response.Content.ReadAsStringAsync());
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent()
{
var body = "Hello World";
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
// Avoid HttpContent's automatic content-length calculation.
Assert.True(response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out var contentLength), HeaderNames.ContentLength);
Assert.Equal((2 * body.Length).ToString(CultureInfo.InvariantCulture), contentLength.First());
Assert.Equal(body + body, await response.Content.ReadAsStringAsync());
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent()
{
var body = "Hello World";
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
// Avoid HttpContent's automatic content-length calculation.
Assert.True(response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out var contentLength), HeaderNames.ContentLength);
Assert.Equal(body.Length.ToString(CultureInfo.InvariantCulture), contentLength.First());
Assert.Equal("TrailerName", response.Headers.Trailer.Single());
Assert.Equal(body, await response.Content.ReadAsStringAsync());
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal("Trailer Value", response.TrailingHeaders.GetValues("TrailerName").Single());
}
[ConditionalFact]
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersionForTrailers)]
public async Task ResponseTrailers_MultipleValues_SentAsSeparateHeaders()
{
var deploymentParameters = GetHttpsDeploymentParameters();
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await SendRequestAsync(deploymentResult.HttpClient.BaseAddress.ToString() + "ResponseTrailers_MultipleValues_SentAsSeparateHeaders");
response.EnsureSuccessStatusCode();
Assert.Equal(HttpVersion.Version20, response.Version);
Assert.NotEmpty(response.TrailingHeaders);
Assert.Equal(new[] { "TrailerValue0", "TrailerValue1" }, response.TrailingHeaders.GetValues("TrailerName"));
}
private IISDeploymentParameters GetHttpsDeploymentParameters()
{
var port = TestPortHelper.GetNextSSLPort();
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
deploymentParameters.AddHttpsToServerConfig();
return deploymentParameters;
}
private async Task<HttpResponseMessage> SendRequestAsync(string uri, bool http2 = true)
{
var handler = new HttpClientHandler();
handler.MaxResponseHeadersLength = 128;
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
using var client = new HttpClient(handler);
client.DefaultRequestVersion = http2 ? HttpVersion.Version20 : HttpVersion.Version11;
return await client.GetAsync(uri);
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -25,6 +26,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
using Xunit;
using HttpFeatures = Microsoft.AspNetCore.Http.Features;
@ -1030,5 +1032,145 @@ namespace TestSite
Assert.Equal("<22>", value);
return Task.CompletedTask;
}
#if !FORWARDCOMPAT
public Task ResponseTrailers_HTTP2_TrailersAvailable(HttpContext context)
{
Assert.Equal("HTTP/2", context.Request.Protocol);
Assert.True(context.Response.SupportsTrailers());
return Task.FromResult(0);
}
public Task ResponseTrailers_HTTP1_TrailersNotAvailable(HttpContext context)
{
Assert.Equal("HTTP/1.1", context.Request.Protocol);
Assert.False(context.Response.SupportsTrailers());
return Task.FromResult(0);
}
public Task ResponseTrailers_ProhibitedTrailers_Blocked(HttpContext context)
{
Assert.True(context.Response.SupportsTrailers());
foreach (var header in DisallowedTrailers)
{
Assert.Throws<InvalidOperationException>(() => context.Response.AppendTrailer(header, "value"));
}
return Task.FromResult(0);
}
public Task ResponseTrailers_NoBody_TrailersSent(HttpContext context)
{
context.Response.DeclareTrailer("trailername");
context.Response.AppendTrailer("trailername", "TrailerValue");
return Task.FromResult(0);
}
public async Task ResponseTrailers_WithBody_TrailersSent(HttpContext context)
{
await context.Response.WriteAsync("Hello World");
context.Response.AppendTrailer("TrailerName", "Trailer Value");
}
public async Task ResponseTrailers_WithContentLengthBody_TrailersSent(HttpContext context)
{
var body = "Hello World";
context.Response.ContentLength = body.Length;
await context.Response.WriteAsync(body);
context.Response.AppendTrailer("TrailerName", "Trailer Value");
}
public async Task ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent(HttpContext context)
{
var body = "Hello World";
context.Response.ContentLength = body.Length * 2;
await context.Response.WriteAsync(body);
context.Response.AppendTrailer("TrailerName", "Trailer Value");
await context.Response.WriteAsync(body);
}
public async Task ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent(HttpContext context)
{
var body = "Hello World";
context.Response.ContentLength = body.Length;
context.Response.DeclareTrailer("TrailerName");
await context.Response.WriteAsync(body);
context.Response.AppendTrailer("TrailerName", "Trailer Value");
}
public async Task ResponseTrailers_WithContentLengthBodyAndDeclaredButMissingTrailers_Completes(HttpContext context)
{
var body = "Hello World";
context.Response.ContentLength = body.Length;
context.Response.DeclareTrailer("TrailerName");
await context.Response.WriteAsync(body);
}
public Task ResponseTrailers_MultipleValues_SentAsSeparateHeaders(HttpContext context)
{
context.Response.AppendTrailer("trailername", new StringValues(new[] { "TrailerValue0", "TrailerValue1" }));
return Task.FromResult(0);
}
public Task ResponseTrailers_LargeTrailers_Success(HttpContext context)
{
var values = new[] {
new string('a', 1024),
new string('b', 1024 * 4),
new string('c', 1024 * 8),
new string('d', 1024 * 16),
new string('e', 1024 * 32),
new string('f', 1024 * 64 - 1) }; // Max header size
context.Response.AppendTrailer("ThisIsALongerHeaderNameThatStillWorksForReals", new StringValues(values));
return Task.FromResult(0);
}
public Task ResponseTrailers_NullValues_Ignored(HttpContext context)
{
foreach (var kvp in NullTrailers)
{
context.Response.AppendTrailer(kvp.Item1, kvp.Item2);
}
return Task.FromResult(0);
}
internal static readonly HashSet<(string, StringValues, StringValues)> NullTrailers = new HashSet<(string, StringValues, StringValues)>()
{
("NullString", (string)null, (string)null),
("EmptyString", "", ""),
("NullStringArray", new string[] { null }, ""),
("EmptyStringArray", new string[] { "" }, ""),
("MixedStringArray", new string[] { null, "" }, new string[] { "", "" }),
("WithValidStrings", new string[] { null, "Value", "" }, new string[] { "", "Value", "" })
};
// https://tools.ietf.org/html/rfc7230#section-4.1.2
internal static readonly HashSet<string> DisallowedTrailers = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
// Message framing headers.
HeaderNames.TransferEncoding, HeaderNames.ContentLength,
// Routing headers.
HeaderNames.Host,
// Request modifiers: controls and conditionals.
// rfc7231#section-5.1: Controls.
HeaderNames.CacheControl, HeaderNames.Expect, HeaderNames.MaxForwards, HeaderNames.Pragma, HeaderNames.Range, HeaderNames.TE,
// rfc7231#section-5.2: Conditionals.
HeaderNames.IfMatch, HeaderNames.IfNoneMatch, HeaderNames.IfModifiedSince, HeaderNames.IfUnmodifiedSince, HeaderNames.IfRange,
// Authentication headers.
HeaderNames.WWWAuthenticate, HeaderNames.Authorization, HeaderNames.ProxyAuthenticate, HeaderNames.ProxyAuthorization, HeaderNames.SetCookie, HeaderNames.Cookie,
// Response control data.
// rfc7231#section-7.1: Control Data.
HeaderNames.Age, HeaderNames.Expires, HeaderNames.Date, HeaderNames.Location, HeaderNames.RetryAfter, HeaderNames.Vary, HeaderNames.Warning,
// Content-Encoding, Content-Type, Content-Range, and Trailer itself.
HeaderNames.ContentEncoding, HeaderNames.ContentType, HeaderNames.ContentRange, HeaderNames.Trailer
};
#endif
}
}

View File

@ -0,0 +1 @@
(Get-Content C:\Windows\System32\inetsrv\config\redirection.config) -replace 'enabled="true"', 'enabled="false"' | Out-File C:\Windows\System32\inetsrv\config\redirection.config