/// <summary> /// Restarts this instance /// </summary> /// <param name="address">The address to listen on</param> /// <param name="port">The port to listen on.</param> /// <param name="usessl">If set to <c>true</c> use ssl.</param> /// <param name="config">The server configuration.</param> public async Task RestartAsync(string address, int port, bool usessl, ServerConfig config) { if (RunnerTask != null) { await StopAsync(); } m_token = new CancellationTokenSource(); Port = port; Config = config; UseSSL = usessl; Address = address; ShouldStop = false; if (Wrapper is ISelfListen isl && !isl.UseManagedListen) { isl.Bind(ParseUtil.ParseEndPoint(address, port), config.SocketBacklog); RunnerTask = HttpServer.ListenToSocketAsync( isl.ListenAsync, usessl, m_token.Token, config, (c, e, l) => ((ISelfListen)Wrapper).HandleRequest(c, e, l) ); }
/// <summary> /// Restarts this instance /// </summary> /// <param name="address">The address to listen on</param> /// <param name="port">The port to listen on.</param> /// <param name="usessl">If set to <c>true</c> use ssl.</param> /// <param name="config">The server configuration.</param> public async Task RestartAsync(IAppDomainWrappedRunner wrapper, string address, int port, bool usessl, ServerConfig config) { if (RunnerTask != null) { await StopAsync(); } m_token = new CancellationTokenSource(); Port = port; Config = config; UseSSL = usessl; Wrapper = wrapper; Address = address; ShouldStop = false; RunnerTask = HttpServer.ListenToSocketAsync( ParseUtil.ParseEndPoint(address, port), usessl, m_token.Token, config, m_handler ); }
/// <summary> /// Starts a runner instance /// </summary> /// <returns>The awaitable task.</returns> /// <param name="domain">The AppDomain to use.</param> /// <param name="config">The server configuration.</param> /// <param name="usessl">If set to <c>true</c> use ssl.</param> /// <param name="port">The port to listen on.</param> private async Task StartRunnerAsync(System.AppDomain domain, ServerConfig config, bool usessl, string address, int port) { var enabled = !string.IsNullOrWhiteSpace(address); // Ensure it parses ParseUtil.ParseEndPoint(address, port); var prev = m_handlers.Where(x => x.UseSSL == usessl).FirstOrDefault(); if (enabled) { var addcrashhandler = true; var wrapper = new AppDomainWrapper(domain); wrapper.SetupFromFile(usessl, m_path, config.Storage); if (prev == null) { prev = new RunnerInstance(wrapper, address, port, usessl, config); m_handlers.Add(prev); } else { if (prev.RunnerTask.IsFaulted || prev.RunnerTask.IsCanceled || prev.RunnerTask.IsCompleted) { var cur = new RunnerInstance(wrapper, address, port, usessl, config); m_handlers.Remove(prev); m_handlers.Add(cur); prev = cur; } else if (prev.Port != port || !string.Equals(prev.Address, address, StringComparison.Ordinal)) { // Address or port change, start new listener first var cur = new RunnerInstance(wrapper, address, port, usessl, config); if (!prev.RunnerTask.IsFaulted) { await prev.StopAsync(); } m_handlers.Remove(prev); m_handlers.Add(cur); prev = cur; } else if (prev.Config.SocketBacklog != config.SocketBacklog) { await prev.RestartAsync(wrapper, address, port, usessl, config); } else { addcrashhandler = false; // We already have it prev.Wrapper = wrapper; // Assign the new wrapper } } if (addcrashhandler) { var dummy = prev.RunnerTask.ContinueWith(x => { if (!prev.ShouldStop && InstanceCrashed != null) { InstanceCrashed(address, usessl, x.IsFaulted ? x.Exception : new Exception("Unexpected stop")); } }); } } else if (prev != null) { await prev.StopAsync(); m_handlers.Remove(prev); } }
/// <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); }