Exemple #1
0
        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);
        }
Exemple #2
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);
        }
Exemple #3
0
        /// <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);
        }
Exemple #4
0
        /// <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);
        }
Exemple #5
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);

            // 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);
        }