From 5e2be2d7b6138343f9fd7909d5d4d458646b7b6f Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 26 Dec 2019 18:08:50 -0800 Subject: [PATCH] LibUv Networking (#65) --- .gitignore | 21 + Contributing.md | 4 +- .../Scripts/Accounting/AccessRestrictions.cs | 4 +- Projects/Scripts/Misc/ClientVerification.cs | 4 +- Projects/Scripts/Misc/ServerList.cs | 11 +- Projects/Scripts/Misc/SocketOptions.cs | 16 - Projects/Scripts/Scripts.csproj | 3 + .../SpecialSystems/Engines/PreventInaccess.cs | 2 +- Projects/Server/ApplicationLifetime.cs | 108 ++++ Projects/Server/Assemblies/libuv.dll | Bin 0 -> 230912 bytes Projects/Server/Assemblies/libuv.linux.so | Bin 0 -> 165720 bytes Projects/Server/Assemblies/libuv.osx.so | Bin 0 -> 185132 bytes .../Server/Buffers/DiagnosticMemoryPool.cs | 132 ++++ .../Server/Buffers/DiagnosticPoolBlock.cs | 188 ++++++ Projects/Server/Buffers/MemoryPoolBlock.cs | 57 ++ Projects/Server/Buffers/MemoryPoolFactory.cs | 19 + Projects/Server/Buffers/MemoryPoolSlab.cs | 74 +++ .../Server/Buffers/MemoryPoolThrowHelper.cs | 107 +++ Projects/Server/Buffers/SlabMemoryPool.cs | 189 ++++++ Projects/Server/Buffers/SpanWriter.cs | 38 +- Projects/Server/EventSink.cs | 7 +- .../Server/Kestrel/CorrelationIdGenerator.cs | 43 ++ .../TransportConnection.FeatureCollection.cs | 44 ++ .../Kestrel/TransportConnection.Generated.cs | 248 +++++++ .../Server/Kestrel/TransportConnection.cs | 59 ++ Projects/Server/LibUv/IAsyncDisposable.cs | 12 + Projects/Server/LibUv/ILibuvTrace.cs | 29 + .../Server/LibUv/Internal/LibuvFunctions.cs | 612 ++++++++++++++++++ Projects/Server/LibUv/Internal/SockAddr.cs | 107 +++ .../Server/LibUv/Internal/UvAsyncHandle.cs | 72 +++ .../Server/LibUv/Internal/UvConnectRequest.cs | 75 +++ Projects/Server/LibUv/Internal/UvException.cs | 14 + Projects/Server/LibUv/Internal/UvHandle.cs | 66 ++ .../Server/LibUv/Internal/UvLoopHandle.cs | 54 ++ Projects/Server/LibUv/Internal/UvMemory.cs | 79 +++ .../Server/LibUv/Internal/UvPipeHandle.cs | 36 ++ Projects/Server/LibUv/Internal/UvRequest.cs | 31 + .../Server/LibUv/Internal/UvStreamHandle.cs | 151 +++++ Projects/Server/LibUv/Internal/UvTcpHandle.cs | 72 +++ .../Server/LibUv/Internal/UvTimerHandle.cs | 56 ++ Projects/Server/LibUv/Internal/UvWriteReq.cs | 237 +++++++ Projects/Server/LibUv/LibuvAwaitable.cs | 79 +++ Projects/Server/LibUv/LibuvConnection.cs | 309 +++++++++ .../Server/LibUv/LibuvConnectionListener.cs | 196 ++++++ Projects/Server/LibUv/LibuvConstants.cs | 98 +++ Projects/Server/LibUv/LibuvOutputConsumer.cs | 111 ++++ Projects/Server/LibUv/LibuvThread.cs | 437 +++++++++++++ Projects/Server/LibUv/LibuvTrace.cs | 94 +++ .../Server/LibUv/LibuvTransportContext.cs | 16 + .../Server/LibUv/LibuvTransportOptions.cs | 51 ++ Projects/Server/LibUv/Listener.cs | 205 ++++++ Projects/Server/LibUv/ListenerContext.cs | 168 +++++ Projects/Server/LibUv/ListenerPrimary.cs | 265 ++++++++ Projects/Server/LibUv/ListenerSecondary.cs | 192 ++++++ Projects/Server/LibUv/WriteReqPool.cs | 63 ++ Projects/Server/Main.cs | 22 +- Projects/Server/Mobile.cs | 2 +- Projects/Server/Network/Listener.cs | 114 ++-- Projects/Server/Network/MessagePump.cs | 21 +- Projects/Server/Network/NetState.cs | 190 ++---- Projects/Server/Network/PacketHandler.cs | 4 +- Projects/Server/Network/PacketHandlers.cs | 290 +-------- Projects/Server/Network/PacketReader.cs | 8 +- Projects/Server/Network/Packets.cs | 8 - Projects/Server/Network/SocketExtensions.cs | 42 +- .../Server/Network/StaticPacketHandlers.cs | 39 +- Projects/Server/Pipelines/DuplexPipe.cs | 42 ++ Projects/Server/Server.csproj | 32 +- .../SignalR/AsyncDisposableExtensions.cs | 19 + 69 files changed, 5612 insertions(+), 586 deletions(-) create mode 100644 Projects/Server/ApplicationLifetime.cs create mode 100644 Projects/Server/Assemblies/libuv.dll create mode 100755 Projects/Server/Assemblies/libuv.linux.so create mode 100755 Projects/Server/Assemblies/libuv.osx.so create mode 100644 Projects/Server/Buffers/DiagnosticMemoryPool.cs create mode 100644 Projects/Server/Buffers/DiagnosticPoolBlock.cs create mode 100644 Projects/Server/Buffers/MemoryPoolBlock.cs create mode 100644 Projects/Server/Buffers/MemoryPoolFactory.cs create mode 100644 Projects/Server/Buffers/MemoryPoolSlab.cs create mode 100644 Projects/Server/Buffers/MemoryPoolThrowHelper.cs create mode 100644 Projects/Server/Buffers/SlabMemoryPool.cs create mode 100644 Projects/Server/Kestrel/CorrelationIdGenerator.cs create mode 100644 Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs create mode 100644 Projects/Server/Kestrel/TransportConnection.Generated.cs create mode 100644 Projects/Server/Kestrel/TransportConnection.cs create mode 100644 Projects/Server/LibUv/IAsyncDisposable.cs create mode 100644 Projects/Server/LibUv/ILibuvTrace.cs create mode 100644 Projects/Server/LibUv/Internal/LibuvFunctions.cs create mode 100644 Projects/Server/LibUv/Internal/SockAddr.cs create mode 100644 Projects/Server/LibUv/Internal/UvAsyncHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvConnectRequest.cs create mode 100644 Projects/Server/LibUv/Internal/UvException.cs create mode 100644 Projects/Server/LibUv/Internal/UvHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvLoopHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvMemory.cs create mode 100644 Projects/Server/LibUv/Internal/UvPipeHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvRequest.cs create mode 100644 Projects/Server/LibUv/Internal/UvStreamHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvTcpHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvTimerHandle.cs create mode 100644 Projects/Server/LibUv/Internal/UvWriteReq.cs create mode 100644 Projects/Server/LibUv/LibuvAwaitable.cs create mode 100644 Projects/Server/LibUv/LibuvConnection.cs create mode 100644 Projects/Server/LibUv/LibuvConnectionListener.cs create mode 100644 Projects/Server/LibUv/LibuvConstants.cs create mode 100644 Projects/Server/LibUv/LibuvOutputConsumer.cs create mode 100644 Projects/Server/LibUv/LibuvThread.cs create mode 100644 Projects/Server/LibUv/LibuvTrace.cs create mode 100644 Projects/Server/LibUv/LibuvTransportContext.cs create mode 100644 Projects/Server/LibUv/LibuvTransportOptions.cs create mode 100644 Projects/Server/LibUv/Listener.cs create mode 100644 Projects/Server/LibUv/ListenerContext.cs create mode 100644 Projects/Server/LibUv/ListenerPrimary.cs create mode 100644 Projects/Server/LibUv/ListenerSecondary.cs create mode 100644 Projects/Server/LibUv/WriteReqPool.cs create mode 100644 Projects/Server/Pipelines/DuplexPipe.cs create mode 100644 Projects/Server/SignalR/AsyncDisposableExtensions.cs diff --git a/.gitignore b/.gitignore index a595e413a..6d4d9c510 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,27 @@ /Distribution/rdrand.so /Distribution/rdrand.dll /Distribution/zlib.dll + +# LibUv Dependencies +/Distribution/libuv.dll +/Distribution/libuv.so +/Distribution/Microsoft.AspNetCore.Connections.Abstractions.dll +/Distribution/Microsoft.AspNetCore.Http.Features.dll +/Distribution/Microsoft.Extensions.Configuration.Abstractions.dll +/Distribution/Microsoft.Extensions.Configuration.Binder.dll +/Distribution/Microsoft.Extensions.Configuration.dll +/Distribution/Microsoft.Extensions.DependencyInjection.Abstractions.dll +/Distribution/Microsoft.Extensions.DependencyInjection.dll +/Distribution/Microsoft.Extensions.FileProviders.Abstractions.dll +/Distribution/Microsoft.Extensions.Hosting.Abstractions.dll +/Distribution/Microsoft.Extensions.Logging.Abstractions.dll +/Distribution/Microsoft.Extensions.Logging.Configuration.dll +/Distribution/Microsoft.Extensions.Logging.Console.dll +/Distribution/Microsoft.Extensions.Logging.dll +/Distribution/Microsoft.Extensions.Options.ConfigurationExtensions.dll +/Distribution/Microsoft.Extensions.Options.dll +/Distribution/Microsoft.Extensions.Primitives.dll + /Projects/Scripts/obj /Projects/Scripts/bin /Projects/Server/obj diff --git a/Contributing.md b/Contributing.md index 6a4faf938..64b88cc70 100644 --- a/Contributing.md +++ b/Contributing.md @@ -6,7 +6,7 @@ One of the easiest ways to contribute is to participate in discussions on GitHub Start a discussion on the [repository issue tracker](https://github.com/modernuo/modernuo/issues). ## Reporting security issues and bugs -Security issues and bugs should be reported privately, via email, to the hi@modernuo.com. +Security issues and bugs should be reported privately, via email, to hi@modernuo.com. You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. @@ -35,7 +35,7 @@ If you don't know what a pull request is read this article: https://help.github. configurations, environment variables, dependencies, etc. 1. Increase the version numbers to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -1. You may merge the Pull Request in once you have the sign-off of two other developers, or if you +1. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. ## Code of Conduct diff --git a/Projects/Scripts/Accounting/AccessRestrictions.cs b/Projects/Scripts/Accounting/AccessRestrictions.cs index ee40c4d10..49389abb8 100644 --- a/Projects/Scripts/Accounting/AccessRestrictions.cs +++ b/Projects/Scripts/Accounting/AccessRestrictions.cs @@ -16,7 +16,7 @@ namespace Server { try { - IPAddress ip = ((IPEndPoint)e.Socket.RemoteEndPoint).Address; + IPAddress ip = ((IPEndPoint)e.Context.RemoteEndPoint).Address; if (Firewall.IsBlocked(ip)) { @@ -43,4 +43,4 @@ namespace Server } } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Misc/ClientVerification.cs b/Projects/Scripts/Misc/ClientVerification.cs index ea24ae0e5..eacc7aa6d 100644 --- a/Projects/Scripts/Misc/ClientVerification.cs +++ b/Projects/Scripts/Misc/ClientVerification.cs @@ -108,7 +108,7 @@ namespace Server.Misc Timer.DelayCall(KickDelay, delegate { - if (state.Socket != null) + if (state.Connection != null) { Console.WriteLine("Client: {0}: Disconnecting, bad version", state); state.Dispose(); @@ -173,4 +173,4 @@ namespace Server.Misc Kick } } -} \ No newline at end of file +} diff --git a/Projects/Scripts/Misc/ServerList.cs b/Projects/Scripts/Misc/ServerList.cs index 469fc8d84..86a6ea029 100644 --- a/Projects/Scripts/Misc/ServerList.cs +++ b/Projects/Scripts/Misc/ServerList.cs @@ -3,24 +3,25 @@ using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; +using Microsoft.AspNetCore.Connections; using Server.Network; namespace Server.Misc { public class ServerList { - /* + /* * The default setting for Address, a value of 'null', will use your local IP address. If all of your local IP addresses * are private network addresses and AutoDetect is 'true' then RunUO will attempt to discover your public IP address * for you automatically. * * If you do not plan on allowing clients outside of your LAN to connect, you can set AutoDetect to 'false' and leave * Address set to 'null'. - * + * * If your public IP address cannot be determined, you must change the value of Address to your public IP address * manually to allow clients outside of your LAN to connect to your server. Address can be either an IP address or * a hostname that will be resolved when RunUO starts. - * + * * If you want players outside your LAN to be able to connect to your server and you are behind a router, you must also * forward TCP port 2593 to your private IP address. The procedure for doing this varies by manufacturer but generally * involves configuration of the router through your web browser. @@ -32,7 +33,7 @@ namespace Server.Misc * properly and fully supports listening on multiple ports. If a client with a public IP address is connecting to a * locally private address, the server will direct the client to either the AutoDetected IP address or the manually entered * IP address or hostname, whichever is applicable. Loopback clients will be directed to loopback. - * + * * If you would like to listen on additional ports (i.e. 22, 23, 80, for clients behind highly restrictive egress * firewalls) or specific IP adddresses you can do so by modifying the file SocketOptions.cs found in this directory. */ @@ -64,7 +65,7 @@ namespace Server.Misc try { NetState ns = e.State; - Socket s = ns.Socket; + ConnectionContext s = ns.Connection; IPEndPoint ipep = (IPEndPoint)s.LocalEndPoint; diff --git a/Projects/Scripts/Misc/SocketOptions.cs b/Projects/Scripts/Misc/SocketOptions.cs index d51fb20e5..56d12f2d7 100644 --- a/Projects/Scripts/Misc/SocketOptions.cs +++ b/Projects/Scripts/Misc/SocketOptions.cs @@ -5,8 +5,6 @@ namespace Server { public class SocketOptions { - private const bool NagleEnabled = false; // Should the Nagle algorithm be enabled? This may reduce performance - private static IPEndPoint[] m_ListenerEndPoints = { new IPEndPoint(IPAddress.Any, 2593) // Default: Listen on port 2593 on all IP addresses @@ -16,24 +14,10 @@ namespace Server // new IPEndPoint( IPAddress.Parse( "1.2.3.4" ), 2593 ), // Listen on port 2593 on IP address 1.2.3.4 }; - public static void Initialize() - { - EventSink.SocketConnect += EventSink_SocketConnect; - } - public static void RegisterListeners() { for (int i = 0; i < m_ListenerEndPoints.Length; i++) Core.MessagePump.AddListener(m_ListenerEndPoints[i]); } - - private static void EventSink_SocketConnect(SocketConnectEventArgs e) - { - if (!e.AllowConnection) - return; - - if (!NagleEnabled) - e.Socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); // RunUO uses its own algorithm - } } } diff --git a/Projects/Scripts/Scripts.csproj b/Projects/Scripts/Scripts.csproj index f3b49987e..e7fa412b8 100644 --- a/Projects/Scripts/Scripts.csproj +++ b/Projects/Scripts/Scripts.csproj @@ -35,4 +35,7 @@ false + + + diff --git a/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs b/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs index 8d81d45fb..fcde69079 100644 --- a/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs +++ b/Projects/Scripts/SpecialSystems/Engines/PreventInaccess.cs @@ -63,7 +63,7 @@ namespace Server.Misc } } - private static bool HasDisconnected(Mobile m) => m.NetState?.Socket == null; + private static bool HasDisconnected(Mobile m) => m.NetState?.Connection == null; private static LocationInfo GetRandomDestination() => m_Destinations[Utility.Random(m_Destinations.Length)]; diff --git a/Projects/Server/ApplicationLifetime.cs b/Projects/Server/ApplicationLifetime.cs new file mode 100644 index 000000000..314cdcdea --- /dev/null +++ b/Projects/Server/ApplicationLifetime.cs @@ -0,0 +1,108 @@ +// 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.Threading; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Hosting +{ + /// + /// Allows consumers to perform cleanup during a graceful shutdown. + /// + internal class ApplicationLifetime : IHostApplicationLifetime + { + private readonly CancellationTokenSource _startedSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _stoppingSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _stoppedSource = new CancellationTokenSource(); + private readonly ILogger _logger; + + public ApplicationLifetime(ILogger logger) => _logger = logger; + + /// + /// Triggered when the application host has fully started and is about to wait + /// for a graceful shutdown. + /// + public CancellationToken ApplicationStarted => _startedSource.Token; + + /// + /// Triggered when the application host is performing a graceful shutdown. + /// Request may still be in flight. Shutdown will block until this event completes. + /// + public CancellationToken ApplicationStopping => _stoppingSource.Token; + + /// + /// Triggered when the application host is performing a graceful shutdown. + /// All requests should be complete at this point. Shutdown will block + /// until this event completes. + /// + public CancellationToken ApplicationStopped => _stoppedSource.Token; + + /// + /// Signals the ApplicationStopping event and blocks until it completes. + /// + public void StopApplication() + { + // Lock on CTS to synchronize multiple calls to StopApplication. This guarantees that the first call + // to StopApplication and its callbacks run to completion before subsequent calls to StopApplication, + // which will no-op since the first call already requested cancellation, get a chance to execute. + lock (_stoppingSource) + { + try + { + ExecuteHandlers(_stoppingSource); + } + catch (Exception) + { + /* _logger.ApplicationError(LoggerEventIds.ApplicationStoppingException, + "An error occurred stopping the application", + ex);*/ + } + } + } + + /// + /// Signals the ApplicationStarted event and blocks until it completes. + /// + public void NotifyStarted() + { + try + { + ExecuteHandlers(_startedSource); + } + catch (Exception) + { + /* _logger.ApplicationError(LoggerEventIds.ApplicationStartupException, + "An error occurred starting the application", + ex);*/ + } + } + + /// + /// Signals the ApplicationStopped event and blocks until it completes. + /// + public void NotifyStopped() + { + try + { + ExecuteHandlers(_stoppedSource); + } + catch (Exception) + { + /* _logger.ApplicationError(LoggerEventIds.ApplicationStoppedException, + "An error occurred stopping the application", + ex);*/ + } + } + + private void ExecuteHandlers(CancellationTokenSource cancel) + { + // Noop if this is already cancelled + if (cancel.IsCancellationRequested) return; + + // Run the cancellation token callbacks + cancel.Cancel(false); + } + } +} diff --git a/Projects/Server/Assemblies/libuv.dll b/Projects/Server/Assemblies/libuv.dll new file mode 100644 index 0000000000000000000000000000000000000000..d744f343de78be93ae40cdcbc4406966a3a02d11 GIT binary patch literal 230912 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 literal 0 HcmV?d00001 diff --git a/Projects/Server/Buffers/DiagnosticMemoryPool.cs b/Projects/Server/Buffers/DiagnosticMemoryPool.cs new file mode 100644 index 000000000..9f032a571 --- /dev/null +++ b/Projects/Server/Buffers/DiagnosticMemoryPool.cs @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace System.Buffers +{ + /// + /// Used to allocate and distribute re-usable blocks of memory. + /// + public class DiagnosticMemoryPool : MemoryPool + { + private readonly MemoryPool _pool; + + private readonly bool _allowLateReturn; + + private readonly bool _rentTracking; + + private readonly object _syncObj; + + private readonly HashSet _blocks; + + private readonly List _blockAccessExceptions; + + private readonly TaskCompletionSource _allBlocksReturned; + + private int _totalBlocks; + + /// + /// This default value passed in to Rent to use the default value for the pool. + /// + private const int AnySize = -1; + + public DiagnosticMemoryPool(MemoryPool pool, bool allowLateReturn = false, bool rentTracking = false) + { + _pool = pool; + _allowLateReturn = allowLateReturn; + _rentTracking = rentTracking; + _blocks = new HashSet(); + _syncObj = new object(); + _allBlocksReturned = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + _blockAccessExceptions = new List(); + } + + public bool IsDisposed { get; private set; } + + public override IMemoryOwner Rent(int size = AnySize) + { + lock (_syncObj) + { + if (IsDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPool); + + var diagnosticPoolBlock = new DiagnosticPoolBlock(this, _pool.Rent(size)); + if (_rentTracking) diagnosticPoolBlock.Track(); + _totalBlocks++; + _blocks.Add(diagnosticPoolBlock); + return diagnosticPoolBlock; + } + } + + public override int MaxBufferSize => _pool.MaxBufferSize; + + internal void Return(DiagnosticPoolBlock block) + { + bool returnedAllBlocks; + lock (_syncObj) + { + _blocks.Remove(block); + returnedAllBlocks = _blocks.Count == 0; + } + + if (IsDisposed) + { + if (!_allowLateReturn) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockReturnedToDisposedPool(block); + + if (returnedAllBlocks) SetAllBlocksReturned(); + } + + } + + internal void ReportException(Exception exception) + { + lock (_syncObj) + { + _blockAccessExceptions.Add(exception); + } + } + + protected override void Dispose(bool disposing) + { + if (IsDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_DoubleDispose(); + + bool allBlocksReturned = false; + try + { + lock (_syncObj) + { + IsDisposed = true; + allBlocksReturned = _blocks.Count == 0; + if (!allBlocksReturned && !_allowLateReturn) MemoryPoolThrowHelper.ThrowInvalidOperationException_DisposingPoolWithActiveBlocks(_totalBlocks - _blocks.Count, _totalBlocks, _blocks.ToArray()); + + if (_blockAccessExceptions.Any()) throw CreateAccessExceptions(); + } + } + finally + { + if (allBlocksReturned) SetAllBlocksReturned(); + } + } + + private void SetAllBlocksReturned() + { + if (_blockAccessExceptions.Any()) + _allBlocksReturned.SetException(CreateAccessExceptions()); + else + _allBlocksReturned.SetResult(null); + } + + private AggregateException CreateAccessExceptions() => new AggregateException("Exceptions occurred while accessing blocks", _blockAccessExceptions.ToArray()); + + public async Task WhenAllBlocksReturnedAsync(TimeSpan timeout) + { + var task = await Task.WhenAny(_allBlocksReturned.Task, Task.Delay(timeout)); + if (task != _allBlocksReturned.Task) + MemoryPoolThrowHelper.ThrowInvalidOperationException_BlocksWereNotReturnedInTime(_totalBlocks - _blocks.Count, _totalBlocks, _blocks.ToArray()); + + await task; + } + } +} diff --git a/Projects/Server/Buffers/DiagnosticPoolBlock.cs b/Projects/Server/Buffers/DiagnosticPoolBlock.cs new file mode 100644 index 000000000..9623596f8 --- /dev/null +++ b/Projects/Server/Buffers/DiagnosticPoolBlock.cs @@ -0,0 +1,188 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Threading; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + /// + /// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The + /// individual blocks are then treated as independent array segments. + /// + public sealed class DiagnosticPoolBlock : MemoryManager + { + /// + /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. + /// + private readonly DiagnosticMemoryPool _pool; + + private readonly IMemoryOwner _memoryOwner; + private MemoryHandle? _memoryHandle; + private Memory _memory; + + private readonly object _syncObj = new object(); + private bool _isDisposed; + private int _pinCount; + + + /// + /// This object cannot be instantiated outside of the static Create method + /// + internal DiagnosticPoolBlock(DiagnosticMemoryPool pool, IMemoryOwner memoryOwner) + { + _pool = pool; + _memoryOwner = memoryOwner; + _memory = memoryOwner.Memory; + } + + public override Memory Memory + { + get + { + try + { + lock (_syncObj) + { + if (_isDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); + + if (_pool.IsDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); + + return CreateMemory(_memory.Length); + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + } + + protected override void Dispose(bool disposing) + { + try + { + lock (_syncObj) + { + if (Volatile.Read(ref _pinCount) > 0) MemoryPoolThrowHelper.ThrowInvalidOperationException_ReturningPinnedBlock(this); + + if (_isDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockDoubleDispose(this); + + _memoryOwner.Dispose(); + + _pool.Return(this); + + _isDisposed = true; + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + + public override Span GetSpan() + { + try + { + lock (_syncObj) + { + if (_isDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); + + if (_pool.IsDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); + + return _memory.Span; + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + + public override MemoryHandle Pin(int byteOffset = 0) + { + try + { + lock (_syncObj) + { + if (_isDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); + + if (_pool.IsDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); + + if (byteOffset < 0 || byteOffset > _memory.Length) MemoryPoolThrowHelper.ThrowArgumentOutOfRangeException(_memory.Length, byteOffset); + + _pinCount++; + + _memoryHandle ??= _memory.Pin(); + + unsafe + { + return new MemoryHandle(((IntPtr)_memoryHandle.Value.Pointer + byteOffset).ToPointer(), default, this); + } + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + + protected override bool TryGetArray(out ArraySegment segment) + { + try + { + lock (_syncObj) + { + if (_isDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPoolBlock); + + if (_pool.IsDisposed) MemoryPoolThrowHelper.ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(this); + + return MemoryMarshal.TryGetArray(_memory, out segment); + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + + public override void Unpin() + { + try + { + lock (_syncObj) + { + if (_pinCount == 0) MemoryPoolThrowHelper.ThrowInvalidOperationException_PinCountZero(this); + + _pinCount--; + + if (_pinCount == 0) + { + Debug.Assert(_memoryHandle.HasValue); + _memoryHandle.Value.Dispose(); + _memoryHandle = null; + } + } + } + catch (Exception exception) + { + _pool.ReportException(exception); + throw; + } + } + + public StackTrace Leaser { get; set; } + + public void Track() + { + Leaser = new StackTrace(false); + } + } +} diff --git a/Projects/Server/Buffers/MemoryPoolBlock.cs b/Projects/Server/Buffers/MemoryPoolBlock.cs new file mode 100644 index 000000000..2f1b87e4c --- /dev/null +++ b/Projects/Server/Buffers/MemoryPoolBlock.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + /// + /// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The + /// individual blocks are then treated as independent array segments. + /// + public sealed class MemoryPoolBlock : IMemoryOwner + { + private readonly int _offset; + private readonly int _length; + + /// + /// This object cannot be instantiated outside of the static Create method + /// + internal MemoryPoolBlock(SlabMemoryPool pool, MemoryPoolSlab slab, int offset, int length) + { + _offset = offset; + _length = length; + + Pool = pool; + Slab = slab; + + Memory = MemoryMarshal.CreateFromPinnedArray(slab.Array, _offset, _length); + } + + /// + /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. + /// + public SlabMemoryPool Pool { get; } + + /// + /// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory. + /// + public MemoryPoolSlab Slab { get; } + + public Memory Memory { get; } + + ~MemoryPoolBlock() + { + Pool.RefreshBlock(Slab, _offset, _length); + } + + public void Dispose() + { + Pool.Return(this); + } + + public void Lease() + { + } + } +} diff --git a/Projects/Server/Buffers/MemoryPoolFactory.cs b/Projects/Server/Buffers/MemoryPoolFactory.cs new file mode 100644 index 000000000..f8a3f37c5 --- /dev/null +++ b/Projects/Server/Buffers/MemoryPoolFactory.cs @@ -0,0 +1,19 @@ +// 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. + +namespace System.Buffers +{ + public static class SlabMemoryPoolFactory + { + public static MemoryPool Create() + { +#if DEBUG + return new DiagnosticMemoryPool(CreateSlabMemoryPool()); +#else + return CreateSlabMemoryPool(); +#endif + } + + public static MemoryPool CreateSlabMemoryPool() => new SlabMemoryPool(); + } +} diff --git a/Projects/Server/Buffers/MemoryPoolSlab.cs b/Projects/Server/Buffers/MemoryPoolSlab.cs new file mode 100644 index 000000000..38be52e5b --- /dev/null +++ b/Projects/Server/Buffers/MemoryPoolSlab.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +namespace System.Buffers +{ + /// + /// Slab tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The + /// individual blocks are then treated as independent array segments. + /// + public class MemoryPoolSlab : IDisposable + { + /// + /// This handle pins the managed array in memory until the slab is disposed. This prevents it from being + /// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls. + /// + private GCHandle _gcHandle; + private bool _isDisposed; + + public MemoryPoolSlab(byte[] data) + { + Array = data; + _gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned); + NativePointer = _gcHandle.AddrOfPinnedObject(); + } + + /// + /// True as long as the blocks from this slab are to be considered returnable to the pool. In order to shrink the + /// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the + /// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will + /// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage + /// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will + /// be unpinned by the slab's Dispose. + /// + public bool IsActive => !_isDisposed; + + public IntPtr NativePointer { get; private set; } + + public byte[] Array { get; private set; } + + public static MemoryPoolSlab Create(int length) + { + // allocate and pin requested memory length + var array = new byte[length]; + + // allocate and return slab tracking object + return new MemoryPoolSlab(array); + } + + protected void Dispose(bool disposing) + { + if (_isDisposed) return; + + _isDisposed = true; + + Array = null; + NativePointer = IntPtr.Zero;; + + if (_gcHandle.IsAllocated) _gcHandle.Free(); + } + + ~MemoryPoolSlab() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/Projects/Server/Buffers/MemoryPoolThrowHelper.cs b/Projects/Server/Buffers/MemoryPoolThrowHelper.cs new file mode 100644 index 000000000..cf6284148 --- /dev/null +++ b/Projects/Server/Buffers/MemoryPoolThrowHelper.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace System.Buffers +{ + public class MemoryPoolThrowHelper + { + public static void ThrowArgumentOutOfRangeException(int sourceLength, int offset) + { + throw GetArgumentOutOfRangeException(sourceLength, offset); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(int sourceLength, int offset) => + (uint)offset > (uint)sourceLength ? new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.offset)) : new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.length)); + public static void ThrowInvalidOperationException_PinCountZero(DiagnosticPoolBlock block) + { + throw new InvalidOperationException(GenerateMessage("Can't unpin, pin count is zero", block)); + } + + public static void ThrowInvalidOperationException_ReturningPinnedBlock(DiagnosticPoolBlock block) + { + throw new InvalidOperationException(GenerateMessage("Disposing pinned block", block)); + } + + public static void ThrowInvalidOperationException_DoubleDispose() + { + throw new InvalidOperationException("Object is being disposed twice"); + } + + public static void ThrowInvalidOperationException_BlockDoubleDispose(DiagnosticPoolBlock block) + { + throw new InvalidOperationException("Block is being disposed twice"); + } + + public static void ThrowInvalidOperationException_BlockReturnedToDisposedPool(DiagnosticPoolBlock block) + { + throw new InvalidOperationException(GenerateMessage("Block is being returned to disposed pool", block)); + } + + public static void ThrowInvalidOperationException_BlockIsBackedByDisposedSlab(DiagnosticPoolBlock block) + { + throw new InvalidOperationException(GenerateMessage("Block is backed by disposed slab", block)); + } + + public static void ThrowInvalidOperationException_DisposingPoolWithActiveBlocks(int returned, int total, DiagnosticPoolBlock[] blocks) + { + throw new InvalidOperationException(GenerateMessage($"Memory pool with active blocks is being disposed, {returned} of {total} returned", blocks)); + } + + public static void ThrowInvalidOperationException_BlocksWereNotReturnedInTime(int returned, int total, DiagnosticPoolBlock[] blocks) + { + throw new InvalidOperationException(GenerateMessage($"Blocks were not returned in time, {returned} of {total} returned ", blocks)); + } + + private static string GenerateMessage(string message, params DiagnosticPoolBlock[] blocks) + { + StringBuilder builder = new StringBuilder(message); + foreach (var diagnosticPoolBlock in blocks) + if (diagnosticPoolBlock.Leaser != null) + { + builder.AppendLine(); + + builder.AppendLine("Block leased from:"); + builder.AppendLine(diagnosticPoolBlock.Leaser.ToString()); + } + + return builder.ToString(); + } + + public static void ThrowArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize) + { + throw GetArgumentOutOfRangeException_BufferRequestTooLarge(maxSize); + } + + public static void ThrowObjectDisposedException(ExceptionArgument argument) + { + throw GetObjectDisposedException(argument); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ArgumentOutOfRangeException GetArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize) => new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.size), $"Cannot allocate more than {maxSize} bytes in a single buffer"); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ObjectDisposedException GetObjectDisposedException(ExceptionArgument argument) => new ObjectDisposedException(GetArgumentName(argument)); + + private static string GetArgumentName(ExceptionArgument argument) + { + Debug.Assert(Enum.IsDefined(typeof(ExceptionArgument), argument), "The enum value is not defined, please check the ExceptionArgument Enum."); + + return argument.ToString(); + } + + public enum ExceptionArgument + { + size, + offset, + length, + MemoryPoolBlock, + MemoryPool + } + } +} diff --git a/Projects/Server/Buffers/SlabMemoryPool.cs b/Projects/Server/Buffers/SlabMemoryPool.cs new file mode 100644 index 000000000..acde6b894 --- /dev/null +++ b/Projects/Server/Buffers/SlabMemoryPool.cs @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading; + +namespace System.Buffers +{ + /// + /// Used to allocate and distribute re-usable blocks of memory. + /// + public sealed class SlabMemoryPool : MemoryPool + { + /// + /// The size of a block. 4096 is chosen because most operating systems use 4k pages. + /// + private const int _blockSize = 4096; + + /// + /// Allocating 32 contiguous blocks per slab makes the slab size 128k. This is larger than the 85k size which will place the memory + /// in the large object heap. This means the GC will not try to relocate this array, so the fact it remains pinned does not negatively + /// affect memory management's compactification. + /// + private const int _blockCount = 32; + + /// + /// Max allocation block size for pooled blocks, + /// larger values can be leased but they will be disposed after use rather than returned to the pool. + /// + public override int MaxBufferSize { get; } = _blockSize; + + /// + /// The size of a block. 4096 is chosen because most operating systems use 4k pages. + /// + public static int BlockSize => _blockSize; + + /// + /// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab + /// + private static readonly int _slabLength = _blockSize * _blockCount; + + /// + /// Thread-safe collection of blocks which are currently in the pool. A slab will pre-allocate all of the block tracking objects + /// and add them to this collection. When memory is requested it is taken from here first, and when it is returned it is re-added. + /// + private readonly ConcurrentQueue _blocks = new ConcurrentQueue(); + + /// + /// Thread-safe collection of slabs which have been allocated by this pool. As long as a slab is in this collection and slab.IsActive, + /// the blocks will be added to _blocks when returned. + /// + private readonly ConcurrentStack _slabs = new ConcurrentStack(); + + /// + /// This is part of implementing the IDisposable pattern. + /// + private bool _isDisposed; // To detect redundant calls + + private int _totalAllocatedBlocks; + + private readonly object _disposeSync = new object(); + + /// + /// This default value passed in to Rent to use the default value for the pool. + /// + private const int AnySize = -1; + + public override IMemoryOwner Rent(int size = AnySize) + { + if (size > _blockSize) MemoryPoolThrowHelper.ThrowArgumentOutOfRangeException_BufferRequestTooLarge(_blockSize); + + var block = Lease(); + return block; + } + + /// + /// Called to take a block from the pool. + /// + /// The block that is reserved for the called. It must be passed to Return when it is no longer being used. + private MemoryPoolBlock Lease() + { + if (_isDisposed) MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPool); + + if (_blocks.TryDequeue(out MemoryPoolBlock block)) + { + // block successfully taken from the stack - return it + + block.Lease(); + return block; + } + // no blocks available - grow the pool + block = AllocateSlab(); + block.Lease(); + return block; + } + + /// + /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the + /// block tracking objects, and adds them all to the pool. + /// + private MemoryPoolBlock AllocateSlab() + { + var slab = MemoryPoolSlab.Create(_slabLength); + _slabs.Push(slab); + + var basePtr = slab.NativePointer; + // Page align the blocks + var offset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr); + // Ensure page aligned + Debug.Assert(((ulong)basePtr + (uint)offset) % _blockSize == 0); + + var blockCount = (_slabLength - offset) / _blockSize; + Interlocked.Add(ref _totalAllocatedBlocks, blockCount); + + MemoryPoolBlock block = null; + + for (int i = 0; i < blockCount; i++) + { + block = new MemoryPoolBlock(this, slab, offset, _blockSize); + + if (i != blockCount - 1) // last block + { +#if BLOCK_LEASE_TRACKING + block.IsLeased = true; +#endif + Return(block); + } + + offset += _blockSize; + } + + return block; + } + + /// + /// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and + /// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the + /// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and return + /// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to avoid + /// leaving "dead zones" in the slab due to lost block tracking objects. + /// + /// The block to return. It must have been acquired by calling Lease on the same memory pool instance. + internal void Return(MemoryPoolBlock block) + { +#if BLOCK_LEASE_TRACKING + Debug.Assert(block.Pool == this, "Returned block was not leased from this pool"); + Debug.Assert(block.IsLeased, $"Block being returned to pool twice: {block.Leaser}{Environment.NewLine}"); + block.IsLeased = false; +#endif + + if (!_isDisposed) + _blocks.Enqueue(block); + else + GC.SuppressFinalize(block); + } + + // This method can ONLY be called from the finalizer of MemoryPoolBlock + internal void RefreshBlock(MemoryPoolSlab slab, int offset, int length) + { + lock (_disposeSync) + { + if (!_isDisposed && slab?.IsActive == true) + // Need to make a new object because this one is being finalized + // Note, this must be called within the _disposeSync lock because the block + // could be disposed at the same time as the finalizer. + Return(new MemoryPoolBlock(this, slab, offset, length)); + } + } + + protected override void Dispose(bool disposing) + { + if (_isDisposed) return; + + lock (_disposeSync) + { + _isDisposed = true; + + if (disposing) + while (_slabs.TryPop(out MemoryPoolSlab slab)) + // dispose managed state (managed objects). + slab.Dispose(); + + // Discard blocks in pool + while (_blocks.TryDequeue(out MemoryPoolBlock block)) GC.SuppressFinalize(block); + } + } + } +} diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index 8634d9e75..4103f462d 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -1,22 +1,22 @@ -/*************************************************************************** - * SpanWriter.cs - * ------------------- - * begin : August 5, 2019 - * copyright : (C) The ModernUO Team - * email : hi@modernuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: SpanWriter.cs - Created: 2019/08/05 - Updated: 2019/12/24 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ using System; using System.Text; diff --git a/Projects/Server/EventSink.cs b/Projects/Server/EventSink.cs index 0d12f3433..f93416e65 100644 --- a/Projects/Server/EventSink.cs +++ b/Projects/Server/EventSink.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; +using Microsoft.AspNetCore.Connections; using Server.Accounting; using Server.Guilds; using Server.Network; @@ -380,13 +381,13 @@ namespace Server public class SocketConnectEventArgs : EventArgs { - public SocketConnectEventArgs(Socket s) + public SocketConnectEventArgs(ConnectionContext c) { - Socket = s; + Context = c; AllowConnection = true; } - public Socket Socket{ get; } + public ConnectionContext Context{ get; } public bool AllowConnection{ get; set; } } diff --git a/Projects/Server/Kestrel/CorrelationIdGenerator.cs b/Projects/Server/Kestrel/CorrelationIdGenerator.cs new file mode 100644 index 000000000..1be328db1 --- /dev/null +++ b/Projects/Server/Kestrel/CorrelationIdGenerator.cs @@ -0,0 +1,43 @@ +// 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.Threading; + +namespace Microsoft.AspNetCore.Connections +{ + public static class CorrelationIdGenerator + { + // Base32 encoding - in ascii sort order for easy text based sorting + private static readonly char[] s_encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV".ToCharArray(); + + // Seed the _lastConnectionId for this application instance with + // the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 + // for a roughly increasing _lastId over restarts + private static long _lastId = DateTime.UtcNow.Ticks; + + public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId)); + + private static string GenerateId(long id) + { + return string.Create(13, id, (buffer, value) => + { + char[] encode32Chars = s_encode32Chars; + + buffer[12] = encode32Chars[value & 31]; + buffer[11] = encode32Chars[(value >> 5) & 31]; + buffer[10] = encode32Chars[(value >> 10) & 31]; + buffer[9] = encode32Chars[(value >> 15) & 31]; + buffer[8] = encode32Chars[(value >> 20) & 31]; + buffer[7] = encode32Chars[(value >> 25) & 31]; + buffer[6] = encode32Chars[(value >> 30) & 31]; + buffer[5] = encode32Chars[(value >> 35) & 31]; + buffer[4] = encode32Chars[(value >> 40) & 31]; + buffer[3] = encode32Chars[(value >> 45) & 31]; + buffer[2] = encode32Chars[(value >> 50) & 31]; + buffer[1] = encode32Chars[(value >> 55) & 31]; + buffer[0] = encode32Chars[(value >> 60) & 31]; + }); + } + } +} diff --git a/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs b/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs new file mode 100644 index 000000000..70033b7c9 --- /dev/null +++ b/Projects/Server/Kestrel/TransportConnection.FeatureCollection.cs @@ -0,0 +1,44 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Threading; +using Microsoft.AspNetCore.Connections.Features; + +namespace Microsoft.AspNetCore.Connections +{ + public partial class TransportConnection : IConnectionIdFeature, + IConnectionTransportFeature, + IConnectionItemsFeature, + IMemoryPoolFeature, + IConnectionLifetimeFeature + { + // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, + // then the list of `features` in the generated code project MUST also be updated. + // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs + + MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; + + IDuplexPipe IConnectionTransportFeature.Transport + { + get => Transport; + set => Transport = value; + } + + IDictionary IConnectionItemsFeature.Items + { + get => Items; + set => Items = value; + } + + CancellationToken IConnectionLifetimeFeature.ConnectionClosed + { + get => ConnectionClosed; + set => ConnectionClosed = value; + } + + void IConnectionLifetimeFeature.Abort() => Abort(new ConnectionAbortedException("The connection was aborted by the application via IConnectionLifetimeFeature.Abort().")); + } +} diff --git a/Projects/Server/Kestrel/TransportConnection.Generated.cs b/Projects/Server/Kestrel/TransportConnection.Generated.cs new file mode 100644 index 000000000..eb6f2ba25 --- /dev/null +++ b/Projects/Server/Kestrel/TransportConnection.Generated.cs @@ -0,0 +1,248 @@ +// 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; +using System.Collections.Generic; + +using Microsoft.AspNetCore.Connections.Features; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Connections +{ + public partial class TransportConnection : IFeatureCollection + { + private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature); + private static readonly Type IConnectionTransportFeatureType = typeof(IConnectionTransportFeature); + private static readonly Type IConnectionItemsFeatureType = typeof(IConnectionItemsFeature); + private static readonly Type IMemoryPoolFeatureType = typeof(IMemoryPoolFeature); + private static readonly Type IConnectionLifetimeFeatureType = typeof(IConnectionLifetimeFeature); + + private object _currentIConnectionIdFeature; + private object _currentIConnectionTransportFeature; + private object _currentIConnectionItemsFeature; + private object _currentIMemoryPoolFeature; + private object _currentIConnectionLifetimeFeature; + + private int _featureRevision; + + private List> MaybeExtra; + + private void FastReset() + { + _currentIConnectionIdFeature = this; + _currentIConnectionTransportFeature = this; + _currentIConnectionItemsFeature = this; + _currentIMemoryPoolFeature = this; + _currentIConnectionLifetimeFeature = this; + + } + + // Internal for testing + internal void ResetFeatureCollection() + { + FastReset(); + MaybeExtra?.Clear(); + _featureRevision++; + } + + private object ExtraFeatureGet(Type key) + { + if (MaybeExtra == null) + { + return null; + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + var kv = MaybeExtra[i]; + if (kv.Key == key) + { + return kv.Value; + } + } + return null; + } + + private void ExtraFeatureSet(Type key, object value) + { + if (MaybeExtra == null) + { + MaybeExtra = new List>(2); + } + + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra[i] = new KeyValuePair(key, value); + return; + } + } + MaybeExtra.Add(new KeyValuePair(key, value)); + } + + bool IFeatureCollection.IsReadOnly => false; + + int IFeatureCollection.Revision => _featureRevision; + + object IFeatureCollection.this[Type key] + { + get + { + object feature = null; + if (key == IConnectionIdFeatureType) + { + feature = _currentIConnectionIdFeature; + } + else if (key == IConnectionTransportFeatureType) + { + feature = _currentIConnectionTransportFeature; + } + else if (key == IConnectionItemsFeatureType) + { + feature = _currentIConnectionItemsFeature; + } + else if (key == IMemoryPoolFeatureType) + { + feature = _currentIMemoryPoolFeature; + } + else if (key == IConnectionLifetimeFeatureType) + { + feature = _currentIConnectionLifetimeFeature; + } + else if (MaybeExtra != null) + { + feature = ExtraFeatureGet(key); + } + + return feature; + } + + set + { + _featureRevision++; + + if (key == IConnectionIdFeatureType) + { + _currentIConnectionIdFeature = value; + } + else if (key == IConnectionTransportFeatureType) + { + _currentIConnectionTransportFeature = value; + } + else if (key == IConnectionItemsFeatureType) + { + _currentIConnectionItemsFeature = value; + } + else if (key == IMemoryPoolFeatureType) + { + _currentIMemoryPoolFeature = value; + } + else if (key == IConnectionLifetimeFeatureType) + { + _currentIConnectionLifetimeFeature = value; + } + else + { + ExtraFeatureSet(key, value); + } + } + } + + TFeature IFeatureCollection.Get() + { + TFeature feature = default; + if (typeof(TFeature) == typeof(IConnectionIdFeature)) + { + feature = (TFeature)_currentIConnectionIdFeature; + } + else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) + { + feature = (TFeature)_currentIConnectionTransportFeature; + } + else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) + { + feature = (TFeature)_currentIConnectionItemsFeature; + } + else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) + { + feature = (TFeature)_currentIMemoryPoolFeature; + } + else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) + { + feature = (TFeature)_currentIConnectionLifetimeFeature; + } + else if (MaybeExtra != null) + { + feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); + } + + return feature; + } + + void IFeatureCollection.Set(TFeature feature) + { + _featureRevision++; + if (typeof(TFeature) == typeof(IConnectionIdFeature)) + { + _currentIConnectionIdFeature = feature; + } + else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) + { + _currentIConnectionTransportFeature = feature; + } + else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) + { + _currentIConnectionItemsFeature = feature; + } + else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) + { + _currentIMemoryPoolFeature = feature; + } + else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) + { + _currentIConnectionLifetimeFeature = feature; + } + else + { + ExtraFeatureSet(typeof(TFeature), feature); + } + } + + private IEnumerable> FastEnumerable() + { + if (_currentIConnectionIdFeature != null) + { + yield return new KeyValuePair(IConnectionIdFeatureType, _currentIConnectionIdFeature); + } + if (_currentIConnectionTransportFeature != null) + { + yield return new KeyValuePair(IConnectionTransportFeatureType, _currentIConnectionTransportFeature); + } + if (_currentIConnectionItemsFeature != null) + { + yield return new KeyValuePair(IConnectionItemsFeatureType, _currentIConnectionItemsFeature); + } + if (_currentIMemoryPoolFeature != null) + { + yield return new KeyValuePair(IMemoryPoolFeatureType, _currentIMemoryPoolFeature); + } + if (_currentIConnectionLifetimeFeature != null) + { + yield return new KeyValuePair(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature); + } + + if (MaybeExtra != null) + { + foreach (var item in MaybeExtra) + { + yield return item; + } + } + } + + IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); + } +} diff --git a/Projects/Server/Kestrel/TransportConnection.cs b/Projects/Server/Kestrel/TransportConnection.cs new file mode 100644 index 000000000..fcd066608 --- /dev/null +++ b/Projects/Server/Kestrel/TransportConnection.cs @@ -0,0 +1,59 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Net; +using System.Threading; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Connections +{ + public abstract partial class TransportConnection : ConnectionContext + { + private IDictionary _items; + private string _connectionId; + + public TransportConnection() + { + FastReset(); + } + + public override EndPoint LocalEndPoint { get; set; } + public override EndPoint RemoteEndPoint { get; set; } + + public override string ConnectionId + { + get => _connectionId ??= CorrelationIdGenerator.GetNextId(); + set => _connectionId = value; + } + + public override IFeatureCollection Features => this; + + public virtual MemoryPool MemoryPool { get; } + + public override IDuplexPipe Transport { get; set; } + + public IDuplexPipe Application { get; set; } + + public override IDictionary Items + { + get => _items ??= new ConnectionItems(); + set => _items = value; + } + + public override CancellationToken ConnectionClosed { get; set; } + + // DO NOT remove this override to ConnectionContext.Abort. Doing so would cause + // any TransportConnection that does not override Abort or calls base.Abort + // to stack overflow when IConnectionLifetimeFeature.Abort() is called. + // That said, all derived types should override this method should override + // this implementation of Abort because canceling pending output reads is not + // sufficient to abort the connection if there is backpressure. + public override void Abort(ConnectionAbortedException abortReason) + { + Application.Input.CancelPendingRead(); + } + } +} diff --git a/Projects/Server/LibUv/IAsyncDisposable.cs b/Projects/Server/LibUv/IAsyncDisposable.cs new file mode 100644 index 000000000..70f4a53a5 --- /dev/null +++ b/Projects/Server/LibUv/IAsyncDisposable.cs @@ -0,0 +1,12 @@ +// 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.Threading.Tasks; + +namespace Libuv +{ + interface IAsyncDisposable + { + Task DisposeAsync(); + } +} diff --git a/Projects/Server/LibUv/ILibuvTrace.cs b/Projects/Server/LibUv/ILibuvTrace.cs new file mode 100644 index 000000000..371018cd2 --- /dev/null +++ b/Projects/Server/LibUv/ILibuvTrace.cs @@ -0,0 +1,29 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Libuv +{ + public interface ILibuvTrace : ILogger + { + void ConnectionRead(string connectionId, int count); + + void ConnectionReadFin(string connectionId); + + void ConnectionWriteFin(string connectionId, string reason); + + void ConnectionWrite(string connectionId, int count); + + void ConnectionWriteCallback(string connectionId, int status); + + void ConnectionError(string connectionId, Exception ex); + + void ConnectionReset(string connectionId); + + void ConnectionPause(string connectionId); + + void ConnectionResume(string connectionId); + } +} diff --git a/Projects/Server/LibUv/Internal/LibuvFunctions.cs b/Projects/Server/LibUv/Internal/LibuvFunctions.cs new file mode 100644 index 000000000..f215c0ae6 --- /dev/null +++ b/Projects/Server/LibUv/Internal/LibuvFunctions.cs @@ -0,0 +1,612 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Server; + +namespace Libuv.Internal +{ + public class LibuvFunctions + { + public LibuvFunctions() + { + _uv_loop_init = NativeMethods.uv_loop_init; + _uv_loop_close = NativeMethods.uv_loop_close; + _uv_run = NativeMethods.uv_run; + _uv_stop = NativeMethods.uv_stop; + _uv_ref = NativeMethods.uv_ref; + _uv_unref = NativeMethods.uv_unref; + _uv_fileno = NativeMethods.uv_fileno; + _uv_close = NativeMethods.uv_close; + _uv_async_init = NativeMethods.uv_async_init; + _uv_async_send = NativeMethods.uv_async_send; + _uv_unsafe_async_send = NativeMethods.uv_unsafe_async_send; + _uv_tcp_init = NativeMethods.uv_tcp_init; + _uv_tcp_bind = NativeMethods.uv_tcp_bind; + _uv_tcp_open = NativeMethods.uv_tcp_open; + _uv_tcp_nodelay = NativeMethods.uv_tcp_nodelay; + _uv_pipe_init = NativeMethods.uv_pipe_init; + _uv_pipe_bind = NativeMethods.uv_pipe_bind; + _uv_pipe_open = NativeMethods.uv_pipe_open; + _uv_listen = NativeMethods.uv_listen; + _uv_accept = NativeMethods.uv_accept; + _uv_pipe_connect = NativeMethods.uv_pipe_connect; + _uv_pipe_pending_count = NativeMethods.uv_pipe_pending_count; + _uv_read_start = NativeMethods.uv_read_start; + _uv_read_stop = NativeMethods.uv_read_stop; + _uv_try_write = NativeMethods.uv_try_write; + unsafe + { + _uv_write = NativeMethods.uv_write; + _uv_write2 = NativeMethods.uv_write2; + } + _uv_err_name = NativeMethods.uv_err_name; + _uv_strerror = NativeMethods.uv_strerror; + _uv_loop_size = NativeMethods.uv_loop_size; + _uv_handle_size = NativeMethods.uv_handle_size; + _uv_req_size = NativeMethods.uv_req_size; + _uv_ip4_addr = NativeMethods.uv_ip4_addr; + _uv_ip6_addr = NativeMethods.uv_ip6_addr; + _uv_tcp_getpeername = NativeMethods.uv_tcp_getpeername; + _uv_tcp_getsockname = NativeMethods.uv_tcp_getsockname; + _uv_walk = NativeMethods.uv_walk; + _uv_timer_init = NativeMethods.uv_timer_init; + _uv_timer_start = NativeMethods.uv_timer_start; + _uv_timer_stop = NativeMethods.uv_timer_stop; + _uv_now = NativeMethods.uv_now; + } + + // Second ctor that doesn't set any fields only to be used by MockLibuv + public LibuvFunctions(bool onlyForTesting) + { + } + + public void ThrowIfErrored(int statusCode) + { + // Note: method is explicitly small so the success case is easily inlined + if (statusCode < 0) ThrowError(statusCode); + } + + private void ThrowError(int statusCode) + { + // Note: only has one throw block so it will marked as "Does not return" by the jit + // and not inlined into previous function, while also marking as a function + // that does not need cpu register prep to call (see: https://github.com/dotnet/coreclr/pull/6103) + throw GetError(statusCode); + } + + public void Check(int statusCode, out UvException error) + { + // Note: method is explicitly small so the success case is easily inlined + error = statusCode < 0 ? GetError(statusCode) : null; + } + + // Note: method marked as NoInlining so it doesn't bloat either of the two preceding functions + // Check and ThrowError and alter their jit heuristics. + [MethodImpl(MethodImplOptions.NoInlining)] + private UvException GetError(int statusCode) => + new UvException($"Error {statusCode} {err_name(statusCode)} {strerror(statusCode)}", statusCode); + + public Func _uv_loop_init; + public void loop_init(UvLoopHandle handle) + { + ThrowIfErrored(_uv_loop_init(handle)); + } + + public Func _uv_loop_close; + public void loop_close(UvLoopHandle handle) + { + handle.Validate(true); + ThrowIfErrored(_uv_loop_close(handle.InternalGetHandle())); + } + + public Func _uv_run; + public void run(UvLoopHandle handle, int mode) + { + handle.Validate(); + ThrowIfErrored(_uv_run(handle, mode)); + } + + public Action _uv_stop; + public void stop(UvLoopHandle handle) + { + handle.Validate(); + _uv_stop(handle); + } + + public Action _uv_ref; + public void @ref(UvHandle handle) + { + handle.Validate(); + _uv_ref(handle); + } + + public Action _uv_unref; + public void unref(UvHandle handle) + { + handle.Validate(); + _uv_unref(handle); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket); + public uv_fileno_func _uv_fileno; + public void uv_fileno(UvHandle handle, ref IntPtr socket) + { + handle.Validate(); + ThrowIfErrored(_uv_fileno(handle, ref socket)); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_close_cb(IntPtr handle); + public Action _uv_close; + public void close(UvHandle handle, uv_close_cb close_cb) + { + handle.Validate(true); + _uv_close(handle.InternalGetHandle(), close_cb); + } + + public void close(IntPtr handle, uv_close_cb close_cb) + { + _uv_close(handle, close_cb); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_async_cb(IntPtr handle); + public Func _uv_async_init; + public void async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb) + { + loop.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_async_init(loop, handle, cb)); + } + + public Func _uv_async_send; + public void async_send(UvAsyncHandle handle) + { + ThrowIfErrored(_uv_async_send(handle)); + } + + public Func _uv_unsafe_async_send; + public void unsafe_async_send(IntPtr handle) + { + ThrowIfErrored(_uv_unsafe_async_send(handle)); + } + + public Func _uv_tcp_init; + public void tcp_init(UvLoopHandle loop, UvTcpHandle handle) + { + loop.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_tcp_init(loop, handle)); + } + + public delegate int uv_tcp_bind_func(UvTcpHandle handle, ref SockAddr addr, int flags); + public uv_tcp_bind_func _uv_tcp_bind; + public void tcp_bind(UvTcpHandle handle, ref SockAddr addr, int flags) + { + handle.Validate(); + ThrowIfErrored(_uv_tcp_bind(handle, ref addr, flags)); + } + + public Func _uv_tcp_open; + public void tcp_open(UvTcpHandle handle, IntPtr hSocket) + { + handle.Validate(); + ThrowIfErrored(_uv_tcp_open(handle, hSocket)); + } + + public Func _uv_tcp_nodelay; + public void tcp_nodelay(UvTcpHandle handle, bool enable) + { + handle.Validate(); + ThrowIfErrored(_uv_tcp_nodelay(handle, enable ? 1 : 0)); + } + + public Func _uv_pipe_init; + public void pipe_init(UvLoopHandle loop, UvPipeHandle handle, bool ipc) + { + loop.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_pipe_init(loop, handle, ipc ? -1 : 0)); + } + + public Func _uv_pipe_bind; + public void pipe_bind(UvPipeHandle handle, string name) + { + handle.Validate(); + ThrowIfErrored(_uv_pipe_bind(handle, name)); + } + + public Func _uv_pipe_open; + public void pipe_open(UvPipeHandle handle, IntPtr hSocket) + { + handle.Validate(); + ThrowIfErrored(_uv_pipe_open(handle, hSocket)); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_connection_cb(IntPtr server, int status); + public Func _uv_listen; + public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb) + { + handle.Validate(); + ThrowIfErrored(_uv_listen(handle, backlog, cb)); + } + + public Func _uv_accept; + public void accept(UvStreamHandle server, UvStreamHandle client) + { + server.Validate(); + client.Validate(); + ThrowIfErrored(_uv_accept(server, client)); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_connect_cb(IntPtr req, int status); + public Action _uv_pipe_connect; + public void pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb) + { + req.Validate(); + handle.Validate(); + _uv_pipe_connect(req, handle, name, cb); + } + + public Func _uv_pipe_pending_count; + public int pipe_pending_count(UvPipeHandle handle) + { + handle.Validate(); + return _uv_pipe_pending_count(handle); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_alloc_cb(IntPtr server, int suggested_size, out uv_buf_t buf); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_read_cb(IntPtr server, int nread, ref uv_buf_t buf); + public Func _uv_read_start; + public void read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) + { + handle.Validate(); + ThrowIfErrored(_uv_read_start(handle, alloc_cb, read_cb)); + } + + public Func _uv_read_stop; + public void read_stop(UvStreamHandle handle) + { + handle.Validate(); + ThrowIfErrored(_uv_read_stop(handle)); + } + + public Func _uv_try_write; + public int try_write(UvStreamHandle handle, uv_buf_t[] bufs, int nbufs) + { + handle.Validate(); + var count = _uv_try_write(handle, bufs, nbufs); + ThrowIfErrored(count); + return count; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_write_cb(IntPtr req, int status); + + public unsafe delegate int uv_write_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb); + public uv_write_func _uv_write; + public unsafe void write(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb) + { + req.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_write(req, handle, bufs, nbufs, cb)); + } + + public unsafe delegate int uv_write2_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); + public uv_write2_func _uv_write2; + public unsafe void write2(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb) + { + req.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_write2(req, handle, bufs, nbufs, sendHandle, cb)); + } + + public Func _uv_err_name; + public string err_name(int err) + { + IntPtr ptr = _uv_err_name(err); + return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); + } + + public Func _uv_strerror; + public string strerror(int err) + { + IntPtr ptr = _uv_strerror(err); + return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); + } + + public Func _uv_loop_size; + public int loop_size() => _uv_loop_size(); + + public Func _uv_handle_size; + public int handle_size(HandleType handleType) => _uv_handle_size(handleType); + + public Func _uv_req_size; + public int req_size(RequestType reqType) => _uv_req_size(reqType); + + public delegate int uv_ip4_addr_func(string ip, int port, out SockAddr addr); + public uv_ip4_addr_func _uv_ip4_addr; + public void ip4_addr(string ip, int port, out SockAddr addr, out UvException error) + { + Check(_uv_ip4_addr(ip, port, out addr), out error); + } + + public delegate int uv_ip6_addr_func(string ip, int port, out SockAddr addr); + public uv_ip6_addr_func _uv_ip6_addr; + public void ip6_addr(string ip, int port, out SockAddr addr, out UvException error) + { + Check(_uv_ip6_addr(ip, port, out addr), out error); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_walk_cb(IntPtr handle, IntPtr arg); + public Func _uv_walk; + public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg) + { + loop.Validate(); + _uv_walk(loop, walk_cb, arg); + } + + public Func _uv_timer_init; + public void timer_init(UvLoopHandle loop, UvTimerHandle handle) + { + loop.Validate(); + handle.Validate(); + ThrowIfErrored(_uv_timer_init(loop, handle)); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void uv_timer_cb(IntPtr handle); + public Func _uv_timer_start; + public void timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat) + { + handle.Validate(); + ThrowIfErrored(_uv_timer_start(handle, cb, timeout, repeat)); + } + + public Func _uv_timer_stop; + public void timer_stop(UvTimerHandle handle) + { + handle.Validate(); + ThrowIfErrored(_uv_timer_stop(handle)); + } + + public Func _uv_now; + public long now(UvLoopHandle loop) + { + loop.Validate(); + return _uv_now(loop); + } + + public delegate int uv_tcp_getsockname_func(UvTcpHandle handle, out SockAddr addr, ref int namelen); + public uv_tcp_getsockname_func _uv_tcp_getsockname; + public void tcp_getsockname(UvTcpHandle handle, out SockAddr addr, ref int namelen) + { + handle.Validate(); + ThrowIfErrored(_uv_tcp_getsockname(handle, out addr, ref namelen)); + } + + public delegate int uv_tcp_getpeername_func(UvTcpHandle handle, out SockAddr addr, ref int namelen); + public uv_tcp_getpeername_func _uv_tcp_getpeername; + public void tcp_getpeername(UvTcpHandle handle, out SockAddr addr, ref int namelen) + { + handle.Validate(); + ThrowIfErrored(_uv_tcp_getpeername(handle, out addr, ref namelen)); + } + + public uv_buf_t buf_init(IntPtr memory, int len) => new uv_buf_t(memory, len, Core.IsWindows); + + public struct uv_buf_t + { + // this type represents a WSABUF struct on Windows + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms741542(v=vs.85).aspx + // and an iovec struct on *nix + // http://man7.org/linux/man-pages/man2/readv.2.html + // because the order of the fields in these structs is different, the field + // names in this type don't have meaningful symbolic names. instead, they are + // assigned in the correct order by the constructor at runtime + + private readonly IntPtr _field0; + private readonly IntPtr _field1; + + public uv_buf_t(IntPtr memory, int len, bool windows) + { + if (windows) + { + _field0 = (IntPtr)len; + _field1 = memory; + } + else + { + _field0 = memory; + _field1 = (IntPtr)len; + } + } + } + + public enum HandleType + { + Unknown = 0, + ASYNC, + CHECK, + FS_EVENT, + FS_POLL, + HANDLE, + IDLE, + NAMED_PIPE, + POLL, + PREPARE, + PROCESS, + STREAM, + TCP, + TIMER, + TTY, + UDP, + SIGNAL, + } + + public enum RequestType + { + Unknown = 0, + REQ, + CONNECT, + WRITE, + SHUTDOWN, + UDP_SEND, + FS, + WORK, + GETADDRINFO, + GETNAMEINFO, + } + + private static class NativeMethods + { + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_loop_init(UvLoopHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_loop_close(IntPtr a0); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_run(UvLoopHandle handle, int mode); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern void uv_stop(UvLoopHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern void uv_ref(UvHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern void uv_unref(UvHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_fileno(UvHandle handle, ref IntPtr socket); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern void uv_close(IntPtr handle, uv_close_cb close_cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_async_send(UvAsyncHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl, EntryPoint = "uv_async_send")] + public static extern int uv_unsafe_async_send(IntPtr handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_init(UvLoopHandle loop, UvTcpHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_bind(UvTcpHandle handle, ref SockAddr addr, int flags); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_open(UvTcpHandle handle, IntPtr hSocket); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_nodelay(UvTcpHandle handle, int enable); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_pipe_init(UvLoopHandle loop, UvPipeHandle handle, int ipc); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_pipe_bind(UvPipeHandle loop, string name); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_pipe_open(UvPipeHandle handle, IntPtr hSocket); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_listen(UvStreamHandle handle, int backlog, uv_connection_cb cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_accept(UvStreamHandle server, UvStreamHandle client); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern void uv_pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_pipe_pending_count(UvPipeHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_read_stop(UvStreamHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_try_write(UvStreamHandle handle, uv_buf_t[] bufs, int nbufs); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe int uv_write(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe int uv_write2(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr uv_err_name(int err); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr uv_strerror(int err); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_loop_size(); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_handle_size(HandleType handleType); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_req_size(RequestType reqType); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_ip4_addr(string ip, int port, out SockAddr addr); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_ip6_addr(string ip, int port, out SockAddr addr); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_getsockname(UvTcpHandle handle, out SockAddr name, ref int namelen); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_tcp_getpeername(UvTcpHandle handle, out SockAddr name, ref int namelen); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_timer_init(UvLoopHandle loop, UvTimerHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern int uv_timer_stop(UvTimerHandle handle); + + [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] + public static extern long uv_now(UvLoopHandle loop); + + [DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)] + public static extern unsafe int WSAIoctl( + IntPtr socket, + int dwIoControlCode, + int* lpvInBuffer, + uint cbInBuffer, + int* lpvOutBuffer, + int cbOutBuffer, + out uint lpcbBytesReturned, + IntPtr lpOverlapped, + IntPtr lpCompletionRoutine + ); + + [DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)] + public static extern int WSAGetLastError(); + } + } +} diff --git a/Projects/Server/LibUv/Internal/SockAddr.cs b/Projects/Server/LibUv/Internal/SockAddr.cs new file mode 100644 index 000000000..09c6e6473 --- /dev/null +++ b/Projects/Server/LibUv/Internal/SockAddr.cs @@ -0,0 +1,107 @@ +// 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.Net; +using System.Runtime.InteropServices; +using Server; + +namespace Libuv.Internal +{ + [StructLayout(LayoutKind.Sequential)] + public struct SockAddr + { + // this type represents native memory occupied by sockaddr struct + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx + // although the c/c++ header defines it as a 2-byte short followed by a 14-byte array, + // the simplest way to reserve the same size in c# is with four nameless long values + private long _field0; + private long _field1; + private long _field2; + private long _field3; + + public SockAddr(long ignored) => _field0 = _field1 = _field2 = _field3 = 0; + + public unsafe IPEndPoint GetIPEndPoint() + { + // The bytes are represented in network byte order. + // + // Example 1: [2001:4898:e0:391:b9ef:1124:9d3e:a354]:39179 + // + // 0000 0000 0b99 0017 => The third and fourth bytes 990B is the actual port + // 9103 e000 9848 0120 => IPv6 address is represented in the 128bit field1 and field2. + // 54a3 3e9d 2411 efb9 Read these two 64-bit long from right to left byte by byte. + // 0000 0000 0000 0010 => Scope ID 0x10 (eg [::1%16]) the first 4 bytes of field3 in host byte order. + // + // Example 2: 10.135.34.141:39178 when adopt dual-stack sockets, IPv4 is mapped to IPv6 + // + // 0000 0000 0a99 0017 => The port representation are the same + // 0000 0000 0000 0000 + // 8d22 870a ffff 0000 => IPv4 occupies the last 32 bit: 0A.87.22.8d is the actual address. + // 0000 0000 0000 0000 + // + // Example 3: 10.135.34.141:12804, not dual-stack sockets + // + // 8d22 870a fd31 0002 => sa_family == AF_INET (02) + // 0000 0000 0000 0000 + // 0000 0000 0000 0000 + // 0000 0000 0000 0000 + // + // Example 4: 127.0.0.1:52798, on a Mac OS + // + // 0100 007F 3ECE 0210 => sa_family == AF_INET (02) Note that struct sockaddr on mac use + // 0000 0000 0000 0000 the second unint8 field for sa family type + // 0000 0000 0000 0000 http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h + // 0000 0000 0000 0000 + // + // Reference: + // - Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx + // - Linux: https://github.com/torvalds/linux/blob/6a13feb9c82803e2b815eca72fa7a9f5561d7861/include/linux/socket.h + // - Linux (sin6_scope_id): https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/net/sunrpc/addr.c#L82 + // - Apple: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h + + // Quick calculate the port by mask the field and locate the byte 3 and byte 4 + // and then shift them to correct place to form a int. + var port = ((int)(_field0 & 0x00FF0000) >> 8) | (int)((_field0 & 0xFF000000) >> 24); + + int family = (int)_field0; + if (Core.IsDarwin) + // see explanation in example 4 + family >>= 8; + family &= 0xFF; + + if (family == 2) + // AF_INET => IPv4 + return new IPEndPoint(new IPAddress((_field0 >> 32) & 0xFFFFFFFF), port); + + if (IsIPv4MappedToIPv6()) + { + var ipv4bits = (_field2 >> 32) & 0x00000000FFFFFFFF; + return new IPEndPoint(new IPAddress(ipv4bits), port); + } + + // otherwise IPv6 + var bytes = new byte[16]; + fixed (byte* b = bytes) + { + *(long*)b = _field1; + *(long*)(b + 8) = _field2; + } + + return new IPEndPoint(new IPAddress(bytes, ScopeId), port); + } + + public uint ScopeId + { + get => (uint)_field3; + set + { + _field3 &= unchecked ((long)0xFFFFFFFF00000000); + _field3 |= value; + } + } + + // If the IPAddress is an IPv4 mapped to IPv6, return the IPv4 representation instead. + // For example [::FFFF:127.0.0.1] will be transform to IPAddress of 127.0.0.1 + private bool IsIPv4MappedToIPv6() => _field1 == 0 && (_field2 & 0xFFFFFFFF) == 0xFFFF0000; + } +} diff --git a/Projects/Server/LibUv/Internal/UvAsyncHandle.cs b/Projects/Server/LibUv/Internal/UvAsyncHandle.cs new file mode 100644 index 000000000..f27a179a8 --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvAsyncHandle.cs @@ -0,0 +1,72 @@ +// 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.Diagnostics; +using System.Threading; + +namespace Libuv.Internal +{ + public class UvAsyncHandle : UvHandle + { + private static readonly LibuvFunctions.uv_close_cb _destroyMemory = handle => DestroyMemory(handle); + + private static readonly LibuvFunctions.uv_async_cb _uv_async_cb = handle => AsyncCb(handle); + private Action _callback; + private Action, IntPtr> _queueCloseHandle; + + public UvAsyncHandle(ILibuvTrace logger) : base(logger) + { + } + + public void Init(UvLoopHandle loop, Action callback, Action, IntPtr> queueCloseHandle) + { + CreateMemory( + loop.Libuv, + loop.ThreadId, + loop.Libuv.handle_size(LibuvFunctions.HandleType.ASYNC)); + + _callback = callback; + _queueCloseHandle = queueCloseHandle; + _uv.async_init(loop, this, _uv_async_cb); + } + + public void Send() + { + _uv.async_send(this); + } + + private static void AsyncCb(IntPtr handle) + { + FromIntPtr(handle)._callback.Invoke(); + } + + protected override bool ReleaseHandle() + { + var memory = handle; + if (memory != IntPtr.Zero) + { + handle = IntPtr.Zero; + + if (Thread.CurrentThread.ManagedThreadId == ThreadId) + { + _uv.close(memory, _destroyMemory); + } + else if (_queueCloseHandle != null) + { + // This can be called from the finalizer. + // Ensure the closure doesn't reference "this". + LibuvFunctions uv = _uv; + _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); + uv.unsafe_async_send(memory); + } + else + { + Debug.Assert(false, "UvAsyncHandle not initialized with queueCloseHandle action"); + return false; + } + } + return true; + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvConnectRequest.cs b/Projects/Server/LibUv/Internal/UvConnectRequest.cs new file mode 100644 index 000000000..cc60539ac --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvConnectRequest.cs @@ -0,0 +1,75 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Libuv.Internal +{ + /// + /// Summary description for UvWriteRequest + /// + public class UvConnectRequest : UvRequest + { + private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = UvConnectCb; + + private Action _callback; + private object _state; + + public UvConnectRequest(ILibuvTrace logger) : base (logger) + { + } + + public override void Init(LibuvThread thread) + { + DangerousInit(thread.Loop); + + base.Init(thread); + } + + public void DangerousInit(UvLoopHandle loop) + { + var requestSize = loop.Libuv.req_size(LibuvFunctions.RequestType.CONNECT); + CreateMemory( + loop.Libuv, + loop.ThreadId, + requestSize); + } + + public void Connect( + UvPipeHandle pipe, + string name, + Action callback, + object state) + { + _callback = callback; + _state = state; + + Libuv.pipe_connect(this, pipe, name, _uv_connect_cb); + } + + private static void UvConnectCb(IntPtr ptr, int status) + { + var req = FromIntPtr(ptr); + + var callback = req._callback; + req._callback = null; + + var state = req._state; + req._state = null; + + UvException error = null; + if (status < 0) req.Libuv.Check(status, out error); + + try + { + callback(req, status, error, state); + } + catch (Exception ex) + { + req._log.LogError(0, ex, "UvConnectRequest"); + throw; + } + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvException.cs b/Projects/Server/LibUv/Internal/UvException.cs new file mode 100644 index 000000000..3d4e492ef --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvException.cs @@ -0,0 +1,14 @@ +// 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; + +namespace Libuv.Internal +{ + public class UvException : Exception + { + public UvException(string message, int statusCode) : base(message) => StatusCode = statusCode; + + public int StatusCode { get; } + } +} diff --git a/Projects/Server/LibUv/Internal/UvHandle.cs b/Projects/Server/LibUv/Internal/UvHandle.cs new file mode 100644 index 000000000..888791bcc --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvHandle.cs @@ -0,0 +1,66 @@ +// 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.Diagnostics; +using System.Threading; + +namespace Libuv.Internal +{ + public abstract class UvHandle : UvMemory + { + private static readonly LibuvFunctions.uv_close_cb _destroyMemory = DestroyMemory; + private Action, IntPtr> _queueCloseHandle; + + protected UvHandle(ILibuvTrace logger) : base (logger) + { + } + + protected void CreateHandle( + LibuvFunctions uv, + int threadId, + int size, + Action, IntPtr> queueCloseHandle) + { + _queueCloseHandle = queueCloseHandle; + CreateMemory(uv, threadId, size); + } + + protected override bool ReleaseHandle() + { + IntPtr memory = handle; + if (memory != IntPtr.Zero) + { + handle = IntPtr.Zero; + + if (Thread.CurrentThread.ManagedThreadId == ThreadId) + { + _uv.close(memory, _destroyMemory); + } + else if (_queueCloseHandle != null) + { + // This can be called from the finalizer. + // Ensure the closure doesn't reference "this". + LibuvFunctions uv = _uv; + _queueCloseHandle(memory2 => uv.close(memory2, _destroyMemory), memory); + } + else + { + Debug.Assert(false, "UvHandle not initialized with queueCloseHandle action"); + return false; + } + } + return true; + } + + public void Reference() + { + _uv.@ref(this); + } + + public void Unreference() + { + _uv.unref(this); + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvLoopHandle.cs b/Projects/Server/LibUv/Internal/UvLoopHandle.cs new file mode 100644 index 000000000..c99c8896f --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvLoopHandle.cs @@ -0,0 +1,54 @@ +// 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.Threading; + +namespace Libuv.Internal +{ + public class UvLoopHandle : UvMemory + { + public UvLoopHandle(ILibuvTrace logger) : base(logger) + { + } + + public void Init(LibuvFunctions uv) + { + CreateMemory( + uv, + Thread.CurrentThread.ManagedThreadId, + uv.loop_size()); + + _uv.loop_init(this); + } + + public void Run(int mode = 0) + { + _uv.run(this, mode); + } + + public void Stop() + { + _uv.stop(this); + } + + public long Now() => _uv.now(this); + + protected override unsafe bool ReleaseHandle() + { + var memory = handle; + if (memory != IntPtr.Zero) + { + // loop_close clears the gcHandlePtr + var gcHandlePtr = *(IntPtr*)memory; + + _uv.loop_close(this); + handle = IntPtr.Zero; + + DestroyMemory(memory, gcHandlePtr); + } + + return true; + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvMemory.cs b/Projects/Server/LibUv/Internal/UvMemory.cs new file mode 100644 index 000000000..b5369ab5c --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvMemory.cs @@ -0,0 +1,79 @@ +// 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. +#define TRACE + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Libuv.Internal +{ + /// + /// Summary description for UvMemory + /// + public abstract class UvMemory : SafeHandle + { + protected LibuvFunctions _uv; + protected int _threadId; + protected readonly ILibuvTrace _log; + private readonly GCHandleType _handleType; + + protected UvMemory(ILibuvTrace logger, GCHandleType handleType = GCHandleType.Weak) : base(IntPtr.Zero, true) + { + _log = logger; + _handleType = handleType; + } + + public LibuvFunctions Libuv => _uv; + + public override bool IsInvalid => handle == IntPtr.Zero; + + public int ThreadId + { + get => _threadId; + private set => _threadId = value; + } + + protected unsafe void CreateMemory(LibuvFunctions uv, int threadId, int size) + { + _uv = uv; + ThreadId = threadId; + + handle = Marshal.AllocCoTaskMem(size); + *(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this, _handleType)); + } + + protected static unsafe void DestroyMemory(IntPtr memory) + { + var gcHandlePtr = *(IntPtr*)memory; + DestroyMemory(memory, gcHandlePtr); + } + + protected static void DestroyMemory(IntPtr memory, IntPtr gcHandlePtr) + { + if (gcHandlePtr != IntPtr.Zero) + { + var gcHandle = GCHandle.FromIntPtr(gcHandlePtr); + gcHandle.Free(); + } + Marshal.FreeCoTaskMem(memory); + } + + public IntPtr InternalGetHandle() => handle; + + public void Validate(bool closed = false) + { + Debug.Assert(closed || !IsClosed, "Handle is closed"); + Debug.Assert(!IsInvalid, "Handle is invalid"); + + Debug.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is incorrect"); + } + + public static unsafe THandle FromIntPtr(IntPtr handle) + { + GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle); + return (THandle)gcHandle.Target; + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvPipeHandle.cs b/Projects/Server/LibUv/Internal/UvPipeHandle.cs new file mode 100644 index 000000000..188777209 --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvPipeHandle.cs @@ -0,0 +1,36 @@ +// 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; + +namespace Libuv.Internal +{ + public class UvPipeHandle : UvStreamHandle + { + public UvPipeHandle(ILibuvTrace logger) : base(logger) + { + } + + public void Init(UvLoopHandle loop, Action, IntPtr> queueCloseHandle, bool ipc = false) + { + CreateHandle( + loop.Libuv, + loop.ThreadId, + loop.Libuv.handle_size(LibuvFunctions.HandleType.NAMED_PIPE), queueCloseHandle); + + _uv.pipe_init(loop, this, ipc); + } + + public void Open(IntPtr fileDescriptor) + { + _uv.pipe_open(this, fileDescriptor); + } + + public void Bind(string name) + { + _uv.pipe_bind(this, name); + } + + public int PendingCount() => _uv.pipe_pending_count(this); + } +} diff --git a/Projects/Server/LibUv/Internal/UvRequest.cs b/Projects/Server/LibUv/Internal/UvRequest.cs new file mode 100644 index 000000000..ed7199ce2 --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvRequest.cs @@ -0,0 +1,31 @@ +// 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.Runtime.InteropServices; + +namespace Libuv.Internal +{ + public class UvRequest : UvMemory + { + protected UvRequest(ILibuvTrace logger) : base(logger, GCHandleType.Normal) + { + } + + public virtual void Init(LibuvThread thread) + { +#if DEBUG + // Store weak handles to all UvRequest objects so we can do leak detection + // while running tests + thread.Requests.Add(new WeakReference(this)); +#endif + } + + protected override bool ReleaseHandle() + { + DestroyMemory(handle); + handle = IntPtr.Zero; + return true; + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvStreamHandle.cs b/Projects/Server/LibUv/Internal/UvStreamHandle.cs new file mode 100644 index 000000000..2edf8253b --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvStreamHandle.cs @@ -0,0 +1,151 @@ +// 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.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Libuv.Internal +{ + public abstract class UvStreamHandle : UvHandle + { + private static readonly LibuvFunctions.uv_connection_cb _uv_connection_cb = UvConnectionCb; + // Ref and out lamda params must be explicitly typed + private static readonly LibuvFunctions.uv_alloc_cb _uv_alloc_cb = + (IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf) => UvAllocCb(handle, suggested_size, out buf); + private static readonly LibuvFunctions.uv_read_cb _uv_read_cb = + (IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf) => UvReadCb(handle, status, ref buf); + + private Action _listenCallback; + private object _listenState; + private GCHandle _listenVitality; + + private Func _allocCallback; + private Action _readCallback; + private object _readState; + private GCHandle _readVitality; + + protected UvStreamHandle(ILibuvTrace logger) : base(logger) + { + } + + protected override bool ReleaseHandle() + { + if (_listenVitality.IsAllocated) _listenVitality.Free(); + if (_readVitality.IsAllocated) _readVitality.Free(); + return base.ReleaseHandle(); + } + + public void Listen(int backlog, Action callback, object state) + { + if (_listenVitality.IsAllocated) throw new InvalidOperationException("TODO: Listen may not be called more than once"); + try + { + _listenCallback = callback; + _listenState = state; + _listenVitality = GCHandle.Alloc(this, GCHandleType.Normal); + _uv.listen(this, backlog, _uv_connection_cb); + } + catch + { + _listenCallback = null; + _listenState = null; + if (_listenVitality.IsAllocated) _listenVitality.Free(); + throw; + } + } + + public void Accept(UvStreamHandle handle) + { + _uv.accept(this, handle); + } + + public void ReadStart( + Func allocCallback, + Action readCallback, + object state) + { + if (_readVitality.IsAllocated) throw new InvalidOperationException("TODO: ReadStop must be called before ReadStart may be called again"); + + try + { + _allocCallback = allocCallback; + _readCallback = readCallback; + _readState = state; + _readVitality = GCHandle.Alloc(this, GCHandleType.Normal); + _uv.read_start(this, _uv_alloc_cb, _uv_read_cb); + } + catch + { + _allocCallback = null; + _readCallback = null; + _readState = null; + if (_readVitality.IsAllocated) _readVitality.Free(); + throw; + } + } + + // UvStreamHandle.ReadStop() should be idempotent to match uv_read_stop() + public void ReadStop() + { + if (_readVitality.IsAllocated) _readVitality.Free(); + _allocCallback = null; + _readCallback = null; + _readState = null; + _uv.read_stop(this); + } + + public int TryWrite(LibuvFunctions.uv_buf_t buf) + { + return _uv.try_write(this, new[] { buf }, 1); + } + + private static void UvConnectionCb(IntPtr handle, int status) + { + var stream = FromIntPtr(handle); + + stream.Libuv.Check(status, out var error); + + try + { + stream._listenCallback(stream, status, error, stream._listenState); + } + catch (Exception ex) + { + stream._log.LogError(0, ex, "UvConnectionCb"); + throw; + } + } + + private static void UvAllocCb(IntPtr handle, int suggested_size, out LibuvFunctions.uv_buf_t buf) + { + var stream = FromIntPtr(handle); + try + { + buf = stream._allocCallback(stream, suggested_size, stream._readState); + } + catch (Exception ex) + { + stream._log.LogError(0, ex, "UvAllocCb"); + buf = stream.Libuv.buf_init(IntPtr.Zero, 0); + throw; + } + } + + private static void UvReadCb(IntPtr handle, int status, ref LibuvFunctions.uv_buf_t buf) + { + var stream = FromIntPtr(handle); + + try + { + stream._readCallback(stream, status, stream._readState); + } + catch (Exception ex) + { + stream._log.LogError(0, ex, "UbReadCb"); + throw; + } + } + + } +} diff --git a/Projects/Server/LibUv/Internal/UvTcpHandle.cs b/Projects/Server/LibUv/Internal/UvTcpHandle.cs new file mode 100644 index 000000000..745477201 --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvTcpHandle.cs @@ -0,0 +1,72 @@ +// 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.Net; +using System.Runtime.InteropServices; + +namespace Libuv.Internal +{ + public class UvTcpHandle : UvStreamHandle + { + public UvTcpHandle(ILibuvTrace logger) : base(logger) + { + } + + public void Init(UvLoopHandle loop, Action, IntPtr> queueCloseHandle) + { + CreateHandle( + loop.Libuv, + loop.ThreadId, + loop.Libuv.handle_size(LibuvFunctions.HandleType.TCP), queueCloseHandle); + + _uv.tcp_init(loop, this); + } + + public void Open(IntPtr fileDescriptor) + { + _uv.tcp_open(this, fileDescriptor); + } + + public void Bind(IPEndPoint endPoint) + { + var addressText = endPoint.Address.ToString(); + + _uv.ip4_addr(addressText, endPoint.Port, out var addr, out var error1); + + if (error1 != null) + { + _uv.ip6_addr(addressText, endPoint.Port, out addr, out var error2); + if (error2 != null) throw error1; + + if (endPoint.Address.ScopeId != addr.ScopeId) + // IPAddress.ScopeId cannot be less than 0 or greater than 0xFFFFFFFF + // https://msdn.microsoft.com/en-us/library/system.net.ipaddress.scopeid(v=vs.110).aspx + addr.ScopeId = (uint)endPoint.Address.ScopeId; + } + + _uv.tcp_bind(this, ref addr, 0); + } + + public IPEndPoint GetPeerIPEndPoint() + { + int namelen = Marshal.SizeOf(); + _uv.tcp_getpeername(this, out var socketAddress, ref namelen); + + return socketAddress.GetIPEndPoint(); + } + + public IPEndPoint GetSockIPEndPoint() + { + int namelen = Marshal.SizeOf(); + _uv.tcp_getsockname(this, out var socketAddress, ref namelen); + + return socketAddress.GetIPEndPoint(); + } + + public void NoDelay(bool enable) + { + _uv.tcp_nodelay(this, enable); + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvTimerHandle.cs b/Projects/Server/LibUv/Internal/UvTimerHandle.cs new file mode 100644 index 000000000..5fc52af17 --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvTimerHandle.cs @@ -0,0 +1,56 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Libuv.Internal +{ + public class UvTimerHandle : UvHandle + { + private static readonly LibuvFunctions.uv_timer_cb _uv_timer_cb = UvTimerCb; + + private Action _callback; + + public UvTimerHandle(ILibuvTrace logger) : base(logger) + { + } + + public void Init(UvLoopHandle loop, Action, IntPtr> queueCloseHandle) + { + CreateHandle( + loop.Libuv, + loop.ThreadId, + loop.Libuv.handle_size(LibuvFunctions.HandleType.TIMER), + queueCloseHandle); + + _uv.timer_init(loop, this); + } + + public void Start(Action callback, long timeout, long repeat) + { + _callback = callback; + _uv.timer_start(this, _uv_timer_cb, timeout, repeat); + } + + public void Stop() + { + _uv.timer_stop(this); + } + + private static void UvTimerCb(IntPtr handle) + { + var timer = FromIntPtr(handle); + + try + { + timer._callback(timer); + } + catch (Exception ex) + { + timer._log.LogError(0, ex, nameof(UvTimerCb)); + throw; + } + } + } +} diff --git a/Projects/Server/LibUv/Internal/UvWriteReq.cs b/Projects/Server/LibUv/Internal/UvWriteReq.cs new file mode 100644 index 000000000..1b30c4ccb --- /dev/null +++ b/Projects/Server/LibUv/Internal/UvWriteReq.cs @@ -0,0 +1,237 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Libuv.Internal +{ + /// + /// Summary description for UvWriteRequest + /// + public class UvWriteReq : UvRequest + { + private static readonly LibuvFunctions.uv_write_cb _uv_write_cb = (ptr, status) => UvWriteCb(ptr, status); + + private IntPtr _bufs; + + private Action _callback; + private object _state; + private const int BUFFER_COUNT = 4; + + private LibuvAwaitable _awaitable = new LibuvAwaitable(); + private List _pins = new List(BUFFER_COUNT + 1); + private List _handles = new List(BUFFER_COUNT + 1); + + public UvWriteReq(ILibuvTrace logger) : base(logger) + { + } + + public override void Init(LibuvThread thread) + { + DangerousInit(thread.Loop); + + base.Init(thread); + } + + public void DangerousInit(UvLoopHandle loop) + { + var requestSize = loop.Libuv.req_size(LibuvFunctions.RequestType.WRITE); + var bufferSize = Marshal.SizeOf() * BUFFER_COUNT; + CreateMemory( + loop.Libuv, + loop.ThreadId, + requestSize + bufferSize); + _bufs = handle + requestSize; + } + + public LibuvAwaitable WriteAsync(UvStreamHandle handle, in ReadOnlySequence buffer) + { + Write(handle, buffer, LibuvAwaitable.Callback, _awaitable); + return _awaitable; + } + + public LibuvAwaitable WriteAsync(UvStreamHandle handle, ArraySegment> bufs) + { + Write(handle, bufs, LibuvAwaitable.Callback, _awaitable); + return _awaitable; + } + + private unsafe void Write( + UvStreamHandle handle, + in ReadOnlySequence buffer, + Action callback, + object state) + { + try + { + var nBuffers = 0; + if (buffer.IsSingleSegment) + nBuffers = 1; + else + foreach (var _ in buffer) + nBuffers++; + + var pBuffers = (LibuvFunctions.uv_buf_t*)_bufs; + if (nBuffers > BUFFER_COUNT) + { + // create and pin buffer array when it's larger than the pre-allocated one + var bufArray = new LibuvFunctions.uv_buf_t[nBuffers]; + var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); + _pins.Add(gcHandle); + pBuffers = (LibuvFunctions.uv_buf_t*)gcHandle.AddrOfPinnedObject(); + } + + if (nBuffers == 1) + { + var memory = buffer.First; + var memoryHandle = memory.Pin(); + _handles.Add(memoryHandle); + + // Fast path for single buffer + pBuffers[0] = Libuv.buf_init( + (IntPtr)memoryHandle.Pointer, + memory.Length); + } + else + { + var index = 0; + foreach (var memory in buffer) + { + // This won't actually pin the buffer since we're already using pinned memory + var memoryHandle = memory.Pin(); + _handles.Add(memoryHandle); + + // create and pin each segment being written + pBuffers[index] = Libuv.buf_init( + (IntPtr)memoryHandle.Pointer, + memory.Length); + index++; + } + } + + _callback = callback; + _state = state; + _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); + } + catch + { + _callback = null; + _state = null; + UnpinGcHandles(); + throw; + } + } + + private void Write( + UvStreamHandle handle, + ArraySegment> bufs, + Action callback, + object state) + { + WriteArraySegmentInternal(handle, bufs, null, callback, state); + } + + public void Write2( + UvStreamHandle handle, + ArraySegment> bufs, + UvStreamHandle sendHandle, + Action callback, + object state) + { + WriteArraySegmentInternal(handle, bufs, sendHandle, callback, state); + } + + private unsafe void WriteArraySegmentInternal( + UvStreamHandle handle, + ArraySegment> bufs, + UvStreamHandle sendHandle, + Action callback, + object state) + { + try + { + var pBuffers = (LibuvFunctions.uv_buf_t*)_bufs; + var nBuffers = bufs.Count; + if (nBuffers > BUFFER_COUNT) + { + // create and pin buffer array when it's larger than the pre-allocated one + var bufArray = new LibuvFunctions.uv_buf_t[nBuffers]; + var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); + _pins.Add(gcHandle); + pBuffers = (LibuvFunctions.uv_buf_t*)gcHandle.AddrOfPinnedObject(); + } + + for (var index = 0; index < nBuffers; index++) + { + // create and pin each segment being written + var buf = bufs.Array[bufs.Offset + index]; + + var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned); + _pins.Add(gcHandle); + pBuffers[index] = Libuv.buf_init( + gcHandle.AddrOfPinnedObject() + buf.Offset, + buf.Count); + } + + _callback = callback; + _state = state; + + if (sendHandle == null) + _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); + else + _uv.write2(this, handle, pBuffers, nBuffers, sendHandle, _uv_write_cb); + } + catch + { + _callback = null; + _state = null; + UnpinGcHandles(); + throw; + } + } + + // Safe handle has instance method called Unpin + // so using UnpinGcHandles to avoid conflict + private void UnpinGcHandles() + { + var pinList = _pins; + var count = pinList.Count; + for (var i = 0; i < count; i++) pinList[i].Free(); + pinList.Clear(); + + var handleList = _handles; + count = handleList.Count; + for (var i = 0; i < count; i++) handleList[i].Dispose(); + handleList.Clear(); + } + + private static void UvWriteCb(IntPtr ptr, int status) + { + var req = FromIntPtr(ptr); + req.UnpinGcHandles(); + + var callback = req._callback; + req._callback = null; + + var state = req._state; + req._state = null; + + UvException error = null; + if (status < 0) req.Libuv.Check(status, out error); + + try + { + callback(req, status, error, state); + } + catch (Exception ex) + { + req._log.LogError(0, ex, "UvWriteCb"); + throw; + } + } + } +} diff --git a/Projects/Server/LibUv/LibuvAwaitable.cs b/Projects/Server/LibUv/LibuvAwaitable.cs new file mode 100644 index 000000000..efdaeae29 --- /dev/null +++ b/Projects/Server/LibUv/LibuvAwaitable.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using Libuv.Internal; + +namespace Libuv +{ + public class LibuvAwaitable : ICriticalNotifyCompletion where TRequest : UvRequest + { + private static readonly Action _callbackCompleted = () => { }; + + private Action _callback; + + private UvException _exception; + + private int _status; + + public static readonly Action Callback = (req, status, error, state) => + { + var awaitable = (LibuvAwaitable)state; + + awaitable._exception = error; + awaitable._status = status; + + var continuation = Interlocked.Exchange(ref awaitable._callback, _callbackCompleted); + + continuation?.Invoke(); + }; + + public LibuvAwaitable GetAwaiter() => this; + public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted); + + public UvWriteResult GetResult() + { + Debug.Assert(_callback == _callbackCompleted); + + var exception = _exception; + var status = _status; + + // Reset the awaitable state + _exception = null; + _status = 0; + _callback = null; + + return new UvWriteResult(status, exception); + } + + public void OnCompleted(Action continuation) + { + // There should never be a race between IsCompleted and OnCompleted since both operations + // should always be on the libuv thread + if (ReferenceEquals(_callback, _callbackCompleted)) + Debug.Fail($"{typeof(LibuvAwaitable)}.{nameof(OnCompleted)} raced with {nameof(IsCompleted)}, scheduling callback."); + + _callback = continuation; + } + + public void UnsafeOnCompleted(Action continuation) + { + OnCompleted(continuation); + } + } + + public struct UvWriteResult + { + public int Status { get; } + public UvException Error { get; } + + public UvWriteResult(int status, UvException error) + { + Status = status; + Error = error; + } + } +} diff --git a/Projects/Server/LibUv/LibuvConnection.cs b/Projects/Server/LibUv/LibuvConnection.cs new file mode 100644 index 000000000..5839c4d6b --- /dev/null +++ b/Projects/Server/LibUv/LibuvConnection.cs @@ -0,0 +1,309 @@ +// 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.Buffers; +using System.IO; +using System.IO.Pipelines; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Logging; + +namespace Libuv +{ + public partial class LibuvConnection : TransportConnection + { + private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; + + private static readonly Action _readCallback = ReadCallback; + + private static readonly Func _allocCallback = AllocCallback; + + private readonly UvStreamHandle _socket; + private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource(); + + private volatile ConnectionAbortedException _abortReason; + + private MemoryHandle _bufferHandle; + private Task _processingTask; + private readonly TaskCompletionSource _waitForConnectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private bool _connectionClosed; + + public LibuvConnection(UvStreamHandle socket, + ILibuvTrace log, + LibuvThread thread, + IPEndPoint remoteEndPoint, + IPEndPoint localEndPoint, + PipeOptions inputOptions = null, + PipeOptions outputOptions = null, + long? maxReadBufferSize = null, + long? maxWriteBufferSize = null) + { + _socket = socket; + + LocalEndPoint = localEndPoint; + RemoteEndPoint = remoteEndPoint; + + ConnectionClosed = _connectionClosedTokenSource.Token; + Log = log; + Thread = thread; + + maxReadBufferSize ??= 0; + maxWriteBufferSize ??= 0; + + inputOptions ??= new PipeOptions(MemoryPool, PipeScheduler.ThreadPool, Thread, maxReadBufferSize.Value, maxReadBufferSize.Value / 2, useSynchronizationContext: false); + outputOptions ??= new PipeOptions(MemoryPool, Thread, PipeScheduler.ThreadPool, maxWriteBufferSize.Value, maxWriteBufferSize.Value / 2, useSynchronizationContext: false); + + var pair = DuplexPipe.CreateConnectionPair(inputOptions, outputOptions); + + // Set the transport and connection id + Transport = pair.Transport; + Application = pair.Application; + } + + public PipeWriter Input => Application.Output; + + public PipeReader Output => Application.Input; + + public LibuvOutputConsumer OutputConsumer { get; set; } + private ILibuvTrace Log { get; } + private LibuvThread Thread { get; } + public override MemoryPool MemoryPool => Thread.MemoryPool; + + public void Start() + { + _processingTask = StartCore(); + } + + private async Task StartCore() + { + try + { + OutputConsumer = new LibuvOutputConsumer(Output, Thread, _socket, ConnectionId, Log); + + StartReading(); + + Exception inputError = null; + Exception outputError = null; + + try + { + // This *must* happen after socket.ReadStart + // The socket output consumer is the only thing that can close the connection. If the + // output pipe is already closed by the time we start then it's fine since, it'll close gracefully afterwards. + await OutputConsumer.WriteOutputAsync(); + } + catch (UvException ex) + { + // The connection reset/error has already been logged by LibuvOutputConsumer + if (ex.StatusCode == LibuvConstants.ECANCELED) + { + // Connection was aborted. + } + else if (LibuvConstants.IsConnectionReset(ex.StatusCode)) + { + // Don't cause writes to throw for connection resets. + inputError = new ConnectionResetException(ex.Message, ex); + } + else + { + // This is unexpected. + Log.ConnectionError(ConnectionId, ex); + + inputError = ex; + outputError = ex; + } + } + finally + { + inputError ??= _abortReason ?? new ConnectionAbortedException("The libuv transport's send loop completed gracefully."); + + // Now, complete the input so that no more reads can happen + Input.Complete(inputError); + Output.Complete(outputError); + + // Make sure it isn't possible for a paused read to resume reading after calling uv_close + // on the stream handle + Input.CancelPendingFlush(); + + // Send a FIN + Log.ConnectionWriteFin(ConnectionId, inputError.Message); + + // We're done with the socket now + _socket.Dispose(); + + // Ensure this always fires + FireConnectionClosed(); + + await _waitForConnectionClosedTcs.Task; + } + } + catch (Exception e) + { + Log.LogCritical(0, e, $"{nameof(LibuvConnection)}.{nameof(Start)}() {ConnectionId}"); + } + } + + public override void Abort(ConnectionAbortedException abortReason) + { + _abortReason = abortReason; + + // Cancel WriteOutputAsync loop after setting _abortReason. + Output.CancelPendingRead(); + + // This cancels any pending I/O. + Thread.Post(s => s.Dispose(), _socket); + } + + public override async ValueTask DisposeAsync() + { + Transport.Input.Complete(); + Transport.Output.Complete(); + + if (_processingTask != null) await _processingTask; + + _connectionClosedTokenSource.Dispose(); + } + + // Called on Libuv thread + private static LibuvFunctions.uv_buf_t AllocCallback(UvStreamHandle handle, int suggestedSize, object state) => ((LibuvConnection)state).OnAlloc(handle, suggestedSize); + + private unsafe LibuvFunctions.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize) + { + var currentWritableBuffer = Input.GetMemory(MinAllocBufferSize); + _bufferHandle = currentWritableBuffer.Pin(); + + return handle.Libuv.buf_init((IntPtr)_bufferHandle.Pointer, currentWritableBuffer.Length); + } + + private static void ReadCallback(UvStreamHandle handle, int status, object state) + { + ((LibuvConnection)state).OnRead(handle, status); + } + + private void OnRead(UvStreamHandle handle, int status) + { + // Cleanup state from last OnAlloc. This is safe even if OnAlloc wasn't called. + _bufferHandle.Dispose(); + if (status == 0) + { + // EAGAIN/EWOULDBLOCK so just return the buffer. + // http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb + } + else if (status > 0) + { + Log.ConnectionRead(ConnectionId, status); + + Input.Advance(status); + var flushTask = Input.FlushAsync(); + + if (!flushTask.IsCompleted) + // We wrote too many bytes to the reader, so pause reading and resume when + // we hit the low water mark. + _ = ApplyBackpressureAsync(flushTask); + } + else + { + // Given a negative status, it's possible that OnAlloc wasn't called. + _socket.ReadStop(); + + Exception error = null; + + if (status == LibuvConstants.EOF) + { + Log.ConnectionReadFin(ConnectionId); + } + else + { + handle.Libuv.Check(status, out var uvError); + error = LogAndWrapReadError(uvError); + } + + FireConnectionClosed(); + + // Complete after aborting the connection + Input.Complete(error); + } + } + + private void FireConnectionClosed() + { + // Guard against scheduling this multiple times + if (_connectionClosed) return; + + _connectionClosed = true; + + ThreadPool.UnsafeQueueUserWorkItem(state => + { + state.CancelConnectionClosedToken(); + + state._waitForConnectionClosedTcs.TrySetResult(null); + }, + this, + false); + } + + private async Task ApplyBackpressureAsync(ValueTask flushTask) + { + Log.ConnectionPause(ConnectionId); + _socket.ReadStop(); + + var result = await flushTask; + + // If the reader isn't complete or cancelled then resume reading + if (!result.IsCompleted && !result.IsCanceled) + { + Log.ConnectionResume(ConnectionId); + StartReading(); + } + } + + private void StartReading() + { + try + { + _socket.ReadStart(_allocCallback, _readCallback, this); + } + catch (UvException ex) + { + // ReadStart() can throw a UvException in some cases (e.g. socket is no longer connected). + // This should be treated the same as OnRead() seeing a negative status. + Input.Complete(LogAndWrapReadError(ex)); + } + } + + private Exception LogAndWrapReadError(UvException uvError) + { + if (uvError.StatusCode == LibuvConstants.ECANCELED) + { + // The operation was canceled by the server not the client. No need for additional logs. + return new ConnectionAbortedException(uvError.Message, uvError); + } + + if (LibuvConstants.IsConnectionReset(uvError.StatusCode)) + { + // Log connection resets at a lower (Debug) level. + Log.ConnectionReset(ConnectionId); + return new ConnectionResetException(uvError.Message, uvError); + } + // This is unexpected. + Log.ConnectionError(ConnectionId, uvError); + return new IOException(uvError.Message, uvError); + } + + private void CancelConnectionClosedToken() + { + try + { + _connectionClosedTokenSource.Cancel(); + } + catch (Exception ex) + { + Log.LogError(0, ex, $"Unexpected exception in {nameof(LibuvConnection)}.{nameof(CancelConnectionClosedToken)}."); + } + } + } +} diff --git a/Projects/Server/LibUv/LibuvConnectionListener.cs b/Projects/Server/LibUv/LibuvConnectionListener.cs new file mode 100644 index 000000000..d04da54c6 --- /dev/null +++ b/Projects/Server/LibUv/LibuvConnectionListener.cs @@ -0,0 +1,196 @@ +// 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.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Server; + +namespace Libuv +{ + public class LibuvConnectionListener : IConnectionListener + { + private readonly List _listeners = new List(); + private IAsyncEnumerator _acceptEnumerator; + private bool _stopped; + private bool _disposed; + + public LibuvConnectionListener(LibuvTransportContext context, EndPoint endPoint) + : this(new LibuvFunctions(), context, endPoint) + { } + + // For testing + public LibuvConnectionListener(LibuvFunctions uv, LibuvTransportContext context, EndPoint endPoint) + { + Libuv = uv; + TransportContext = context; + + EndPoint = endPoint; + } + + public LibuvFunctions Libuv { get; } + public LibuvTransportContext TransportContext { get; } + public List Threads { get; } = new List(); + + public IHostApplicationLifetime AppLifetime => TransportContext.AppLifetime; + public ILibuvTrace Log => TransportContext.Log; + public LibuvTransportOptions TransportOptions => TransportContext.Options; + + public EndPoint EndPoint { get; set; } + + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + if (_disposed) throw new ObjectDisposedException(GetType().FullName); + + if (await _acceptEnumerator.MoveNextAsync()) return _acceptEnumerator.Current; + + // null means we're done... + return null; + } + + public async ValueTask UnbindAsync(CancellationToken cancellationToken = default) + { + if (_stopped) return; + + _stopped = true; + + var disposeTasks = _listeners.Select(listener => ((IAsyncDisposable)listener).DisposeAsync()).ToArray(); + + if (!await WaitAsync(Task.WhenAll(disposeTasks), TimeSpan.FromSeconds(5)).ConfigureAwait(false)) + Log.LogError(0, null, "Disposing listeners failed"); + } + + + public async ValueTask DisposeAsync() + { + if (_disposed) return; + + _disposed = true; + + await UnbindAsync().ConfigureAwait(false); + + foreach (var listener in _listeners) await listener.AbortQueuedConnectionAsync().ConfigureAwait(false); + + _listeners.Clear(); + + await StopThreadsAsync().ConfigureAwait(false); + } + + internal async Task StopThreadsAsync() + { + try + { + await Task.WhenAll(Threads.Select(thread => thread.StopAsync(TimeSpan.FromSeconds(5))).ToArray()) + .ConfigureAwait(false); + } + catch (AggregateException aggEx) + { + // An uncaught exception was likely thrown from the libuv event loop. + // The original error that crashed one loop may have caused secondary errors in others. + // Make sure that the stack trace of the original error is logged. + foreach (var ex in aggEx.InnerExceptions) Log.LogCritical("Failed to gracefully close Kestrel.", ex); + + throw; + } + + Threads.Clear(); +#if DEBUG && !INNER_LOOP + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); +#endif + } + + public async Task BindAsync() + { + // TODO: Move thread management to LibuvTransportFactory + // TODO: Split endpoint management from thread management + for (var index = 0; index < TransportOptions.ThreadCount; index++) Threads.Add(new LibuvThread(Libuv, TransportContext)); + + foreach (var thread in Threads) await thread.StartAsync().ConfigureAwait(false); + + try + { + if (TransportOptions.ThreadCount == 1) + { + var listener = new Listener(TransportContext); + _listeners.Add(listener); + await listener.StartAsync(EndPoint, Threads[0]).ConfigureAwait(false); + EndPoint = listener.EndPoint; + } + else + { + var pipeName = (Core.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); + var pipeMessage = Guid.NewGuid().ToByteArray(); + + var listenerPrimary = new ListenerPrimary(TransportContext); + _listeners.Add(listenerPrimary); + await listenerPrimary.StartAsync(pipeName, pipeMessage, EndPoint, Threads[0]).ConfigureAwait(false); + EndPoint = listenerPrimary.EndPoint; + + foreach (var thread in Threads.Skip(1)) + { + var listenerSecondary = new ListenerSecondary(TransportContext); + _listeners.Add(listenerSecondary); + await listenerSecondary.StartAsync(pipeName, pipeMessage, EndPoint, thread).ConfigureAwait(false); + } + } + _acceptEnumerator = AcceptConnections(); + } + catch (UvException ex) when (ex.StatusCode == LibuvConstants.EADDRINUSE) + { + await UnbindAsync().ConfigureAwait(false); + throw new AddressInUseException(ex.Message, ex); + } + catch + { + await UnbindAsync().ConfigureAwait(false); + throw; + } + } + + private async IAsyncEnumerator AcceptConnections() + { + var slots = new Task<(LibuvConnection, int)>[_listeners.Count]; + // This is the task we'll put in the slot when each listening completes. It'll prevent + // us from having to shrink the array. We'll just loop while there are active slots. + var incompleteTask = new TaskCompletionSource<(LibuvConnection, int)>().Task; + + var remainingSlots = slots.Length; + + // Issue parallel accepts on all listeners + for (int i = 0; i < remainingSlots; i++) slots[i] = AcceptAsync(_listeners[i], i); + + while (remainingSlots > 0) + { + // Calling GetAwaiter().GetResult() is safe because we know the task is completed + var (connection, slot) = (await Task.WhenAny(slots)).GetAwaiter().GetResult(); + + // If the connection is null then the listener was closed + if (connection == null) + { + remainingSlots--; + slots[slot] = incompleteTask; + } + else + { + // Fill that slot with another accept and yield the connection + slots[slot] = AcceptAsync(_listeners[slot], slot); + + yield return connection; + } + } + + static async Task<(LibuvConnection, int)> AcceptAsync(ListenerContext listener, int slot) => (await listener.AcceptAsync(), slot); + } + + private static async Task WaitAsync(Task task, TimeSpan timeout) => await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task; + } +} diff --git a/Projects/Server/LibUv/LibuvConstants.cs b/Projects/Server/LibUv/LibuvConstants.cs new file mode 100644 index 000000000..bce142a81 --- /dev/null +++ b/Projects/Server/LibUv/LibuvConstants.cs @@ -0,0 +1,98 @@ +// 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.Runtime.CompilerServices; +using Server; + +namespace Libuv +{ + public static class LibuvConstants + { + public const int EOF = -4095; + public static readonly int? ECONNRESET = GetECONNRESET(); + public static readonly int? EADDRINUSE = GetEADDRINUSE(); + public static readonly int? ENOTSUP = GetENOTSUP(); + public static readonly int? EPIPE = GetEPIPE(); + public static readonly int? ECANCELED = GetECANCELED(); + public static readonly int? ENOTCONN = GetENOTCONN(); + public static readonly int? EINVAL = GetEINVAL(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsConnectionReset(int errno) => errno == ECONNRESET || errno == EPIPE || errno == ENOTCONN || errno == EINVAL; + + private static int? GetECONNRESET() + { + if (Core.IsWindows) + return -4077; + if (Core.IsLinux) + return -104; + if (Core.IsDarwin) + return -5; + return null; + } + + private static int? GetEPIPE() + { + if (Core.IsWindows) + return -4047; + if (Core.IsLinux) + return -32; + if (Core.IsDarwin) + return -32; + return null; + } + + private static int? GetENOTCONN() + { + if (Core.IsWindows) + return -4053; + if (Core.IsLinux) + return -107; + if (Core.IsDarwin) + return -57; + return null; + } + + private static int? GetEINVAL() + { + if (Core.IsWindows) + return -4071; + if (Core.IsLinux) + return -22; + if (Core.IsDarwin) + return -22; + return null; + } + + private static int? GetEADDRINUSE() + { + if (Core.IsWindows) + return -4091; + if (Core.IsLinux) + return -98; + if (Core.IsDarwin) + return -48; + return null; + } + + private static int? GetENOTSUP() + { + if (Core.IsLinux) + return -95; + if (Core.IsDarwin) + return -45; + return null; + } + + private static int? GetECANCELED() + { + if (Core.IsWindows) + return -4081; + if (Core.IsLinux) + return -125; + if (Core.IsDarwin) + return -89; + return null; + } + } +} diff --git a/Projects/Server/LibUv/LibuvOutputConsumer.cs b/Projects/Server/LibUv/LibuvOutputConsumer.cs new file mode 100644 index 000000000..e9d42ca52 --- /dev/null +++ b/Projects/Server/LibUv/LibuvOutputConsumer.cs @@ -0,0 +1,111 @@ +// 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.IO.Pipelines; +using System.Threading.Tasks; +using Libuv.Internal; + +namespace Libuv +{ + public class LibuvOutputConsumer + { + private readonly LibuvThread _thread; + private readonly UvStreamHandle _socket; + private readonly string _connectionId; + private readonly ILibuvTrace _log; + private readonly PipeReader _pipe; + + public LibuvOutputConsumer( + PipeReader pipe, + LibuvThread thread, + UvStreamHandle socket, + string connectionId, + ILibuvTrace log) + { + _pipe = pipe; + _thread = thread; + _socket = socket; + _connectionId = connectionId; + _log = log; + } + + public async Task WriteOutputAsync() + { + var pool = _thread.WriteReqPool; + + while (true) + { + var result = await _pipe.ReadAsync(); + + var buffer = result.Buffer; + var consumed = buffer.End; + + try + { + if (result.IsCanceled) break; + + if (!buffer.IsEmpty) + { + var writeReq = pool.Allocate(); + + try + { + if (_socket.IsClosed) break; + + var writeResult = await writeReq.WriteAsync(_socket, buffer); + + LogWriteInfo(writeResult.Status, writeResult.Error); + + if (writeResult.Error != null) + { + consumed = buffer.Start; + throw writeResult.Error; + } + } + finally + { + // Make sure we return the writeReq to the pool + pool.Return(writeReq); + + // Null out writeReq so it doesn't get caught by CheckUvReqLeaks. + // It is rooted by a TestSink scope through Pipe continuations in + // ResponseTests.HttpsConnectionClosedWhenResponseDoesNotSatisfyMinimumDataRate + writeReq = null; + } + } + + if (result.IsCompleted) break; + } + finally + { + _pipe.AdvanceTo(consumed); + } + } + } + + private void LogWriteInfo(int status, Exception error) + { + if (error == null) + { + _log.ConnectionWriteCallback(_connectionId, status); + } + else + { + // Log connection resets at a lower (Debug) level. + if (status == LibuvConstants.ECANCELED) + { + // Connection was aborted. + } + else if (LibuvConstants.IsConnectionReset(status)) + { + _log.ConnectionReset(_connectionId); + } + else + { + _log.ConnectionError(_connectionId, error); + } + } + } + } +} diff --git a/Projects/Server/LibUv/LibuvThread.cs b/Projects/Server/LibUv/LibuvThread.cs new file mode 100644 index 000000000..d57d7f2dc --- /dev/null +++ b/Projects/Server/LibUv/LibuvThread.cs @@ -0,0 +1,437 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO.Pipelines; +using System.Runtime.ExceptionServices; +using System.Threading; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Libuv +{ + public class LibuvThread : PipeScheduler + { + // maximum times the work queues swapped and are processed in a single pass + // as completing a task may immediately have write data to put on the network + // otherwise it needs to wait till the next pass of the libuv loop + private readonly int _maxLoops; + + private readonly LibuvFunctions _libuv; + private readonly IHostApplicationLifetime _appLifetime; + private readonly Thread _thread; + private readonly TaskCompletionSource _threadTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly UvLoopHandle _loop; + private readonly UvAsyncHandle _post; + private Queue _workAdding = new Queue(1024); + private Queue _workRunning = new Queue(1024); + private Queue _closeHandleAdding = new Queue(256); + private Queue _closeHandleRunning = new Queue(256); + private readonly object _workSync = new object(); + private readonly object _closeHandleSync = new object(); + private readonly object _startSync = new object(); + private bool _stopImmediate; + private bool _initCompleted; + private Exception _closeError; + private readonly ILibuvTrace _log; + + public LibuvThread(LibuvFunctions libuv, LibuvTransportContext libuvTransportContext, int maxLoops = 8) + : this(libuv, libuvTransportContext.AppLifetime, libuvTransportContext.Options.MemoryPoolFactory(), libuvTransportContext.Log, maxLoops) + { + } + + public LibuvThread(LibuvFunctions libuv, IHostApplicationLifetime appLifetime, MemoryPool pool, ILibuvTrace log, int maxLoops = 8) + { + _libuv = libuv; + _appLifetime = appLifetime; + _log = log; + _loop = new UvLoopHandle(_log); + _post = new UvAsyncHandle(_log); + _maxLoops = maxLoops; + + _thread = new Thread(ThreadStart); +#if !INNER_LOOP + _thread.Name = nameof(LibuvThread); +#endif + +#if !DEBUG + // Mark the thread as being as unimportant to keeping the process alive. + // Don't do this for debug builds, so we know if the thread isn't terminating. + _thread.IsBackground = true; +#endif + QueueCloseHandle = PostCloseHandle; + QueueCloseAsyncHandle = EnqueueCloseHandle; + MemoryPool = pool; + WriteReqPool = new WriteReqPool(this, _log); + } + + public UvLoopHandle Loop => _loop; + + public MemoryPool MemoryPool { get; } + + public WriteReqPool WriteReqPool { get; } + +#if DEBUG + public List Requests { get; } = new List(); +#endif + + public Exception FatalError => _closeError; + + public Action, IntPtr> QueueCloseHandle { get; } + + private Action, IntPtr> QueueCloseAsyncHandle { get; } + + public Task StartAsync() + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + _thread.Start(tcs); + return tcs.Task; + } + + public async Task StopAsync(TimeSpan timeout) + { + lock (_startSync) + { + if (!_initCompleted) return; + } + + Debug.Assert(!_threadTcs.Task.IsCompleted, "The loop thread was completed before calling uv_unref on the post handle."); + + var stepTimeout = TimeSpan.FromTicks(timeout.Ticks / 3); + + try + { + Post(t => t.AllowStop()); + if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) + { + _log.LogWarning($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(AllowStop)}"); + + Post(t => t.OnStopRude()); + if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) + { + _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(OnStopRude)}."); + + Post(t => t.OnStopImmediate()); + if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread, {nameof(OnStopImmediate)}."); + } + } + } + catch (ObjectDisposedException) + { + if (!await WaitAsync(_threadTcs.Task, stepTimeout).ConfigureAwait(false)) _log.LogCritical($"{nameof(LibuvThread)}.{nameof(StopAsync)} failed to terminate libuv thread."); + } + + if (_closeError != null) ExceptionDispatchInfo.Capture(_closeError).Throw(); + } + +#if DEBUG && !INNER_LOOP + private void CheckUvReqLeaks() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + // Detect leaks in UvRequest objects + foreach (var request in Requests) Debug.Assert(request.Target == null, $"{request.Target?.GetType()} object is still alive."); + } +#endif + + private void AllowStop() + { + _post.Unreference(); + } + + private void OnStopRude() + { + Walk(ptr => + { + var handle = UvMemory.FromIntPtr(ptr); + if (handle != _post) + // handle can be null because UvMemory.FromIntPtr looks up a weak reference + handle?.Dispose(); + }); + } + + private void OnStopImmediate() + { + _stopImmediate = true; + _loop.Stop(); + } + + public void Post(Action callback, T state) + { + // Handle is closed to don't bother scheduling anything + if (_post.IsClosed) return; + + var work = new Work + { + CallbackAdapter = CallbackAdapter.PostCallbackAdapter, + Callback = callback, + // TODO: This boxes + State = state + }; + + lock (_workSync) + { + _workAdding.Enqueue(work); + } + + try + { + _post.Send(); + } + catch (ObjectDisposedException) + { + // There's an inherent race here where we're in the middle of shutdown + } + } + + private void Post(Action callback) + { + Post(callback, this); + } + + public Task PostAsync(Action callback, T state) + { + // Handle is closed to don't bother scheduling anything + if (_post.IsClosed) return Task.CompletedTask; + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var work = new Work + { + CallbackAdapter = CallbackAdapter.PostAsyncCallbackAdapter, + Callback = callback, + State = state, + Completion = tcs + }; + + lock (_workSync) + { + _workAdding.Enqueue(work); + } + + try + { + _post.Send(); + } + catch (ObjectDisposedException) + { + // There's an inherent race here where we're in the middle of shutdown + } + return tcs.Task; + } + + public void Walk(Action callback) + { + Walk((ptr, arg) => callback(ptr), IntPtr.Zero); + } + + private void Walk(LibuvFunctions.uv_walk_cb callback, IntPtr arg) + { + _libuv.walk( + _loop, + callback, + arg + ); + } + + private void PostCloseHandle(Action callback, IntPtr handle) + { + EnqueueCloseHandle(callback, handle); + _post.Send(); + } + + private void EnqueueCloseHandle(Action callback, IntPtr handle) + { + var closeHandle = new CloseHandle { Callback = callback, Handle = handle }; + lock (_closeHandleSync) + { + _closeHandleAdding.Enqueue(closeHandle); + } + } + + private void ThreadStart(object parameter) + { + lock (_startSync) + { + var tcs = (TaskCompletionSource)parameter; + try + { + _loop.Init(_libuv); + _post.Init(_loop, OnPost, EnqueueCloseHandle); + _initCompleted = true; + tcs.SetResult(0); + } + catch (Exception ex) + { + tcs.SetException(ex); + return; + } + } + + try + { + _loop.Run(); + if (_stopImmediate) + // thread-abort form of exit, resources will be leaked + return; + + // run the loop one more time to delete the open handles + _post.Reference(); + _post.Dispose(); + + // We need this walk because we call ReadStop on accepted connections when there's back pressure + // Calling ReadStop makes the handle as in-active which means the loop can + // end while there's still valid handles around. This makes loop.Dispose throw + // with an EBUSY. To avoid that, we walk all of the handles and dispose them. + Walk(ptr => + { + var handle = UvMemory.FromIntPtr(ptr); + // handle can be null because UvMemory.FromIntPtr looks up a weak reference + handle?.Dispose(); + }); + + // Ensure the Dispose operations complete in the event loop. + _loop.Run(); + + _loop.Dispose(); + } + catch (Exception ex) + { + _closeError = ex; + // Request shutdown so we can rethrow this exception + // in Stop which should be observable. + _appLifetime.StopApplication(); + } + finally + { + try + { + MemoryPool.Dispose(); + } + catch (Exception ex) + { + _closeError = _closeError == null ? ex : new AggregateException(_closeError, ex); + } + WriteReqPool.Dispose(); + _threadTcs.SetResult(null); + +#if DEBUG && !INNER_LOOP + // Check for handle leaks after disposing everything + CheckUvReqLeaks(); +#endif + } + } + + private void OnPost() + { + var loopsRemaining = _maxLoops; + bool wasWork; + do + { + wasWork = DoPostWork(); + wasWork = DoPostCloseHandle() || wasWork; + loopsRemaining--; + } while (wasWork && loopsRemaining > 0); + } + + private bool DoPostWork() + { + Queue queue; + lock (_workSync) + { + queue = _workAdding; + _workAdding = _workRunning; + _workRunning = queue; + } + + bool wasWork = queue.Count > 0; + + while (queue.Count != 0) + { + var work = queue.Dequeue(); + try + { + work.CallbackAdapter(work.Callback, work.State); + work.Completion?.TrySetResult(null); + } + catch (Exception ex) + { + if (work.Completion != null) + { + work.Completion.TrySetException(ex); + } + else + { + _log.LogError(0, ex, $"{nameof(LibuvThread)}.{nameof(DoPostWork)}"); + throw; + } + } + } + + return wasWork; + } + + private bool DoPostCloseHandle() + { + Queue queue; + lock (_closeHandleSync) + { + queue = _closeHandleAdding; + _closeHandleAdding = _closeHandleRunning; + _closeHandleRunning = queue; + } + + bool wasWork = queue.Count > 0; + + while (queue.Count != 0) + { + var closeHandle = queue.Dequeue(); + try + { + closeHandle.Callback(closeHandle.Handle); + } + catch (Exception ex) + { + _log.LogError(0, ex, $"{nameof(LibuvThread)}.{nameof(DoPostCloseHandle)}"); + throw; + } + } + + return wasWork; + } + + private static async Task WaitAsync(Task task, TimeSpan timeout) => + await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task; + + public override void Schedule(Action action, object state) + { + Post(action, state); + } + + private struct Work + { + public Action CallbackAdapter; + public object Callback; + public object State; + public TaskCompletionSource Completion; + } + + private struct CloseHandle + { + public Action Callback; + public IntPtr Handle; + } + + private class CallbackAdapter + { + public static readonly Action PostCallbackAdapter = (callback, state) => ((Action)callback).Invoke((T)state); + public static readonly Action PostAsyncCallbackAdapter = (callback, state) => ((Action)callback).Invoke((T)state); + } + } +} diff --git a/Projects/Server/LibUv/LibuvTrace.cs b/Projects/Server/LibUv/LibuvTrace.cs new file mode 100644 index 000000000..ebc090316 --- /dev/null +++ b/Projects/Server/LibUv/LibuvTrace.cs @@ -0,0 +1,94 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Libuv +{ + public class LibuvTrace : ILibuvTrace + { + // ConnectionRead: Reserved: 3 + + private static readonly Action _connectionPause = + LoggerMessage.Define(LogLevel.Debug, new EventId(4, nameof(ConnectionPause)), @"Connection id ""{ConnectionId}"" paused."); + + private static readonly Action _connectionResume = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, nameof(ConnectionResume)), @"Connection id ""{ConnectionId}"" resumed."); + + private static readonly Action _connectionReadFin = + LoggerMessage.Define(LogLevel.Debug, new EventId(6, nameof(ConnectionReadFin)), @"Connection id ""{ConnectionId}"" received FIN."); + + private static readonly Action _connectionWriteFin = + LoggerMessage.Define(LogLevel.Debug, new EventId(7, nameof(ConnectionWriteFin)), @"Connection id ""{ConnectionId}"" sending FIN because: ""{Reason}"""); + + // ConnectionWrite: Reserved: 11 + + // ConnectionWriteCallback: Reserved: 12 + + private static readonly Action _connectionError = + LoggerMessage.Define(LogLevel.Debug, new EventId(14, nameof(ConnectionError)), @"Connection id ""{ConnectionId}"" communication error."); + + private static readonly Action _connectionReset = + LoggerMessage.Define(LogLevel.Debug, new EventId(19, nameof(ConnectionReset)), @"Connection id ""{ConnectionId}"" reset."); + + private readonly ILogger _logger; + + public LibuvTrace(ILogger logger) => _logger = logger; + + public void ConnectionRead(string connectionId, int count) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 3 + } + + public void ConnectionReadFin(string connectionId) + { + _connectionReadFin(_logger, connectionId, null); + } + + public void ConnectionWriteFin(string connectionId, string reason) + { + _connectionWriteFin(_logger, connectionId, reason, null); + } + + public void ConnectionWrite(string connectionId, int count) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 11 + } + + public void ConnectionWriteCallback(string connectionId, int status) + { + // Don't log for now since this could be *too* verbose. + // Reserved: Event ID 12 + } + + public void ConnectionError(string connectionId, Exception ex) + { + _connectionError(_logger, connectionId, ex); + } + + public void ConnectionReset(string connectionId) + { + _connectionReset(_logger, connectionId, null); + } + + public void ConnectionPause(string connectionId) + { + _connectionPause(_logger, connectionId, null); + } + + public void ConnectionResume(string connectionId) + { + _connectionResume(_logger, connectionId, null); + } + + public IDisposable BeginScope(TState state) => _logger.BeginScope(state); + + public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled(logLevel); + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + => _logger.Log(logLevel, eventId, state, exception, formatter); + } +} diff --git a/Projects/Server/LibUv/LibuvTransportContext.cs b/Projects/Server/LibUv/LibuvTransportContext.cs new file mode 100644 index 000000000..730f31eb3 --- /dev/null +++ b/Projects/Server/LibUv/LibuvTransportContext.cs @@ -0,0 +1,16 @@ +// 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 Microsoft.Extensions.Hosting; + +namespace Libuv +{ + public class LibuvTransportContext + { + public LibuvTransportOptions Options { get; set; } + + public IHostApplicationLifetime AppLifetime { get; set; } + + public ILibuvTrace Log { get; set; } + } +} diff --git a/Projects/Server/LibUv/LibuvTransportOptions.cs b/Projects/Server/LibUv/LibuvTransportOptions.cs new file mode 100644 index 000000000..9de07cc83 --- /dev/null +++ b/Projects/Server/LibUv/LibuvTransportOptions.cs @@ -0,0 +1,51 @@ +// 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.Buffers; + +namespace Libuv +{ + /// + /// Provides programmatic configuration of Libuv transport features. + /// + public class LibuvTransportOptions + { + /// + /// The number of libuv I/O threads used to process requests. + /// + /// + /// Defaults to half of rounded down and clamped between 1 and 16. + /// + public int ThreadCount { get; set; } = ProcessorThreadCount; + + /// + /// The maximum length of the pending connection queue. + /// + /// + /// Defaults to 128. + /// + public int Backlog { get; set; } = 128; + + public long? MaxReadBufferSize { get; set; } = 1024 * 1024; + + public long? MaxWriteBufferSize { get; set; } = 64 * 1024; + + internal Func> MemoryPoolFactory { get; set; } = SlabMemoryPoolFactory.Create; + + private static int ProcessorThreadCount + { + get + { + // Actual core count would be a better number + // rather than logical cores which includes hyper-threaded cores. + // Divide by 2 for hyper-threading, and good defaults (still need threads for the game thread). + var threadCount = Environment.ProcessorCount >> 1; + + // Receive Side Scaling RSS Processor count currently maxes out at 16 + // would be better to check the NIC's current hardware queues; but xplat... + return threadCount < 1 ? 1 : threadCount > 16 ? 16 : threadCount; + } + } + } +} diff --git a/Projects/Server/LibUv/Listener.cs b/Projects/Server/LibUv/Listener.cs new file mode 100644 index 000000000..4ed799b78 --- /dev/null +++ b/Projects/Server/LibUv/Listener.cs @@ -0,0 +1,205 @@ +// 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.Net; +using System.Net.Sockets; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Logging; + +namespace Libuv +{ + /// + /// Base class for listeners in Kestrel. Listens for incoming connections + /// + public class Listener : ListenerContext, IAsyncDisposable + { + // REVIEW: This needs to be bounded and we need a strategy for what to do when the queue is full + private bool _closed; + + public Listener(LibuvTransportContext transportContext) : base(transportContext) + { + } + + protected UvStreamHandle ListenSocket { get; private set; } + + public ILibuvTrace Log => TransportContext.Log; + + public Task StartAsync( + EndPoint endPoint, + LibuvThread thread) + { + EndPoint = endPoint; + Thread = thread; + + return Thread.PostAsync(listener => + { + listener.ListenSocket = listener.CreateListenSocket(); + listener.ListenSocket.Listen(TransportContext.Options.Backlog, ConnectionCallback, listener); + }, this); + } + + /// + /// Creates the socket used to listen for incoming connections + /// + private UvStreamHandle CreateListenSocket() + { + return EndPoint switch + { + IPEndPoint _ => ListenTcp(false), + UnixDomainSocketEndPoint _ => ListenPipe(false), + FileHandleEndPoint _ => ListenHandle(), + _ => throw new NotSupportedException() + }; + } + + private UvTcpHandle ListenTcp(bool useFileHandle) + { + var socket = new UvTcpHandle(Log); + + try + { + socket.Init(Thread.Loop, Thread.QueueCloseHandle); + socket.NoDelay(true); + + if (!useFileHandle) + { + socket.Bind((IPEndPoint)EndPoint); + + // If requested port was "0", replace with assigned dynamic port. + EndPoint = socket.GetSockIPEndPoint(); + } + else + { + socket.Open((IntPtr)((FileHandleEndPoint)EndPoint).FileHandle); + } + } + catch + { + socket.Dispose(); + throw; + } + + return socket; + } + + private UvPipeHandle ListenPipe(bool useFileHandle) + { + var pipe = new UvPipeHandle(Log); + + try + { + pipe.Init(Thread.Loop, Thread.QueueCloseHandle, false); + + if (!useFileHandle) + // UnixDomainSocketEndPoint.ToString() returns the path + pipe.Bind(EndPoint.ToString()); + else + pipe.Open((IntPtr)((FileHandleEndPoint)EndPoint).FileHandle); + } + catch + { + pipe.Dispose(); + throw; + } + + return pipe; + } + + private UvStreamHandle ListenHandle() + { + var handleEndPoint = (FileHandleEndPoint)EndPoint; + + switch (handleEndPoint.FileHandleType) + { + case FileHandleType.Auto: + break; + case FileHandleType.Tcp: + return ListenTcp(true); + case FileHandleType.Pipe: + return ListenPipe(true); + default: + throw new NotSupportedException(); + } + + UvStreamHandle handle; + try + { + handle = ListenTcp(true); + EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Tcp); + return handle; + } + catch (UvException exception) when (exception.StatusCode == LibuvConstants.ENOTSUP) + { + Log.LogDebug(0, exception, "Listener.ListenHandle"); + } + + handle = ListenPipe(true); + EndPoint = new FileHandleEndPoint(handleEndPoint.FileHandle, FileHandleType.Pipe); + return handle; + } + + private static void ConnectionCallback(UvStreamHandle stream, int status, UvException error, object state) + { + var listener = (Listener)state; + + if (error != null) + listener.Log.LogError(0, error, "Listener.ConnectionCallback"); + else if (!listener._closed) listener.OnConnection(stream, status); + } + + /// + /// Handles an incoming connection + /// + /// Socket being used to listen on + /// Connection status + private void OnConnection(UvStreamHandle listenSocket, int status) + { + UvStreamHandle acceptSocket = null; + + try + { + acceptSocket = CreateAcceptSocket(); + listenSocket.Accept(acceptSocket); + DispatchConnection(acceptSocket); + } + catch (UvException ex) when (LibuvConstants.IsConnectionReset(ex.StatusCode)) + { + Log.ConnectionReset("(null)"); + acceptSocket?.Dispose(); + } + catch (UvException ex) + { + Log.LogError(0, ex, "Listener.OnConnection"); + acceptSocket?.Dispose(); + } + } + + protected virtual void DispatchConnection(UvStreamHandle socket) + { + HandleConnection(socket); + } + + public virtual async Task DisposeAsync() + { + // Ensure the event loop is still running. + // If the event loop isn't running and we try to wait on this Post + // to complete, then LibuvTransport will never be disposed and + // the exception that stopped the event loop will never be surfaced. + if (Thread.FatalError == null && ListenSocket != null) + await Thread.PostAsync(listener => + { + listener.ListenSocket.Dispose(); + + listener._closed = true; + + listener.StopAcceptingConnections(); + + }, this).ConfigureAwait(false); + + ListenSocket = null; + } + } +} diff --git a/Projects/Server/LibUv/ListenerContext.cs b/Projects/Server/LibUv/ListenerContext.cs new file mode 100644 index 000000000..52b5b9149 --- /dev/null +++ b/Projects/Server/LibUv/ListenerContext.cs @@ -0,0 +1,168 @@ +// 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.Diagnostics; +using System.IO.Pipelines; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Channels; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.Extensions.Logging; + +namespace Libuv +{ + public class ListenerContext + { + // Single reader, single writer queue since all writes happen from the uv thread and reads happen sequentially + private readonly Channel _acceptQueue = Channel.CreateUnbounded(new UnboundedChannelOptions + { + SingleReader = true, + SingleWriter = true + }); + + public ListenerContext(LibuvTransportContext transportContext) => TransportContext = transportContext; + + public LibuvTransportContext TransportContext { get; set; } + + public EndPoint EndPoint { get; set; } + + public LibuvThread Thread { get; set; } + + public PipeOptions InputOptions { get; set; } + + public PipeOptions OutputOptions { get; set; } + + public async ValueTask AcceptAsync(CancellationToken cancellationToken = default) + { + while (await _acceptQueue.Reader.WaitToReadAsync()) + while (_acceptQueue.Reader.TryRead(out var connection)) + return connection; + + return null; + } + + /// + /// Aborts all unaccepted connections in the queue + /// + /// + public async Task AbortQueuedConnectionAsync() + { + while (await _acceptQueue.Reader.WaitToReadAsync()) + while (_acceptQueue.Reader.TryRead(out var connection)) + // REVIEW: Pass an abort reason? + connection.Abort(); + } + + /// + /// Creates a socket which can be used to accept an incoming connection. + /// + protected UvStreamHandle CreateAcceptSocket() + { + switch (EndPoint) + { + case IPEndPoint _: + return AcceptTcp(); + case UnixDomainSocketEndPoint _: + return AcceptPipe(); + case FileHandleEndPoint _: + return AcceptHandle(); + default: + throw new InvalidOperationException(); + } + } + + protected internal void HandleConnection(UvStreamHandle socket) + { + try + { + IPEndPoint remoteEndPoint = null; + IPEndPoint localEndPoint = null; + + if (socket is UvTcpHandle tcpHandle) + try + { + remoteEndPoint = tcpHandle.GetPeerIPEndPoint(); + localEndPoint = tcpHandle.GetSockIPEndPoint(); + } + catch (UvException ex) when (LibuvConstants.IsConnectionReset(ex.StatusCode)) + { + TransportContext.Log.ConnectionReset("(null)"); + socket.Dispose(); + return; + } + + var options = TransportContext.Options; + var connection = new LibuvConnection(socket, TransportContext.Log, Thread, remoteEndPoint, localEndPoint, InputOptions, OutputOptions, options.MaxReadBufferSize, options.MaxWriteBufferSize); + connection.Start(); + + bool accepted = _acceptQueue.Writer.TryWrite(connection); + Debug.Assert(accepted, "The connection was not written to the channel!"); + } + catch (Exception ex) + { + TransportContext.Log.LogCritical(ex, $"Unexpected exception in {nameof(ListenerContext)}.{nameof(HandleConnection)}."); + } + } + + private UvTcpHandle AcceptTcp() + { + var socket = new UvTcpHandle(TransportContext.Log); + + try + { + socket.Init(Thread.Loop, Thread.QueueCloseHandle); + socket.NoDelay(true); + } + catch + { + socket.Dispose(); + throw; + } + + return socket; + } + + private UvPipeHandle AcceptPipe() + { + var pipe = new UvPipeHandle(TransportContext.Log); + + try + { + pipe.Init(Thread.Loop, Thread.QueueCloseHandle); + } + catch + { + pipe.Dispose(); + throw; + } + + return pipe; + } + + protected void StopAcceptingConnections() + { + _acceptQueue.Writer.TryComplete(); + } + + private UvStreamHandle AcceptHandle() + { + var fileHandleEndPoint = (FileHandleEndPoint)EndPoint; + + switch (fileHandleEndPoint.FileHandleType) + { + case FileHandleType.Auto: + throw new InvalidOperationException("Cannot accept on a non-specific file handle, listen should be performed first."); + case FileHandleType.Tcp: + return AcceptTcp(); + case FileHandleType.Pipe: + return AcceptPipe(); + default: + throw new NotSupportedException(); + } + } + } +} diff --git a/Projects/Server/LibUv/ListenerPrimary.cs b/Projects/Server/LibUv/ListenerPrimary.cs new file mode 100644 index 000000000..da006b7fe --- /dev/null +++ b/Projects/Server/LibUv/ListenerPrimary.cs @@ -0,0 +1,265 @@ +// 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.IO; +using System.Net; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.Extensions.Logging; +using Server; + +#pragma warning disable 169 + +namespace Libuv +{ + /// + /// A primary listener waits for incoming connections on a specified socket. Incoming + /// connections may be passed to a secondary listener to handle. + /// + public class ListenerPrimary : Listener + { + // The list of pipes that can be dispatched to (where we've confirmed the _pipeMessage) + private readonly List _dispatchPipes = new List(); + // The list of pipes we've created but may not be part of _dispatchPipes + private readonly List _createdPipes = new List(); + private int _dispatchIndex; + private string _pipeName; + private byte[] _pipeMessage; + private IntPtr _fileCompletionInfoPtr; + private bool _tryDetachFromIOCP = Core.IsWindows; + + // this message is passed to write2 because it must be non-zero-length, + // but it has no other functional significance + private readonly ArraySegment> _dummyMessage = new ArraySegment>(new[] { new ArraySegment(new byte[] { 1, 2, 3, 4 }) }); + + public ListenerPrimary(LibuvTransportContext transportContext) : base(transportContext) + { + } + + /// + /// For testing purposes. + /// + public int UvPipeCount => _dispatchPipes.Count; + + private UvPipeHandle ListenPipe { get; set; } + + public async Task StartAsync( + string pipeName, + byte[] pipeMessage, + EndPoint endPoint, + LibuvThread thread) + { + _pipeName = pipeName; + _pipeMessage = pipeMessage; + + if (_fileCompletionInfoPtr == IntPtr.Zero) + { + var fileCompletionInfo = new FILE_COMPLETION_INFORMATION { Key = IntPtr.Zero, Port = IntPtr.Zero }; + _fileCompletionInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fileCompletionInfo)); + Marshal.StructureToPtr(fileCompletionInfo, _fileCompletionInfoPtr, false); + } + + await StartAsync(endPoint, thread).ConfigureAwait(false); + + await Thread.PostAsync(listener => listener.PostCallback(), this).ConfigureAwait(false); + } + + private void PostCallback() + { + ListenPipe = new UvPipeHandle(Log); + ListenPipe.Init(Thread.Loop, Thread.QueueCloseHandle, false); + ListenPipe.Bind(_pipeName); + ListenPipe.Listen(TransportContext.Options.Backlog, + (pipe, status, error, state) => ((ListenerPrimary)state).OnListenPipe(pipe, status, error), this); + } + + private void OnListenPipe(UvStreamHandle pipe, int status, UvException error) + { + if (status < 0) return; + + var dispatchPipe = new UvPipeHandle(Log); + // Add to the list of created pipes for disposal tracking + _createdPipes.Add(dispatchPipe); + + try + { + dispatchPipe.Init(Thread.Loop, Thread.QueueCloseHandle, true); + pipe.Accept(dispatchPipe); + + // Ensure client sends _pipeMessage before adding pipe to _dispatchPipes. + var readContext = new PipeReadContext(this); + dispatchPipe.ReadStart( + (handle, status2, state) => ((PipeReadContext)state).AllocCallback(handle, status2), + (handle, status2, state) => ((PipeReadContext)state).ReadCallback(handle, status2), + readContext); + } + catch (UvException ex) + { + dispatchPipe.Dispose(); + Log.LogError(0, ex, "ListenerPrimary.OnListenPipe"); + } + } + + protected override void DispatchConnection(UvStreamHandle socket) + { + var index = _dispatchIndex++ % (_dispatchPipes.Count + 1); + if (index == _dispatchPipes.Count) + { + base.DispatchConnection(socket); + } + else + { + DetachFromIOCP(socket); + var dispatchPipe = _dispatchPipes[index]; + var write = new UvWriteReq(Log); + try + { + write.Init(Thread); + write.Write2( + dispatchPipe, + _dummyMessage, + socket, + (write2, status, error, state) => + { + write2.Dispose(); + ((UvStreamHandle)state).Dispose(); + }, + socket); + } + catch (UvException) + { + write.Dispose(); + throw; + } + } + } + + private void DetachFromIOCP(UvHandle handle) + { + if (!_tryDetachFromIOCP) return; + + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff728840(v=vs.85).aspx + const int FileReplaceCompletionInformation = 61; + // https://msdn.microsoft.com/en-us/library/cc704588.aspx + const uint STATUS_INVALID_INFO_CLASS = 0xC0000003; + + var statusBlock = new IO_STATUS_BLOCK(); + var socket = IntPtr.Zero; + Thread.Loop.Libuv.uv_fileno(handle, ref socket); + + if (NtSetInformationFile(socket, out statusBlock, _fileCompletionInfoPtr, + (uint)Marshal.SizeOf(), FileReplaceCompletionInformation) == STATUS_INVALID_INFO_CLASS) + // Replacing IOCP information is only supported on Windows 8.1 or newer + _tryDetachFromIOCP = false; + } + + private struct IO_STATUS_BLOCK + { + uint status; + ulong information; + } + + private struct FILE_COMPLETION_INFORMATION + { + public IntPtr Port; + public IntPtr Key; + } + + [DllImport("NtDll.dll")] + private static extern uint NtSetInformationFile(IntPtr FileHandle, + out IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, uint Length, + int FileInformationClass); + + public override async Task DisposeAsync() + { + // Call base first so the ListenSocket gets closed and doesn't + // try to dispatch connections to closed pipes. + await base.DisposeAsync().ConfigureAwait(false); + + if (_fileCompletionInfoPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_fileCompletionInfoPtr); + _fileCompletionInfoPtr = IntPtr.Zero; + } + + if (Thread.FatalError == null && ListenPipe != null) + await Thread.PostAsync(listener => + { + listener.ListenPipe.Dispose(); + + foreach (var pipe in listener._createdPipes) pipe.Dispose(); + }, this).ConfigureAwait(false); + } + + private class PipeReadContext + { + private const int _bufferLength = 16; + + private readonly ListenerPrimary _listener; + private readonly byte[] _buf = new byte[_bufferLength]; + private readonly IntPtr _bufPtr; + private GCHandle _bufHandle; + private int _bytesRead; + + public PipeReadContext(ListenerPrimary listener) + { + _listener = listener; + _bufHandle = GCHandle.Alloc(_buf, GCHandleType.Pinned); + _bufPtr = _bufHandle.AddrOfPinnedObject(); + } + + public LibuvFunctions.uv_buf_t AllocCallback(UvStreamHandle dispatchPipe, int suggestedSize) => dispatchPipe.Libuv.buf_init(_bufPtr + _bytesRead, _bufferLength - _bytesRead); + + public void ReadCallback(UvStreamHandle dispatchPipe, int status) + { + if (status == LibuvConstants.EOF && _bytesRead == 0) + { + // This is an unexpected immediate termination of the dispatch pipe most likely caused by an + // external process scanning the pipe, so don't we don't log it too severely. + // https://github.com/aspnet/AspNetCore/issues/4741 + + dispatchPipe.Dispose(); + _bufHandle.Free(); + _listener.Log.LogDebug("An internal pipe was opened unexpectedly."); + return; + } + + try + { + dispatchPipe.Libuv.ThrowIfErrored(status); + + _bytesRead += status; + + if (_bytesRead == _bufferLength) + { + var correctMessage = true; + + for (var i = 0; i < _bufferLength; i++) + if (_buf[i] != _listener._pipeMessage[i]) + correctMessage = false; + + if (correctMessage) + { + _listener._dispatchPipes.Add((UvPipeHandle)dispatchPipe); + dispatchPipe.ReadStop(); + _bufHandle.Free(); + } + else + { + throw new IOException("Bad data sent over an internal pipe."); + } + } + } + catch (Exception ex) + { + dispatchPipe.Dispose(); + _bufHandle.Free(); + _listener.Log.LogError(0, ex, "ListenerPrimary.ReadCallback"); + } + } + } + } +} diff --git a/Projects/Server/LibUv/ListenerSecondary.cs b/Projects/Server/LibUv/ListenerSecondary.cs new file mode 100644 index 000000000..4377a4e9c --- /dev/null +++ b/Projects/Server/LibUv/ListenerSecondary.cs @@ -0,0 +1,192 @@ +// 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.Net; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Libuv.Internal; +using Microsoft.Extensions.Logging; + +namespace Libuv +{ + /// + /// A secondary listener is delegated requests from a primary listener via a named pipe or + /// UNIX domain socket. + /// + public class ListenerSecondary : ListenerContext, IAsyncDisposable + { + private string _pipeName; + private byte[] _pipeMessage; + private IntPtr _ptr; + private LibuvFunctions.uv_buf_t _buf; + private bool _closed; + + public ListenerSecondary(LibuvTransportContext transportContext) : base(transportContext) => _ptr = Marshal.AllocHGlobal(4); + + UvPipeHandle DispatchPipe { get; set; } + + public ILibuvTrace Log => TransportContext.Log; + + public Task StartAsync( + string pipeName, + byte[] pipeMessage, + EndPoint endPoint, + LibuvThread thread) + { + _pipeName = pipeName; + _pipeMessage = pipeMessage; + _buf = thread.Loop.Libuv.buf_init(_ptr, 4); + + EndPoint = endPoint; + Thread = thread; + DispatchPipe = new UvPipeHandle(Log); + + var tcs = new TaskCompletionSource(this, TaskCreationOptions.RunContinuationsAsynchronously); + Thread.Post(StartCallback, tcs); + return tcs.Task; + } + + private static void StartCallback(TaskCompletionSource tcs) + { + var listener = (ListenerSecondary)tcs.Task.AsyncState; + listener.StartedCallback(tcs); + } + + private void StartedCallback(TaskCompletionSource tcs) + { + var connect = new UvConnectRequest(Log); + try + { + DispatchPipe.Init(Thread.Loop, Thread.QueueCloseHandle, true); + connect.Init(Thread); + connect.Connect( + DispatchPipe, + _pipeName, + (connect2, status, error, state) => ConnectCallback(connect2, status, error, (TaskCompletionSource)state), + tcs); + } + catch (Exception ex) + { + DispatchPipe.Dispose(); + connect.Dispose(); + tcs.SetException(ex); + } + } + + private static void ConnectCallback(UvConnectRequest connect, int status, UvException error, TaskCompletionSource tcs) + { + var listener = (ListenerSecondary)tcs.Task.AsyncState; + _ = listener.ConnectedCallback(connect, status, error, tcs); + } + + private async Task ConnectedCallback(UvConnectRequest connect, int status, UvException error, TaskCompletionSource tcs) + { + connect.Dispose(); + if (error != null) + { + tcs.SetException(error); + return; + } + + var writeReq = new UvWriteReq(Log); + + try + { + DispatchPipe.ReadStart( + (handle, status2, state) => ((ListenerSecondary)state)._buf, + (handle, status2, state) => ((ListenerSecondary)state).ReadStartCallback(handle, status2), + this); + + writeReq.Init(Thread); + var result = await writeReq.WriteAsync( + DispatchPipe, + new ArraySegment>(new[] { new ArraySegment(_pipeMessage) })); + + if (result.Error != null) + tcs.SetException(result.Error); + else + tcs.SetResult(0); + } + catch (Exception ex) + { + DispatchPipe.Dispose(); + tcs.SetException(ex); + } + finally + { + writeReq.Dispose(); + } + } + + private void ReadStartCallback(UvStreamHandle handle, int status) + { + if (status < 0) + { + if (status != LibuvConstants.EOF) + { + Thread.Loop.Libuv.Check(status, out var ex); + Log.LogError(0, ex, "DispatchPipe.ReadStart"); + } + + DispatchPipe.Dispose(); + return; + } + + if (_closed || DispatchPipe.PendingCount() == 0) return; + + var acceptSocket = CreateAcceptSocket(); + + try + { + DispatchPipe.Accept(acceptSocket); + + HandleConnection(acceptSocket); + } + catch (UvException ex) when (LibuvConstants.IsConnectionReset(ex.StatusCode)) + { + Log.ConnectionReset("(null)"); + acceptSocket.Dispose(); + } + catch (UvException ex) + { + Log.LogError(0, ex, "DispatchPipe.Accept"); + acceptSocket.Dispose(); + } + } + + private void FreeBuffer() + { + var ptr = Interlocked.Exchange(ref _ptr, IntPtr.Zero); + if (ptr != IntPtr.Zero) Marshal.FreeHGlobal(ptr); + } + + public async Task DisposeAsync() + { + // Ensure the event loop is still running. + // If the event loop isn't running and we try to wait on this Post + // to complete, then LibuvTransport will never be disposed and + // the exception that stopped the event loop will never be surfaced. + if (Thread.FatalError == null) + { + await Thread.PostAsync(listener => + { + listener.DispatchPipe.Dispose(); + listener.FreeBuffer(); + + listener._closed = true; + + listener.StopAcceptingConnections(); + + }, this).ConfigureAwait(false); + } + else + { + FreeBuffer(); + + StopAcceptingConnections(); + } + } + } +} diff --git a/Projects/Server/LibUv/WriteReqPool.cs b/Projects/Server/LibUv/WriteReqPool.cs new file mode 100644 index 000000000..745874344 --- /dev/null +++ b/Projects/Server/LibUv/WriteReqPool.cs @@ -0,0 +1,63 @@ +// 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 Libuv.Internal; + +namespace Libuv +{ + public class WriteReqPool + { + private const int _maxPooledWriteReqs = 1024; + + private readonly LibuvThread _thread; + private readonly Queue _pool = new Queue(_maxPooledWriteReqs); + private readonly ILibuvTrace _log; + private bool _disposed; + + public WriteReqPool(LibuvThread thread, ILibuvTrace log) + { + _thread = thread; + _log = log; + } + + public UvWriteReq Allocate() + { + if (_disposed) throw new ObjectDisposedException(GetType().Name); + + UvWriteReq req; + if (_pool.Count > 0) + { + req = _pool.Dequeue(); + } + else + { + req = new UvWriteReq(_log); + req.Init(_thread); + } + + return req; + } + + public void Return(UvWriteReq req) + { + if (_disposed) throw new ObjectDisposedException(GetType().Name); + + if (_pool.Count < _maxPooledWriteReqs) + _pool.Enqueue(req); + else + req.Dispose(); + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + + while (_pool.Count > 0) _pool.Dequeue().Dispose(); + } + } + } +} diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 53a975885..bb1b29bc9 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -143,9 +143,13 @@ namespace Server public static int ProcessorCount{ get; private set; } - public static bool Unix{ get; private set; } + public static bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsDarwin = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + public static bool IsFreeBSD = RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); + public static bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || IsFreeBSD; + public static bool Unix = IsDarwin || IsFreeBSD || IsLinux; - public static string ExePath => m_ExePath ?? (m_ExePath = Assembly.Location); + public static string ExePath => m_ExePath ??= Assembly.Location; public static string BaseDirectory { @@ -261,8 +265,9 @@ namespace Server { try { - foreach (Listener l in MessagePump.Listeners) - l.Dispose(); + Task.WhenAll( + MessagePump.Listeners.Select(listener => listener.Dispose()) + ).Wait(); } catch { @@ -413,14 +418,7 @@ namespace Server Console.WriteLine("Core: Optimizing for {0} {2}processor{1}", ProcessorCount, ProcessorCount == 1 ? "" : "s", Is64Bit ? "64-bit " : ""); - int platform = (int)Environment.OSVersion.Platform; - if (platform == 4 || platform == 128) - { - // MS 4, MONO 128 - Unix = true; - Console.WriteLine("Core: Unix environment detected"); - } - else + if (IsWindows) { m_ConsoleEventHandler = OnConsoleEvent; UnsafeNativeMethods.SetConsoleCtrlHandler(m_ConsoleEventHandler, true); diff --git a/Projects/Server/Mobile.cs b/Projects/Server/Mobile.cs index 0fe1f420e..5a000073c 100644 --- a/Projects/Server/Mobile.cs +++ b/Projects/Server/Mobile.cs @@ -1445,7 +1445,7 @@ namespace Server [CommandProperty(AccessLevel.GameMaster, AccessLevel.Owner)] public NetState NetState { - get => m_NetState?.Socket != null && !m_NetState.IsDisposing ? m_NetState : null; + get => m_NetState?.Connection != null && !m_NetState.IsDisposing ? m_NetState : null; set { if (m_NetState != value) diff --git a/Projects/Server/Network/Listener.cs b/Projects/Server/Network/Listener.cs index 2146f9e16..f0b929748 100644 --- a/Projects/Server/Network/Listener.cs +++ b/Projects/Server/Network/Listener.cs @@ -24,53 +24,54 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Libuv; +using Libuv.Internal; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; namespace Server.Network { public class Listener { - private Socket m_Socket; - private IPEndPoint m_EndPoint; + private static readonly LibuvFunctions functions = new LibuvFunctions(); + + private readonly IPEndPoint m_EndPoint; + private LibuvConnectionListener m_Listener; + public Listener(IPEndPoint ipep) { -#pragma warning disable IDE0068 // Use recommended dispose pattern - m_Socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp); -#pragma warning restore IDE0068 // Use recommended dispose pattern - - m_Socket.LingerState.Enabled = false; - m_Socket.ExclusiveAddressUse = false; m_EndPoint = ipep; + LibuvTransportContext transport = new LibuvTransportContext + { + Options = new LibuvTransportOptions(), + AppLifetime = new ApplicationLifetime( + LoggerFactory.Create(builder => { builder.AddConsole(); }).CreateLogger() + ), + Log = new LibuvTrace(LoggerFactory.Create(builder => { builder.AddConsole(); }).CreateLogger("network")) + }; + + m_Listener = new LibuvConnectionListener(functions, transport, ipep); } public virtual async Task Start(MessagePump pump) { try { - m_Socket.Bind(m_EndPoint); - m_Socket.Listen(8); + await m_Listener.BindAsync(); + } + catch (AddressInUseException) + { + Console.WriteLine("Listener Failed: {0}:{1} (In Use)", m_EndPoint.Address, m_EndPoint.Port); + m_Listener = null; + return; } catch (Exception e) { - if (e is SocketException se) - { - if (se.ErrorCode == 10048) - { - // WSAEADDRINUSE - Console.WriteLine("Listener Failed: {0}:{1} (In Use)", m_EndPoint.Address, m_EndPoint.Port); - } - else if (se.ErrorCode == 10049) - { - // WSAEADDRNOTAVAIL - Console.WriteLine("Listener Failed: {0}:{1} (Unavailable)", m_EndPoint.Address, m_EndPoint.Port); - } - else - { - Console.WriteLine("Listener Exception:"); - Console.WriteLine(e); - } - } + Console.WriteLine("Listener Exception:"); + Console.WriteLine(e); - m_Socket = null; + m_Listener = null; return; } @@ -78,10 +79,10 @@ namespace Server.Network while (true) { - Socket s; + ConnectionContext context; try { - s = await m_Socket.AcceptAsync().ConfigureAwait(false); + context = await m_Listener.AcceptAsync(); } catch (SocketException ex) { @@ -89,16 +90,16 @@ namespace Server.Network continue; } - if (VerifySocket(s)) - _ = new NetState(s, pump); + if (VerifySocket(context)) + _ = new NetState(context, pump); else - Release(s); + Release(context); } } private void DisplayListener() { - if (!(m_Socket.LocalEndPoint is IPEndPoint ipep)) + if (!(m_Listener.EndPoint is IPEndPoint ipep)) return; if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any)) @@ -113,16 +114,14 @@ namespace Server.Network } } else - { Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port); - } } - private bool VerifySocket(Socket socket) + private static bool VerifySocket(ConnectionContext context) { try { - SocketConnectEventArgs args = new SocketConnectEventArgs(socket); + SocketConnectEventArgs args = new SocketConnectEventArgs(context); EventSink.InvokeSocketConnect(args); @@ -131,44 +130,47 @@ namespace Server.Network catch (Exception ex) { NetState.TraceException(ex); - return false; } } - private void Release(Socket socket) + private static void Release(ConnectionContext context) { try { - socket.Shutdown(SocketShutdown.Both); + context.Abort(new ConnectionAbortedException("Failed socket verification.")); } - catch (SocketException ex) + catch (Exception ex) { NetState.TraceException(ex); } try { - socket.Close(); + // TODO: Is this needed? + context.DisposeAsync(); } - catch (SocketException ex) - { - NetState.TraceException(ex); - } - - try - { - socket.Dispose(); - } - catch (SocketException ex) + catch (Exception ex) { NetState.TraceException(ex); } } - public void Dispose() + public async Task Dispose() { - Interlocked.Exchange(ref m_Socket, null)?.Close(); + LibuvConnectionListener listener = Interlocked.Exchange(ref m_Listener, null); + if (listener != null) + try + { + await listener.UnbindAsync(); + await listener.DisposeAsync(); + } + catch (Exception ex) + { + Console.WriteLine("Listener: Failed to dispose."); + Console.WriteLine(ex); + } + GC.SuppressFinalize(this); } } diff --git a/Projects/Server/Network/MessagePump.cs b/Projects/Server/Network/MessagePump.cs index 0919630d9..3904d73aa 100644 --- a/Projects/Server/Network/MessagePump.cs +++ b/Projects/Server/Network/MessagePump.cs @@ -23,6 +23,8 @@ using System; using System.Buffers; using System.Collections.Concurrent; using System.Net; +using System.Threading.Tasks; +using SignalR; namespace Server.Network { @@ -40,9 +42,9 @@ namespace Server.Network listeners[Listeners.Length] = listener; } - public void QueueWork(NetState ns, in ReadOnlySequence seq, OnPacketReceive onReceive) + public void QueueWork(NetState ns, IMemoryOwner memOwner, OnPacketReceive onReceive) { - m_WorkQueue.Enqueue(new Work(ns, seq, onReceive)); + m_WorkQueue.Enqueue(new Work(ns, memOwner, onReceive)); Core.Set(); } @@ -54,21 +56,22 @@ namespace Server.Network if (!m_WorkQueue.TryDequeue(out Work work)) break; - work.OnReceive(work.State, new PacketReader(work.Sequence)); + work.OnReceive(work.State, new PacketReader(new ReadOnlySequence(work.MemoryOwner.Memory))); + work.MemoryOwner.Dispose(); } } - // TODO: Optimize this with a pool private class Work { - public NetState State; - public ReadOnlySequence Sequence; - public OnPacketReceive OnReceive; + public readonly NetState State; + // TODO: Force dispose? + public readonly IMemoryOwner MemoryOwner; + public readonly OnPacketReceive OnReceive; - public Work(NetState ns, in ReadOnlySequence seq, OnPacketReceive onReceive) + public Work(NetState ns, IMemoryOwner memOwner, OnPacketReceive onReceive) { State = ns; - Sequence = seq; + MemoryOwner = memOwner; OnReceive = onReceive; } } diff --git a/Projects/Server/Network/NetState.cs b/Projects/Server/Network/NetState.cs index 14355146d..290f6a6e9 100644 --- a/Projects/Server/Network/NetState.cs +++ b/Projects/Server/Network/NetState.cs @@ -28,6 +28,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; using Server.Accounting; using Server.Diagnostics; using Server.Gumps; @@ -93,7 +94,6 @@ namespace Server.Network { private string m_ToString; private ClientVersion m_Version; - private SendQueue m_SendQueue = new SendQueue(); public DateTime ConnectedOn { get; } @@ -266,11 +266,9 @@ namespace Server.Network return newTrade.From.Container; } - public bool Running { get; private set; } - public bool Seeded { get; set; } - public Socket Socket { get; private set; } + public ConnectionContext Connection { get; private set; } public bool CompressionEnabled { get; set; } @@ -282,12 +280,16 @@ namespace Server.Network public List Menus { get; private set; } + public PipeReader RecvPipe => Connection.Transport.Input; + public PipeWriter SendPipe => Connection.Transport.Output; + public static int GumpCap { get; set; } = 512; public static int HuePickerCap { get; set; } = 512; public static int MenuCap { get; set; } = 512; + public void WriteConsole(string text) { Console.WriteLine("Client: {0}: {1}", this, text); @@ -403,23 +405,24 @@ namespace Server.Network public static List Instances { get; } = new List(); - public NetState(Socket socket, MessagePump pump) + public void SetConnectionAlive() => m_NextCheckActivity = Core.TickCount + 60000; + + public NetState(ConnectionContext connection, MessagePump pump) { - Socket = socket; + Connection = connection; Seeded = false; - Running = false; Gumps = new List(); HuePickers = new List(); Menus = new List(); Trades = new List(); - m_NextCheckActivity = Core.TickCount + 30000; + SetConnectionAlive(); Instances.Add(this); try { - Address = Utility.Intern(((IPEndPoint)Socket.RemoteEndPoint).Address); + Address = Utility.Intern(((IPEndPoint)Connection.RemoteEndPoint).Address); m_ToString = Address.ToString(); } catch (Exception ex) @@ -430,10 +433,11 @@ namespace Server.Network } ConnectedOn = DateTime.UtcNow; - _ = Start(pump); + Console.WriteLine("Client: {0}: Connected. [{1} Online]", this, Instances.Count); + + _ = ProcessRecvs(pump); CreatedCallback?.Invoke(this); - Console.WriteLine("Client: {0}: Connected. [{1} Online]", this, NetState.Instances.Count); } public static void Pause() @@ -448,13 +452,38 @@ namespace Server.Network public virtual void Send(Packet p) { - if (Socket == null || BlockAllPackets) + if (Connection == null || BlockAllPackets) { p.OnSend(); return; } - m_SendQueue.Enqueue(p); + try + { + ReadOnlySpan buffer = p.Compile(CompressionEnabled, out int length); + + if (buffer.Length <= 0 || length <= 0) + { + p.OnSend(); + return; + } + + buffer.Slice(0, length).CopyTo(SendPipe.GetSpan(length)); + SendPipe.Advance(length); + SendPipe.FlushAsync().GetAwaiter().GetResult(); + + p.OnSend(); + } + catch (SocketException ex) + { + Console.WriteLine(ex); + TraceException(ex); + Dispose(); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } } public bool CheckEncrypted(int packetID) @@ -470,121 +499,31 @@ namespace Server.Network return false; } - private Pipe m_RecvdPipe; - - private async Task Start(MessagePump pump) + private async Task ProcessRecvs(MessagePump pump) { - m_RecvdPipe = new Pipe(); - Running = true; - - await Task.WhenAll(ProcessSends(), ProcessRecvs(), HandlePackets(pump)).ConfigureAwait(false); - } - - private async Task ProcessRecvs() - { - PipeWriter w = m_RecvdPipe.Writer; - while (true) { - if (m_AsyncState.Paused) - { - await Timer.Pause(50).ConfigureAwait(false); - continue; - } - - try - { - Memory memory = w.GetMemory(); - int bytesRead = await Socket.ReceiveAsync(memory, SocketFlags.None).ConfigureAwait(false); - if (bytesRead == 0) - break; - - Interlocked.Exchange(ref m_NextCheckActivity, Core.TickCount + 90000); - - w.Advance(bytesRead); - - FlushResult result = await w.FlushAsync().ConfigureAwait(false); - if (result.IsCompleted || result.IsCanceled) - break; - } - catch - { - break; - } - } - - w.Complete(); - Dispose(); - } - - private async Task ProcessSends() - { - while (true) - try - { - Packet p = await m_SendQueue?.DequeueAsync(); - if (p == null) - break; - - ReadOnlyMemory buffer = p.Compile(CompressionEnabled, out int length); - - if (buffer.Length <= 0 || length <= 0) - { - p.OnSend(); - return; - } - - buffer = buffer.Slice(0, length); - - PacketSendProfile prof = null; - - if (Core.Profiling) - prof = PacketSendProfile.Acquire(p.GetType()); - - prof?.Start(); - - await Socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false); - - p.OnSend(); - - prof?.Finish(length); - } - catch (SocketException ex) - { - Console.WriteLine(ex); - TraceException(ex); - Dispose(); - break; - } - catch (Exception ex) - { - Console.WriteLine(ex); - } - } - - private async Task HandlePackets(MessagePump pump) - { - PipeReader pr = m_RecvdPipe.Reader; - - while (true) - { - ReadResult result = await pr.ReadAsync(); + ReadResult result = await RecvPipe.ReadAsync(); ReadOnlySequence seq = result.Buffer; - if (seq.Length == 0) + + if (seq.IsEmpty) break; - long pos = PacketHandlers.ProcessPacket(pump, this, seq); + SetConnectionAlive(); + + int pos = PacketHandlers.ProcessPacket(pump, this, seq); if (pos <= 0) break; - pr.AdvanceTo(seq.GetPosition(pos, seq.Start)); + RecvPipe.AdvanceTo(seq.Slice(0, pos).End); if (result.IsCompleted || result.IsCanceled) break; } - pr.Complete(); + RecvPipe.Complete(); + Dispose(); } public PacketHandler GetHandler(int packetID) => @@ -595,7 +534,7 @@ namespace Server.Network public void CheckAlive(long curTicks) { - if (Socket == null || m_NextCheckActivity - curTicks >= 0) + if (Connection == null || m_NextCheckActivity - curTicks >= 0) return; Console.WriteLine("Client: {0}: Disconnecting due to inactivity...", this); @@ -637,21 +576,18 @@ namespace Server.Network if (disposing == 1) return; - Task.Run(async () => + try { - m_RecvdPipe.Reader.CancelPendingRead(); - m_RecvdPipe.Reader.Complete(); - await m_RecvdPipe.Writer.FlushAsync().ConfigureAwait(false); - m_RecvdPipe.Writer.Complete(); + Connection.Abort(); + Task.Run(Connection.DisposeAsync).Wait(); + } + catch (Exception ex) + { + TraceException(ex); + } - try { Socket.Shutdown(SocketShutdown.Both); } catch (Exception ex) { TraceException(ex); } - try { Socket.Close(); } catch (Exception ex) { TraceException(ex); } - - Socket = null; - m_RecvdPipe = null; - m_SendQueue = null; - m_Disposed.Enqueue(this); - }); + Connection = null; + m_Disposed.Enqueue(this); } public static void Initialize() diff --git a/Projects/Server/Network/PacketHandler.cs b/Projects/Server/Network/PacketHandler.cs index 022405fd5..3b87df425 100644 --- a/Projects/Server/Network/PacketHandler.cs +++ b/Projects/Server/Network/PacketHandler.cs @@ -28,7 +28,7 @@ namespace Server.Network public class PacketHandler { - public PacketHandler(int packetID, long length, bool ingame, OnPacketReceive onReceive) + public PacketHandler(int packetID, int length, bool ingame, OnPacketReceive onReceive) { PacketID = packetID; Length = length; @@ -38,7 +38,7 @@ namespace Server.Network public int PacketID{ get; } - public long Length{ get; } + public int Length{ get; } public OnPacketReceive OnReceive{ get; } diff --git a/Projects/Server/Network/PacketHandlers.cs b/Projects/Server/Network/PacketHandlers.cs index 37ec26672..beda676b3 100644 --- a/Projects/Server/Network/PacketHandlers.cs +++ b/Projects/Server/Network/PacketHandlers.cs @@ -109,43 +109,32 @@ namespace Server.Network Register(0x01, 5, false, Disconnect); Register(0x02, 7, true, MovementReq); Register(0x03, 0, true, AsciiSpeech); - Register(0x04, 2, true, GodModeRequest); Register(0x05, 5, true, AttackReq); Register(0x06, 5, true, UseReq); Register(0x07, 7, true, LiftReq); Register(0x08, 14, true, DropReq); Register(0x09, 5, true, LookReq); - Register(0x0A, 11, true, Edit); Register(0x12, 0, true, TextCommand); Register(0x13, 10, true, EquipReq); - Register(0x14, 6, true, ChangeZ); Register(0x22, 3, true, Resynchronize); Register(0x2C, 2, true, DeathStatusResponse); Register(0x34, 10, true, MobileQuery); Register(0x3A, 0, true, ChangeSkillLock); Register(0x3B, 0, true, VendorBuyReply); - Register(0x47, 11, true, NewTerrain); - Register(0x48, 73, true, NewAnimData); - Register(0x58, 106, true, NewRegion); Register(0x5D, 73, false, PlayCharacter); - Register(0x61, 9, true, DeleteStatic); Register(0x6C, 19, true, TargetResponse); Register(0x6F, 0, true, SecureTrade); Register(0x72, 5, true, SetWarMode); Register(0x73, 2, false, PingReq); Register(0x75, 35, true, RenameRequest); - Register(0x79, 9, true, ResourceQuery); - Register(0x7E, 2, true, GodviewQuery); Register(0x7D, 13, true, MenuResponse); Register(0x80, 62, false, AccountLogin); Register(0x83, 39, false, DeleteCharacter); Register(0x91, 65, false, GameLogin); Register(0x95, 9, true, HuePickerResponse); - Register(0x96, 0, true, GameCentralMoniter); Register(0x98, 0, true, MobileNameRequest); Register(0x9A, 0, true, AsciiPromptResponse); Register(0x9B, 258, true, HelpRequest); - Register(0x9D, 51, true, GMSingle); Register(0x9F, 0, true, VendorSellReply); Register(0xA0, 3, false, PlayServer); Register(0xA4, 149, false, SystemInfo); @@ -161,8 +150,6 @@ namespace Server.Network Register(0xBF, 0, true, ExtendedCommand); Register(0xC2, 0, true, UnicodePromptResponse); Register(0xC8, 2, true, SetUpdateRange); - Register(0xC9, 6, true, TripTime); - Register(0xCA, 6, true, UTripTime); Register(0xCF, 0, false, AccountLogin); Register(0xD0, 0, true, ConfigurationFile); Register(0xD1, 2, true, LogoutReq); @@ -172,6 +159,7 @@ namespace Server.Network Register(0xEF, 21, false, LoginServerSeed); Register(0xF4, 0, false, CrashReport); Register(0xF8, 106, false, CreateCharacter70160); + Register(0xFB, 2, false, ShowPublicHouseContent); Register6017(0x08, 15, true, DropReq6017); @@ -214,9 +202,7 @@ namespace Server.Network public static void Register(int packetID, int length, bool ingame, OnPacketReceive onReceive) { Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); - - if (m_6017Handlers[packetID] == null) - m_6017Handlers[packetID] = new PacketHandler(packetID, length, ingame, onReceive); + m_6017Handlers[packetID] ??= new PacketHandler(packetID, length, ingame, onReceive); } public static PacketHandler GetHandler(int packetID) => Handlers[packetID]; @@ -291,7 +277,9 @@ namespace Server.Network ph.ThrottleCallback = t; } - public static long ProcessPacket(MessagePump pump, NetState ns, in ReadOnlySequence seq) + private static MemoryPool _memoryPool = SlabMemoryPoolFactory.Create(); + + public static int ProcessPacket(MessagePump pump, NetState ns, in ReadOnlySequence seq) { PacketReader r = new PacketReader(seq); @@ -342,7 +330,7 @@ namespace Server.Network return -1; } - long packetLength = handler.Length; + int packetLength = handler.Length; if (handler.Length <= 0 && r.Length >= 3) { packetLength = r.ReadUInt16(); @@ -373,16 +361,12 @@ namespace Server.Network if (throttled > TimeSpan.Zero) ns.ThrottledUntil = DateTime.UtcNow + throttled; - PacketReceiveProfile prof = null; + ReadOnlySequence packet = seq.Slice(r.Position); + IMemoryOwner memOwner = _memoryPool.Rent((int)packet.Length); - if (Core.Profiling) - prof = PacketReceiveProfile.Acquire(packetId); + packet.CopyTo(memOwner.Memory.Span); - prof?.Start(); - - pump.QueueWork(ns, seq.Slice(r.Position), handler.OnReceive); - - prof?.Finish(packetLength); + pump.QueueWork(ns, memOwner, handler.OnReceive); return packetLength; } @@ -413,9 +397,9 @@ namespace Server.Network public static void EncodedCommand(NetState state, PacketReader pvSrc) { IEntity e = World.FindEntity(pvSrc.ReadUInt32()); - int packetID = pvSrc.ReadUInt16(); + int packetId = pvSrc.ReadUInt16(); - EncodedPacketHandler ph = GetEncodedHandler(packetID); + EncodedPacketHandler ph = GetEncodedHandler(packetId); if (ph != null) { @@ -423,7 +407,7 @@ namespace Server.Network { Console.WriteLine( "Client: {0}: Sent ingame packet (0xD7x{1:X2}) before having been attached to a mobile", state, - packetID); + packetId); state.Dispose(); } else if (ph.Ingame && state.Mobile.Deleted) @@ -609,39 +593,6 @@ namespace Server.Network EventSink.InvokeDeleteRequest(new DeleteRequestEventArgs(state, index)); } - public static void ResourceQuery(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - } - } - - public static void GameCentralMoniter(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - int type = pvSrc.ReadByte(); - int num1 = pvSrc.ReadInt32(); - - Console.WriteLine("God Client: {0}: Game central moniter", state); - Console.WriteLine(" - Type: {0}", type); - Console.WriteLine(" - Number: {0}", num1); - - pvSrc.Trace(state); - } - } - - public static void GodviewQuery(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) Console.WriteLine("God Client: {0}: Godview query 0x{1:X}", state, pvSrc.ReadByte()); - } - - public static void GMSingle(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - pvSrc.Trace(state); - } - public static void DeathStatusResponse(NetState state, PacketReader pvSrc) { // Ignored @@ -711,35 +662,7 @@ namespace Server.Network huePicker.OnResponse(hue); break; - } } - - public static void TripTime(NetState state, PacketReader pvSrc) - { - int unk1 = pvSrc.ReadByte(); - int unk2 = pvSrc.ReadInt32(); - - state.Send(new TripTimeResponse(unk1)); - } - - public static void UTripTime(NetState state, PacketReader pvSrc) - { - int unk1 = pvSrc.ReadByte(); - int unk2 = pvSrc.ReadInt32(); - - state.Send(new UTripTimeResponse(unk1)); - } - - public static void ChangeZ(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - int x = pvSrc.ReadInt16(); - int y = pvSrc.ReadInt16(); - int z = pvSrc.ReadSByte(); - - Console.WriteLine("God Client: {0}: Change Z ({1}, {2}, {3})", state, x, y, z); - } } public static void SystemInfo(NetState state, PacketReader pvSrc) @@ -758,101 +681,10 @@ namespace Server.Network int v8 = pvSrc.ReadInt32(); } - public static void Edit(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - int type = pvSrc.ReadByte(); // 10 = static, 7 = npc, 4 = dynamic - int x = pvSrc.ReadInt16(); - int y = pvSrc.ReadInt16(); - int id = pvSrc.ReadInt16(); - int z = pvSrc.ReadSByte(); - int hue = pvSrc.ReadUInt16(); - - Console.WriteLine("God Client: {0}: Edit {6} ({1}, {2}, {3}) 0x{4:X} (0x{5:X})", state, x, y, z, id, hue, - type); - } - } - - public static void DeleteStatic(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - int x = pvSrc.ReadInt16(); - int y = pvSrc.ReadInt16(); - int z = pvSrc.ReadInt16(); - int id = pvSrc.ReadUInt16(); - - Console.WriteLine("God Client: {0}: Delete Static ({1}, {2}, {3}) 0x{4:X}", state, x, y, z, id); - } - } - - public static void NewAnimData(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - Console.WriteLine("God Client: {0}: New tile animation", state); - - pvSrc.Trace(state); - } - } - - public static void NewTerrain(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - int x = pvSrc.ReadInt16(); - int y = pvSrc.ReadInt16(); - int id = pvSrc.ReadUInt16(); - int width = pvSrc.ReadInt16(); - int height = pvSrc.ReadInt16(); - - Console.WriteLine("God Client: {0}: New Terrain ({1}, {2})+({3}, {4}) 0x{5:X4}", state, x, y, width, height, - id); - } - } - - public static void NewRegion(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - { - string name = pvSrc.ReadString(40); - int unk = pvSrc.ReadInt32(); - int x = pvSrc.ReadInt16(); - int y = pvSrc.ReadInt16(); - int width = pvSrc.ReadInt16(); - int height = pvSrc.ReadInt16(); - int zStart = pvSrc.ReadInt16(); - int zEnd = pvSrc.ReadInt16(); - string desc = pvSrc.ReadString(40); - int soundFX = pvSrc.ReadInt16(); - int music = pvSrc.ReadInt16(); - int nightFX = pvSrc.ReadInt16(); - int dungeon = pvSrc.ReadByte(); - int light = pvSrc.ReadInt16(); - - Console.WriteLine("God Client: {0}: New Region '{1}' ('{2}')", state, name, desc); - } - } - public static void AccountID(NetState state, PacketReader pvSrc) { } - public static bool VerifyGC(NetState state) - { - if (state.Mobile == null || state.Mobile.AccessLevel <= AccessLevel.Counselor) - { - if (state.Running) - Console.WriteLine("Warning: {0}: Player using godclient, disconnecting", state); - - state.Dispose(); - return false; - } - - return true; - } - public static void TextCommand(NetState state, PacketReader pvSrc) { int type = pvSrc.ReadByte(); @@ -862,34 +694,6 @@ namespace Server.Network switch (type) { - case 0x00: // Go - { - if (VerifyGC(state)) - try - { - string[] split = command.Split(' '); - - int x = Utility.ToInt32(split[0]); - int y = Utility.ToInt32(split[1]); - - int z; - - if (split.Length >= 3) - z = Utility.ToInt32(split[2]); - else if (m.Map != null) - z = m.Map.GetAverageZ(x, y); - else - z = 0; - - m.Location = new Point3D(x, y, z); - } - catch - { - // ignored - } - - break; - } case 0xC7: // Animate { EventSink.InvokeAnimateRequest(new AnimateRequestEventArgs(m, command)); @@ -970,12 +774,6 @@ namespace Server.Network } } - public static void GodModeRequest(NetState state, PacketReader pvSrc) - { - if (VerifyGC(state)) - state.Send(new GodModeReply(pvSrc.ReadBoolean())); - } - public static void AsciiPromptResponse(NetState state, PacketReader pvSrc) { uint serial = pvSrc.ReadUInt32(); @@ -1550,9 +1348,7 @@ namespace Server.Network uint value = pvSrc.ReadUInt32(); if ((value & ~0x7FFFFFFF) != 0) - { from.OnPaperdollRequest(); - } else { Serial s = value; @@ -1576,9 +1372,7 @@ namespace Server.Network from.NextActionTime = Core.TickCount + Mobile.ActionDelay; } else - { from.SendActionMessage(); - } } public static void LookReq(NetState state, PacketReader pvSrc) @@ -1594,9 +1388,7 @@ namespace Server.Network if (m != null && from.CanSee(m) && Utility.InUpdateRange(from, m)) { if (SingleClickProps) - { m.OnAosSingleClick(from); - } else { if (from.Region.OnSingleClick(from, m)) @@ -1612,9 +1404,7 @@ namespace Server.Network Utility.InUpdateRange(from.Location, item.GetWorldLocation())) { if (SingleClickProps) - { item.OnAosSingleClick(from); - } else if (from.Region.OnSingleClick(from, item)) { if (item.Parent is Item item1) @@ -2062,13 +1852,6 @@ namespace Server.Network if (m != null) switch (type) { - case 0x00: // Unknown, sent by godclient - { - if (VerifyGC(state)) - Console.WriteLine("God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, m.Serial, m.Name); - - break; - } case 0x04: // Stats { m.OnStatsQuery(from); @@ -2094,44 +1877,10 @@ namespace Server.Network string name = pvSrc.ReadString(30); pvSrc.Seek(2, SeekOrigin.Current); + int flags = pvSrc.ReadInt32(); -/* if (FeatureProtection.DisabledFeatures != 0 && ThirdPartyAuthCallback != null) - { - bool authOK = false; - - ulong razorFeatures = ((ulong)pvSrc.ReadUInt32() << 32) | pvSrc.ReadUInt32(); - - if (razorFeatures == (ulong)FeatureProtection.DisabledFeatures) - { - bool doesNotMatch = false; - for (int i = 0; !doesNotMatch && i < m_ThirdPartyAuthKey.Length; i++) - doesNotMatch = pvSrc.ReadByte() != m_ThirdPartyAuthKey[i]; - - if (!doesNotMatch) - authOK = true; - } - else - { - pvSrc.Seek(16, SeekOrigin.Current); - } - - ThirdPartyAuthCallback(state, authOK); - }*/ -/* else - {*/ - pvSrc.Seek(24, SeekOrigin.Current); -/* }*/ - -/* if (ThirdPartyHackedCallback != null) - { - pvSrc.Seek(-2, SeekOrigin.Current); - if (pvSrc.ReadUInt16() == 0xDEAD) - ThirdPartyHackedCallback(state, true); - }*/ - - if (!state.Running) - return; + pvSrc.Seek(24, SeekOrigin.Current); int charSlot = pvSrc.ReadInt32(); int clientIP = pvSrc.ReadInt32(); @@ -2181,6 +1930,11 @@ namespace Server.Network } } + public static void ShowPublicHouseContent(NetState state, PacketReader pvSrc) + { + bool showPublicHouseContent = pvSrc.ReadBoolean(); + } + public static void DoLogin(NetState state, Mobile m) { state.Send(new LoginConfirm(m)); @@ -2614,9 +2368,7 @@ namespace Server.Network state.Send(new CharacterListOld(state.Account, state.CityInfo)); } else - { state.Dispose(); - } } public static void PlayServer(NetState state, PacketReader pvSrc) @@ -2626,9 +2378,7 @@ namespace Server.Network IAccount a = state.Account; if (info == null || a == null || index < 0 || index >= info.Length) - { state.Dispose(); - } else { ServerInfo si = info[index]; diff --git a/Projects/Server/Network/PacketReader.cs b/Projects/Server/Network/PacketReader.cs index de6159bb1..49625d649 100644 --- a/Projects/Server/Network/PacketReader.cs +++ b/Projects/Server/Network/PacketReader.cs @@ -175,13 +175,13 @@ namespace Server.Network return sb.ToString(); } - public bool IsSafeChar(int c) => c >= 0x20 && c < 0xFFFE; + private static bool IsSafeChar(int c) => c >= 0x20 && c < 0xFFFE; public string ReadUTF8StringSafe(int fixedLength) { string s; - if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true)) + if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0')) s = Utility.UTF8.GetString(span.Length > fixedLength ? span.Slice(0, fixedLength) : span); else { @@ -203,7 +203,7 @@ namespace Server.Network { string s; - if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true)) + if (m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0')) s = Utility.UTF8.GetString(span); else { @@ -222,7 +222,7 @@ namespace Server.Network public string ReadUTF8String() => Utility.UTF8.GetString( - m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0', true) ? span : + m_Reader.TryReadTo(out ReadOnlySpan span, (byte)'\0') ? span : m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray() ); diff --git a/Projects/Server/Network/Packets.cs b/Projects/Server/Network/Packets.cs index 330f3d3fe..7cf9e74ef 100644 --- a/Projects/Server/Network/Packets.cs +++ b/Projects/Server/Network/Packets.cs @@ -4142,14 +4142,6 @@ namespace Server.Network } } - public sealed class GodModeReply : Packet - { - public GodModeReply(bool reply) : base(0x2B, 2) - { - m_Stream.Write(reply); - } - } - public sealed class PlayServerAck : Packet { internal static int m_AuthID = -1; diff --git a/Projects/Server/Network/SocketExtensions.cs b/Projects/Server/Network/SocketExtensions.cs index e03a79243..3fdf9d3b2 100644 --- a/Projects/Server/Network/SocketExtensions.cs +++ b/Projects/Server/Network/SocketExtensions.cs @@ -1,22 +1,22 @@ -/*************************************************************************** - * SocketExtensions.cs - * ------------------- - * begin : August 2, 2019 - * copyright : (C) The ModernUO Team - * email : hi@modernuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: SocketExtensions.cs - Created: 2019/08/02 - Updated: 2019/12/24 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ using System; using System.Buffers; @@ -34,7 +34,7 @@ namespace Server.Network public static ArraySegment GetArray(this ReadOnlyMemory memory) { - if (MemoryMarshal.TryGetArray(memory, out var result)) + if (MemoryMarshal.TryGetArray(memory, out ArraySegment result)) return result; throw new InvalidOperationException("Buffer backed by array was expected"); @@ -42,7 +42,7 @@ namespace Server.Network public static ArraySegment GetArray(this ReadOnlySequence memory) { - if (SequenceMarshal.TryGetArray(memory, out var result)) + if (SequenceMarshal.TryGetArray(memory, out ArraySegment result)) return result; throw new InvalidOperationException("Buffer backed by array was expected"); diff --git a/Projects/Server/Network/StaticPacketHandlers.cs b/Projects/Server/Network/StaticPacketHandlers.cs index b2c710581..186de7840 100644 --- a/Projects/Server/Network/StaticPacketHandlers.cs +++ b/Projects/Server/Network/StaticPacketHandlers.cs @@ -1,22 +1,23 @@ -/*************************************************************************** - * StaticPacketHandlers.cs - * ------------------- - * begin : March 15, 2019 - * copyright : (C) The ModernUO Team - * email : hi@modernuo.com - * - * $Id$ - * - ***************************************************************************/ - -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - ***************************************************************************/ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: StaticPacketHandlers.cs * + * Created: 2019/03/15 - Updated: 2019/12/24 * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program 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 General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ using System.Collections.Concurrent; diff --git a/Projects/Server/Pipelines/DuplexPipe.cs b/Projects/Server/Pipelines/DuplexPipe.cs new file mode 100644 index 000000000..089591cc9 --- /dev/null +++ b/Projects/Server/Pipelines/DuplexPipe.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.IO.Pipelines +{ + public class DuplexPipe : IDuplexPipe + { + public DuplexPipe(PipeReader reader, PipeWriter writer) + { + Input = reader; + Output = writer; + } + + public PipeReader Input { get; } + + public PipeWriter Output { get; } + + public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions) + { + var input = new Pipe(inputOptions); + var output = new Pipe(outputOptions); + + var transportToApplication = new DuplexPipe(output.Reader, input.Writer); + var applicationToTransport = new DuplexPipe(input.Reader, output.Writer); + + return new DuplexPipePair(applicationToTransport, transportToApplication); + } + + // This class exists to work around issues with value tuple on .NET Framework + public readonly struct DuplexPipePair + { + public IDuplexPipe Transport { get; } + public IDuplexPipe Application { get; } + + public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application) + { + Transport = transport; + Application = application; + } + } + } +} diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index e7ce12400..94457e180 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -7,7 +7,7 @@ ModernUO - 0.0.1 + 0.1.2 Kamron Batman ModernUO ModernUO @@ -39,20 +39,38 @@ 8.0 - + + libuv.dll + Always + + + libuv.so + Always + + + libuv.so + libuv.so + Always + + zlib.dll - PreserveNewest + Always - + rdrand.dll - PreserveNewest + Always - + rdrand.so - PreserveNewest + Always + + + + + diff --git a/Projects/Server/SignalR/AsyncDisposableExtensions.cs b/Projects/Server/SignalR/AsyncDisposableExtensions.cs new file mode 100644 index 000000000..ac2ab7c2d --- /dev/null +++ b/Projects/Server/SignalR/AsyncDisposableExtensions.cs @@ -0,0 +1,19 @@ +// 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.Threading.Tasks; + +namespace SignalR +{ + public static class AsyncDisposableExtensions + { + // Does a light up check to see if a type is IAsyncDisposable and calls DisposeAsync if it is + public static ValueTask DisposeAsync(this IDisposable disposable) + { + if (disposable is IAsyncDisposable asyncDisposable) return asyncDisposable.DisposeAsync(); + disposable.Dispose(); + return default; + } + } +}