using System; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using EmbedIO.Net.Internal; using EmbedIO.Routing; using EmbedIO.Utilities; using Swan.Logging; namespace EmbedIO { /// /// EmbedIO's web server. This is the default implementation of . /// This class also contains some useful constants related to EmbedIO's internal working. /// public partial class WebServer : WebServerBase { /// /// Initializes a new instance of the class, /// that will respond on HTTP port 80 on all network interfaces. /// public WebServer() : this(80) { } /// /// Initializes a new instance of the class, /// that will respond on the specified HTTP port on all network interfaces. /// /// The port. public WebServer(int port) : this($"http://*:{port}/") { } /// /// Initializes a new instance of the class /// with the specified URL prefixes. /// /// The URL prefixes to configure. /// is . /// /// One or more of the elements of is the empty string. /// - or - /// One or more of the elements of is already registered. /// public WebServer(params string[] urlPrefixes) : this(new WebServerOptions().WithUrlPrefixes(urlPrefixes)) { } /// /// Initializes a new instance of the class. /// /// The type of HTTP listener to configure. /// The URL prefixes to configure. /// is . /// /// One or more of the elements of is the empty string. /// - or - /// One or more of the elements of is already registered. /// public WebServer(HttpListenerMode mode, params string[] urlPrefixes) : this(new WebServerOptions().WithMode(mode).WithUrlPrefixes(urlPrefixes)) { } /// /// Initializes a new instance of the class. /// /// The type of HTTP listener to configure. /// The X.509 certificate to use for SSL connections. /// The URL prefixes to configure. /// is . /// /// One or more of the elements of is the empty string. /// - or - /// One or more of the elements of is already registered. /// public WebServer(HttpListenerMode mode, X509Certificate2 certificate, params string[] urlPrefixes) : this(new WebServerOptions() .WithMode(mode) .WithCertificate(certificate) .WithUrlPrefixes(urlPrefixes)) { } /// /// Initializes a new instance of the class. /// /// A object used to configure this instance. /// is . public WebServer(WebServerOptions options) : base(options) { Listener = CreateHttpListener(); } /// /// Initializes a new instance of the class. /// /// A callback that will be used to configure /// the server's options. /// is . public WebServer(Action configure) : base(configure) { Listener = CreateHttpListener(); } /// /// Gets the underlying HTTP listener. /// public IHttpListener Listener { get; } /// protected override void Dispose(bool disposing) { if (disposing) { try { Listener.Dispose(); } catch (Exception ex) { ex.Log(LogSource, "Exception thrown while disposing HTTP listener."); } "Listener closed.".Info(LogSource); } base.Dispose(disposing); } /// protected override void Prepare(CancellationToken cancellationToken) { Listener.Start(); "Started HTTP Listener".Info(LogSource); // close port when the cancellation token is cancelled _ = cancellationToken.Register(() => Listener?.Stop()); } /// protected override async Task ProcessRequestsAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested && (Listener?.IsListening ?? false)) { var context = await Listener.GetContextAsync(cancellationToken).ConfigureAwait(false); context.CancellationToken = cancellationToken; context.Route = RouteMatch.UnsafeFromRoot(UrlPath.Normalize(context.Request.Url.AbsolutePath, false)); #pragma warning disable CS4014 // Call is not awaited - of course, it has to run in parallel. _ = Task.Run(() => DoHandleContextAsync(context), cancellationToken); #pragma warning restore CS4014 } } /// protected override void OnFatalException() => Listener?.Dispose(); private IHttpListener CreateHttpListener() { IHttpListener DoCreate() => Options.Mode switch { HttpListenerMode.Microsoft => System.Net.HttpListener.IsSupported ? new SystemHttpListener(new System.Net.HttpListener()) as IHttpListener : new Net.HttpListener(Options.Certificate), _ => new Net.HttpListener(Options.Certificate) }; var listener = DoCreate(); $"Running HTTPListener: {listener.Name}".Info(LogSource); foreach (var prefix in Options.UrlPrefixes) { var urlPrefix = new string(prefix?.ToCharArray()); if (!urlPrefix.EndsWith("/")) urlPrefix += "/"; urlPrefix = urlPrefix.ToLowerInvariant(); listener.AddPrefix(urlPrefix); $"Web server prefix '{urlPrefix}' added.".Info(LogSource); } return listener; } } }