using System;
using System.Net;
using System.Runtime.ExceptionServices;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using EmbedIO.Utilities;
using Swan.Logging;
namespace EmbedIO
{
///
/// Provides standard handlers for HTTP exceptions at both module and server level.
///
///
/// Where applicable, HTTP exception handlers defined in this class
/// use the and
/// properties to customize
/// their behavior.
///
///
///
public static class HttpExceptionHandler
{
///
/// Gets the default handler used by .
/// This is the same as .
///
public static HttpExceptionHandlerCallback Default { get; } = HtmlResponse;
///
/// Sends an empty response.
///
/// An interface representing the context of the request.
/// The HTTP exception.
/// A representing the ongoing operation.
#pragma warning disable CA1801 // Unused parameter
public static Task EmptyResponse(IHttpContext context, IHttpException httpException)
#pragma warning restore CA1801
=> Task.CompletedTask;
///
/// Sends a HTTP exception's Message property
/// as a plain text response.
/// This handler does not use the DataObject property.
///
/// An interface representing the context of the request.
/// The HTTP exception.
/// A representing the ongoing operation.
public static Task PlainTextResponse(IHttpContext context, IHttpException httpException)
=> context.SendStringAsync(httpException.Message ?? string.Empty, MimeType.PlainText, WebServer.DefaultEncoding);
///
/// Sends a response with a HTML payload
/// briefly describing the error, including contact information and/or a stack trace
/// if specified via the
/// and properties, respectively.
/// This handler does not use the DataObject property.
///
/// An interface representing the context of the request.
/// The HTTP exception.
/// A representing the ongoing operation.
public static Task HtmlResponse(IHttpContext context, IHttpException httpException)
=> context.SendStandardHtmlAsync(
httpException.StatusCode,
text => {
text.Write(
"
Exception type: {0}
Message: {1}",
WebUtility.HtmlEncode(httpException.GetType().FullName ?? ""),
WebUtility.HtmlEncode(httpException.Message));
text.Write("
If this error is completely unexpected to you, and you think you should not seeing this page, please contact the server administrator");
if (!string.IsNullOrEmpty(ExceptionHandler.ContactInformation))
text.Write(" ({0})", WebUtility.HtmlEncode(ExceptionHandler.ContactInformation));
text.Write(", informing them of the time this error occurred and the action(s) you performed that resulted in this error.
");
if (ExceptionHandler.IncludeStackTraces)
{
text.Write(
"
Stack trace:
{0}",
WebUtility.HtmlEncode(httpException.StackTrace));
}
});
///
/// Gets a that will serialize a HTTP exception's
/// DataObject property and send it as a JSON response.
///
/// A used to serialize data and send it to the client.
/// A .
/// is .
public static HttpExceptionHandlerCallback DataResponse(ResponseSerializerCallback serializerCallback)
{
Validate.NotNull(nameof(serializerCallback), serializerCallback);
return (context, httpException) => serializerCallback(context, httpException.DataObject);
}
///
/// Gets a that will serialize a HTTP exception's
/// Message and DataObject properties
/// and send them as a JSON response.
/// The response will be a JSON object with a message property and a data property.
///
/// A used to serialize data and send it to the client.
/// A .
/// is .
public static HttpExceptionHandlerCallback FullDataResponse(ResponseSerializerCallback serializerCallback)
{
Validate.NotNull(nameof(serializerCallback), serializerCallback);
return (context, httpException) => serializerCallback(context, new
{
message = httpException.Message,
data = httpException.DataObject,
});
}
internal static async Task Handle(string logSource, IHttpContext context, Exception exception, HttpExceptionHandlerCallback? handler)
{
if (handler == null || !(exception is IHttpException httpException))
{
ExceptionDispatchInfo.Capture(exception).Throw();
return;
}
exception.Log(logSource, $"[{context.Id}] HTTP exception {httpException.StatusCode}");
try
{
context.Response.SetEmptyResponse(httpException.StatusCode);
context.Response.DisableCaching();
httpException.PrepareResponse(context);
await handler(context, httpException)
.ConfigureAwait(false);
}
catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested)
{
throw;
}
catch (HttpListenerException)
{
throw;
}
catch (Exception exception2)
{
exception2.Log(logSource, $"[{context.Id}] Unhandled exception while handling HTTP exception {httpException.StatusCode}");
}
}
}
}