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