/* * Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.ServiceStack * File: HttpServiceStack.cs * * HttpServiceStack.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger * VNLib collection of libraries and utilities. * * VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. * * VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using VNLib.Utils; using VNLib.Net.Http; using VNLib.Utils.Logging; namespace VNLib.Plugins.Essentials.ServiceStack { /// /// An HTTP servicing stack that manages a collection of HTTP servers /// their service domain /// public sealed class HttpServiceStack : VnDisposeable { private readonly LinkedList _servers; private readonly ServiceDomain _serviceDomain; private readonly PluginManager _plugins; private CancellationTokenSource? _cts; private Task WaitForAllTask; /// /// A collection of all loaded servers /// public IReadOnlyCollection Servers => _servers; /// /// Gets the internal that manages plugins for the entire /// /// public IHttpPluginManager PluginManager => _plugins; /// /// Initializes a new that will /// generate servers to listen for services exposed by the /// specified host context /// internal HttpServiceStack(LinkedList servers, ServiceDomain serviceDomain, IPluginInitializer plugins) { _servers = servers; _serviceDomain = serviceDomain; _plugins = new(plugins); WaitForAllTask = Task.CompletedTask; } /// /// Starts all configured servers that observe a cancellation /// token to cancel /// /// The token to observe which may stop servers and cleanup the provider public void StartServers(CancellationToken parentToken = default) { Check(); //Init new linked cts to stop all servers if cancelled _cts = CancellationTokenSource.CreateLinkedTokenSource(parentToken); //Start all servers Task[] runners = _servers.Select(s => s.Start(_cts.Token)).ToArray(); //Check for failed startups Task? firstFault = runners.Where(static t => t.IsFaulted).FirstOrDefault(); //Raise first exception firstFault?.GetAwaiter().GetResult(); //Task that waits for all to exit then cleans up WaitForAllTask = Task.WhenAll(runners) .ContinueWith(OnAllServerExit, CancellationToken.None, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); } /// /// Loads all plugins into the service stack /// /// A log provider for writing loading logs to public void LoadPlugins(ILogProvider logProvider) => _plugins.LoadPlugins(logProvider); /// /// Stops listening on all configured servers and returns a task that completes /// when the service host has stopped all servers and unloaded resources /// /// The task that completes when public Task StopAndWaitAsync() { Check(); _cts?.Cancel(); return WaitForAllTask; } private void OnAllServerExit(Task allExit) { //Unload plugins _plugins.UnloadPlugins(); //Unload the hosts _serviceDomain.TearDown(); } /// protected override void Free() { _cts?.Dispose(); _plugins.Dispose(); //remove all lists _servers.Clear(); } } }