Родитель
61f7c82d49
Коммит
bec259f976
|
@ -0,0 +1,26 @@
|
|||
// 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.HttpSys
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum declaring the allowed values for the verbosity level when http.sys reject requests due to throttling.
|
||||
/// </summary>
|
||||
public enum Http503VerbosityLevel : long
|
||||
{
|
||||
/// <summary>
|
||||
/// A 503 response is not sent; the connection is reset. This is the default HTTP Server API behavior.
|
||||
/// </summary>
|
||||
Basic = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP Server API sends a 503 response with a "Service Unavailable" reason phrase.
|
||||
/// </summary>
|
||||
Limited = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP Server API sends a 503 response with a detailed reason phrase.
|
||||
/// </summary>
|
||||
Full = 2
|
||||
}
|
||||
}
|
|
@ -2,18 +2,21 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.HttpSys
|
||||
{
|
||||
public class HttpSysOptions
|
||||
{
|
||||
private const Http503VerbosityLevel DefaultRejectionVerbosityLevel = Http503VerbosityLevel.Basic; // Http.sys default.
|
||||
private const long DefaultRequestQueueLength = 1000; // Http.sys default.
|
||||
internal static readonly int DefaultMaxAccepts = 5 * Environment.ProcessorCount;
|
||||
// Matches the default maxAllowedContentLength in IIS (~28.6 MB)
|
||||
// https://www.iis.net/configreference/system.webserver/security/requestfiltering/requestlimits#005
|
||||
private const long DefaultMaxRequestBodySize = 30000000;
|
||||
|
||||
private Http503VerbosityLevel _rejectionVebosityLevel = DefaultRejectionVerbosityLevel;
|
||||
// The native request queue
|
||||
private long _requestQueueLength = DefaultRequestQueueLength;
|
||||
private long? _maxConnections;
|
||||
|
@ -137,6 +140,38 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
/// </summary>
|
||||
public bool AllowSynchronousIO { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that controls how http.sys reacts when rejecting requests due to throttling conditions - like when the request
|
||||
/// queue limit is reached. The default in http.sys is "Basic" which means http.sys is just resetting the TCP connection. IIS uses Limited
|
||||
/// as its default behavior which will result in sending back a 503 - Service Unavailable back to the client.
|
||||
/// </summary>
|
||||
public Http503VerbosityLevel Http503Verbosity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rejectionVebosityLevel;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < Http503VerbosityLevel.Basic || value > Http503VerbosityLevel.Full)
|
||||
{
|
||||
string message = String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"The value must be one of the values defined in the '{0}' enum.",
|
||||
typeof(Http503VerbosityLevel).Name);
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, message);
|
||||
}
|
||||
|
||||
if (_requestQueue != null)
|
||||
{
|
||||
_requestQueue.SetRejectionVerbosity(value);
|
||||
}
|
||||
// Only store it if it succeeds or hasn't started yet
|
||||
_rejectionVebosityLevel = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Apply(UrlGroup urlGroup, RequestQueue requestQueue)
|
||||
{
|
||||
_urlGroup = urlGroup;
|
||||
|
@ -152,6 +187,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
_requestQueue.SetLengthLimit(_requestQueueLength);
|
||||
}
|
||||
|
||||
if (_rejectionVebosityLevel != DefaultRejectionVerbosityLevel)
|
||||
{
|
||||
_requestQueue.SetRejectionVerbosity(_rejectionVebosityLevel);
|
||||
}
|
||||
|
||||
Authentication.SetUrlGroupSecurity(urlGroup);
|
||||
Timeouts.SetUrlGroupTimeouts(urlGroup);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,21 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
}
|
||||
}
|
||||
|
||||
// The listener must be active for this to work.
|
||||
internal unsafe void SetRejectionVerbosity(Http503VerbosityLevel verbosity)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
var result = HttpApi.HttpSetRequestQueueProperty(Handle,
|
||||
HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServer503VerbosityProperty,
|
||||
new IntPtr((void*)&verbosity), (uint)Marshal.SizeOf<long>(), 0, IntPtr.Zero);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
throw new HttpSysException((int)result);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
|
|
|
@ -245,6 +245,22 @@ namespace Microsoft.AspNetCore.Server.HttpSys.Listener
|
|||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task Server_SetRejectionVerbosityLevel_Success()
|
||||
{
|
||||
using (var server = Utilities.CreateHttpServer(out string address))
|
||||
{
|
||||
server.Options.Http503Verbosity = Http503VerbosityLevel.Limited;
|
||||
var responseTask = SendRequestAsync(address);
|
||||
|
||||
var context = await server.AcceptAsync(Utilities.DefaultTimeout);
|
||||
context.Dispose();
|
||||
|
||||
var response = await responseTask;
|
||||
Assert.Equal(string.Empty, response);
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task Server_HotAddPrefix_Success()
|
||||
{
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
@ -298,6 +299,44 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task Server_SetHttp503VebosittHittingThrottle_Success()
|
||||
{
|
||||
// This is just to get a dynamic port
|
||||
string address;
|
||||
using (Utilities.CreateHttpServer(out address, httpContext => Task.FromResult(0))) { }
|
||||
|
||||
var server = Utilities.CreatePump();
|
||||
server.Listener.Options.UrlPrefixes.Add(UrlPrefix.Create(address));
|
||||
Assert.Null(server.Listener.Options.MaxConnections);
|
||||
server.Listener.Options.MaxConnections = 3;
|
||||
server.Listener.Options.Http503Verbosity = Http503VerbosityLevel.Limited;
|
||||
|
||||
using (server)
|
||||
{
|
||||
await server.StartAsync(new DummyApplication(), CancellationToken.None);
|
||||
|
||||
using (var client1 = await SendHungRequestAsync("GET", address))
|
||||
using (var client2 = await SendHungRequestAsync("GET", address))
|
||||
{
|
||||
using (var client3 = await SendHungRequestAsync("GET", address))
|
||||
{
|
||||
using (HttpClient client4 = new HttpClient())
|
||||
{
|
||||
// Maxed out, refuses connection should return 503
|
||||
HttpResponseMessage response = await client4.GetAsync(address);
|
||||
|
||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
// A connection has been closed, try again.
|
||||
string responseText = await SendRequestAsync(address);
|
||||
Assert.Equal(string.Empty, responseText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public void Server_SetConnectionLimitArgumentValidation_Success()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче