Skip to content

Commit aaaaf57

Browse files
authored
Implement MaxRequestBodySize feature for IIS inprocess (#9475)
1 parent 93d82b0 commit aaaaf57

33 files changed

+832
-35
lines changed

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSource.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define CS_WINDOWS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/windowsAuthentication"
1414
#define CS_BASIC_AUTHENTICATION_SECTION L"system.webServer/security/authentication/basicAuthentication"
1515
#define CS_ANONYMOUS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/anonymousAuthentication"
16+
#define CS_MAX_REQUEST_BODY_SIZE_SECTION L"system.webServer/security/requestFiltering"
1617

1718
class ConfigurationSource: NonCopyable
1819
{

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
4444
m_fWindowsAuthEnabled(false),
4545
m_fBasicAuthEnabled(false),
4646
m_fAnonymousAuthEnabled(false),
47+
m_dwMaxRequestBodySize(INFINITE),
4748
m_dwStartupTimeLimitInMS(INFINITE),
4849
m_dwShutdownTimeLimitInMS(INFINITE)
4950
{
@@ -71,6 +72,24 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
7172
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
7273
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false);
7374

75+
const auto requestFilteringSection = configurationSource.GetSection(CS_MAX_REQUEST_BODY_SIZE_SECTION);
76+
if (requestFilteringSection != nullptr)
77+
{
78+
// The requestFiltering section is enabled by default in most scenarios. However, if the value
79+
// maxAllowedContentLength isn't set, it defaults to 30_000_000 in IIS.
80+
// The section element won't be defined if the feature is disabled, so the presence of the section tells
81+
// us whether there should be a default or not.
82+
auto requestLimitSection = requestFilteringSection->GetSection(L"requestLimits").value_or(nullptr);
83+
if (requestLimitSection != nullptr)
84+
{
85+
m_dwMaxRequestBodySize = requestLimitSection->GetLong(L"maxAllowedContentLength").value_or(30000000);
86+
}
87+
else
88+
{
89+
m_dwMaxRequestBodySize = 30000000;
90+
}
91+
}
92+
7493
if (pSite != nullptr)
7594
{
7695
m_bindingInformation = BindingInformation::Load(configurationSource, *pSite);

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ class InProcessOptions: NonCopyable
9494
return m_dwShutdownTimeLimitInMS;
9595
}
9696

97+
DWORD
98+
QueryMaxRequestBodySizeLimit() const
99+
{
100+
return m_dwMaxRequestBodySize;
101+
}
102+
97103
const std::map<std::wstring, std::wstring, ignore_case_comparer>&
98104
QueryEnvironmentVariables() const
99105
{
@@ -128,6 +134,7 @@ class InProcessOptions: NonCopyable
128134
bool m_fAnonymousAuthEnabled;
129135
DWORD m_dwStartupTimeLimitInMS;
130136
DWORD m_dwShutdownTimeLimitInMS;
137+
DWORD m_dwMaxRequestBodySize;
131138
std::map<std::wstring, std::wstring, ignore_case_comparer> m_environmentVariables;
132139
std::vector<BindingInformation> m_bindingInformation;
133140

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
252252
}
253253

254254
// Used to make .NET Runtime always log to event log when there is an unhandled exception.
255-
LOG_LAST_ERROR_IF(SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
255+
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
256256

257257
bool clrThreadExited;
258258
{

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,13 @@ struct IISConfigurationData
191191
BOOL fBasicAuthEnabled;
192192
BOOL fAnonymousAuthEnable;
193193
BSTR pwzBindings;
194+
DWORD maxRequestBodySize;
194195
};
195196

196197
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
197198
HRESULT
198199
http_get_application_properties(
199-
_In_ IISConfigurationData* pIISCofigurationData
200+
_In_ IISConfigurationData* pIISConfigurationData
200201
)
201202
{
202203
auto pInProcessApplication = IN_PROCESS_APPLICATION::GetInstance();
@@ -207,15 +208,16 @@ http_get_application_properties(
207208

208209
const auto& pConfiguration = pInProcessApplication->QueryConfig();
209210

210-
pIISCofigurationData->pInProcessApplication = pInProcessApplication;
211-
pIISCofigurationData->pwzFullApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationPhysicalPath().c_str());
212-
pIISCofigurationData->pwzVirtualApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationVirtualPath().c_str());
213-
pIISCofigurationData->fWindowsAuthEnabled = pConfiguration.QueryWindowsAuthEnabled();
214-
pIISCofigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
215-
pIISCofigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
211+
pIISConfigurationData->pInProcessApplication = pInProcessApplication;
212+
pIISConfigurationData->pwzFullApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationPhysicalPath().c_str());
213+
pIISConfigurationData->pwzVirtualApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationVirtualPath().c_str());
214+
pIISConfigurationData->fWindowsAuthEnabled = pConfiguration.QueryWindowsAuthEnabled();
215+
pIISConfigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
216+
pIISConfigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
216217

217218
auto const serverAddresses = BindingInformation::Format(pConfiguration.QueryBindings(), pInProcessApplication->QueryApplicationVirtualPath());
218-
pIISCofigurationData->pwzBindings = SysAllocString(serverAddresses.c_str());
219+
pIISConfigurationData->pwzBindings = SysAllocString(serverAddresses.c_str());
220+
pIISConfigurationData->maxRequestBodySize = pInProcessApplication->QueryConfig().QueryMaxRequestBodySizeLimit();
219221
return S_OK;
220222
}
221223

