public static int Main(string[] args) { DebugConsoleOutput("{0}: Started new process", System.Diagnostics.Process.GetCurrentProcess().Id); if (IsSpawnedChild) { try { DebugConsoleOutput("{0}: Starting child process", System.Diagnostics.Process.GetCurrentProcess().Id); Runner.SubProcess.SpawnedRunner.RunClientRPCListenerAsync().Wait(); DebugConsoleOutput("{0}: Finished child process", System.Diagnostics.Process.GetCurrentProcess().Id); } catch (Exception ex) { DebugConsoleOutput("{0}: Crashed child: {1}", System.Diagnostics.Process.GetCurrentProcess().Id, ex); return(1); } return(0); } if (args.Length != 1) { ConsoleOutput("Usage: {0} [path-to-config-file]", Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().Location)); return(1); } if (!File.Exists(args[0])) { ConsoleOutput("CWD: {0}", Directory.GetCurrentDirectory()); ConsoleOutput("File not found: {0}", args[0]); return(1); } var tcs = new System.Threading.CancellationTokenSource(); CLIConfiguration = ConfigParser.ParseTextFile(args[0]); var tasks = new List <Task>(); Runner.IRunnerHandler app; if (CLIConfiguration.IsolatedProcesses) { app = new Runner.SubProcess.Runner(args[0]); } else if (CLIConfiguration.IsolatedAppDomain) { app = new Runner.AppDomain.Runner(args[0]); } else { app = new Runner.InProcess.Runner(args[0]); } tasks.Add(app.StoppedAsync); var reloadevent = new TaskCompletionSource <bool>(); var stopevent = new TaskCompletionSource <bool>(); var hasrequestedstop = false; var hasrequestedreload = false; IDisposable fsw = null; ConsoleOutput("Server is running, press CTRL+C to reload config..."); Func <bool> stop = () => { if (!hasrequestedstop) { ConsoleOutput("Sending stop request to server"); hasrequestedstop = true; stopevent.SetResult(true); return(true); } return(false); }; Func <bool> reload = () => { if (!hasrequestedreload) { ConsoleOutput("Sending reload request to server"); hasrequestedreload = true; reloadevent.SetResult(true); return(true); } else { return(stop()); } }; Console.CancelKeyPress += (sender, e) => { if (reload()) { e.Cancel = true; } }; if (CLIConfiguration.WatchConfigFile) { var configname = Path.GetFullPath(args[0]); var f = new FileSystemWatcher(Path.GetDirectoryName(configname)); f.Changed += (sender, e) => { if (e.FullPath == configname) { Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => reloadevent.SetResult(true)); } }; f.EnableRaisingEvents = true; fsw = f; } app.InstanceCrashed += (address, ssl, ex) => { ConsoleOutput($"Crashed for {(ssl ? "https" : "http")} {address}: {ex}"); reloadevent.SetResult(true); }; var primarytask = app.ReloadAsync(CLIConfiguration.ListenHttp, CLIConfiguration.ListenHttps); primarytask.Wait(); if (primarytask.IsFaulted) { throw primarytask.Exception; } var sigtask = SignalHandler(tcs.Token, () => { reloadevent.TrySetResult(true); return(true); }, () => { stopevent.TrySetResult(true); return(true); } ); var allitems = Task.WhenAll(tasks); // Setup automatic child restart if (CLIConfiguration.MaxRunnerLifeSeconds.Ticks > 0) { Task.Run(() => { // Capture this instance var r = reloadevent; Task .Delay(CLIConfiguration.MaxRunnerLifeSeconds) .ContinueWith(_ => r.TrySetResult(true)); }); } var t = Task.WhenAny(allitems, stopevent.Task, reloadevent.Task).Result; using (fsw) while (t == reloadevent.Task) { reloadevent = new TaskCompletionSource <bool>(); var waitdelay = Task.Delay(TimeSpan.FromSeconds(2)); ConsoleOutput("Reloading configuration ..."); try { CLIConfiguration = ConfigParser.ParseTextFile(args[0]); var tr = app.ReloadAsync(CLIConfiguration.ListenHttp, CLIConfiguration.ListenHttps); tr.Wait(); if (tr.IsFaulted) { throw tr.Exception; } if (CLIConfiguration.MaxRunnerLifeSeconds.Ticks > 0) { Task.Run(() => { // Capture this instance var r = reloadevent; Task .Delay(CLIConfiguration.MaxRunnerLifeSeconds) .ContinueWith(_ => r.TrySetResult(true)); }); } ConsoleOutput("Configuration reloaded!"); } catch (Exception ex) { ConsoleOutput("Failed to reload configuration with message: {0}", ex); } waitdelay.Wait(); hasrequestedreload = false; t = Task.WhenAny(allitems, stopevent.Task, reloadevent.Task).Result; } if (t == stopevent.Task) { ConsoleOutput("Got stop signal, stopping server ..."); if (app != null) { app.StopAsync(); } tcs.Cancel(); allitems.Wait(); sigtask.Wait(); } ConsoleOutput("Server has stopped..."); return(0); }
/// <summary> /// Creates a server configuration from a CLI config instance /// </summary> /// <returns>The server configuration.</returns> /// <param name="config">The CLI config instance.</param> public static ServerConfig ValidateConfig(CLIServerConfiguration config) { var cfg = CreateServerConfig(config); // Expose our configuration to allow modules to call back, // beware that these values should never be reported to the client // as there might be proxies in front which will make them useless // to clients not running on the local machine if (config.ListenHttp) { var parsedip = ParseUtil.ParseIPAddress(config.HttpAddress); var ip = parsedip == IPAddress.Any ? "127.0.0.1" : parsedip.ToString(); Environment.SetEnvironmentVariable("CEEN_SELF_HTTP_URL", "http://" + ip + ":" + config.HttpPort); } if (config.ListenHttps) { var parsedip = ParseUtil.ParseIPAddress(config.HttpsAddress); var ip = parsedip == IPAddress.Any ? "127.0.0.1" : parsedip.ToString(); Environment.SetEnvironmentVariable("CEEN_SELF_HTTPS_URL", "http://" + ip + ":" + config.HttpsPort); } if (config.AutoLoadAssemblies) { // Workaround for unittests not providing EntryAssembly var callingasm = Assembly.GetEntryAssembly(); var callingdir = callingasm == null ? null : Path.GetDirectoryName(callingasm.Location); var paths = (config.Assemblypath ?? string.Empty) .Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries) .Union(new[] { config.Basepath, callingdir }) .Where(x => !string.IsNullOrWhiteSpace(x) && Directory.Exists(x)) .Distinct(); foreach (var p in paths) { foreach (var file in Directory.GetFiles(p, "*.*", SearchOption.TopDirectoryOnly)) { if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { try { Assembly.LoadFrom(file); } catch { } } } } var ceenpath = Path.GetDirectoryName(typeof(CLIServerConfiguration).Assembly.Location); if (Directory.Exists(ceenpath)) { foreach (var dll in Directory.GetFiles(ceenpath, "Ceen.*.dll", SearchOption.TopDirectoryOnly)) { try { Assembly.LoadFrom(dll); } catch { } } } } if (config.Loggers != null) { foreach (var logger in config.Loggers) { var inst = CreateInstance(logger.Classname, logger.ConstructorArguments, typeof(ILogger)); if (logger.Options != null) { SetProperties(inst, logger.Options); } if (inst is ILoggerWithSetup mse) { mse.AfterConfigure(); } cfg.AddLogger((ILogger)inst); } } if (config.Modules != null) { foreach (var module in config.Modules) { object handler; var moduletype = ResolveType(module.Classname); handler = CreateInstance(moduletype, module.ConstructorArguments, typeof(IModule)); if (module.Options != null) { SetProperties(handler, module.Options); } if (handler is IModuleWithSetup mse) { mse.AfterConfigure(); } cfg.AddModule((IModule)handler); } } if (config.PostProcessors != null) { foreach (var postProcessor in config.PostProcessors) { var inst = CreateInstance(postProcessor.Classname, postProcessor.ConstructorArguments, typeof(IPostProcessor)); if (postProcessor.Options != null) { SetProperties(inst, postProcessor.Options); } if (inst is IPostProcessorWithSetup mse) { mse.AfterConfigure(); } cfg.AddPostProcessor((IPostProcessor)inst); } } if (config.Routes != null) { foreach (var route in config.Routes) { // Check if this is a module if (route.RoutePrefix != null) { object handler; var moduletype = ResolveType(route.Classname); if (typeof(Ceen.Httpd.Handler.FileHandler).IsAssignableFrom(moduletype)) { Func <IHttpRequest, string, string> mimehandler = null; if (config.MimeTypes != null && config.MimeTypes.Count > 0) { string default_mimetype; config.MimeTypes.TryGetValue("*", out default_mimetype); if (string.IsNullOrWhiteSpace(default_mimetype)) { default_mimetype = null; } if (config.IgnoreDefaultMimeTypes) { mimehandler = (req, mappedpath) => { var ext = Path.GetExtension(mappedpath).ToLowerInvariant(); string mime; config.MimeTypes.TryGetValue(ext, out mime); if (default_mimetype != null && string.IsNullOrWhiteSpace(mime)) { return(default_mimetype); } return(mime); }; } else { mimehandler = (req, mappedpath) => { var ext = Path.GetExtension(mappedpath).ToLowerInvariant(); string mime; config.MimeTypes.TryGetValue(ext, out mime); if (!string.IsNullOrWhiteSpace(mime)) { return(mime); } else if (default_mimetype != null && string.IsNullOrWhiteSpace(mime)) { return(default_mimetype); } else { return(Ceen.Httpd.Handler.FileHandler.DefaultMimeTypes(mappedpath)); } }; } } else if (config.IgnoreDefaultMimeTypes) { mimehandler = (req, path) => null; } handler = Activator.CreateInstance( moduletype, ExpandEnvironmentVariables(route.ConstructorArguments.First()), mimehandler ); if (config.IndexDocuments.Count != 0) { if (route.Options.ContainsKey(nameof(Handler.FileHandler.IndexDocuments))) { throw new Exception($"Cannot use both the `Index` option and {nameof(Handler.FileHandler.IndexDocuments)} in the configuration for {moduletype.FullName}"); } route.Options[nameof(Handler.FileHandler.IndexDocuments)] = string.Join(", ", config.IndexDocuments.Select(x => $"\"{x}\"")); } } else { handler = CreateInstance(moduletype, route.ConstructorArguments, typeof(IHttpModule)); } if (route.Options != null) { SetProperties(handler, route.Options); } if (handler is IHttpModuleWithSetup mse) { mse.AfterConfigure(); } if (string.IsNullOrWhiteSpace(route.RoutePrefix)) { cfg.AddRoute((IHttpModule)handler); } else { cfg.AddRoute(route.RoutePrefix, (IHttpModule)handler); } } else { var assembly = Assembly.Load(route.Assembly); if (assembly == null) { throw new Exception($"Failed to find load assembly {route.Assembly}"); } Type defaulttype = null; if (!string.IsNullOrWhiteSpace(route.Classname)) { defaulttype = assembly.GetType(route.Classname, false); if (defaulttype == null) { throw new Exception($"Failed to find class {route.Classname} in {route.Assembly}"); } } var rt = new Ceen.Mvc.ControllerRouterConfig(defaulttype); if (route.Options != null) { SetProperties(rt, route.Options); } cfg.AddRoute(new Ceen.Mvc.ControllerRouter(rt, assembly)); } } } return(cfg); }
/// <summary> /// Parse a text file and build a configuration instance /// </summary> /// <returns>The parsed instance.</returns> /// <param name="path">The path to the file to read.</param> public static CLIServerConfiguration ParseTextFile(string path) { var cfg = new CLIServerConfiguration(); var line = string.Empty; var re = new System.Text.RegularExpressions.Regex(@"(?<comment>\#.*)|(\""(?<value>[^\""]*)\"")|((?<value>[^ ]*))"); var s1names = BuildPropertyMap <CLIServerConfiguration>(); var s2names = BuildPropertyMap <ServerConfig>(); var lineindex = 0; Dictionary <string, string> lastitemprops = null; using (var fs = File.OpenRead(path)) using (var sr = new StreamReader(fs, System.Text.Encoding.UTF8, true)) while ((line = sr.ReadLine()) != null) { var args = re.Matches(line) .Cast <System.Text.RegularExpressions.Match>() .Where(x => x.Groups["value"].Success && !string.IsNullOrWhiteSpace(x.Value)) .Select(x => x.Groups["value"].Value) .ToArray(); lineindex++; if (args.Length == 0) { continue; } var cmd = args.First(); if (s1names.ContainsKey(cmd)) { if (args.Length > 2) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } SetProperty(cfg, s1names[cmd], args.Skip(1).FirstOrDefault()); } else if (s2names.ContainsKey(cmd)) { if (args.Length > 2) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } cfg.ServerOptions[cmd] = args.Skip(1).FirstOrDefault(); } else if (string.Equals(cmd, "route", StringComparison.OrdinalIgnoreCase)) { if (args.Length == 2) { var route = new RouteDefinition() { Assembly = args.Skip(1).First() }; cfg.Routes.Add(route); lastitemprops = route.Options; } else { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } var route = new RouteDefinition() { Assembly = args.Skip(1).First(), Classname = args.Skip(2).First(), ConstructorArguments = args.Skip(3).ToList() }; cfg.Routes.Add(route); lastitemprops = route.Options; } } else if (string.Equals(cmd, "handler", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } var route = new RouteDefinition() { RoutePrefix = args.Skip(1).First(), Classname = args.Skip(2).First(), ConstructorArguments = args.Skip(3).ToList() }; cfg.Routes.Add(route); lastitemprops = route.Options; } else if (string.Equals(cmd, "module", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 2) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } var module = new ModuleDefinition() { Classname = args.Skip(1).First(), ConstructorArguments = args.Skip(2).ToList() }; cfg.Modules.Add(module); lastitemprops = module.Options; } else if (string.Equals(cmd, "postprocessor", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 2) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } var postprocessor = new PostProcessorDefinition() { Classname = args.Skip(1).First(), ConstructorArguments = args.Skip(2).ToList() }; cfg.PostProcessors.Add(postprocessor); lastitemprops = postprocessor.Options; } else if (string.Equals(cmd, "logger", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 2) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } var logger = new LoggerDefinition() { Classname = args.Skip(1).First(), ConstructorArguments = args.Skip(2).ToList() }; cfg.Loggers.Add(logger); lastitemprops = logger.Options; } else if (string.Equals(cmd, "set", StringComparison.OrdinalIgnoreCase)) { if (lastitemprops == null) { throw new Exception($"There was no active entry to set properties for in line {lineindex}"); } if (args.Length > 3) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } if (args.Length < 2) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } lastitemprops[args.Skip(1).First()] = args.Skip(2).FirstOrDefault(); } else if (string.Equals(cmd, "serve", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } if (args.Length > 3) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } var routearg = args.Skip(1).First(); if (routearg == string.Empty) { routearg = "/"; } if (!routearg.StartsWith("/", StringComparison.Ordinal)) { throw new Exception($"The route must start with a forward slash in line {lineindex}: {line}"); } while (routearg.Length > 1 && routearg.EndsWith("/", StringComparison.Ordinal)) { routearg = routearg.Substring(0, routearg.Length - 1); } var pathprefix = routearg; routearg = routearg == "/" ? "[/.*]" : $"[{routearg}(/.*)?]"; var route = new RouteDefinition() { RoutePrefix = routearg, Classname = typeof(Ceen.Httpd.Handler.FileHandler).AssemblyQualifiedName, ConstructorArguments = args.Skip(2).ToList() }; cfg.Routes.Add(route); lastitemprops = route.Options; lastitemprops.Add(nameof(Ceen.Httpd.Handler.FileHandler.PathPrefix), pathprefix); } else if (string.Equals(cmd, "redirect", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } if (args.Length > 3) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } var routearg = args.Skip(1).First(); var route = new RouteDefinition() { RoutePrefix = routearg, Classname = typeof(Ceen.Httpd.Handler.RedirectHandler).AssemblyQualifiedName, ConstructorArguments = args.Skip(2).ToList() }; cfg.Routes.Add(route); lastitemprops = route.Options; } else if (string.Equals(cmd, "mime", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } if (args.Length > 3) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } var key = args.Skip(1).First(); if (!string.IsNullOrEmpty(key) && key != "*" && !key.StartsWith(".", StringComparison.Ordinal)) { key = "." + key; } cfg.MimeTypes[key] = args.Skip(2).First(); } else if (string.Equals(cmd, "header", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 3) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } if (args.Length > 3) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } cfg.DefaultHeaders[args.Skip(1).First()] = args.Skip(2).First(); } else if (string.Equals(cmd, "index", StringComparison.OrdinalIgnoreCase)) { if (args.Length < 2) { throw new Exception($"Too few arguments in line {lineindex}: {line}"); } if (args.Length > 2) { throw new Exception($"Too many arguments in line {lineindex}: {line}"); } cfg.IndexDocuments.Add(args.Skip(1).First()); } else { throw new Exception($"Unable to parse the action \"{cmd}\" for line {lineindex}: \"{line}\""); } } cfg.Basepath = Path.GetFullPath(cfg.Basepath ?? "."); cfg.Assemblypath = string.Join(Path.PathSeparator.ToString(), (cfg.Assemblypath ?? "").Split(new char[] { Path.PathSeparator }).Select(x => Path.GetFullPath(Path.Combine(cfg.Basepath, ExpandEnvironmentVariables(x))))); return(cfg); }
/// <summary> /// Creates the server config instance, but does not attempt to load assemblies /// </summary> /// <returns>The server config.</returns> /// <param name="config">The CLI configuration.</param> public static ServerConfig CreateServerConfig(CLIServerConfiguration config) { var enablehttp = !string.IsNullOrWhiteSpace(config.HttpAddress); var enablehttps = !string.IsNullOrWhiteSpace(config.HttpsAddress); if (!(enablehttp || enablehttps)) { throw new Exception("Either http or https must be enabled"); } if (enablehttp && (config.HttpPort < 0 || config.HttpPort > ushort.MaxValue)) { throw new Exception($"Invalid value for http port: {config.HttpPort}"); } if (enablehttps && (config.HttpsPort < 0 || config.HttpsPort > ushort.MaxValue)) { throw new Exception($"Invalid value for https port: {config.HttpsPort}"); } var cfg = new ServerConfig(); if (enablehttps && !string.IsNullOrWhiteSpace(config.CertificatePath)) { cfg.LoadCertificate(config.CertificatePath, config.CertificatePassword); } if (config.ServerOptions != null) { SetProperties(cfg, config.ServerOptions); } if (config.SupressDefaultHeaders) { cfg.AddDefaultResponseHeaders = resp => { if (config.DefaultHeaders != null) { foreach (var e in config.DefaultHeaders) { if (!resp.Headers.ContainsKey(e.Key)) { resp.AddHeader(e.Key, e.Value); } } } }; } else if (config.DefaultHeaders != null && config.DefaultHeaders.Count != 0) { cfg.AddDefaultResponseHeaders = resp => { foreach (var e in config.DefaultHeaders) { if (!resp.Headers.ContainsKey(e.Key)) { resp.AddHeader(e.Key, e.Value); } } }; } return(cfg); }
/// <summary> /// Creates a server configuration from a CLI config instance /// </summary> /// <returns>The server configuration.</returns> /// <param name="config">The CLI config instance.</param> public static ServerConfig ValidateConfig(CLIServerConfiguration config) { var cfg = CreateServerConfig(config); // Set up the module context var ctx = LoaderContext.StartContext(); cfg.LoaderContext = ctx; // Expose our configuration to allow modules to call back, // beware that these values should never be reported to the client // as there might be proxies in front which will make them useless // to clients not running on the local machine if (config.ListenHttp) { var parsedaddr = ParseUtil.ParseEndPoint(config.HttpAddress, config.HttpPort); if (parsedaddr is IPEndPoint ipe) { var ip = ipe.Address == IPAddress.Any ? "127.0.0.1" : ipe.Address.ToString(); Environment.SetEnvironmentVariable("CEEN_SELF_HTTP_URL", "http://" + ip + ":" + ipe.Port); } // else // { // Environment.SetEnvironmentVariable("CEEN_SELF_HTTP_URL", config.HttpAddress); // } } if (config.ListenHttps) { var parsedaddr = ParseUtil.ParseEndPoint(config.HttpsAddress, config.HttpsPort); if (parsedaddr is IPEndPoint ipe) { var ip = ipe.Address == IPAddress.Any ? "127.0.0.1" : ipe.Address.ToString(); Environment.SetEnvironmentVariable("CEEN_SELF_HTTPS_URL", "http://" + ip + ":" + ipe.Port); } // else // { // Environment.SetEnvironmentVariable("CEEN_SELF_HTTPS_URL", config.HttpsAddress); // } } if (config.AutoLoadAssemblies) { // Workaround for unittests not providing EntryAssembly var callingasm = Assembly.GetEntryAssembly(); var callingdir = callingasm == null ? null : Path.GetDirectoryName(callingasm.Location); var paths = (config.Assemblypath ?? string.Empty) .Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries) .Union(new[] { config.Basepath, callingdir }) .Where(x => !string.IsNullOrWhiteSpace(x) && Directory.Exists(x)) .Distinct(); foreach (var p in paths) { foreach (var file in Directory.GetFiles(p, "*.*", SearchOption.TopDirectoryOnly)) { if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { try { Assembly.LoadFrom(file); } catch { } } } } var ceenpath = Path.GetDirectoryName(typeof(CLIServerConfiguration).Assembly.Location); if (Directory.Exists(ceenpath)) { foreach (var dll in Directory.GetFiles(ceenpath, "Ceen.*.dll", SearchOption.TopDirectoryOnly)) { try { Assembly.LoadFrom(dll); } catch { } } } } if (config.Loggers != null) { foreach (var logger in config.Loggers) { var inst = CreateInstance(logger.Classname, logger.ConstructorArguments, typeof(ILogger)); if (logger.Options != null) { SetProperties(inst, logger.Options); } if (inst is IWithSetup mse) { mse.AfterConfigure(); } cfg.AddLogger((ILogger)inst); } } if (config.Modules != null) { foreach (var module in config.Modules) { object handler; var moduletype = ResolveType(module.Classname); handler = CreateInstance(moduletype, module.ConstructorArguments, typeof(IModule)); if (module.Options != null) { SetProperties(handler, module.Options); } if (handler is IWithSetup mse) { mse.AfterConfigure(); } cfg.AddModule((IModule)handler); } } if (config.PostProcessors != null) { foreach (var postProcessor in config.PostProcessors) { var inst = CreateInstance(postProcessor.Classname, postProcessor.ConstructorArguments, typeof(IPostProcessor)); if (postProcessor.Options != null) { SetProperties(inst, postProcessor.Options); } if (inst is IWithSetup mse) { mse.AfterConfigure(); } cfg.AddPostProcessor((IPostProcessor)inst); } } if (config.Routes != null) { Ceen.Mvc.ManualRoutingController wirecontroller = null; Ceen.Mvc.ControllerRouterConfig wirecontrollerconfig = null; foreach (var route in config.Routes) { // If we have a wired controller, and a non-wire item // add it now to ensure the order is maintained if (wirecontroller != null && !(route is WiredRouteDefinition)) { cfg.AddRoute(wirecontroller.ToRoute(wirecontrollerconfig)); wirecontroller = null; } // Check for wireups if (route is WiredRouteDefinition wired) { // Make a new manual router, if none is present or if we add one if (wirecontroller == null || (wired.RoutePrefix == null)) { // Add any previous wireup to the routes if (wirecontroller != null) { cfg.AddRoute(wirecontroller.ToRoute(wirecontrollerconfig)); } wirecontrollerconfig = new Ceen.Mvc.ControllerRouterConfig(); if (route.Options != null) { SetProperties(wirecontrollerconfig, route.Options); } wirecontroller = new Ceen.Mvc.ManualRoutingController(); } if (wired.Verbs == null) { var itemtype = ResolveType(wired.Classname); wirecontroller.WireController(itemtype, wired.RoutePrefix, wirecontrollerconfig); } else { var ix = wired.Classname.LastIndexOf('.'); var classname = wired.Classname.Substring(0, ix); var methodname = wired.Classname.Substring(ix + 1); var itemtype = ResolveType(classname); var argumenttypes = wired.ConstructorArguments.Select(x => ResolveType(x)).ToArray(); if (argumenttypes.Any(x => x == null)) { throw new Exception($"Cannot resolve type argument {argumenttypes.Select((a, b) => a == null ? wired.ConstructorArguments[b] : null).Where(x => x != null).First()}"); } wirecontroller.Wire(itemtype, wired.Verbs + " " + wired.RoutePrefix, methodname, argumenttypes); } } // Check if this is a module else if (route.RoutePrefix != null) { object handler; var moduletype = ResolveType(route.Classname); if (typeof(Ceen.Httpd.Handler.FileHandler).IsAssignableFrom(moduletype)) { Func <IHttpRequest, string, string> mimehandler = null; if (config.MimeTypes != null && config.MimeTypes.Count > 0) { string default_mimetype; config.MimeTypes.TryGetValue("*", out default_mimetype); if (string.IsNullOrWhiteSpace(default_mimetype)) { default_mimetype = null; } if (config.IgnoreDefaultMimeTypes) { mimehandler = (req, mappedpath) => { var ext = Path.GetExtension(mappedpath).ToLowerInvariant(); string mime; config.MimeTypes.TryGetValue(ext, out mime); if (default_mimetype != null && string.IsNullOrWhiteSpace(mime)) { return(default_mimetype); } return(mime); }; } else { mimehandler = (req, mappedpath) => { var ext = Path.GetExtension(mappedpath).ToLowerInvariant(); string mime; config.MimeTypes.TryGetValue(ext, out mime); if (!string.IsNullOrWhiteSpace(mime)) { return(mime); } else if (default_mimetype != null && string.IsNullOrWhiteSpace(mime)) { return(default_mimetype); } else { return(Ceen.Httpd.Handler.FileHandler.DefaultMimeTypes(mappedpath)); } }; } } else if (config.IgnoreDefaultMimeTypes) { mimehandler = (req, path) => null; } handler = Activator.CreateInstance( moduletype, ExpandEnvironmentVariables(route.ConstructorArguments.First()), mimehandler ); if (config.IndexDocuments.Count != 0) { if (route.Options.ContainsKey(nameof(Handler.FileHandler.IndexDocuments))) { throw new Exception($"Cannot use both the `Index` option and {nameof(Handler.FileHandler.IndexDocuments)} in the configuration for {moduletype.FullName}"); } route.Options[nameof(Handler.FileHandler.IndexDocuments)] = string.Join(", ", config.IndexDocuments.Select(x => $"\"{x}\"")); } } else { handler = CreateInstance(moduletype, route.ConstructorArguments, typeof(IHttpModule)); } if (route.Options != null) { SetProperties(handler, route.Options); } if (handler is IWithSetup mse) { mse.AfterConfigure(); } if (string.IsNullOrWhiteSpace(route.RoutePrefix)) { cfg.AddRoute((IHttpModule)handler); } else { cfg.AddRoute(route.RoutePrefix, (IHttpModule)handler); } } // Otherwise it is automatic routing of an assembly else { var assembly = Assembly.Load(route.Assembly); if (assembly == null) { throw new Exception($"Failed to load assembly {route.Assembly}"); } Type defaulttype = null; if (!string.IsNullOrWhiteSpace(route.Classname)) { defaulttype = assembly.GetType(route.Classname, false); if (defaulttype == null) { throw new Exception($"Failed to find class {route.Classname} in {route.Assembly}"); } } var rt = new Ceen.Mvc.ControllerRouterConfig(defaulttype); if (route.Options != null) { SetProperties(rt, route.Options); } cfg.AddRoute(new Ceen.Mvc.ControllerRouter(rt, assembly)); } } // If we have a wired controller, add it if (wirecontroller != null) { cfg.AddRoute(wirecontroller.ToRoute(wirecontrollerconfig)); } } ctx.Freeze(); return(cfg); }