diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs new file mode 100644 index 0000000..5cc2888 --- /dev/null +++ b/src/Api/Startup.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace OpenFaaS.Api +{ + public class Startup : IPluginStartup + { + public Startup( IConfiguration configuration ) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices( IServiceCollection services ) + { + var mvcBuilder = services.AddControllers( options => + { + } ); + + mvcBuilder.AddNewtonsoftJson( options => + { + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; + options.SerializerSettings.Converters.Add( new StringEnumConverter( new CamelCaseNamingStrategy(), false ) ); + }); + // Replaced with Newtonsoft because Microsoft's serializer doesn't do polymorphic serialization + + mvcBuilder.AddPluginControllers(); + } + + public void Configure( IApplicationBuilder app, IWebHostEnvironment env ) + { + if ( env.IsDevelopment() ) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + if ( !Configuration.GetValue( "Args:SkipAuth" ) ) + { + app.UseAuthentication(); + app.UseAuthorization(); + } + + // allow api implementation to customize the pipeline + app.ConfigurePlugin( env ); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/src/FunctionServiceExtensions.cs b/src/FunctionServiceExtensions.cs deleted file mode 100644 index fcce0f1..0000000 --- a/src/FunctionServiceExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Reflection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace OpenFaaS -{ - internal static class FunctionServiceExtensions - { - private static readonly string typeName = "OpenFaaS.Startup"; - - public static void AddFunctionServices( this IServiceCollection services, IConfiguration configuration ) - { - var type = AssemblyResolver.Default.Assembly.GetType( typeName ); - - if ( type == null ) - { - throw new TypeLoadException( $"Failed to load '{typeName}' type." ); - } - - var instance = AssemblyResolver.Default.Assembly.CreateInstance( typeName, false, BindingFlags.CreateInstance, null, new object[] - { - configuration - }, null, null ); - - type.InvokeMember( "ConfigureServices", BindingFlags.InvokeMethod, null, instance, new object[] - { - services - } ); - } - } -} diff --git a/src/HttpFunctionExtensions.cs b/src/Functions/HttpFunctionExtensions.cs similarity index 96% rename from src/HttpFunctionExtensions.cs rename to src/Functions/HttpFunctionExtensions.cs index 72f3907..5e340d5 100644 --- a/src/HttpFunctionExtensions.cs +++ b/src/Functions/HttpFunctionExtensions.cs @@ -3,7 +3,7 @@ using OpenFaaS; using System.Linq; -namespace OpenFaaS +namespace OpenFaaS.Functions { internal static class HttpFunctionExtensions { diff --git a/src/HttpMethodAttributeExtensions.cs b/src/Functions/HttpMethodAttributeExtensions.cs similarity index 95% rename from src/HttpMethodAttributeExtensions.cs rename to src/Functions/HttpMethodAttributeExtensions.cs index 2447889..08ffeb5 100644 --- a/src/HttpMethodAttributeExtensions.cs +++ b/src/Functions/HttpMethodAttributeExtensions.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; +using OpenFaaS.Functions.Middleware; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -namespace OpenFaaS +namespace OpenFaaS.Functions { internal static class HttpMethodAttributeExtensions { diff --git a/src/HttpRequestHandlerOptions.cs b/src/Functions/HttpRequestHandlerOptions.cs similarity index 80% rename from src/HttpRequestHandlerOptions.cs rename to src/Functions/HttpRequestHandlerOptions.cs index d6c87d0..31c025e 100644 --- a/src/HttpRequestHandlerOptions.cs +++ b/src/Functions/HttpRequestHandlerOptions.cs @@ -1,6 +1,6 @@ using System; -namespace OpenFaaS +namespace OpenFaaS.Functions { internal class HttpRequestHandlerOptions { diff --git a/src/Middleware/Authentication/AuthenticationHandler.cs b/src/Functions/Middleware/Authentication/AuthenticationHandler.cs similarity index 97% rename from src/Middleware/Authentication/AuthenticationHandler.cs rename to src/Functions/Middleware/Authentication/AuthenticationHandler.cs index 8a7144f..af748f3 100644 --- a/src/Middleware/Authentication/AuthenticationHandler.cs +++ b/src/Functions/Middleware/Authentication/AuthenticationHandler.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Threading.Tasks; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { internal class HttpAuthenticationHandler { diff --git a/src/Middleware/AuthenticationMiddleware.cs b/src/Functions/Middleware/AuthenticationMiddleware.cs similarity index 95% rename from src/Middleware/AuthenticationMiddleware.cs rename to src/Functions/Middleware/AuthenticationMiddleware.cs index 67ce117..b603082 100644 --- a/src/Middleware/AuthenticationMiddleware.cs +++ b/src/Functions/Middleware/AuthenticationMiddleware.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { /// /// Deals with authentication diff --git a/src/Middleware/AuthorizationMiddleware.cs b/src/Functions/Middleware/AuthorizationMiddleware.cs similarity index 96% rename from src/Middleware/AuthorizationMiddleware.cs rename to src/Functions/Middleware/AuthorizationMiddleware.cs index d7d3bbd..a64d431 100644 --- a/src/Middleware/AuthorizationMiddleware.cs +++ b/src/Functions/Middleware/AuthorizationMiddleware.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { /// /// Deals with authorization diff --git a/src/Middleware/FunctionMiddleware.cs b/src/Functions/Middleware/FunctionMiddleware.cs similarity index 95% rename from src/Middleware/FunctionMiddleware.cs rename to src/Functions/Middleware/FunctionMiddleware.cs index 9a904b3..6e58449 100644 --- a/src/Middleware/FunctionMiddleware.cs +++ b/src/Functions/Middleware/FunctionMiddleware.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { /// /// Deals with function execution and response handling diff --git a/src/Middleware/Routing/IRouteMatcher.cs b/src/Functions/Middleware/Routing/IRouteMatcher.cs similarity index 80% rename from src/Middleware/Routing/IRouteMatcher.cs rename to src/Functions/Middleware/Routing/IRouteMatcher.cs index 29d14f7..872f5b9 100644 --- a/src/Middleware/Routing/IRouteMatcher.cs +++ b/src/Functions/Middleware/Routing/IRouteMatcher.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Routing; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { internal interface IRouteMatcher { diff --git a/src/Middleware/Routing/RouteMatcher.cs b/src/Functions/Middleware/Routing/RouteMatcher.cs similarity index 96% rename from src/Middleware/Routing/RouteMatcher.cs rename to src/Functions/Middleware/Routing/RouteMatcher.cs index 5629ba7..381f882 100644 --- a/src/Middleware/Routing/RouteMatcher.cs +++ b/src/Functions/Middleware/Routing/RouteMatcher.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Threading.Tasks; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { internal class RouteMatcher : IRouteMatcher { diff --git a/src/Middleware/RoutingMiddleware.cs b/src/Functions/Middleware/RoutingMiddleware.cs similarity index 98% rename from src/Middleware/RoutingMiddleware.cs rename to src/Functions/Middleware/RoutingMiddleware.cs index 26be51a..c82f69f 100644 --- a/src/Middleware/RoutingMiddleware.cs +++ b/src/Functions/Middleware/RoutingMiddleware.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace OpenFaaS +namespace OpenFaaS.Functions.Middleware { /// /// Deals with routing and routing templates diff --git a/src/Functions/Startup.cs b/src/Functions/Startup.cs new file mode 100644 index 0000000..21a1bd4 --- /dev/null +++ b/src/Functions/Startup.cs @@ -0,0 +1,62 @@ +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Newtonsoft.Json.Serialization; + +namespace OpenFaaS.Functions +{ + public class Startup : IPluginStartup + { + public Startup( IConfiguration configuration ) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices( IServiceCollection services ) + { + services.AddMvc() + .AddNewtonsoftJson( options => + { + options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; + } ); + // Replaced with Newtonsoft because Microsoft's serializer doesn't do polymorphic serialization + + services.AddSingleton(); + + // add root request handler to the container + services.AddSingleton(); + services.TryAddScoped(); + services.Configure( options => + { + options.SkipAuth = Configuration.GetValue( "Args:SkipAuth" ); + + if ( options.SkipAuth ) + { + Console.WriteLine( "Skipping authentication and authorization" ); + } + } ); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure( IApplicationBuilder app, IWebHostEnvironment env ) + { + app.UseDeveloperExceptionPage(); + + Console.WriteLine(); + Console.WriteLine( "Running..." ); + + app.UseMiddleware() + .UseMiddleware() + .UseMiddleware() + .UseMiddleware(); + } + } +} diff --git a/src/IPluginStartup.cs b/src/IPluginStartup.cs new file mode 100644 index 0000000..99b5dc2 --- /dev/null +++ b/src/IPluginStartup.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenFaaS +{ + public interface IPluginStartup + { + void ConfigureServices( IServiceCollection services ); + void Configure( IApplicationBuilder app, IWebHostEnvironment env ); + } +} diff --git a/src/PluginServiceExtensions.cs b/src/PluginServiceExtensions.cs new file mode 100644 index 0000000..a060ae9 --- /dev/null +++ b/src/PluginServiceExtensions.cs @@ -0,0 +1,56 @@ +using System; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace OpenFaaS +{ + internal static class PluginServiceExtensions + { + private static readonly string typeName = "OpenFaaS.Startup"; + + private static object startup; + + public static void AddPluginControllers( this IMvcBuilder builder ) + { + builder.AddApplicationPart( AssemblyResolver.Default.Assembly ); + } + + public static IServiceCollection ConfigurePluginServices( this IServiceCollection services, IConfiguration configuration ) + { + startup = AssemblyResolver.Default.Assembly.CreateInstance( typeName, false, BindingFlags.CreateInstance, null, new object[] + { + configuration + }, null, null ); + + startup.GetType().InvokeMember( "ConfigureServices", BindingFlags.InvokeMethod, null, startup, new object[] + { + services + } ); + + return ( services ); + } + + public static IApplicationBuilder ConfigurePlugin( this IApplicationBuilder app, IWebHostEnvironment env ) + { + var type = startup.GetType(); + + var configureMethod = type.GetMethod( "Configure", BindingFlags.Public | BindingFlags.Instance, null, new Type[] + { + typeof( IApplicationBuilder ), typeof( bool ) + }, null ); + + // the Configure( IApplicationBuilder, bool ) method is optional + + configureMethod?.Invoke( startup, new object[] + { + app, env.IsDevelopment() + } ); + + return ( app ); + } + } +} diff --git a/src/Program.cs b/src/Program.cs index a46c89b..90e2aa9 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -37,8 +37,6 @@ static void Main(string[] args) Console.WriteLine("OpenFaaS ASPNET Function Loader"); Console.WriteLine(); - //AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad; - parsed.WithParsed( options => { if ( options.Detach ) @@ -84,9 +82,8 @@ public static IHostBuilder CreateHostBuilder( string[] args, Options options ) = } ); } ); - webBuilder.UseKestrel() - .UseStartup(); - + webBuilder.UseKestrel(); + webBuilder.UseStartup(); webBuilder.UseUrls( $"http://*:{options.Port}" ); } ); diff --git a/src/Startup.cs b/src/Startup.cs index f367e5f..1c0ce7f 100644 --- a/src/Startup.cs +++ b/src/Startup.cs @@ -1,25 +1,24 @@ using System; -using Microsoft.AspNetCore.Authentication; +using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Newtonsoft.Json.Serialization; namespace OpenFaaS { public class Startup { - public Startup(IConfiguration configuration) + public Startup( IConfiguration configuration ) { Configuration = configuration; } public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) + private Action configure; + + public void ConfigureServices( IServiceCollection services ) { services.AddCors( options => { @@ -34,45 +33,22 @@ public void ConfigureServices(IServiceCollection services) options.LowercaseUrls = true; } ); - services.AddMvc() - .AddNewtonsoftJson( options => - { - options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; - } ); - // Replaced with Newtonsoft because Microsoft's serializer doesn't do polymorphic serialization + services.ConfigurePluginServices( Configuration ); - services.AddSingleton(); + bool isHttpFunction = services.Any( x => x.ServiceType == typeof( IHttpFunction ) ); - // allow function implementation to add services to the container - services.AddFunctionServices( Configuration ); + IPluginStartup pluginStartup = isHttpFunction + ? new Functions.Startup( Configuration ) + : new Api.Startup( Configuration ); - // add root request handler to the container - services.AddSingleton(); - services.TryAddScoped(); - services.Configure( options => - { - options.SkipAuth = Configuration.GetValue( "Args:SkipAuth" ); + pluginStartup.ConfigureServices( services ); - if ( options.SkipAuth ) - { - Console.WriteLine( "Skipping authentication and authorization" ); - } - } ); + configure = pluginStartup.Configure; } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure( IApplicationBuilder app, IWebHostEnvironment env ) { - app.UseDeveloperExceptionPage(); - - Console.WriteLine(); - Console.WriteLine( "Running..." ); - - app.UseMiddleware() - .UseMiddleware() - .UseMiddleware() - .UseMiddleware(); + configure?.Invoke( app, env ); } } } diff --git a/src/faas-run.csproj b/src/faas-run.csproj index 79214ba..3a87415 100644 --- a/src/faas-run.csproj +++ b/src/faas-run.csproj @@ -3,7 +3,7 @@ Exe net5.0 - 1.5 + 1.6 Goncalo Oliveira FaaS runner for ASPNET functions https://github.com/goncalo-oliveira/faas-run @@ -11,7 +11,7 @@ - +