Skip to content
代码片段 群组 项目
未验证 提交 c9eeba8b 编辑于 作者: Aditya Mandaleeka's avatar Aditya Mandaleeka 提交者: GitHub
浏览文件

Add feature and DiagnosticSource event for rejected request info (#34783)

Add feature and DiagnosticSource event for rejected request info.
上级 3ce1231d
No related branches found
No related tags found
无相关合并请求
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.AspNetCore.Http.Features
{
/// <summary>
/// Provides information about rejected HTTP requests.
/// </summary>
public interface IBadRequestExceptionFeature
{
/// <summary>
/// Synchronously retrieves the exception associated with the rejected HTTP request.
/// </summary>
Exception? Error { get; }
}
}
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
*REMOVED*Microsoft.AspNetCore.Http.Features.FeatureReferences<TCache>.Revision.get -> int *REMOVED*Microsoft.AspNetCore.Http.Features.FeatureReferences<TCache>.Revision.get -> int
*REMOVED*static readonly Microsoft.AspNetCore.Http.Features.FeatureReference<T>.Default -> Microsoft.AspNetCore.Http.Features.FeatureReference<T> *REMOVED*static readonly Microsoft.AspNetCore.Http.Features.FeatureReference<T>.Default -> Microsoft.AspNetCore.Http.Features.FeatureReference<T>
*REMOVED*virtual Microsoft.AspNetCore.Http.Features.FeatureCollection.Revision.get -> int *REMOVED*virtual Microsoft.AspNetCore.Http.Features.FeatureCollection.Revision.get -> int
Microsoft.AspNetCore.Http.Features.IBadRequestExceptionFeature
Microsoft.AspNetCore.Http.Features.IBadRequestExceptionFeature.Error.get -> System.Exception?
Microsoft.AspNetCore.Http.Features.IServerVariablesFeature.this[string! variableName].get -> string? Microsoft.AspNetCore.Http.Features.IServerVariablesFeature.this[string! variableName].get -> string?
Microsoft.AspNetCore.Http.IHeaderDictionary.Accept.get -> Microsoft.Extensions.Primitives.StringValues Microsoft.AspNetCore.Http.IHeaderDictionary.Accept.get -> Microsoft.Extensions.Primitives.StringValues
Microsoft.AspNetCore.Http.IHeaderDictionary.Accept.set -> void Microsoft.AspNetCore.Http.IHeaderDictionary.Accept.set -> void
......
...@@ -241,6 +241,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -241,6 +241,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Stream IHttpResponseBodyFeature.Stream => ResponseBody; Stream IHttpResponseBodyFeature.Stream => ResponseBody;
Exception? IBadRequestExceptionFeature.Error
{
get => _requestRejectedException;
}
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state) void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
{ {
OnStarting(callback, state); OnStarting(callback, state);
......
...@@ -29,7 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -29,7 +29,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
IHttpRequestLifetimeFeature, IHttpRequestLifetimeFeature,
IHttpBodyControlFeature, IHttpBodyControlFeature,
IHttpMaxRequestBodySizeFeature, IHttpMaxRequestBodySizeFeature,
IHttpRequestBodyDetectionFeature IHttpRequestBodyDetectionFeature,
IBadRequestExceptionFeature
{ {
// Implemented features // Implemented features
internal protected IHttpRequestFeature? _currentIHttpRequestFeature; internal protected IHttpRequestFeature? _currentIHttpRequestFeature;
...@@ -46,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -46,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
internal protected IHttpBodyControlFeature? _currentIHttpBodyControlFeature; internal protected IHttpBodyControlFeature? _currentIHttpBodyControlFeature;
internal protected IHttpMaxRequestBodySizeFeature? _currentIHttpMaxRequestBodySizeFeature; internal protected IHttpMaxRequestBodySizeFeature? _currentIHttpMaxRequestBodySizeFeature;
internal protected IHttpRequestBodyDetectionFeature? _currentIHttpRequestBodyDetectionFeature; internal protected IHttpRequestBodyDetectionFeature? _currentIHttpRequestBodyDetectionFeature;
internal protected IBadRequestExceptionFeature? _currentIBadRequestExceptionFeature;
// Other reserved feature slots // Other reserved feature slots
internal protected IServiceProvidersFeature? _currentIServiceProvidersFeature; internal protected IServiceProvidersFeature? _currentIServiceProvidersFeature;
...@@ -85,6 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -85,6 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpBodyControlFeature = this; _currentIHttpBodyControlFeature = this;
_currentIHttpMaxRequestBodySizeFeature = this; _currentIHttpMaxRequestBodySizeFeature = this;
_currentIHttpRequestBodyDetectionFeature = this; _currentIHttpRequestBodyDetectionFeature = this;
_currentIBadRequestExceptionFeature = this;
_currentIServiceProvidersFeature = null; _currentIServiceProvidersFeature = null;
_currentIHttpActivityFeature = null; _currentIHttpActivityFeature = null;
...@@ -257,6 +260,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -257,6 +260,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
feature = _currentIHttpWebSocketFeature; feature = _currentIHttpWebSocketFeature;
} }
else if (key == typeof(IBadRequestExceptionFeature))
{
feature = _currentIBadRequestExceptionFeature;
}
else if (key == typeof(IHttp2StreamIdFeature)) else if (key == typeof(IHttp2StreamIdFeature))
{ {
feature = _currentIHttp2StreamIdFeature; feature = _currentIHttp2StreamIdFeature;
...@@ -389,6 +396,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -389,6 +396,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
_currentIHttpWebSocketFeature = (IHttpWebSocketFeature?)value; _currentIHttpWebSocketFeature = (IHttpWebSocketFeature?)value;
} }
else if (key == typeof(IBadRequestExceptionFeature))
{
_currentIBadRequestExceptionFeature = (IBadRequestExceptionFeature?)value;
}
else if (key == typeof(IHttp2StreamIdFeature)) else if (key == typeof(IHttp2StreamIdFeature))
{ {
_currentIHttp2StreamIdFeature = (IHttp2StreamIdFeature?)value; _currentIHttp2StreamIdFeature = (IHttp2StreamIdFeature?)value;
...@@ -523,6 +534,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -523,6 +534,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
feature = Unsafe.As<IHttpWebSocketFeature?, TFeature?>(ref _currentIHttpWebSocketFeature); feature = Unsafe.As<IHttpWebSocketFeature?, TFeature?>(ref _currentIHttpWebSocketFeature);
} }
else if (typeof(TFeature) == typeof(IBadRequestExceptionFeature))
{
feature = Unsafe.As<IBadRequestExceptionFeature?, TFeature?>(ref _currentIBadRequestExceptionFeature);
}
else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature))
{ {
feature = Unsafe.As<IHttp2StreamIdFeature?, TFeature?>(ref _currentIHttp2StreamIdFeature); feature = Unsafe.As<IHttp2StreamIdFeature?, TFeature?>(ref _currentIHttp2StreamIdFeature);
...@@ -663,6 +678,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -663,6 +678,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
_currentIHttpWebSocketFeature = Unsafe.As<TFeature?, IHttpWebSocketFeature?>(ref feature); _currentIHttpWebSocketFeature = Unsafe.As<TFeature?, IHttpWebSocketFeature?>(ref feature);
} }
else if (typeof(TFeature) == typeof(IBadRequestExceptionFeature))
{
_currentIBadRequestExceptionFeature = Unsafe.As<TFeature?, IBadRequestExceptionFeature?>(ref feature);
}
else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature))
{ {
_currentIHttp2StreamIdFeature = Unsafe.As<TFeature?, IHttp2StreamIdFeature?>(ref feature); _currentIHttp2StreamIdFeature = Unsafe.As<TFeature?, IHttp2StreamIdFeature?>(ref feature);
...@@ -791,6 +810,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -791,6 +810,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
yield return new KeyValuePair<Type, object>(typeof(IHttpWebSocketFeature), _currentIHttpWebSocketFeature); yield return new KeyValuePair<Type, object>(typeof(IHttpWebSocketFeature), _currentIHttpWebSocketFeature);
} }
if (_currentIBadRequestExceptionFeature != null)
{
yield return new KeyValuePair<Type, object>(typeof(IBadRequestExceptionFeature), _currentIBadRequestExceptionFeature);
}
if (_currentIHttp2StreamIdFeature != null) if (_currentIHttp2StreamIdFeature != null)
{ {
yield return new KeyValuePair<Type, object>(typeof(IHttp2StreamIdFeature), _currentIHttp2StreamIdFeature); yield return new KeyValuePair<Type, object>(typeof(IHttp2StreamIdFeature), _currentIHttp2StreamIdFeature);
......
...@@ -1324,14 +1324,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http ...@@ -1324,14 +1324,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
public void SetBadRequestState(BadHttpRequestException ex) public void SetBadRequestState(BadHttpRequestException ex)
{ {
Log.ConnectionBadRequest(ConnectionId, ex); Log.ConnectionBadRequest(ConnectionId, ex);
_requestRejectedException = ex;
if (!HasResponseStarted) if (!HasResponseStarted)
{ {
SetErrorResponseException(ex); SetErrorResponseException(ex);
} }
const string badRequestEventName = "Microsoft.AspNetCore.Server.Kestrel.BadRequest";
if (ServiceContext.DiagnosticSource?.IsEnabled(badRequestEventName) == true)
{
ServiceContext.DiagnosticSource.Write(badRequestEventName, this);
}
_keepAlive = false; _keepAlive = false;
_requestRejectedException = ex;
} }
public void ReportApplicationError(Exception? ex) public void ReportApplicationError(Exception? ex)
......
...@@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ...@@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
IOptions<KestrelServerOptions> options, IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories, IEnumerable<IConnectionListenerFactory> transportFactories,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: this(transportFactories, null, CreateServiceContext(options, loggerFactory)) : this(transportFactories, null, CreateServiceContext(options, loggerFactory, null))
{ {
} }
...@@ -49,7 +49,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ...@@ -49,7 +49,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
IEnumerable<IConnectionListenerFactory> transportFactories, IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories, IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
: this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory)) : this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory, null))
{
}
public KestrelServerImpl(
IOptions<KestrelServerOptions> options,
IEnumerable<IConnectionListenerFactory> transportFactories,
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
ILoggerFactory loggerFactory,
DiagnosticSource diagnosticSource)
: this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory, diagnosticSource))
{ {
} }
...@@ -89,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ...@@ -89,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
HttpCharacters.Initialize(); HttpCharacters.Initialize();
} }
private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory) private static ServiceContext CreateServiceContext(IOptions<KestrelServerOptions> options, ILoggerFactory loggerFactory, DiagnosticSource? diagnosticSource)
{ {
if (options == null) if (options == null)
{ {
...@@ -124,7 +134,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core ...@@ -124,7 +134,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
DateHeaderValueManager = dateHeaderValueManager, DateHeaderValueManager = dateHeaderValueManager,
ConnectionManager = connectionManager, ConnectionManager = connectionManager,
Heartbeat = heartbeat, Heartbeat = heartbeat,
ServerOptions = serverOptions ServerOptions = serverOptions,
DiagnosticSource = diagnosticSource
}; };
} }
......
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.IO.Pipelines; using System.IO.Pipelines;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
...@@ -27,5 +28,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal ...@@ -27,5 +28,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
public Heartbeat Heartbeat { get; set; } = default!; public Heartbeat Heartbeat { get; set; } = default!;
public KestrelServerOptions ServerOptions { get; set; } = default!; public KestrelServerOptions ServerOptions { get; set; } = default!;
public DiagnosticSource? DiagnosticSource { get; set; }
} }
} }
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic; using System.Diagnostics;
using System.Linq; using Microsoft.AspNetCore.Http.Features;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport; using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Moq; using Moq;
using Xunit; using Xunit;
using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException; using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException;
...@@ -195,6 +193,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests ...@@ -195,6 +193,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
} }
} }
private class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
private IDisposable _subscription;
private Action<KeyValuePair<string, object>> _callback;
public bool EventFired { get; set; }
public BadRequestEventListener(DiagnosticListener diagnosticListener, Action<KeyValuePair<string, object>> callback)
{
_subscription = diagnosticListener.Subscribe(this, IsEnabled);
_callback = callback;
}
private static readonly Predicate<string> IsEnabled = (provider) => provider switch
{
"Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
_ => false
};
public void OnNext(KeyValuePair<string, object> pair)
{
EventFired = true;
_callback(pair);
}
public void OnError(Exception error) { }
public void OnCompleted() { }
public virtual void Dispose() => _subscription.Dispose();
}
private async Task TestBadRequest(string request, string expectedResponseStatusCode, string expectedExceptionMessage, string expectedAllowHeader = null) private async Task TestBadRequest(string request, string expectedResponseStatusCode, string expectedExceptionMessage, string expectedAllowHeader = null)
{ {
BadHttpRequestException loggedException = null; BadHttpRequestException loggedException = null;
...@@ -207,7 +232,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests ...@@ -207,7 +232,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
.Setup(trace => trace.ConnectionBadRequest(It.IsAny<string>(), It.IsAny<BadHttpRequestException>())) .Setup(trace => trace.ConnectionBadRequest(It.IsAny<string>(), It.IsAny<BadHttpRequestException>()))
.Callback<string, BadHttpRequestException>((connectionId, exception) => loggedException = exception); .Callback<string, BadHttpRequestException>((connectionId, exception) => loggedException = exception);
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory, mockKestrelTrace.Object))) // Set up a listener to catch the BadRequest event
var diagListener = new DiagnosticListener("BadRequestTestsDiagListener");
string eventProviderName = "";
string exceptionString = "";
var badRequestEventListener = new BadRequestEventListener(diagListener, (pair) => {
eventProviderName = pair.Key;
var featureCollection = pair.Value as IFeatureCollection;
if (featureCollection is not null)
{
var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();
exceptionString = badRequestFeature.Error.ToString();
}
});
await using (var server = new TestServer(context => Task.CompletedTask, new TestServiceContext(LoggerFactory, mockKestrelTrace.Object) { DiagnosticSource = diagListener }))
{ {
using (var connection = server.CreateConnection()) using (var connection = server.CreateConnection())
{ {
...@@ -218,6 +257,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests ...@@ -218,6 +257,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
mockKestrelTrace.Verify(trace => trace.ConnectionBadRequest(It.IsAny<string>(), It.IsAny<BadHttpRequestException>())); mockKestrelTrace.Verify(trace => trace.ConnectionBadRequest(It.IsAny<string>(), It.IsAny<BadHttpRequestException>()));
Assert.Equal(expectedExceptionMessage, loggedException.Message); Assert.Equal(expectedExceptionMessage, loggedException.Message);
// Verify DiagnosticSource event for bad request
Assert.True(badRequestEventListener.EventFired);
Assert.Equal("Microsoft.AspNetCore.Server.Kestrel.BadRequest", eventProviderName);
Assert.Contains(expectedExceptionMessage, exceptionString);
} }
private async Task ReceiveBadRequestResponse(InMemoryConnection connection, string expectedResponseStatusCode, string expectedDateHeaderValue, string expectedAllowHeader = null) private async Task ReceiveBadRequestResponse(InMemoryConnection connection, string expectedResponseStatusCode, string expectedDateHeaderValue, string expectedAllowHeader = null)
......
...@@ -39,7 +39,8 @@ namespace CodeGenerator ...@@ -39,7 +39,8 @@ namespace CodeGenerator
"IHttpResponseTrailersFeature", "IHttpResponseTrailersFeature",
"ITlsConnectionFeature", "ITlsConnectionFeature",
"IHttpUpgradeFeature", "IHttpUpgradeFeature",
"IHttpWebSocketFeature" "IHttpWebSocketFeature",
"IBadRequestExceptionFeature"
}; };
var maybeFeatures = new[] var maybeFeatures = new[]
{ {
...@@ -78,6 +79,7 @@ namespace CodeGenerator ...@@ -78,6 +79,7 @@ namespace CodeGenerator
"IHttpBodyControlFeature", "IHttpBodyControlFeature",
"IHttpMaxRequestBodySizeFeature", "IHttpMaxRequestBodySizeFeature",
"IHttpRequestBodyDetectionFeature", "IHttpRequestBodyDetectionFeature",
"IBadRequestExceptionFeature"
}; };
var usings = $@" var usings = $@"
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册