Skip to content

Commit

Permalink
fix(CA1031): use exception handler middleware instead of catching all…
Browse files Browse the repository at this point in the history
… exceptions in ServerRequestHandler.
  • Loading branch information
skwasjer committed Feb 3, 2024
1 parent f22be46 commit 6049da2
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/MockHttp.Server/MockHttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ private IWebHostBuilder CreateWebHostBuilder(ILoggerFactory? loggerFactory)

AddMockHttpServerHeader(applicationBuilder);

applicationBuilder.UseMockHttpExceptionHandler();

ServerRequestHandler serverRequestHandler = applicationBuilder.ApplicationServices.GetRequiredService<ServerRequestHandler>();
applicationBuilder.Use(serverRequestHandler.HandleAsync);
});
Expand Down
42 changes: 42 additions & 0 deletions src/MockHttp.Server/Server/ExceptionHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MockHttp.Http;

namespace MockHttp.Server;

internal static class ExceptionHandler
{
internal static IApplicationBuilder UseMockHttpExceptionHandler(this IApplicationBuilder app)
{
return app.UseExceptionHandler(new ExceptionHandlerOptions { ExceptionHandler = HandleExceptionAsync });
}

private static Task HandleExceptionAsync(HttpContext ctx)
{
if (ctx.Response.HasStarted)
{
return Task.CompletedTask;
}

Exception? ex = ctx.Features.Get<IExceptionHandlerFeature>()?.Error;
ILogger logger = ctx.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger(typeof(ExceptionHandler).FullName!);
logger.LogRequestMessage(ctx, Resources.Error_VerifyMockSetup);

ctx.Response.StatusCode = StatusCodes.Status500InternalServerError;

IHttpResponseFeature? responseFeature = ctx.Features.Get<IHttpResponseFeature>();
if (responseFeature is not null)
{
responseFeature.ReasonPhrase = Resources.Error_VerifyMockSetup;
}

ctx.Response.ContentType = $"{MediaTypes.PlainText}; charset={MockHttpHandler.DefaultEncoding.WebName}";
byte[] body = MockHttpHandler.DefaultEncoding.GetBytes(Resources.Error_VerifyMockSetup + Environment.NewLine + ex);
return ctx.Response.BodyWriter.WriteAsync(body, ctx.RequestAborted).AsTask();
}
}
18 changes: 2 additions & 16 deletions src/MockHttp.Server/Server/ServerRequestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using MockHttp.Http;

namespace MockHttp.Server;

Expand Down Expand Up @@ -38,18 +35,6 @@ public async Task HandleAsync(HttpContext httpContext, Func<Task> _)
{
httpResponseMessage = await SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogRequestMessage(httpContext, Resources.Error_VerifyMockSetup);

#pragma warning disable CA2000 // Dispose objects before losing scope
httpResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError)
#pragma warning restore CA2000 // Dispose objects before losing scope
{
ReasonPhrase = Resources.Error_VerifyMockSetup,
Content = new StringContent(Resources.Error_VerifyMockSetup + Environment.NewLine + ex, MockHttpHandler.DefaultEncoding, MediaTypes.PlainText)
};
}
finally
{
_logger.LogRequestMessage(httpContext, Resources.Debug_RequestHandled);
Expand All @@ -65,6 +50,7 @@ public async Task HandleAsync(HttpContext httpContext, Func<Task> _)
{
throw new InvalidOperationException(Resources.MissingHttpResponseFeature);
}

await httpResponseMessage.MapToFeatureAsync(responseFeature, responseBodyFeature, cancellationToken).ConfigureAwait(false);
}
}
19 changes: 19 additions & 0 deletions test/MockHttp.Server.Tests/MockHttpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,30 @@ public async Task Given_request_is_configured_to_throw_when_sending_it_should_re

// Assert
response.Should().HaveStatusCode(HttpStatusCode.InternalServerError);
response.Should().HaveContentType(MediaTypes.PlainText, Encoding.UTF8);
await response.Should().HaveContentAsync(expectedErrorMessage + Environment.NewLine + ex);
response.ReasonPhrase.Should().Be(expectedErrorMessage);
_fixture.Handler.Verify();
}

[Fact]
public async Task Given_that_request_is_configured_with_server_timeout_when_sending_it_should_respond_with_request_timed_out()
{
_fixture.Handler
.When(matching => matching.Method(HttpMethod.Get))
.Respond(with => with.ServerTimeout())
.Verifiable();

using HttpClient client = _fixture.Server.CreateClient();

// Act
HttpResponseMessage response = await client.GetAsync("");

// Assert
response.Should().HaveStatusCode(HttpStatusCode.RequestTimeout);
_fixture.Handler.Verify();
}

[Fact]
public void When_creating_server_with_null_handler_it_should_throw()
{
Expand Down

0 comments on commit 6049da2

Please sign in to comment.