< Summary

Information
Class: Elsa.Http.HttpEndpoint
Assembly: Elsa.Http
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Activities/HttpEndpoint.cs
Line coverage
65%
Covered lines: 174
Uncovered lines: 92
Coverable lines: 266
Total lines: 618
Line coverage: 65.4%
Branch coverage
60%
Covered branches: 46
Total branches: 76
Branch coverage: 60.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%210%
.ctor(...)100%11100%
get_Path()100%11100%
get_SupportedMethods()100%11100%
get_Authorize()100%11100%
get_Policy()100%11100%
get_RequestTimeout()100%11100%
get_RequestSizeLimit()100%11100%
get_FileSizeLimit()100%11100%
get_AllowedFileExtensions()100%11100%
get_BlockedFileExtensions()100%11100%
get_AllowedMimeTypes()100%11100%
get_ExposeRequestTooLargeOutcome()100%11100%
get_ExposeFileTooLargeOutcome()100%11100%
get_ExposeInvalidFileExtensionOutcome()100%11100%
get_ExposeInvalidFileMimeTypeOutcome()100%11100%
get_ParsedContent()100%11100%
get_Files()100%11100%
get_File()100%11100%
get_RouteData()100%11100%
get_QueryStringData()100%11100%
get_Headers()100%11100%
GetTriggerPayloads(...)100%11100%
ExecuteAsync()50%22100%
OnResumeAsync()50%2271.42%
HandleRequestAsync()78.57%151482.6%
ValidateDeclaredRequestSize(...)100%22100%
ApplyRequestSizeLimit(...)100%44100%
HandleRequestTooLargeAsync()50%2291.66%
ValidateFileSizes(...)25%6450%
HandleFileSizeTooLargeAsync()0%620%
ValidateFileExtensionWhitelist(...)33.33%11650%
HandleInvalidFileExtensionWhitelistAsync()0%620%
ValidateFileExtensionBlacklist(...)100%66100%
HandleInvalidFileExtensionBlacklistAsync()50%3233.33%
ValidateFileMimeTypes(...)33.33%11650%
HandleInvalidFileMimeTypesAsync()0%620%
HandleInvalidJsonPayloadAsync()100%11100%
ParseContentAsync()100%22100%
HasContent(...)100%44100%
GetBookmarkPayloads(...)50%22100%
GetRouteData(...)75%4492.85%
.ctor(...)100%11100%
get_CanRead()100%11100%
get_CanSeek()100%210%
get_CanWrite()100%210%
get_Length()100%210%
get_Position()100%210%
set_Position(...)100%210%
Flush()100%210%
Read(...)100%210%
Read(...)100%210%
ReadAsync()100%11100%
ReadAsync()100%210%
Seek(...)100%210%
SetLength(...)100%210%
Write(...)100%210%
GetPermittedReadCount(...)50%8662.5%
CountBytesRead(...)50%2275%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Activities/HttpEndpoint.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using System.Text.Json;
 3using Elsa.Expressions.Models;
 4using Elsa.Extensions;
 5using Elsa.Http.Bookmarks;
 6using Elsa.Http.Extensions;
 7using Elsa.Http.UIHints;
 8using Elsa.Workflows;
 9using Elsa.Workflows.Attributes;
 10using Elsa.Workflows.UIHints;
 11using Elsa.Workflows.Models;
 12using Microsoft.AspNetCore.Http;
 13using Microsoft.AspNetCore.Routing;
 14using Microsoft.Extensions.DependencyInjection;
 15
 16namespace Elsa.Http;
 17
 18/// <summary>
 19/// Wait for an inbound HTTP request that matches the specified path and methods.
 20/// </summary>
 21[Activity("Elsa", "HTTP", "Wait for an inbound HTTP request that matches the specified path and methods.", DisplayName =
 22[Output(IsSerializable = false)]
 23public class HttpEndpoint : Trigger<HttpRequest>
 24{
 25    internal const string HttpContextInputKey = "HttpContext";
 26    internal const string PathInputKey = "Path";
 27
 28    /// <inheritdoc />
 115129    public HttpEndpoint([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line
 30    {
 115131    }
 32
 33    /// <inheritdoc />
 034    public HttpEndpoint(Input<string> path, Input<string> method, [CallerFilePath] string? source = null, [CallerLineNum
 35    {
 036        Path = path;
 037        SupportedMethods = new(ObjectLiteral.From(new[] { method }));
 038    }
 39
 40    /// <inheritdoc />
 141    public HttpEndpoint(Input<string> path, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null)
 42    {
 143        Path = path;
 144    }
 45
 46    /// <summary>
 47    /// The path to associate with the workflow.
 48    /// </summary>
 49    [Input(
 50        Description = "The path to associate with the workflow.",
 51        UIHint = InputUIHints.SingleLine,
 52        UIHandler = typeof(HttpEndpointPathUIHandler)
 53    )]
 459354    public Input<string> Path { get; set; } = null!;
 55
 56    /// <summary>
 57    /// The HTTP methods to accept.
 58    /// </summary>
 59    [Input(
 60        Description = "The HTTP methods to accept.",
 61        Options = new[] { "GET", "POST", "PUT", "HEAD", "DELETE" },
 62        UIHint = InputUIHints.CheckList)]
 574563    public Input<ICollection<string>> SupportedMethods { get; set; } = new(ObjectLiteral.From(new[] { HttpMethods.Get })
 64
 65    /// <summary>
 66    /// Allow authenticated requests only.
 67    /// </summary>
 68    [Input(Description = "Allow authenticated requests only.", Category = "Security")]
 474769    public Input<bool> Authorize { get; set; } = new(false);
 70
 71    /// <summary>
 72    /// Provide a policy to evaluate. If the policy fails, the request is forbidden.
 73    /// </summary>
 74    [Input(Description = "Provide a policy to evaluate. If the policy fails, the request is forbidden.", Category = "Sec
 474675    public Input<string?> Policy { get; set; } = new(default(string?));
 76
 77    /// <summary>
 78    /// The maximum time allowed to process the request.
 79    /// </summary>
 80    [Input(Description = "The maximum time allowed to process the request.", Category = "Upload")]
 359481    public Input<TimeSpan?> RequestTimeout { get; set; } = null!;
 82
 83    /// <summary>
 84    /// The maximum request size allowed in bytes.
 85    /// </summary>
 86    [Input(Description = "The maximum request size allowed in bytes.", Category = "Upload")]
 417087    public Input<long?> RequestSizeLimit { get; set; } = null!;
 88
 89    /// <summary>
 90    /// The maximum request size allowed in bytes.
 91    /// </summary>
 92    [Input(Description = "The maximum file size allowed in bytes for an individual file.", Category = "Upload")]
 332493    public Input<long?> FileSizeLimit { get; set; } = null!;
 94
 95    /// <summary>
 96    /// The allowed file extensions,
 97    /// </summary>
 98    [Input(Description = "Only file extensions in this list are allowed. Leave empty to allow all extensions", Category 
 332499    public Input<ICollection<string>> AllowedFileExtensions { get; set; } = null!;
 100
 101    /// <summary>
 102    /// The allowed file extensions,
 103    /// </summary>
 104    [Input(Description = "File extensions in this list are forbidden. Leave empty to not block any extension.", Category
 3380105    public Input<ICollection<string>> BlockedFileExtensions { get; set; } = null!;
 106
 107    /// <summary>
 108    /// The allowed file extensions,
 109    /// </summary>
 110    [Input(Description = "Only MIME types in this list are allowed. Leave empty to allow all types", Category = "Upload"
 3323111    public Input<ICollection<string>> AllowedMimeTypes { get; set; } = null!;
 112
 113    /// <summary>
 114    /// A value indicating whether to expose the "Request too large" outcome.
 115    /// </summary>
 116    [Input(Description = "A value indicating whether to expose the \"Request too large\" outcome.", Category = "Outcomes
 1253117    public bool ExposeRequestTooLargeOutcome { get; set; }
 118
 119    /// <summary>
 120    /// A value indicating whether to expose the "File too large" outcome.
 121    /// </summary>
 122    [Input(Description = "A value indicating whether to expose the \"File too large\" outcome.", Category = "Outcomes")]
 1252123    public bool ExposeFileTooLargeOutcome { get; set; }
 124
 125    /// <summary>
 126    /// A value indicating whether to expose the "Invalid file extension" outcome.
 127    /// </summary>
 128    [Input(Description = "A value indicating whether to expose the \"Invalid file extension\" outcome.", Category = "Out
 1309129    public bool ExposeInvalidFileExtensionOutcome { get; set; }
 130
 131    /// <summary>
 132    /// A value indicating whether to expose the "Invalid file MIME type" outcome.
 133    /// </summary>
 134    [Input(Description = "A value indicating whether to expose the \"Invalid file MIME type\" outcome.", Category = "Out
 1252135    public bool ExposeInvalidFileMimeTypeOutcome { get; set; }
 136
 137    /// <summary>
 138    /// The parsed request content, if any.
 139    /// </summary>
 140    [Output(Description = "The parsed request content, if any.")]
 3454141    public Output<object?> ParsedContent { get; set; } = null!;
 142
 143    /// <summary>
 144    /// The uploaded files, if any.
 145    /// </summary>
 146    [Output(Description = "The uploaded files, if any.", IsSerializable = false)]
 2890147    public Output<IFormFile[]> Files { get; set; } = null!;
 148
 149    /// <summary>
 150    /// The first uploaded file, if any.
 151    /// </summary>
 152    [Output(Description = "The first uploaded file, if any.", IsSerializable = false)]
 2440153    public Output<IFormFile?> File { get; set; } = null!;
 154
 155    /// <summary>
 156    /// The parsed route data, if any.
 157    /// </summary>
 158    [Output(Description = "The parsed route data, if any.")]
 3149159    public Output<IDictionary<string, object>> RouteData { get; set; } = null!;
 160
 161    /// <summary>
 162    /// The querystring data, if any.
 163    /// </summary>
 164    [Output(Description = "The querystring data, if any.")]
 3093165    public Output<IDictionary<string, object>> QueryStringData { get; set; } = null!;
 166
 167    /// <summary>
 168    /// The headers, if any.
 169    /// </summary>
 170    [Output(Description = "The headers, if any.")]
 3093171    public Output<IDictionary<string, object>> Headers { get; set; } = null!;
 172
 173    /// <inheritdoc />
 174    protected override IEnumerable<object> GetTriggerPayloads(TriggerIndexingContext context)
 175    {
 278176        context.TriggerName = HttpStimulusNames.HttpEndpoint;
 278177        return GetBookmarkPayloads(context.ExpressionExecutionContext);
 178    }
 179
 180    /// <inheritdoc />
 181    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 182    {
 265183        var path = Path.Get(context);
 265184        var methods = SupportedMethods.GetOrDefault(context) ?? new List<string> { HttpMethods.Get };
 265185        await context.WaitForHttpRequestAsync(path, methods, OnResumeAsync, Elsa.Http.HttpStimulusNames.HttpEndpoint);
 265186    }
 187
 188    private async ValueTask OnResumeAsync(ActivityExecutionContext context)
 189    {
 260190        var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>();
 260191        var httpContext = httpContextAccessor.HttpContext;
 192
 260193        if (httpContext == null)
 194        {
 195            // We're executing in a non-HTTP context (e.g. in a virtual actor).
 196            // Create a bookmark to allow the invoker to export the state and resume execution from there.
 0197            context.CreateCrossBoundaryBookmark();
 0198            return;
 199        }
 200
 260201        await HandleRequestAsync(context);
 260202    }
 203
 204    private async Task HandleRequestAsync(ActivityExecutionContext context)
 205    {
 260206        var httpContextAccessor = context.GetRequiredService<IHttpContextAccessor>();
 260207        var httpContext = httpContextAccessor.HttpContext!;
 208
 209        // Provide the received HTTP request as output.
 260210        var request = httpContext.Request;
 260211        context.Set(Result, request);
 212
 213        // Read route data, if any.
 260214        var path = context.GetWorkflowInput<PathString>(PathInputKey);
 260215        var routeData = GetRouteData(httpContext, path);
 294216        var routeDictionary = routeData.Values.ToDictionary(route => route.Key, route => route.Value!);
 260217        var queryStringDictionary = httpContext.Request.Query.ToObjectDictionary();
 260218        var headersDictionary = httpContext.Request.Headers.ToObjectDictionary();
 219
 260220        context.Set(RouteData, routeDictionary);
 260221        context.Set(QueryStringData, queryStringDictionary);
 260222        context.Set(Headers, headersDictionary);
 223
 224        // Validate declared request size before reading, then enforce the same limit while reading.
 260225        if (!ValidateDeclaredRequestSize(context, httpContext))
 226        {
 1227            await HandleRequestTooLargeAsync(context, httpContext);
 1228            return;
 229        }
 230
 259231        ApplyRequestSizeLimit(context, request);
 232
 233        try
 234        {
 235            // Handle Form Fields
 259236            if (request.HasFormContentType)
 237            {
 12238                var form = await request.ReadFormAsync(context.CancellationToken);
 12239                var formFields = form.ToObjectDictionary();
 240
 12241                ParsedContent.Set(context, formFields);
 242
 243                // Read files, if any.
 12244                var files = form.Files;
 245
 12246                if (files.Any())
 247                {
 8248                    if (!ValidateFileSizes(context, httpContext, files))
 249                    {
 0250                        await HandleFileSizeTooLargeAsync(context, httpContext);
 0251                        return;
 252                    }
 253
 8254                    if (!ValidateFileExtensionWhitelist(context, httpContext, files))
 255                    {
 0256                        await HandleInvalidFileExtensionWhitelistAsync(context, httpContext);
 0257                        return;
 258                    }
 259
 8260                    if (!ValidateFileExtensionBlacklist(context, httpContext, files))
 261                    {
 1262                        await HandleInvalidFileExtensionBlacklistAsync(context, httpContext);
 1263                        return;
 264                    }
 265
 7266                    if (!ValidateFileMimeTypes(context, httpContext, files))
 267                    {
 0268                        await HandleInvalidFileMimeTypesAsync(context, httpContext);
 0269                        return;
 270                    }
 271
 7272                    Files.Set(context, files.ToArray());
 7273                    File.Set(context, files.FirstOrDefault());
 274                }
 275            }
 276            else
 277            {
 278                // Parse Non-Form content.
 279                try
 280                {
 247281                    var content = await ParseContentAsync(context, request);
 246282                    ParsedContent.Set(context, content);
 246283                }
 1284                catch (JsonException e)
 285                {
 1286                    await HandleInvalidJsonPayloadAsync(context, httpContext, e);
 1287                    return;
 288                }
 289            }
 257290        }
 291        catch (RequestBodyTooLargeException)
 292        {
 0293            await HandleRequestTooLargeAsync(context, httpContext);
 0294            return;
 295        }
 296
 297        // Complete.
 257298        await context.CompleteActivityAsync();
 260299    }
 300
 301    private bool ValidateDeclaredRequestSize(ActivityExecutionContext context, HttpContext httpContext)
 302    {
 260303        var requestSizeLimit = RequestSizeLimit.GetOrDefault(context);
 304
 260305        if (!requestSizeLimit.HasValue)
 258306            return true;
 307
 2308        var requestSize = httpContext.Request.ContentLength ?? 0;
 2309        return requestSize <= requestSizeLimit;
 310    }
 311
 312    private void ApplyRequestSizeLimit(ActivityExecutionContext context, HttpRequest request)
 313    {
 259314        var requestSizeLimit = RequestSizeLimit.GetOrDefault(context);
 315
 259316        if (!requestSizeLimit.HasValue || request.Body is RequestSizeLimitStream)
 258317            return;
 318
 1319        request.Body = new RequestSizeLimitStream(request.Body, requestSizeLimit.Value);
 1320    }
 321
 322    private async Task HandleRequestTooLargeAsync(ActivityExecutionContext context, HttpContext httpContext)
 323    {
 1324        var exposeRequestTooLargeOutcome = ExposeRequestTooLargeOutcome;
 325
 1326        if (exposeRequestTooLargeOutcome)
 327        {
 0328            await context.CompleteActivityWithOutcomesAsync("Request too large");
 329        }
 330        else
 331        {
 1332            var response = httpContext.Response;
 1333            response.StatusCode = StatusCodes.Status413PayloadTooLarge;
 1334            await response.WriteAsJsonAsync(new
 1335            {
 1336                Message = $"The maximum request size allowed is {RequestSizeLimit.Get(context)} bytes."
 1337            });
 1338            await response.Body.FlushAsync();
 1339        }
 1340    }
 341
 342    private bool ValidateFileSizes(ActivityExecutionContext context, HttpContext httpContext, IFormFileCollection files)
 343    {
 8344        var fileSizeLimit = FileSizeLimit.GetOrDefault(context);
 345
 8346        if (!fileSizeLimit.HasValue)
 8347            return true;
 348
 0349        if (!files.Any(file => file.Length > fileSizeLimit.Value))
 0350            return true;
 351
 0352        return false;
 353    }
 354
 355    private async Task HandleFileSizeTooLargeAsync(ActivityExecutionContext context, HttpContext httpContext)
 356    {
 0357        var exposeFileTooLargeOutcome = ExposeFileTooLargeOutcome;
 358
 0359        if (exposeFileTooLargeOutcome)
 360        {
 0361            await context.CompleteActivityWithOutcomesAsync("File too large");
 362        }
 363        else
 364        {
 0365            var response = httpContext.Response;
 0366            response.StatusCode = StatusCodes.Status413PayloadTooLarge;
 0367            await response.WriteAsJsonAsync(new
 0368            {
 0369                Message = $"The maximum file size allowed is {FileSizeLimit.Get(context)} bytes."
 0370            });
 0371            await response.Body.FlushAsync();
 0372        }
 0373    }
 374
 375    private bool ValidateFileExtensionWhitelist(ActivityExecutionContext context, HttpContext httpContext, IFormFileColl
 376    {
 8377        var allowedFileExtensions = AllowedFileExtensions.GetOrDefault(context);
 378
 8379        if (allowedFileExtensions == null || !allowedFileExtensions.Any())
 8380            return true;
 381
 0382        if (files.All(file => allowedFileExtensions.Contains(System.IO.Path.GetExtension(file.FileName), StringComparer.
 0383            return true;
 384
 0385        return false;
 386    }
 387
 388    private async Task HandleInvalidFileExtensionWhitelistAsync(ActivityExecutionContext context, HttpContext httpContex
 389    {
 0390        if (ExposeInvalidFileExtensionOutcome)
 391        {
 0392            await context.CompleteActivityWithOutcomesAsync("Invalid file extension");
 0393            return;
 394        }
 395
 0396        var response = httpContext.Response;
 0397        var allowedFileExtensions = AllowedFileExtensions.GetOrDefault(context)!;
 0398        response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
 0399        await response.WriteAsJsonAsync(new
 0400        {
 0401            Message = $"Only the following file extensions are allowed: {string.Join(", ", allowedFileExtensions)}"
 0402        });
 0403        await response.Body.FlushAsync();
 0404    }
 405
 406    private bool ValidateFileExtensionBlacklist(ActivityExecutionContext context, HttpContext httpContext, IFormFileColl
 407    {
 8408        var blockedFileExtensions = BlockedFileExtensions.GetOrDefault(context);
 409
 8410        if (blockedFileExtensions == null || !blockedFileExtensions.Any())
 6411            return true;
 412
 4413        if (!files.Any(file => blockedFileExtensions.Contains(System.IO.Path.GetExtension(file.FileName), StringComparer
 1414            return true;
 415
 1416        return false;
 417    }
 418
 419    private async Task HandleInvalidFileExtensionBlacklistAsync(ActivityExecutionContext context, HttpContext httpContex
 420    {
 1421        if (ExposeInvalidFileExtensionOutcome)
 422        {
 1423            await context.CompleteActivityWithOutcomesAsync("Invalid file extension");
 1424            return;
 425        }
 426
 0427        var blockedFileExtensions = BlockedFileExtensions.GetOrDefault(context)!;
 0428        var response = httpContext.Response;
 0429        response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
 0430        await response.WriteAsJsonAsync(new
 0431        {
 0432            Message = $"The following file extensions are not allowed: {string.Join(", ", blockedFileExtensions)}"
 0433        });
 0434        await response.Body.FlushAsync();
 1435    }
 436
 437    private bool ValidateFileMimeTypes(ActivityExecutionContext context, HttpContext httpContext, IFormFileCollection fi
 438    {
 7439        var allowedMimeTypes = AllowedMimeTypes.GetOrDefault(context);
 440
 7441        if (allowedMimeTypes == null || !allowedMimeTypes.Any())
 7442            return true;
 443
 0444        if (files.All(file => allowedMimeTypes.Contains(file.ContentType, StringComparer.OrdinalIgnoreCase)))
 0445            return true;
 446
 0447        return false;
 448    }
 449
 450    private async Task HandleInvalidFileMimeTypesAsync(ActivityExecutionContext context, HttpContext httpContext)
 451    {
 0452        if (ExposeInvalidFileMimeTypeOutcome)
 453        {
 0454            await context.CompleteActivityWithOutcomesAsync("Invalid file MIME type");
 0455            return;
 456        }
 457
 0458        var allowedMimeTypes = AllowedMimeTypes.GetOrDefault(context)!;
 0459        var response = httpContext.Response;
 0460        response.StatusCode = StatusCodes.Status415UnsupportedMediaType;
 0461        await response.WriteAsJsonAsync(new
 0462        {
 0463            Message = $"Only the following MIME types are allowed: {string.Join(", ", allowedMimeTypes)}"
 0464        });
 0465        await response.Body.FlushAsync();
 0466    }
 467
 468    private async Task HandleInvalidJsonPayloadAsync(ActivityExecutionContext context, HttpContext httpContext, JsonExce
 469    {
 1470        var response = httpContext.Response;
 1471        response.StatusCode = StatusCodes.Status400BadRequest;
 1472        await response.WriteAsJsonAsync(new
 1473        {
 1474            exception.Message,
 1475            exception.Path,
 1476            exception.LineNumber,
 1477        });
 1478        await response.Body.FlushAsync();
 1479    }
 480
 481    private async Task<object?> ParseContentAsync(ActivityExecutionContext context, HttpRequest httpRequest)
 482    {
 247483        if (!HasContent(httpRequest))
 44484            return null;
 485
 203486        var cancellationToken = context.CancellationToken;
 203487        var targetType = ParsedContent.GetTargetType(context);
 203488        var contentStream = httpRequest.Body;
 203489        var contentType = httpRequest.ContentType!;
 1827490        var headers = httpRequest.Headers.ToDictionary(x => x.Key, x => x.Value.ToArray());
 491
 203492        return await context.ParseContentAsync(contentStream, contentType, targetType, headers!, cancellationToken);
 246493    }
 494
 495    private static bool HasContent(HttpRequest httpRequest)
 496    {
 247497        if (httpRequest.ContentLength > 0)
 203498            return true;
 499
 44500        return httpRequest.ContentLength == null && !string.IsNullOrWhiteSpace(httpRequest.ContentType);
 501    }
 502
 503    private IEnumerable<object> GetBookmarkPayloads(ExpressionExecutionContext context)
 504    {
 505        // Generate bookmark data for path and selected methods.
 278506        var normalizedRoute = context.Get(Path)!.NormalizeRoute();
 278507        var methods = SupportedMethods.GetOrDefault(context) ?? new List<string> { HttpMethods.Get };
 278508        var authorize = Authorize.GetOrDefault(context);
 278509        var policy = Policy.GetOrDefault(context);
 278510        var requestTimeout = RequestTimeout.GetOrDefault(context);
 278511        var requestSizeLimit = RequestSizeLimit.GetOrDefault(context);
 512
 278513        return methods
 332514            .Select(x => new HttpEndpointBookmarkPayload(normalizedRoute, x.ToLowerInvariant(), authorize, policy, reque
 278515            .Cast<object>()
 278516            .ToArray();
 517    }
 518
 519    private static RouteData GetRouteData(HttpContext httpContext, string path)
 520    {
 260521        var routeData = httpContext.GetRouteData();
 260522        var routeTable = httpContext.RequestServices.GetRequiredService<IRouteTable>();
 260523        var routeMatcher = httpContext.RequestServices.GetRequiredService<IRouteMatcher>();
 524
 260525        var matchingRouteQuery =
 260526            from route in routeTable
 2298527            let routeValues = routeMatcher.Match(route.Route, path)
 2298528            where routeValues != null
 520529            select new { route, routeValues };
 530
 260531        var matchingRoute = matchingRouteQuery.FirstOrDefault();
 532
 260533        if (matchingRoute == null)
 0534            return routeData;
 535
 554536        foreach (var (key, value) in matchingRoute.routeValues!)
 17537            routeData.Values[key] = value;
 538
 260539        return routeData;
 540    }
 541
 1542    private sealed class RequestSizeLimitStream(Stream inner, long requestSizeLimit) : Stream
 543    {
 544        private long _bytesRead;
 545
 1546        public override bool CanRead => inner.CanRead;
 0547        public override bool CanSeek => false;
 0548        public override bool CanWrite => false;
 0549        public override long Length => inner.Length;
 550
 551        public override long Position
 552        {
 0553            get => inner.Position;
 0554            set => throw new NotSupportedException();
 555        }
 556
 0557        public override void Flush() => inner.Flush();
 558
 559        public override int Read(byte[] buffer, int offset, int count)
 560        {
 0561            var bytesRead = inner.Read(buffer, offset, GetPermittedReadCount(count));
 0562            CountBytesRead(bytesRead);
 0563            return bytesRead;
 564        }
 565
 566        public override int Read(Span<byte> buffer)
 567        {
 0568            var bytesRead = inner.Read(buffer[..GetPermittedReadCount(buffer.Length)]);
 0569            CountBytesRead(bytesRead);
 0570            return bytesRead;
 571        }
 572
 573        public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = defaul
 574        {
 2575            var bytesRead = await inner.ReadAsync(buffer[..GetPermittedReadCount(buffer.Length)], cancellationToken);
 2576            CountBytesRead(bytesRead);
 2577            return bytesRead;
 2578        }
 579
 580        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationTo
 581        {
 0582            var bytesRead = await inner.ReadAsync(buffer, offset, GetPermittedReadCount(count), cancellationToken);
 0583            CountBytesRead(bytesRead);
 0584            return bytesRead;
 0585        }
 586
 0587        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 588
 0589        public override void SetLength(long value) => throw new NotSupportedException();
 590
 0591        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
 592
 593        private int GetPermittedReadCount(int requestedCount)
 594        {
 2595            if (requestedCount == 0)
 0596                return 0;
 597
 2598            var remainingBytes = requestSizeLimit - _bytesRead;
 2599            if (remainingBytes >= requestedCount)
 0600                return requestedCount;
 601
 2602            if (remainingBytes < 0)
 0603                return 1;
 604
 2605            return (int)remainingBytes + 1;
 606        }
 607
 608        private void CountBytesRead(int bytesRead)
 609        {
 2610            _bytesRead += bytesRead;
 611
 2612            if (_bytesRead > requestSizeLimit)
 0613                throw new RequestBodyTooLargeException();
 2614        }
 615    }
 616
 617    private sealed class RequestBodyTooLargeException : Exception;
 618}

Methods/Properties

.ctor(System.String,System.Nullable`1<System.Int32>)
.ctor(Elsa.Workflows.Models.Input`1<System.String>,Elsa.Workflows.Models.Input`1<System.String>,System.String,System.Nullable`1<System.Int32>)
.ctor(Elsa.Workflows.Models.Input`1<System.String>,System.String,System.Nullable`1<System.Int32>)
get_Path()
get_SupportedMethods()
get_Authorize()
get_Policy()
get_RequestTimeout()
get_RequestSizeLimit()
get_FileSizeLimit()
get_AllowedFileExtensions()
get_BlockedFileExtensions()
get_AllowedMimeTypes()
get_ExposeRequestTooLargeOutcome()
get_ExposeFileTooLargeOutcome()
get_ExposeInvalidFileExtensionOutcome()
get_ExposeInvalidFileMimeTypeOutcome()
get_ParsedContent()
get_Files()
get_File()
get_RouteData()
get_QueryStringData()
get_Headers()
GetTriggerPayloads(Elsa.Workflows.TriggerIndexingContext)
ExecuteAsync()
OnResumeAsync()
HandleRequestAsync()
ValidateDeclaredRequestSize(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpContext)
ApplyRequestSizeLimit(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpRequest)
HandleRequestTooLargeAsync()
ValidateFileSizes(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Http.IFormFileCollection)
HandleFileSizeTooLargeAsync()
ValidateFileExtensionWhitelist(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Http.IFormFileCollection)
HandleInvalidFileExtensionWhitelistAsync()
ValidateFileExtensionBlacklist(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Http.IFormFileCollection)
HandleInvalidFileExtensionBlacklistAsync()
ValidateFileMimeTypes(Elsa.Workflows.ActivityExecutionContext,Microsoft.AspNetCore.Http.HttpContext,Microsoft.AspNetCore.Http.IFormFileCollection)
HandleInvalidFileMimeTypesAsync()
HandleInvalidJsonPayloadAsync()
ParseContentAsync()
HasContent(Microsoft.AspNetCore.Http.HttpRequest)
GetBookmarkPayloads(Elsa.Expressions.Models.ExpressionExecutionContext)
GetRouteData(Microsoft.AspNetCore.Http.HttpContext,System.String)
.ctor(System.IO.Stream,System.Int64)
get_CanRead()
get_CanSeek()
get_CanWrite()
get_Length()
get_Position()
set_Position(System.Int64)
Flush()
Read(System.Byte[],System.Int32,System.Int32)
Read(System.Span`1<System.Byte>)
ReadAsync()
ReadAsync()
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
Write(System.Byte[],System.Int32,System.Int32)
GetPermittedReadCount(System.Int32)
CountBytesRead(System.Int32)