< Summary

Information
Class: Elsa.Http.Features.HttpFeature
Assembly: Elsa.Http
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Features/HttpFeature.cs
Line coverage
93%
Covered lines: 144
Uncovered lines: 10
Coverable lines: 154
Total lines: 268
Line coverage: 93.5%
Branch coverage
66%
Covered branches: 8
Total branches: 12
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Http/Features/HttpFeature.cs

#LineLine coverage
 1using System.Net;
 2using Elsa.Expressions.Options;
 3using Elsa.Extensions;
 4using Elsa.Features.Abstractions;
 5using Elsa.Features.Attributes;
 6using Elsa.Features.Services;
 7using Elsa.Http.Bookmarks;
 8using Elsa.Http.ContentWriters;
 9using Elsa.Http.DownloadableContentHandlers;
 10using Elsa.Http.FileCaches;
 11using Elsa.Http.Handlers;
 12using Elsa.Http.Options;
 13using Elsa.Http.Parsers;
 14using Elsa.Http.PortResolvers;
 15using Elsa.Http.Resilience;
 16using Elsa.Http.Selectors;
 17using Elsa.Http.Services;
 18using Elsa.Http.Tasks;
 19using Elsa.Http.TriggerPayloadValidators;
 20using Elsa.Http.UIHints;
 21using Elsa.Resilience.Extensions;
 22using Elsa.Resilience.Features;
 23using Elsa.Workflows;
 24using FluentStorage;
 25using Microsoft.AspNetCore.Http;
 26using Microsoft.AspNetCore.StaticFiles;
 27using Microsoft.Extensions.DependencyInjection;
 28using Microsoft.Extensions.DependencyInjection.Extensions;
 29using Microsoft.Extensions.Options;
 30
 31namespace Elsa.Http.Features;
 32
 33/// <summary>
 34/// Installs services related to HTTP services and activities.
 35/// </summary>
 36[DependsOn(typeof(HttpJavaScriptFeature))]
 37[DependsOn(typeof(ResilienceFeature))]
 738public class HttpFeature(IModule module) : FeatureBase(module)
 39{
 40640    private Func<IServiceProvider, IHttpEndpointRoutesProvider> _httpEndpointRouteProvider = sp => sp.GetRequiredService
 501741    private Func<IServiceProvider, IHttpEndpointBasePathProvider> _httpEndpointBasePathProvider = sp => sp.GetRequiredSe
 42
 43    /// <summary>
 44    /// A delegate to configure <see cref="HttpActivityOptions"/>.
 45    /// </summary>
 1046    public Action<HttpActivityOptions>? ConfigureHttpOptions { get; set; }
 47
 48    /// <summary>
 49    /// A delegate to configure <see cref="HttpFileCacheOptions"/>.
 50    /// </summary>
 751    public Action<HttpFileCacheOptions>? ConfigureHttpFileCacheOptions { get; set; }
 52
 53    /// <summary>
 54    /// A delegate that is invoked when authorizing an inbound HTTP request.
 55    /// </summary>
 27456    public Func<IServiceProvider, IHttpEndpointAuthorizationHandler> HttpEndpointAuthorizationHandler { get; set; } = sp
 57
 58    /// <summary>
 59    /// A delegate that is invoked when an HTTP workflow faults.
 60    /// </summary>
 1461    public Func<IServiceProvider, IHttpEndpointFaultHandler> HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.G
 62
 63    /// <summary>
 64    /// A delegate to configure the <see cref="IContentTypeProvider"/>.
 65    /// </summary>
 1466    public Func<IServiceProvider, IContentTypeProvider> ContentTypeProvider { get; set; } = _ => new FileExtensionConten
 67
 68    /// <summary>
 69    /// A delegate to configure the <see cref="IFileCacheStorageProvider"/>.
 70    /// </summary>
 1471    public Func<IServiceProvider, IFileCacheStorageProvider> FileCache { get; set; } = sp =>
 772    {
 073        var options = sp.GetRequiredService<IOptions<HttpFileCacheOptions>>().Value;
 074        var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory);
 075        return new BlobFileCacheStorageProvider(blobStorage);
 776    };
 77
 78    /// <summary>
 79    /// A delegate to configure the <see cref="HttpClient"/> used when by the <see cref="FlowSendHttpRequest"/> and <see
 80    /// </summary>
 1581    public Action<IServiceProvider, HttpClient> HttpClient { get; set; } = (_, _) => { };
 82
 83    /// <summary>
 84    /// A delegate to configure the <see cref="HttpClientBuilder"/> for <see cref="HttpClient"/>.
 85    /// </summary>
 2186    public Action<IHttpClientBuilder> HttpClientBuilder { get; set; } = _ => { };
 87
 88    /// <summary>
 89    /// A list of <see cref="IHttpCorrelationIdSelector"/> types to register with the service collection.
 90    /// </summary>
 1491    public ICollection<Type> HttpCorrelationIdSelectorTypes { get; } = new List<Type>
 792    {
 793        typeof(HeaderHttpCorrelationIdSelector),
 794        typeof(QueryStringHttpCorrelationIdSelector)
 795    };
 96
 97    /// <summary>
 98    /// A list of <see cref="IHttpWorkflowInstanceIdSelector"/> types to register with the service collection.
 99    /// </summary>
 14100    public ICollection<Type> HttpWorkflowInstanceIdSelectorTypes { get; } = new List<Type>
 7101    {
 7102        typeof(HeaderHttpWorkflowInstanceIdSelector),
 7103        typeof(QueryStringHttpWorkflowInstanceIdSelector)
 7104    };
 105
 106    public HttpFeature WithHttpEndpointRoutesProvider<T>() where T : IHttpEndpointRoutesProvider
 107    {
 0108        return WithHttpEndpointRoutesProvider(sp => sp.GetRequiredService<T>());
 109    }
 110
 111    public HttpFeature WithHttpEndpointRoutesProvider(Func<IServiceProvider, IHttpEndpointRoutesProvider> httpEndpointRo
 112    {
 0113        _httpEndpointRouteProvider = httpEndpointRouteProvider;
 0114        return this;
 115    }
 116
 117    public HttpFeature WithHttpEndpointBasePathProvider<T>() where T : class, IHttpEndpointBasePathProvider
 118    {
 0119        Services.TryAddScoped<T>();
 0120        return WithHttpEndpointBasePathProvider(sp => sp.GetRequiredService<T>());
 121    }
 122
 123    public HttpFeature WithHttpEndpointBasePathProvider(Func<IServiceProvider, IHttpEndpointBasePathProvider> httpEndpoi
 124    {
 0125        _httpEndpointBasePathProvider = httpEndpointBasePathProvider;
 0126        return this;
 127    }
 128
 129    /// <inheritdoc />
 130    public override void Configure()
 131    {
 7132        Module.UseWorkflowManagement(management =>
 7133        {
 7134            management.AddVariableTypes([
 7135                typeof(HttpRouteData),
 7136                typeof(HttpRequest),
 7137                typeof(HttpResponse),
 7138                typeof(HttpResponseMessage),
 7139                typeof(HttpHeaders),
 7140                typeof(IFormFile),
 7141                typeof(HttpFile),
 7142                typeof(Downloadable)
 7143            ], "HTTP");
 7144
 7145            management.AddActivitiesFrom<HttpFeature>();
 14146        });
 147
 14148        Module.UseResilience(resilience => resilience.AddResilienceStrategyType<HttpResilienceStrategy>());
 7149    }
 150
 151    /// <inheritdoc />
 152    public override void Apply()
 153    {
 7154        var configureOptions = ConfigureHttpOptions ?? (options =>
 7155        {
 4156            options.BasePath = "/workflows";
 4157            options.BaseUrl = new Uri("http://localhost");
 11158        });
 159
 7160        var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.Fro
 161
 7162        Services.Configure(configureOptions);
 7163        Services.Configure(configureFileCacheOptions);
 164
 7165        var httpClientBuilder = Services.AddHttpClient<SendHttpRequestBase>(HttpClient);
 7166        HttpClientBuilder(httpClientBuilder);
 167
 7168        Services
 7169            .AddScoped<IRouteMatcher, RouteMatcher>()
 7170            .AddScoped<IRouteTable, RouteTable>()
 7171            .AddScoped<IAbsoluteUrlProvider, DefaultAbsoluteUrlProvider>()
 7172            .AddScoped<IRouteTableUpdater, DefaultRouteTableUpdater>()
 7173            .AddScoped<IHttpWorkflowLookupService, HttpWorkflowLookupService>()
 7174            .AddScoped(ContentTypeProvider)
 7175            .AddHttpContextAccessor()
 7176
 7177            // Handlers.
 7178            .AddNotificationHandler<UpdateRouteTable>()
 7179
 7180            // Graceful shutdown: register HTTP ingress for diagnostic visibility in the runtime's IIngressSourceRegistr
 7181            // The middleware short-circuits to 503 when paused (FR-006).
 7182            .AddSingleton<Elsa.Workflows.Runtime.IIngressSource, Elsa.Http.IngressSources.HttpTriggerIngressSource>()
 7183
 7184            // Content parsers.
 7185            .AddSingleton<IHttpContentParser, JsonHttpContentParser>()
 7186            .AddSingleton<IHttpContentParser, XmlHttpContentParser>()
 7187            .AddSingleton<IHttpContentParser, PlainTextHttpContentParser>()
 7188            .AddSingleton<IHttpContentParser, TextHtmlHttpContentParser>()
 7189            .AddSingleton<IHttpContentParser, FileHttpContentParser>()
 7190
 7191            // HTTP content factories.
 7192            .AddScoped<IHttpContentFactory, TextContentFactory>()
 7193            .AddScoped<IHttpContentFactory, JsonContentFactory>()
 7194            .AddScoped<IHttpContentFactory, XmlContentFactory>()
 7195            .AddScoped<IHttpContentFactory, FormUrlEncodedHttpContentFactory>()
 7196
 7197            // Activity property options providers.
 7198            .AddScoped<IPropertyUIHandler, HttpContentTypeOptionsProvider>()
 7199            .AddScoped<IPropertyUIHandler, HttpEndpointPathUIHandler>()
 7200            .AddScoped(_httpEndpointBasePathProvider)
 7201
 7202            // Port resolvers.
 7203            .AddScoped<IActivityResolver, SendHttpRequestActivityResolver>()
 7204
 7205            // HTTP endpoint handlers.
 7206            .AddScoped<AuthenticationBasedHttpEndpointAuthorizationHandler>()
 7207            .AddScoped<AllowAnonymousHttpEndpointAuthorizationHandler>()
 7208            .AddScoped<DefaultHttpEndpointFaultHandler>()
 7209            .AddScoped<DefaultHttpEndpointRoutesProvider>()
 7210            .AddScoped<DefaultHttpEndpointBasePathProvider>()
 7211            .AddScoped(HttpEndpointWorkflowFaultHandler)
 7212            .AddScoped(HttpEndpointAuthorizationHandler)
 7213            .AddScoped(_httpEndpointRouteProvider)
 7214
 7215            // Startup tasks.
 7216            .AddStartupTask<UpdateRouteTableStartupTask>()
 7217
 7218            // Downloadable content handlers.
 7219            .AddScoped<IDownloadableManager, DefaultDownloadableManager>()
 7220            .AddScoped<IDownloadableContentHandler, MultiDownloadableContentHandler>()
 7221            .AddScoped<IDownloadableContentHandler, BinaryDownloadableContentHandler>()
 7222            .AddScoped<IDownloadableContentHandler, StreamDownloadableContentHandler>()
 7223            .AddScoped<IDownloadableContentHandler, FormFileDownloadableContentHandler>()
 7224            .AddScoped<IDownloadableContentHandler, DownloadableDownloadableContentHandler>()
 7225            .AddScoped<IDownloadableContentHandler, UrlDownloadableContentHandler>()
 7226            .AddScoped<IDownloadableContentHandler, StringDownloadableContentHandler>()
 7227            .AddScoped<IDownloadableContentHandler, HttpFileDownloadableContentHandler>()
 7228
 7229            //Trigger payload validators.
 7230            .AddTriggerPayloadValidator<HttpEndpointTriggerPayloadValidator, HttpEndpointBookmarkPayload>()
 7231
 7232            // File caches.
 7233            .AddScoped(FileCache)
 7234            .AddScoped<ZipManager>()
 7235
 7236            // AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services.
 7237            // We could consider creating a separate module for installing authorization services.
 7238            .AddAuthorization();
 239
 240        // HTTP clients.
 7241        Services.AddHttpClient<IFileDownloader, HttpClientFileDownloader>();
 242
 243        // Add selectors.
 42244        foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
 14245            Services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType);
 246
 42247        foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes)
 14248            Services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType);
 249
 7250        Services.Configure<ExpressionOptions>(options =>
 7251        {
 7252            options.AddTypeAlias<HttpRequest>("HttpRequest");
 7253            options.AddTypeAlias<HttpResponse>("HttpResponse");
 7254            options.AddTypeAlias<HttpResponseMessage>("HttpResponseMessage");
 7255            options.AddTypeAlias<HttpHeaders>("HttpHeaders");
 7256            options.AddTypeAlias<HttpRouteData>("RouteData");
 7257            options.AddTypeAlias<IFormFile>("FormFile");
 7258            options.AddTypeAlias<IFormFile[]>("FormFile[]");
 7259            options.AddTypeAlias<HttpFile>("HttpFile");
 7260            options.AddTypeAlias<HttpFile[]>("HttpFile[]");
 7261            options.AddTypeAlias<Downloadable>("Downloadable");
 7262            options.AddTypeAlias<Downloadable[]>("Downloadable[]");
 7263            options.AddTypeAlias<HttpStatusCode>();
 7264            options.AddTypeAlias<HttpRequestException>();
 7265            options.AddTypeAlias<HttpEndpointBookmarkPayload>();
 14266        });
 7267    }
 268}