src/Servers/IIS/IIS/benchmarks/IIS.Performance/PlaintextBenchmark.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class PlaintextBenchmark
2323
[GlobalSetup]
2424
public void Setup()
2525
{
26-
_server = TestServer.Create(builder => builder.UseMiddleware<PlaintextMiddleware>(), new LoggerFactory()).GetAwaiter().GetResult();
26+
_server = TestServer.Create(builder => builder.UseMiddleware<PlaintextMiddleware>(), new LoggerFactory(), new IISServerOptions()).GetAwaiter().GetResult();
2727
// Recreate client, TestServer.Client has additional logging that can hurt performance
2828
_client = new HttpClient()
2929
{

src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.netcoreapp3.0.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public IISServerOptions() { }
99
public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
1010
public string AuthenticationDisplayName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
1111
public bool AutomaticAuthentication { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
12+
public long? MaxRequestBodySize { get { throw null; } set { } }
1213
}
1314
}
1415
namespace Microsoft.AspNetCore.Hosting
@@ -27,6 +28,11 @@ public partial interface IServerVariablesFeature
2728
}
2829
namespace Microsoft.AspNetCore.Server.IIS
2930
{
31+
public sealed partial class BadHttpRequestException : System.IO.IOException
32+
{
33+
internal BadHttpRequestException() { }
34+
public int StatusCode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
35+
}
3036
public static partial class HttpContextExtensions
3137
{
3238
public static string GetIISServerVariable(this Microsoft.AspNetCore.Http.HttpContext context, string variableName) { throw null; }

src/Servers/IIS/IIS/src/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
using System.Runtime.CompilerServices;
55

66
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.IISIntegration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
7+
[assembly: InternalsVisibleTo("IIS.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
78

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO;
5+
using System.Runtime.CompilerServices;
6+
using Microsoft.AspNetCore.Http;
7+
8+
namespace Microsoft.AspNetCore.Server.IIS
9+
{
10+
public sealed class BadHttpRequestException : IOException
11+
{
12+
private BadHttpRequestException(string message, int statusCode, RequestRejectionReason reason)
13+
: base(message)
14+
{
15+
StatusCode = statusCode;
16+
Reason = reason;
17+
}
18+
19+
public int StatusCode { get; }
20+
21+
internal RequestRejectionReason Reason { get; }
22+
23+
internal static void Throw(RequestRejectionReason reason)
24+
{
25+
throw GetException(reason);
26+
}
27+
28+
[MethodImpl(MethodImplOptions.NoInlining)]
29+
internal static BadHttpRequestException GetException(RequestRejectionReason reason)
30+
{
31+
BadHttpRequestException ex;
32+
switch (reason)
33+
{
34+
case RequestRejectionReason.RequestBodyTooLarge:
35+
ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestBodyTooLarge, StatusCodes.Status413PayloadTooLarge, reason);
36+
break;
37+
default:
38+
ex = new BadHttpRequestException(CoreStrings.BadRequest, StatusCodes.Status400BadRequest, reason);
39+
break;
40+
}
41+
return ex;
42+
}
43+
}
44+
}

src/Servers/IIS/IIS/src/Core/IISConfigurationData.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ internal struct IISConfigurationData
1919
public bool fAnonymousAuthEnable;
2020
[MarshalAs(UnmanagedType.BStr)]
2121
public string pwzBindings;
22+
public int maxRequestBodySize;
2223
}
2324
}

0 commit comments

Comments
 (0)