// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Http; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Server.Kestrel { public class KestrelServer : IServer { private Stack _disposables; private readonly IApplicationLifetime _applicationLifetime; private readonly ILogger _logger; private readonly IServerAddressesFeature _serverAddresses; public KestrelServer(IOptions options, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (applicationLifetime == null) { throw new ArgumentNullException(nameof(applicationLifetime)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } Options = options.Value ?? new KestrelServerOptions(); _applicationLifetime = applicationLifetime; _logger = loggerFactory.CreateLogger(typeof(KestrelServer).GetTypeInfo().Namespace); Features = new FeatureCollection(); var componentFactory = new HttpComponentFactory(Options); Features.Set(componentFactory); _serverAddresses = new ServerAddressesFeature(); Features.Set(_serverAddresses); } public IFeatureCollection Features { get; } public KestrelServerOptions Options { get; } public void Start(IHttpApplication application) { if (_disposables != null) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException("Server has already started."); } _disposables = new Stack(); try { var componentFactory = Features.Get(); var dateHeaderValueManager = new DateHeaderValueManager(); var trace = new KestrelTrace(_logger); var engine = new KestrelEngine(new ServiceContext { FrameFactory = context => { return new Frame(application, context); }, AppLifetime = _applicationLifetime, Log = trace, ThreadPool = new LoggingThreadPool(trace), DateHeaderValueManager = dateHeaderValueManager, ServerOptions = Options, HttpComponentFactory = componentFactory }); _disposables.Push(engine); _disposables.Push(dateHeaderValueManager); var threadCount = Options.ThreadCount; if (threadCount <= 0) { throw new ArgumentOutOfRangeException(nameof(threadCount), threadCount, "ThreadCount must be positive."); } engine.Start(threadCount); var atLeastOneListener = false; foreach (var address in _serverAddresses.Addresses) { var parsedAddress = ServerAddress.FromUrl(address); if (parsedAddress == null) { throw new FormatException("Unrecognized listening address: " + address); } else { atLeastOneListener = true; _disposables.Push(engine.CreateServer( parsedAddress)); } } if (!atLeastOneListener) { throw new InvalidOperationException("No recognized listening addresses were configured."); } } catch { Dispose(); throw; } } public void Dispose() { if (_disposables != null) { while (_disposables.Count > 0) { _disposables.Pop().Dispose(); } _disposables = null; } } } }