Implement MaxRequestBodySize feature for IIS inprocess (#9475)
This commit is contained in:
parent
93d82b0883
commit
aaaaf572fd
|
|
@ -13,6 +13,7 @@
|
||||||
#define CS_WINDOWS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/windowsAuthentication"
|
#define CS_WINDOWS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/windowsAuthentication"
|
||||||
#define CS_BASIC_AUTHENTICATION_SECTION L"system.webServer/security/authentication/basicAuthentication"
|
#define CS_BASIC_AUTHENTICATION_SECTION L"system.webServer/security/authentication/basicAuthentication"
|
||||||
#define CS_ANONYMOUS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/anonymousAuthentication"
|
#define CS_ANONYMOUS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/anonymousAuthentication"
|
||||||
|
#define CS_MAX_REQUEST_BODY_SIZE_SECTION L"system.webServer/security/requestFiltering"
|
||||||
|
|
||||||
class ConfigurationSource: NonCopyable
|
class ConfigurationSource: NonCopyable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
|
||||||
m_fWindowsAuthEnabled(false),
|
m_fWindowsAuthEnabled(false),
|
||||||
m_fBasicAuthEnabled(false),
|
m_fBasicAuthEnabled(false),
|
||||||
m_fAnonymousAuthEnabled(false),
|
m_fAnonymousAuthEnabled(false),
|
||||||
|
m_dwMaxRequestBodySize(INFINITE),
|
||||||
m_dwStartupTimeLimitInMS(INFINITE),
|
m_dwStartupTimeLimitInMS(INFINITE),
|
||||||
m_dwShutdownTimeLimitInMS(INFINITE)
|
m_dwShutdownTimeLimitInMS(INFINITE)
|
||||||
{
|
{
|
||||||
|
|
@ -71,6 +72,24 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
|
||||||
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
|
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
|
||||||
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false);
|
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false);
|
||||||
|
|
||||||
|
const auto requestFilteringSection = configurationSource.GetSection(CS_MAX_REQUEST_BODY_SIZE_SECTION);
|
||||||
|
if (requestFilteringSection != nullptr)
|
||||||
|
{
|
||||||
|
// The requestFiltering section is enabled by default in most scenarios. However, if the value
|
||||||
|
// maxAllowedContentLength isn't set, it defaults to 30_000_000 in IIS.
|
||||||
|
// The section element won't be defined if the feature is disabled, so the presence of the section tells
|
||||||
|
// us whether there should be a default or not.
|
||||||
|
auto requestLimitSection = requestFilteringSection->GetSection(L"requestLimits").value_or(nullptr);
|
||||||
|
if (requestLimitSection != nullptr)
|
||||||
|
{
|
||||||
|
m_dwMaxRequestBodySize = requestLimitSection->GetLong(L"maxAllowedContentLength").value_or(30000000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_dwMaxRequestBodySize = 30000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pSite != nullptr)
|
if (pSite != nullptr)
|
||||||
{
|
{
|
||||||
m_bindingInformation = BindingInformation::Load(configurationSource, *pSite);
|
m_bindingInformation = BindingInformation::Load(configurationSource, *pSite);
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,12 @@ public:
|
||||||
return m_dwShutdownTimeLimitInMS;
|
return m_dwShutdownTimeLimitInMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD
|
||||||
|
QueryMaxRequestBodySizeLimit() const
|
||||||
|
{
|
||||||
|
return m_dwMaxRequestBodySize;
|
||||||
|
}
|
||||||
|
|
||||||
const std::map<std::wstring, std::wstring, ignore_case_comparer>&
|
const std::map<std::wstring, std::wstring, ignore_case_comparer>&
|
||||||
QueryEnvironmentVariables() const
|
QueryEnvironmentVariables() const
|
||||||
{
|
{
|
||||||
|
|
@ -128,6 +134,7 @@ private:
|
||||||
bool m_fAnonymousAuthEnabled;
|
bool m_fAnonymousAuthEnabled;
|
||||||
DWORD m_dwStartupTimeLimitInMS;
|
DWORD m_dwStartupTimeLimitInMS;
|
||||||
DWORD m_dwShutdownTimeLimitInMS;
|
DWORD m_dwShutdownTimeLimitInMS;
|
||||||
|
DWORD m_dwMaxRequestBodySize;
|
||||||
std::map<std::wstring, std::wstring, ignore_case_comparer> m_environmentVariables;
|
std::map<std::wstring, std::wstring, ignore_case_comparer> m_environmentVariables;
|
||||||
std::vector<BindingInformation> m_bindingInformation;
|
std::vector<BindingInformation> m_bindingInformation;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to make .NET Runtime always log to event log when there is an unhandled exception.
|
// Used to make .NET Runtime always log to event log when there is an unhandled exception.
|
||||||
LOG_LAST_ERROR_IF(SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
|
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
|
||||||
|
|
||||||
bool clrThreadExited;
|
bool clrThreadExited;
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -191,12 +191,13 @@ struct IISConfigurationData
|
||||||
BOOL fBasicAuthEnabled;
|
BOOL fBasicAuthEnabled;
|
||||||
BOOL fAnonymousAuthEnable;
|
BOOL fAnonymousAuthEnable;
|
||||||
BSTR pwzBindings;
|
BSTR pwzBindings;
|
||||||
|
DWORD maxRequestBodySize;
|
||||||
};
|
};
|
||||||
|
|
||||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||||
HRESULT
|
HRESULT
|
||||||
http_get_application_properties(
|
http_get_application_properties(
|
||||||
_In_ IISConfigurationData* pIISCofigurationData
|
_In_ IISConfigurationData* pIISConfigurationData
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto pInProcessApplication = IN_PROCESS_APPLICATION::GetInstance();
|
auto pInProcessApplication = IN_PROCESS_APPLICATION::GetInstance();
|
||||||
|
|
@ -207,15 +208,16 @@ http_get_application_properties(
|
||||||
|
|
||||||
const auto& pConfiguration = pInProcessApplication->QueryConfig();
|
const auto& pConfiguration = pInProcessApplication->QueryConfig();
|
||||||
|
|
||||||
pIISCofigurationData->pInProcessApplication = pInProcessApplication;
|
pIISConfigurationData->pInProcessApplication = pInProcessApplication;
|
||||||
pIISCofigurationData->pwzFullApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationPhysicalPath().c_str());
|
pIISConfigurationData->pwzFullApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationPhysicalPath().c_str());
|
||||||
pIISCofigurationData->pwzVirtualApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationVirtualPath().c_str());
|
pIISConfigurationData->pwzVirtualApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationVirtualPath().c_str());
|
||||||
pIISCofigurationData->fWindowsAuthEnabled = pConfiguration.QueryWindowsAuthEnabled();
|
pIISConfigurationData->fWindowsAuthEnabled = pConfiguration.QueryWindowsAuthEnabled();
|
||||||
pIISCofigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
|
pIISConfigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
|
||||||
pIISCofigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
|
pIISConfigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
|
||||||
|
|
||||||
auto const serverAddresses = BindingInformation::Format(pConfiguration.QueryBindings(), pInProcessApplication->QueryApplicationVirtualPath());
|
auto const serverAddresses = BindingInformation::Format(pConfiguration.QueryBindings(), pInProcessApplication->QueryApplicationVirtualPath());
|
||||||
pIISCofigurationData->pwzBindings = SysAllocString(serverAddresses.c_str());
|
pIISConfigurationData->pwzBindings = SysAllocString(serverAddresses.c_str());
|
||||||
|
pIISConfigurationData->maxRequestBodySize = pInProcessApplication->QueryConfig().QueryMaxRequestBodySizeLimit();
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Performance
|
||||||
[GlobalSetup]
|
[GlobalSetup]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
_server = TestServer.Create(builder => builder.UseMiddleware<PlaintextMiddleware>(), new LoggerFactory()).GetAwaiter().GetResult();
|
_server = TestServer.Create(builder => builder.UseMiddleware<PlaintextMiddleware>(), new LoggerFactory(), new IISServerOptions()).GetAwaiter().GetResult();
|
||||||
// Recreate client, TestServer.Client has additional logging that can hurt performance
|
// Recreate client, TestServer.Client has additional logging that can hurt performance
|
||||||
_client = new HttpClient()
|
_client = new HttpClient()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public string AuthenticationDisplayName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public string AuthenticationDisplayName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
public bool AutomaticAuthentication { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
public bool AutomaticAuthentication { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
|
||||||
|
public long? MaxRequestBodySize { get { throw null; } set { } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace Microsoft.AspNetCore.Hosting
|
namespace Microsoft.AspNetCore.Hosting
|
||||||
|
|
@ -27,6 +28,11 @@ namespace Microsoft.AspNetCore.Http.Features
|
||||||
}
|
}
|
||||||
namespace Microsoft.AspNetCore.Server.IIS
|
namespace Microsoft.AspNetCore.Server.IIS
|
||||||
{
|
{
|
||||||
|
public sealed partial class BadHttpRequestException : System.IO.IOException
|
||||||
|
{
|
||||||
|
internal BadHttpRequestException() { }
|
||||||
|
public int StatusCode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
|
||||||
|
}
|
||||||
public static partial class HttpContextExtensions
|
public static partial class HttpContextExtensions
|
||||||
{
|
{
|
||||||
public static string GetIISServerVariable(this Microsoft.AspNetCore.Http.HttpContext context, string variableName) { throw null; }
|
public static string GetIISServerVariable(this Microsoft.AspNetCore.Http.HttpContext context, string variableName) { throw null; }
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,5 @@
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.IISIntegration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.IISIntegration.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
[assembly: InternalsVisibleTo("IIS.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IIS
|
||||||
|
{
|
||||||
|
public sealed class BadHttpRequestException : IOException
|
||||||
|
{
|
||||||
|
private BadHttpRequestException(string message, int statusCode, RequestRejectionReason reason)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
Reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StatusCode { get; }
|
||||||
|
|
||||||
|
internal RequestRejectionReason Reason { get; }
|
||||||
|
|
||||||
|
internal static void Throw(RequestRejectionReason reason)
|
||||||
|
{
|
||||||
|
throw GetException(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
internal static BadHttpRequestException GetException(RequestRejectionReason reason)
|
||||||
|
{
|
||||||
|
BadHttpRequestException ex;
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case RequestRejectionReason.RequestBodyTooLarge:
|
||||||
|
ex = new BadHttpRequestException(CoreStrings.BadRequest_RequestBodyTooLarge, StatusCodes.Status413PayloadTooLarge, reason);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ex = new BadHttpRequestException(CoreStrings.BadRequest, StatusCodes.Status400BadRequest, reason);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,5 +19,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
public bool fAnonymousAuthEnable;
|
public bool fAnonymousAuthEnable;
|
||||||
[MarshalAs(UnmanagedType.BStr)]
|
[MarshalAs(UnmanagedType.BStr)]
|
||||||
public string pwzBindings;
|
public string pwzBindings;
|
||||||
|
public int maxRequestBodySize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||||
using Microsoft.AspNetCore.Server.IIS.Core.IO;
|
using Microsoft.AspNetCore.Server.IIS.Core.IO;
|
||||||
using Microsoft.AspNetCore.WebUtilities;
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IIS.Core
|
namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
|
|
@ -28,7 +29,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
IServerVariablesFeature,
|
IServerVariablesFeature,
|
||||||
IHttpBufferingFeature,
|
IHttpBufferingFeature,
|
||||||
ITlsConnectionFeature,
|
ITlsConnectionFeature,
|
||||||
IHttpBodyControlFeature
|
IHttpBodyControlFeature,
|
||||||
|
IHttpMaxRequestBodySizeFeature
|
||||||
{
|
{
|
||||||
// NOTE: When feature interfaces are added to or removed from this HttpProtocol implementation,
|
// 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.
|
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
|
||||||
|
|
@ -277,7 +279,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
Debug.Assert(_readBodyTask == null || _readBodyTask.IsCompleted);
|
Debug.Assert(_readBodyTask == null || _readBodyTask.IsCompleted);
|
||||||
|
|
||||||
// Reset reading status to allow restarting with new IO
|
// Reset reading status to allow restarting with new IO
|
||||||
_hasRequestReadingStarted = false;
|
HasStartedConsumingRequestBody = false;
|
||||||
|
|
||||||
// Upgrade async will cause the stream processing to go into duplex mode
|
// Upgrade async will cause the stream processing to go into duplex mode
|
||||||
AsyncIO = new WebSocketsAsyncIOEngine(_contextLock, _pInProcessHandler);
|
AsyncIO = new WebSocketsAsyncIOEngine(_contextLock, _pInProcessHandler);
|
||||||
|
|
@ -322,6 +324,35 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
|
|
||||||
bool IHttpBodyControlFeature.AllowSynchronousIO { get; set; }
|
bool IHttpBodyControlFeature.AllowSynchronousIO { get; set; }
|
||||||
|
|
||||||
|
bool IHttpMaxRequestBodySizeFeature.IsReadOnly => HasStartedConsumingRequestBody || _wasUpgraded;
|
||||||
|
|
||||||
|
long? IHttpMaxRequestBodySizeFeature.MaxRequestBodySize
|
||||||
|
{
|
||||||
|
get => MaxRequestBodySize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (HasStartedConsumingRequestBody)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedAfterRead);
|
||||||
|
}
|
||||||
|
if (_wasUpgraded)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(CoreStrings.MaxRequestBodySizeCannotBeModifiedForUpgradedRequests);
|
||||||
|
}
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.NonNegativeNumberOrNullRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value > _options.IisMaxRequestSizeLimit)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(CoreStrings.MaxRequestLimitWarning);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxRequestBodySize = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IHttpBufferingFeature.DisableRequestBuffering()
|
void IHttpBufferingFeature.DisableRequestBuffering()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
private static readonly Type IISHttpContextType = typeof(IISHttpContext);
|
private static readonly Type IISHttpContextType = typeof(IISHttpContext);
|
||||||
private static readonly Type IServerVariablesFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
|
private static readonly Type IServerVariablesFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IServerVariablesFeature);
|
||||||
private static readonly Type IHttpBufferingFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature);
|
private static readonly Type IHttpBufferingFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature);
|
||||||
|
private static readonly Type IHttpMaxRequestBodySizeFeature = typeof(global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
|
||||||
|
|
||||||
private object _currentIHttpRequestFeature;
|
private object _currentIHttpRequestFeature;
|
||||||
private object _currentIHttpResponseFeature;
|
private object _currentIHttpResponseFeature;
|
||||||
|
|
@ -48,6 +49,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
private object _currentIHttpSendFileFeature;
|
private object _currentIHttpSendFileFeature;
|
||||||
private object _currentIServerVariablesFeature;
|
private object _currentIServerVariablesFeature;
|
||||||
private object _currentIHttpBufferingFeature;
|
private object _currentIHttpBufferingFeature;
|
||||||
|
private object _currentIHttpMaxRequestBodySizeFeature;
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
|
|
@ -61,6 +63,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
_currentIHttpAuthenticationFeature = this;
|
_currentIHttpAuthenticationFeature = this;
|
||||||
_currentIServerVariablesFeature = this;
|
_currentIServerVariablesFeature = this;
|
||||||
_currentIHttpBufferingFeature = this;
|
_currentIHttpBufferingFeature = this;
|
||||||
|
_currentIHttpMaxRequestBodySizeFeature = this;
|
||||||
_currentITlsConnectionFeature = this;
|
_currentITlsConnectionFeature = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,6 +149,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
return _currentIHttpBufferingFeature;
|
return _currentIHttpBufferingFeature;
|
||||||
}
|
}
|
||||||
|
if (key == IHttpMaxRequestBodySizeFeature)
|
||||||
|
{
|
||||||
|
return _currentIHttpMaxRequestBodySizeFeature;
|
||||||
|
}
|
||||||
|
|
||||||
return ExtraFeatureGet(key);
|
return ExtraFeatureGet(key);
|
||||||
}
|
}
|
||||||
|
|
@ -249,6 +256,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
_currentIHttpBufferingFeature = feature;
|
_currentIHttpBufferingFeature = feature;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (key == IHttpMaxRequestBodySizeFeature)
|
||||||
|
{
|
||||||
|
_currentIHttpMaxRequestBodySizeFeature = feature;
|
||||||
|
}
|
||||||
if (key == IISHttpContextType)
|
if (key == IISHttpContextType)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Cannot set IISHttpContext in feature collection");
|
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>(IHttpBufferingFeature, _currentIHttpBufferingFeature as global::Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature);
|
yield return new KeyValuePair<Type, object>(IHttpBufferingFeature, _currentIHttpBufferingFeature as global::Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature);
|
||||||
}
|
}
|
||||||
|
if (_currentIHttpMaxRequestBodySizeFeature != null)
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeature, _currentIHttpMaxRequestBodySizeFeature as global::Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature);
|
||||||
|
}
|
||||||
|
|
||||||
if (MaybeExtra != null)
|
if (MaybeExtra != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
internal partial class IISHttpContext
|
internal partial class IISHttpContext
|
||||||
{
|
{
|
||||||
|
private long _consumedBytes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads data from the Input pipe to the user.
|
/// Reads data from the Input pipe to the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -22,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
internal async ValueTask<int> ReadAsync(Memory<byte> memory, CancellationToken cancellationToken)
|
internal async ValueTask<int> ReadAsync(Memory<byte> memory, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_hasRequestReadingStarted)
|
if (!HasStartedConsumingRequestBody)
|
||||||
{
|
{
|
||||||
InitializeRequestIO();
|
InitializeRequestIO();
|
||||||
}
|
}
|
||||||
|
|
@ -105,9 +107,15 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
// Read was not canceled because of incoming write or IO stopping
|
// Read was not canceled because of incoming write or IO stopping
|
||||||
if (read != -1)
|
if (read != -1)
|
||||||
{
|
{
|
||||||
|
_consumedBytes += read;
|
||||||
_bodyInputPipe.Writer.Advance(read);
|
_bodyInputPipe.Writer.Advance(read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_consumedBytes > MaxRequestBodySize)
|
||||||
|
{
|
||||||
|
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
|
||||||
|
}
|
||||||
|
|
||||||
var result = await _bodyInputPipe.Writer.FlushAsync();
|
var result = await _bodyInputPipe.Writer.FlushAsync();
|
||||||
|
|
||||||
if (result.IsCompleted || result.IsCanceled)
|
if (result.IsCompleted || result.IsCanceled)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,9 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
private static readonly Action<ILogger, string, string, Exception> _unexpectedError =
|
private static readonly Action<ILogger, string, string, Exception> _unexpectedError =
|
||||||
LoggerMessage.Define<string, string>(LogLevel.Error, new EventId(3, "UnexpectedError"), @"Unexpected exception in ""{ClassName}.{MethodName}"".");
|
LoggerMessage.Define<string, string>(LogLevel.Error, new EventId(3, "UnexpectedError"), @"Unexpected exception in ""{ClassName}.{MethodName}"".");
|
||||||
|
|
||||||
|
private static readonly Action<ILogger, string, string, Exception> _connectionBadRequest =
|
||||||
|
LoggerMessage.Define<string, string>(LogLevel.Information, new EventId(4, nameof(ConnectionBadRequest)), @"Connection id ""{ConnectionId}"" bad request data: ""{message}""");
|
||||||
|
|
||||||
public static void ConnectionDisconnect(ILogger logger, string connectionId)
|
public static void ConnectionDisconnect(ILogger logger, string connectionId)
|
||||||
{
|
{
|
||||||
_connectionDisconnect(logger, connectionId, null);
|
_connectionDisconnect(logger, connectionId, null);
|
||||||
|
|
@ -34,6 +37,11 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
_unexpectedError(logger, className, methodName, ex);
|
_unexpectedError(logger, className, methodName, ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ConnectionBadRequest(ILogger logger, string connectionId, BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
_connectionBadRequest(logger, connectionId, ex.Message, ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
protected Streams _streams;
|
protected Streams _streams;
|
||||||
|
|
||||||
private volatile bool _hasResponseStarted;
|
private volatile bool _hasResponseStarted;
|
||||||
private volatile bool _hasRequestReadingStarted;
|
|
||||||
|
|
||||||
private int _statusCode;
|
private int _statusCode;
|
||||||
private string _reasonPhrase;
|
private string _reasonPhrase;
|
||||||
|
|
@ -50,6 +49,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
protected Stack<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||||
|
|
||||||
protected Exception _applicationException;
|
protected Exception _applicationException;
|
||||||
|
protected BadHttpRequestException _requestRejectedException;
|
||||||
|
|
||||||
private readonly MemoryPool<byte> _memoryPool;
|
private readonly MemoryPool<byte> _memoryPool;
|
||||||
private readonly IISHttpServer _server;
|
private readonly IISHttpServer _server;
|
||||||
|
|
||||||
|
|
@ -112,6 +113,9 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
private HeaderCollection HttpResponseHeaders { get; set; }
|
private HeaderCollection HttpResponseHeaders { get; set; }
|
||||||
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
|
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
|
||||||
|
|
||||||
|
private bool HasStartedConsumingRequestBody { get; set; }
|
||||||
|
public long? MaxRequestBodySize { get; set; }
|
||||||
|
|
||||||
protected void InitializeContext()
|
protected void InitializeContext()
|
||||||
{
|
{
|
||||||
_thisHandle = GCHandle.Alloc(this);
|
_thisHandle = GCHandle.Alloc(this);
|
||||||
|
|
@ -156,6 +160,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaxRequestBodySize = _options.MaxRequestBodySize;
|
||||||
|
|
||||||
ResetFeatureCollection();
|
ResetFeatureCollection();
|
||||||
|
|
||||||
if (!_server.IsWebSocketAvailable(_pInProcessHandler))
|
if (!_server.IsWebSocketAvailable(_pInProcessHandler))
|
||||||
|
|
@ -282,9 +288,14 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
|
|
||||||
private void InitializeRequestIO()
|
private void InitializeRequestIO()
|
||||||
{
|
{
|
||||||
Debug.Assert(!_hasRequestReadingStarted);
|
Debug.Assert(!HasStartedConsumingRequestBody);
|
||||||
|
|
||||||
_hasRequestReadingStarted = true;
|
if (RequestHeaders.ContentLength > MaxRequestBodySize)
|
||||||
|
{
|
||||||
|
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
|
||||||
|
}
|
||||||
|
|
||||||
|
HasStartedConsumingRequestBody = true;
|
||||||
|
|
||||||
EnsureIOInitialized();
|
EnsureIOInitialized();
|
||||||
|
|
||||||
|
|
@ -308,7 +319,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
|
|
||||||
protected Task ProduceEnd()
|
protected Task ProduceEnd()
|
||||||
{
|
{
|
||||||
if (_applicationException != null)
|
if (_requestRejectedException != null || _applicationException != null)
|
||||||
{
|
{
|
||||||
if (HasResponseStarted)
|
if (HasResponseStarted)
|
||||||
{
|
{
|
||||||
|
|
@ -318,6 +329,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
|
|
||||||
// If the request was rejected, the error state has already been set by SetBadRequestState and
|
// If the request was rejected, the error state has already been set by SetBadRequestState and
|
||||||
// that should take precedence.
|
// that should take precedence.
|
||||||
|
if (_requestRejectedException != null)
|
||||||
|
{
|
||||||
|
SetErrorResponseException(_requestRejectedException);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 500 Internal Server Error
|
// 500 Internal Server Error
|
||||||
|
|
@ -461,6 +476,23 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetBadRequestState(BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
Log.ConnectionBadRequest(_logger, RequestConnectionId, ex);
|
||||||
|
|
||||||
|
if (!HasResponseStarted)
|
||||||
|
{
|
||||||
|
SetErrorResponseException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestRejectedException = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetErrorResponseException(BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
SetErrorResponseHeaders(ex.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
protected void ReportApplicationError(Exception ex)
|
protected void ReportApplicationError(Exception ex)
|
||||||
{
|
{
|
||||||
if (_applicationException == null)
|
if (_applicationException == null)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,12 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
|
|
||||||
await _application.ProcessRequestAsync(context);
|
await _application.ProcessRequestAsync(context);
|
||||||
}
|
}
|
||||||
|
catch (BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
SetBadRequestState(ex);
|
||||||
|
ReportApplicationError(ex);
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
ReportApplicationError(ex);
|
ReportApplicationError(ex);
|
||||||
|
|
@ -59,7 +65,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
{
|
{
|
||||||
await ProduceEnd();
|
await ProduceEnd();
|
||||||
}
|
}
|
||||||
else if (!HasResponseStarted)
|
else if (!HasResponseStarted && _requestRejectedException == null)
|
||||||
{
|
{
|
||||||
// If the request was aborted and no response was sent, there's no
|
// If the request was aborted and no response was sent, there's no
|
||||||
// meaningful status code to log.
|
// meaningful status code to log.
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,11 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
Features.Set<IServerAddressesFeature>(_serverAddressesFeature);
|
Features.Set<IServerAddressesFeature>(_serverAddressesFeature);
|
||||||
|
|
||||||
|
if (_options.MaxRequestBodySize > _options.IisMaxRequestSizeLimit)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(CoreStrings.MaxRequestLimitWarning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
|
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
|
||||||
|
|
|
||||||
|
|
@ -147,4 +147,22 @@
|
||||||
<data name="ParameterReadOnlyAfterResponseStarted" xml:space="preserve">
|
<data name="ParameterReadOnlyAfterResponseStarted" xml:space="preserve">
|
||||||
<value>{name} cannot be set because the response has already started.</value>
|
<value>{name} cannot be set because the response has already started.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="BadRequest_RequestBodyTooLarge" xml:space="preserve">
|
||||||
|
<value>Request body too large.</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaxRequestBodySizeCannotBeModifiedAfterRead" xml:space="preserve">
|
||||||
|
<value>The maximum request body size cannot be modified after the app has already started reading from the request body.</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaxRequestBodySizeCannotBeModifiedForUpgradedRequests" xml:space="preserve">
|
||||||
|
<value>The maximum request body size cannot be modified after the request has been upgraded.</value>
|
||||||
|
</data>
|
||||||
|
<data name="NonNegativeNumberOrNullRequired" xml:space="preserve">
|
||||||
|
<value>Value must be null or a non-negative number.</value>
|
||||||
|
</data>
|
||||||
|
<data name="BadRequest" xml:space="preserve">
|
||||||
|
<value>Bad request.</value>
|
||||||
|
</data>
|
||||||
|
<data name="MaxRequestLimitWarning" xml:space="preserve">
|
||||||
|
<value>Increasing the MaxRequestBodySize conflicts with the max value for IIS limit maxAllowedContentLength. HTTP requests that have a content length greater than maxAllowedContentLength will still be rejected by IIS. You can disable the limit by either removing or setting the maxAllowedContentLength value to a higher limit.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
|
|
@ -34,5 +37,33 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
internal bool ForwardWindowsAuthentication { get; set; } = true;
|
internal bool ForwardWindowsAuthentication { get; set; } = true;
|
||||||
|
|
||||||
internal string[] ServerAddresses { get; set; }
|
internal string[] ServerAddresses { get; set; }
|
||||||
|
|
||||||
|
// Matches the default maxAllowedContentLength in IIS (~28.6 MB)
|
||||||
|
// https://www.iis.net/configreference/system.webserver/security/requestfiltering/requestlimits#005
|
||||||
|
private long? _maxRequestBodySize = 30000000;
|
||||||
|
|
||||||
|
internal long IisMaxRequestSizeLimit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum allowed size of any request body in bytes.
|
||||||
|
/// When set to null, the maximum request body size is unlimited.
|
||||||
|
/// This limit has no effect on upgraded connections which are always unlimited.
|
||||||
|
/// This can be overridden per-request via <see cref="IHttpMaxRequestBodySizeFeature"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Defaults to null (unlimited).
|
||||||
|
/// </remarks>
|
||||||
|
public long? MaxRequestBodySize
|
||||||
|
{
|
||||||
|
get => _maxRequestBodySize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.NonNegativeNumberOrNullRequired);
|
||||||
|
}
|
||||||
|
_maxRequestBodySize = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,90 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
internal static string FormatParameterReadOnlyAfterResponseStarted(object name)
|
internal static string FormatParameterReadOnlyAfterResponseStarted(object name)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ParameterReadOnlyAfterResponseStarted", "name"), name);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ParameterReadOnlyAfterResponseStarted", "name"), name);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request body too large.
|
||||||
|
/// </summary>
|
||||||
|
internal static string BadRequest_RequestBodyTooLarge
|
||||||
|
{
|
||||||
|
get => GetString("BadRequest_RequestBodyTooLarge");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request body too large.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatBadRequest_RequestBodyTooLarge()
|
||||||
|
=> GetString("BadRequest_RequestBodyTooLarge");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum request body size cannot be modified after the app has already started reading from the request body.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MaxRequestBodySizeCannotBeModifiedAfterRead
|
||||||
|
{
|
||||||
|
get => GetString("MaxRequestBodySizeCannotBeModifiedAfterRead");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum request body size cannot be modified after the app has already started reading from the request body.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMaxRequestBodySizeCannotBeModifiedAfterRead()
|
||||||
|
=> GetString("MaxRequestBodySizeCannotBeModifiedAfterRead");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum request body size cannot be modified after the request has been upgraded.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MaxRequestBodySizeCannotBeModifiedForUpgradedRequests
|
||||||
|
{
|
||||||
|
get => GetString("MaxRequestBodySizeCannotBeModifiedForUpgradedRequests");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum request body size cannot be modified after the request has been upgraded.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMaxRequestBodySizeCannotBeModifiedForUpgradedRequests()
|
||||||
|
=> GetString("MaxRequestBodySizeCannotBeModifiedForUpgradedRequests");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value must be null or a non-negative number.
|
||||||
|
/// </summary>
|
||||||
|
internal static string NonNegativeNumberOrNullRequired
|
||||||
|
{
|
||||||
|
get => GetString("NonNegativeNumberOrNullRequired");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value must be null or a non-negative number.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatNonNegativeNumberOrNullRequired()
|
||||||
|
=> GetString("NonNegativeNumberOrNullRequired");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bad request.
|
||||||
|
/// </summary>
|
||||||
|
internal static string BadRequest
|
||||||
|
{
|
||||||
|
get => GetString("BadRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bad request.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatBadRequest()
|
||||||
|
=> GetString("BadRequest");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increasing the MaxRequestBodySize conflicts with the max value for IIS limit maxAllowedContentLength. HTTP requests that have a content length greater than maxAllowedContentLength will still be rejected by IIS. You can disable the limit by either removing or setting the maxAllowedContentLength value to a higher limit.
|
||||||
|
/// </summary>
|
||||||
|
internal static string MaxRequestLimitWarning
|
||||||
|
{
|
||||||
|
get => GetString("MaxRequestLimitWarning");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increasing the MaxRequestBodySize conflicts with the max value for IIS limit maxAllowedContentLength. HTTP requests that have a content length greater than maxAllowedContentLength will still be rejected by IIS. You can disable the limit by either removing or setting the maxAllowedContentLength value to a higher limit.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatMaxRequestLimitWarning()
|
||||||
|
=> GetString("MaxRequestLimitWarning");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IIS
|
||||||
|
{
|
||||||
|
internal enum RequestRejectionReason
|
||||||
|
{
|
||||||
|
RequestBodyTooLarge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
options => {
|
options => {
|
||||||
options.ServerAddresses = iisConfigData.pwzBindings.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
options.ServerAddresses = iisConfigData.pwzBindings.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled;
|
options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled;
|
||||||
|
options.IisMaxRequestSizeLimit = iisConfigData.maxRequestBodySize;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// 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.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
{
|
||||||
|
[Collection(PublishedSitesCollection.Name)]
|
||||||
|
public class MaxRequestBodySizeTests : IISFunctionalTestBase
|
||||||
|
{
|
||||||
|
public MaxRequestBodySizeTests(PublishedSitesFixture fixture) : base(fixture)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
public async Task MaxRequestBodySizeE2EWorks()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.TransformArguments((a, _) => $"{a} DecreaseRequestLimit");
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
var result = await deploymentResult.HttpClient.PostAsync("/ReadRequestBody", new StringContent("test"));
|
||||||
|
Assert.Equal(HttpStatusCode.RequestEntityTooLarge, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
public async Task SetIISLimitMaxRequestBodySizeE2EWorks()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.ServerConfigActionList.Add(
|
||||||
|
(config, _) => {
|
||||||
|
config
|
||||||
|
.RequiredElement("system.webServer")
|
||||||
|
.GetOrAdd("security")
|
||||||
|
.GetOrAdd("requestFiltering")
|
||||||
|
.GetOrAdd("requestLimits", "maxAllowedContentLength", "1");
|
||||||
|
});
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
var result = await deploymentResult.HttpClient.PostAsync("/ReadRequestBody", new StringContent("test"));
|
||||||
|
|
||||||
|
// IIS returns a 404 instead of a 413...
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, result.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
public async Task IISRejectsContentLengthTooLargeByDefault()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
using (var connection = new TestConnection(deploymentResult.HttpClient.BaseAddress.Port))
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST /HelloWorld HTTP/1.1",
|
||||||
|
$"Content-Length: 30000001",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"A");
|
||||||
|
await connection.Receive("HTTP/1.1 404 Not Found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
[RequiresIIS(IISCapability.PoolEnvironmentVariables)]
|
||||||
|
public async Task SetIISLimitMaxRequestBodyLogsWarning()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.ServerConfigActionList.Add(
|
||||||
|
(config, _) => {
|
||||||
|
config
|
||||||
|
.RequiredElement("system.webServer")
|
||||||
|
.GetOrAdd("security")
|
||||||
|
.GetOrAdd("requestFiltering")
|
||||||
|
.GetOrAdd("requestLimits", "maxAllowedContentLength", "1");
|
||||||
|
});
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
var result = await deploymentResult.HttpClient.PostAsync("/DecreaseRequestLimit", new StringContent("1"));
|
||||||
|
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||||
|
if (deploymentParameters.ServerType == ServerType.IISExpress)
|
||||||
|
{
|
||||||
|
Assert.Single(TestSink.Writes, w => w.Message.Contains("Increasing the MaxRequestBodySize conflicts with the max value for IIS limit maxAllowedContentLength." +
|
||||||
|
" HTTP requests that have a content length greater than maxAllowedContentLength will still be rejected by IIS." +
|
||||||
|
" You can disable the limit by either removing or setting the maxAllowedContentLength value to a higher limit."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,339 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS;
|
||||||
|
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace IIS.Tests
|
||||||
|
{
|
||||||
|
[SkipIfHostableWebCoreNotAvailable]
|
||||||
|
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
|
||||||
|
public class MaxRequestBodySizeTests : LoggedTest
|
||||||
|
{
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task RequestBodyTooLargeContentLengthExceedsGlobalLimit()
|
||||||
|
{
|
||||||
|
var globalMaxRequestBodySize = 0x100000000;
|
||||||
|
|
||||||
|
BadHttpRequestException exception = null;
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
}
|
||||||
|
catch (BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}, LoggerFactory))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Content-Length: {globalMaxRequestBodySize + 1}",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.Receive("HTTP/1.1 413 Payload Too Large");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(CoreStrings.BadRequest_RequestBodyTooLarge, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task RequestBodyTooLargeContentLengthExceedingPerRequestLimit()
|
||||||
|
{
|
||||||
|
var maxRequestSize = 0x10000;
|
||||||
|
var perRequestMaxRequestBodySize = 0x100;
|
||||||
|
|
||||||
|
BadHttpRequestException exception = null;
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var feature = ctx.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
||||||
|
Assert.Equal(maxRequestSize, feature.MaxRequestBodySize);
|
||||||
|
feature.MaxRequestBodySize = perRequestMaxRequestBodySize;
|
||||||
|
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
}
|
||||||
|
catch (BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = maxRequestSize }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Content-Length: {perRequestMaxRequestBodySize + 1}",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.Receive("HTTP/1.1 413 Payload Too Large");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(CoreStrings.BadRequest_RequestBodyTooLarge, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task DoesNotRejectRequestWithContentLengthHeaderExceedingGlobalLimitIfLimitDisabledPerRequest()
|
||||||
|
{
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
var feature = ctx.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
||||||
|
Assert.Equal(0, feature.MaxRequestBodySize);
|
||||||
|
feature.MaxRequestBodySize = null;
|
||||||
|
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Content-Length: 1",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"A");
|
||||||
|
await connection.Receive("HTTP/1.1 200 OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task DoesNotRejectRequestWithChunkedExceedingGlobalLimitIfLimitDisabledPerRequest()
|
||||||
|
{
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
var feature = ctx.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
||||||
|
Assert.Equal(0, feature.MaxRequestBodySize);
|
||||||
|
feature.MaxRequestBodySize = null;
|
||||||
|
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Transfer-Encoding: chunked",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"a",
|
||||||
|
"0",
|
||||||
|
"");
|
||||||
|
await connection.Receive("HTTP/1.1 200 OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task DoesNotRejectBodylessGetRequestWithZeroMaxRequestBodySize()
|
||||||
|
{
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
|
||||||
|
await connection.Receive("HTTP/1.1 200 OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task DoesNotRejectBodylessPostWithZeroContentLengthRequestWithZeroMaxRequestBodySize()
|
||||||
|
{
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Content-Length: 0",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
|
||||||
|
await connection.Receive("HTTP/1.1 200 OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task DoesNotRejectBodylessPostWithEmptyChunksRequestWithZeroMaxRequestBodySize()
|
||||||
|
{
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
$"Transfer-Encoding: chunked",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"0",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
|
||||||
|
await connection.Receive("HTTP/1.1 200 OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task SettingMaxRequestBodySizeAfterReadingFromRequestBodyThrows()
|
||||||
|
{
|
||||||
|
var perRequestMaxRequestBodySize = 0x10;
|
||||||
|
var payloadSize = perRequestMaxRequestBodySize + 1;
|
||||||
|
var payload = new string('A', payloadSize);
|
||||||
|
InvalidOperationException invalidOpEx = null;
|
||||||
|
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[1];
|
||||||
|
Assert.Equal(1, await ctx.Request.Body.ReadAsync(buffer, 0, 1));
|
||||||
|
|
||||||
|
var feature = ctx.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
||||||
|
Assert.True(feature.IsReadOnly);
|
||||||
|
|
||||||
|
invalidOpEx = Assert.Throws<InvalidOperationException>(() =>
|
||||||
|
feature.MaxRequestBodySize = perRequestMaxRequestBodySize);
|
||||||
|
throw invalidOpEx;
|
||||||
|
}, LoggerFactory))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
"Host: localhost",
|
||||||
|
"Content-Length: " + payloadSize,
|
||||||
|
"",
|
||||||
|
payload);
|
||||||
|
await connection.Receive(
|
||||||
|
"HTTP/1.1 500 Internal Server Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task RequestBodyTooLargeChunked()
|
||||||
|
{
|
||||||
|
var maxRequestSize = 0x1000;
|
||||||
|
|
||||||
|
BadHttpRequestException exception = null;
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var num = await ctx.Request.Body.ReadAsync(new byte[2000]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (BadHttpRequestException ex)
|
||||||
|
{
|
||||||
|
exception = ex;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = maxRequestSize }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
"Transfer-Encoding: chunked",
|
||||||
|
"Host: localhost",
|
||||||
|
"",
|
||||||
|
"1001",
|
||||||
|
new string('a', 4097),
|
||||||
|
"0",
|
||||||
|
"");
|
||||||
|
await connection.Receive("HTTP/1.1 413 Payload Too Large");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotNull(exception);
|
||||||
|
Assert.Equal(CoreStrings.BadRequest_RequestBodyTooLarge, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task EveryReadFailsWhenContentLengthHeaderExceedsGlobalLimit()
|
||||||
|
{
|
||||||
|
BadHttpRequestException requestRejectedEx1 = null;
|
||||||
|
BadHttpRequestException requestRejectedEx2 = null;
|
||||||
|
using (var testServer = await TestServer.Create(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
var buffer = new byte[1];
|
||||||
|
requestRejectedEx1 = await Assert.ThrowsAsync<BadHttpRequestException>(
|
||||||
|
async () => await ctx.Request.Body.ReadAsync(buffer, 0, 1));
|
||||||
|
requestRejectedEx2 = await Assert.ThrowsAsync<BadHttpRequestException>(
|
||||||
|
async () => await ctx.Request.Body.ReadAsync(buffer, 0, 1));
|
||||||
|
throw requestRejectedEx2;
|
||||||
|
}, LoggerFactory, new IISServerOptions { MaxRequestBodySize = 0 }))
|
||||||
|
{
|
||||||
|
using (var connection = testServer.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.Send(
|
||||||
|
"POST / HTTP/1.1",
|
||||||
|
"Host: localhost",
|
||||||
|
"Content-Length: " + (new IISServerOptions().MaxRequestBodySize + 1),
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.Receive(
|
||||||
|
"HTTP/1.1 413 Payload Too Large");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotNull(requestRejectedEx1);
|
||||||
|
Assert.NotNull(requestRejectedEx2);
|
||||||
|
Assert.Equal(CoreStrings.BadRequest_RequestBodyTooLarge, requestRejectedEx1.Message);
|
||||||
|
Assert.Equal(CoreStrings.BadRequest_RequestBodyTooLarge, requestRejectedEx2.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,6 @@ using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Loader;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
@ -15,7 +14,6 @@ using System.Xml.XPath;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
|
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||||
|
|
@ -51,6 +49,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
public HttpClient HttpClient { get; private set; }
|
public HttpClient HttpClient { get; private set; }
|
||||||
public TestConnection CreateConnection() => new TestConnection(_currentPort);
|
public TestConnection CreateConnection() => new TestConnection(_currentPort);
|
||||||
|
|
||||||
|
private static IISServerOptions _options;
|
||||||
private IWebHost _host;
|
private IWebHost _host;
|
||||||
|
|
||||||
private string _appHostConfigPath;
|
private string _appHostConfigPath;
|
||||||
|
|
@ -63,9 +62,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<TestServer> Create(Action<IApplicationBuilder> appBuilder, ILoggerFactory loggerFactory)
|
public static async Task<TestServer> Create(Action<IApplicationBuilder> appBuilder, ILoggerFactory loggerFactory, IISServerOptions options)
|
||||||
{
|
{
|
||||||
await WebCoreLock.WaitAsync();
|
await WebCoreLock.WaitAsync();
|
||||||
|
_options = options;
|
||||||
var server = new TestServer(appBuilder, loggerFactory);
|
var server = new TestServer(appBuilder, loggerFactory);
|
||||||
server.Start();
|
server.Start();
|
||||||
(await server.HttpClient.GetAsync("/start")).EnsureSuccessStatusCode();
|
(await server.HttpClient.GetAsync("/start")).EnsureSuccessStatusCode();
|
||||||
|
|
@ -75,7 +75,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
|
||||||
public static Task<TestServer> Create(RequestDelegate app, ILoggerFactory loggerFactory)
|
public static Task<TestServer> Create(RequestDelegate app, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
return Create(builder => builder.Run(app), loggerFactory);
|
return Create(builder => builder.Run(app), loggerFactory, new IISServerOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<TestServer> Create(RequestDelegate app, ILoggerFactory loggerFactory, IISServerOptions options)
|
||||||
|
{
|
||||||
|
return Create(builder => builder.Run(app), loggerFactory, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
|
|
@ -125,14 +130,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
|
||||||
private int Main(IntPtr argc, IntPtr argv)
|
private int Main(IntPtr argc, IntPtr argv)
|
||||||
{
|
{
|
||||||
_host = new WebHostBuilder()
|
var builder = new WebHostBuilder()
|
||||||
.UseIIS()
|
.UseIIS()
|
||||||
.ConfigureServices(services => {
|
.ConfigureServices(services =>
|
||||||
services.AddSingleton<IStartup>(this);
|
{
|
||||||
services.AddSingleton(_loggerFactory);
|
services.Configure<IISServerOptions>(options => options.MaxRequestBodySize = _options.MaxRequestBodySize);
|
||||||
|
services.AddSingleton<IStartup>(this);
|
||||||
|
services.AddSingleton(_loggerFactory);
|
||||||
})
|
})
|
||||||
.UseSetting(WebHostDefaults.ApplicationKey, typeof(TestServer).GetTypeInfo().Assembly.FullName)
|
.UseSetting(WebHostDefaults.ApplicationKey, typeof(TestServer).GetTypeInfo().Assembly.FullName);
|
||||||
.Build();
|
_host = builder.Build();
|
||||||
|
|
||||||
var doneEvent = new ManualResetEventSlim();
|
var doneEvent = new ManualResetEventSlim();
|
||||||
var lifetime = _host.Services.GetService<IHostApplicationLifetime>();
|
var lifetime = _host.Services.GetService<IHostApplicationLifetime>();
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IIS.csproj" />
|
||||||
<ProjectReference Include="..\Common.Tests\Common.Tests.csproj" />
|
<ProjectReference Include="..\Common.Tests\Common.Tests.csproj" />
|
||||||
<ProjectReference Include="$(RepositoryRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
|
<ProjectReference Include="$(RepositoryRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
|
||||||
<ProjectReference Include="$(RepositoryRoot)src\Servers\IIS\IntegrationTesting.IIS\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
|
<ProjectReference Include="$(RepositoryRoot)src\Servers\IIS\IntegrationTesting.IIS\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
<TestAssetOutputName>InProcessForwardsCompatWebSite</TestAssetOutputName>
|
<TestAssetOutputName>InProcessForwardsCompatWebSite</TestAssetOutputName>
|
||||||
|
<DefineConstants>FORWARDCOMPAT</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,27 @@ namespace TestSite
|
||||||
case "ConsoleWriteStartServer":
|
case "ConsoleWriteStartServer":
|
||||||
Console.WriteLine("TEST MESSAGE");
|
Console.WriteLine("TEST MESSAGE");
|
||||||
return StartServer();
|
return StartServer();
|
||||||
|
#if !FORWARDCOMPAT
|
||||||
|
case "DecreaseRequestLimit":
|
||||||
|
{
|
||||||
|
var host = new WebHostBuilder()
|
||||||
|
.ConfigureLogging((_, factory) =>
|
||||||
|
{
|
||||||
|
factory.AddConsole();
|
||||||
|
factory.AddFilter("Console", level => level >= LogLevel.Information);
|
||||||
|
})
|
||||||
|
.UseIIS()
|
||||||
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.Configure<IISServerOptions>(options => options.MaxRequestBodySize = 2);
|
||||||
|
})
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
host.Run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return StartServer();
|
return StartServer();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,6 @@ namespace TestSite
|
||||||
{
|
{
|
||||||
result = await ctx.Request.Body.ReadAsync(readBuffer, 0, 1);
|
result = await ctx.Request.Body.ReadAsync(readBuffer, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _requestsInFlight = 0;
|
private int _requestsInFlight = 0;
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
// Handle cases where debug file is redirected by test
|
// Handle cases where debug file is redirected by test
|
||||||
var debugLogLocations = new List<string>();
|
var debugLogLocations = new List<string>();
|
||||||
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile"))
|
||||||
|
|
@ -193,8 +192,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"Unable to find non-empty debug log files. Tried: {string.Join(", ", debugLogLocations)}");
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
|
|
||||||
public static string DecodeAndUnescapePath(Span<byte> rawUrlBytes)
|
public static string DecodeAndUnescapePath(Span<byte> rawUrlBytes)
|
||||||
{
|
{
|
||||||
Debug.Assert(rawUrlBytes.Length > 0, "Length of the URL cannot be zero.");
|
Debug.Assert(rawUrlBytes.Length != 0, "Length of the URL cannot be zero.");
|
||||||
var rawPath = RawUrlHelper.GetPath(rawUrlBytes);
|
var rawPath = RawUrlHelper.GetPath(rawUrlBytes);
|
||||||
|
|
||||||
if (rawPath.Length == 0)
|
if (rawPath.Length == 0)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue