Return Bad Request when receiving invalid header values

This commit is contained in:
Sipke Schoorstra 2023-10-03 10:25:57 +02:00
parent 2a7ca95456
commit 98e424faa2
3 changed files with 59 additions and 5 deletions

View file

@ -1,6 +1,7 @@
using System.Security.Cryptography;
using Elsa.Extensions;
using Elsa.Http.Contracts;
using Elsa.Http.Exceptions;
using Elsa.Http.Models;
using Elsa.Http.Options;
using Elsa.Http.Services;
@ -256,8 +257,8 @@ public class WriteFileHttpResponse : Activity
var manager = context.GetRequiredService<IDownloadableManager>();
var headers = httpContext.Request.Headers;
var eTag = headers.TryGetValue(HeaderNames.IfMatch, out var header) ? new EntityTagHeaderValue(header.ToString()) : default;
var range = headers.TryGetValue(HeaderNames.Range, out header) ? RangeHeaderValue.Parse(header.ToString()) : default;
var eTag = GetIfMatchHeaderValue(headers);
var range = GetRangeHeaderHeaderValue(headers);
var options = new DownloadableOptions { ETag = eTag, Range = range };
return manager.GetDownloadablesAsync(content, options, context.CancellationToken);
}
@ -267,6 +268,32 @@ public class WriteFileHttpResponse : Activity
var provider = context.GetRequiredService<IContentTypeProvider>();
return provider.TryGetContentType(filename, out var contentType) ? contentType : System.Net.Mime.MediaTypeNames.Application.Octet;
}
private static RangeHeaderValue? GetRangeHeaderHeaderValue(IHeaderDictionary headers)
{
try
{
return headers.TryGetValue(HeaderNames.Range, out var header) ? RangeHeaderValue.Parse(header.ToString()) : default;
}
catch (Exception e)
{
throw new HttpBadRequestException("Failed to parse Range header value", e);
}
}
private static EntityTagHeaderValue? GetIfMatchHeaderValue(IHeaderDictionary headers)
{
try
{
return headers.TryGetValue(HeaderNames.IfMatch, out var header) ? new EntityTagHeaderValue(header.ToString()) : default;
}
catch (Exception e)
{
throw new HttpBadRequestException("Failed to parse If-Match header value", e);
}
}
private async ValueTask OnResumeAsync(ActivityExecutionContext context)
{

View file

@ -0,0 +1,12 @@
namespace Elsa.Http.Exceptions;
/// <summary>
/// Exception thrown when a bad request is received.
/// </summary>
public class HttpBadRequestException : Exception
{
/// <inheritdoc />
public HttpBadRequestException(string message, Exception exception) : base(message, exception)
{
}
}

View file

@ -2,6 +2,7 @@ using Elsa.Http.Models;
using Microsoft.AspNetCore.Http;
using System.Net.Mime;
using Elsa.Http.Contracts;
using Elsa.Http.Exceptions;
using Elsa.Workflows.Core.Contracts;
namespace Elsa.Http.Handlers;
@ -16,16 +17,30 @@ public sealed class DefaultHttpEndpointFaultHandler : IHttpEndpointFaultHandler
{
var httpContext = context.HttpContext;
var isTimeoutIncident = GetIsTimeoutFault(context);
var statusCode = isTimeoutIncident ? StatusCodes.Status408RequestTimeout : StatusCodes.Status500InternalServerError;
var isBadRequest = GetIsBadRequestFault(context);
var statusCode = isTimeoutIncident
? StatusCodes.Status408RequestTimeout
: isBadRequest
? StatusCodes.Status400BadRequest
: StatusCodes.Status500InternalServerError;
httpContext.Response.StatusCode = statusCode;
return ValueTask.CompletedTask;
}
private bool GetIsTimeoutFault(HttpEndpointFaultContext context)
{
return ContainsException(context, typeof(OperationCanceledException), typeof(TaskCanceledException), typeof(TimeoutException));
}
private bool GetIsBadRequestFault(HttpEndpointFaultContext context)
{
return ContainsException(context, typeof(HttpBadRequestException));
}
private bool ContainsException(HttpEndpointFaultContext context, params Type[] exceptionTypes)
{
var workflowState = context.WorkflowState;
var exceptionTypes = new[] { typeof(OperationCanceledException), typeof(TaskCanceledException), typeof(TimeoutException) };
var timeoutIncident = workflowState.Incidents.FirstOrDefault(x => exceptionTypes.Contains(x.Exception?.Type));
return timeoutIncident != null;