/// <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(new IPEndPoint(ParseUtil.ParseIPAddress(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( new IPEndPoint(ParseUtil.ParseIPAddress(address), port), usessl, m_token.Token, config, m_handler ); }
public static IPAddress ParseIpAddress(string text) { return(ParseUtil.ParseIPAddress(text)); }
/// <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> /// 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.ParseIPAddress(address); 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); } }