From 2770ab1d50a553fb2b582036fe23bb6c541a07c0 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 24 May 2020 10:47:47 -0700 Subject: [PATCH] Removes libdrng (rdrand) (#139) --- Projects/Server/Assemblies/libdrng.dll | Bin 9728 -> 0 bytes .../Internal/IAsyncDisposable.cs | 12 - .../Transport.LIbuv/Internal/ILibuvTrace.cs | 29 - .../Internal/LibuvAwaitable.cs | 78 --- .../Internal/LibuvConnection.cs | 313 --------- .../Internal/LibuvConnectionListener.cs | 189 ------ .../Internal/LibuvConstants.cs | 91 --- .../Internal/LibuvOutputConsumer.cs | 111 ---- .../Transport.LIbuv/Internal/LibuvThread.cs | 415 ------------ .../Transport.LIbuv/Internal/LibuvTrace.cs | 94 --- .../Internal/LibuvTransportContext.cs | 16 - .../Internal/LibuvTransportFactory.cs | 64 -- .../Transport.LIbuv/Internal/Listener.cs | 209 ------ .../Internal/ListenerContext.cs | 165 ----- .../Internal/ListenerPrimary.cs | 264 -------- .../Internal/ListenerSecondary.cs | 192 ------ .../Internal/Networking/LibuvFunctions.cs | 619 ------------------ .../Internal/Networking/PlatformApis.cs | 20 - .../Internal/Networking/SockAddr.cs | 114 ---- .../Internal/Networking/UvAsyncHandle.cs | 72 -- .../Internal/Networking/UvConnectRequest.cs | 75 --- .../Internal/Networking/UvException.cs | 14 - .../Internal/Networking/UvHandle.cs | 66 -- .../Internal/Networking/UvLoopHandle.cs | 54 -- .../Internal/Networking/UvMemory.cs | 78 --- .../Internal/Networking/UvPipeHandle.cs | 36 - .../Internal/Networking/UvRequest.cs | 27 - .../Internal/Networking/UvStreamHandle.cs | 149 ----- .../Internal/Networking/UvTcpHandle.cs | 72 -- .../Internal/Networking/UvTimerHandle.cs | 56 -- .../Internal/Networking/UvWriteReq.cs | 237 ------- .../Transport.LIbuv/Internal/WriteReqPool.cs | 63 -- .../Transport.LIbuv/LibuvTransportOptions.cs | 66 -- .../WebHostBuilderLibuvExtensions.cs | 51 -- Projects/Server/Main.cs | 3 - Projects/Server/Server.csproj | 16 - Projects/Server/Utilities/DRng64.cs | 115 ---- Projects/Server/Utilities/RandomProviders.cs | 42 ++ .../Utilities/{Random.cs => SecureRandom.cs} | 47 +- Projects/Server/Utilities/Utility.cs | 22 +- .../Server/Utilities/Xoshiro256PlusPlus.cs | 5 +- README.md | 1 - 42 files changed, 64 insertions(+), 4298 deletions(-) delete mode 100644 Projects/Server/Assemblies/libdrng.dll delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs delete mode 100644 Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs delete mode 100644 Projects/Server/Utilities/DRng64.cs create mode 100644 Projects/Server/Utilities/RandomProviders.cs rename Projects/Server/Utilities/{Random.cs => SecureRandom.cs} (53%) diff --git a/Projects/Server/Assemblies/libdrng.dll b/Projects/Server/Assemblies/libdrng.dll deleted file mode 100644 index 1fa4d7477f8fd8ea38e1dbf8d0176c7aac4769af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9728 zcmeHMe{@t;e!r7sLVhqz(2UOFqK`P)#A-swq%KGz6Pf6Z9o8`fuo@hO%nMAJ%)~cu zAYrivC)qN2J6m+I6>D3ho_0@9-Gi;i4G8YUpa}{UV|xng?lJ9p>I}rxZvo34hxTx{OrPF9X{GqeCz;d=e0RH?Y#Cs zk0o`w;3&bCaQjyBeWjWBn-~jyC!fi;7d@EOnPO!^an1rKTL8`?gWGyFV2O_RV%i3t z!&tt~nt`kf3<@SnF53iE!zc^BNKbQgo6-u#{Dh$yr;a>~J*#&}Oq5NGIRUzspb~wB zG7fpcY&(H5^$NPgu9n4a8Sp(D8X7Xq-JAz7y$I;NS_%c_AjVb%78qQ$(-iU*^v zHq_X13Q!FyV#TgUomD)HWvaUjMSYH7=z0R+tl}YmQe0{WPmc*=qwAH;QUdDH^~6XF ziWDP?m9CdfHsYap7`sD9zG~P>y>xWgZ|Kvb!B^ToxPh^YiL3a~P)Wx}Lz;Fmk#ljF z51lBP_~Tqg20VGbvVTlokvwOSE2+6A`LRVV8l-gN$>Wv7>hELKC#~u|0QMI%In{vr8WH-`x8St>4XtawYds-N_Lg-)Gn9MxE>t{T)F5|K5(Bj#9%GwQq`T(Y>M@C3C z}~j?w#b%E$TziR)!}J8iTlt z2C?@fSB~@4eS$CLai;uOA6~G2gT6qXW@SqG7)#VYmawmI{9-XP&4~8T7TJ(} zhA63763gMV4rcB#GW!4_pC;Pi16>D=uH^+;f#&!nPPcwdwkEaQglD?XTW0ks-vrAF zbI^t9`z%<4n#t0vuI6|NE{su3=DXh^sWME9*^Tw^G(H%3aM)NkJ)ddugl6TE@)NFn zfK8IHs(*qY_>>XlZB7X?odj6_z{DT^Qq!>Rp0&mXIO{&4IlhfF<;okH<0&ATISTLe@32nm{Rf7O~~n7ERg!wpt#~i(xt5D zz~E`ZlNszj>rSE->)QlUR`1shO%HGm)`<0baH!W2s}mS4<%qS+#0Ie&7~htm&9jIs zNAfJx^9*0HJ#`)UDPNwDWm>pmdpOI4;bBZe#Un(+yp|8^aad0Q^l;^@=Gcbq$d$jl z1g~zb340*j>e_@DYK~@-V6rQCGUQs0A^2QdxdN$A8Ktn)VDX6AMMS0h1Z^upa}>c$ z8%b(8yPl_|3q1G$^{ETjapk558Nq|7Kt-hZ8bSica*h#)*8wEy$H7 zCr>q%XpTdmlnh@+_%94Ry+$zT^F%iZFe~9h;{taBcQ=)A_YgmfDWkyCjUe1JU2(g2 z2;mRX5RfUHe{>q;-9%#LHx6Tnm6TK^JlPP(Y*->+bF`zQnUnY=;(8Y6IkMyq2r$32 z?F8tFk8!HiZZ9PZqe;e>IRC^i_$RCfQ%X2!UQAawV_4hL&961(R03 za#SvLXQsIQdD}iXOkAin&iq4`$&VRQk^HuoCryxPpfkI-Es zyPW0*y-bH0_UMFq9ETuImTQuqSP~2MgM%xlxH6(SUcr{4w7O66x%}qNjnMHeyF9cW zfr*!4tIzcj-HBX+VDH1(OcR>4|5782jJ;qV*m+@lU)& zd(G}0-eX-w2mN*&gX+UjGb2!{b{Rw=;w1u|JU0NhI2w#SgdT+6hCG-uoN*Ja#Z^d`yG`koRH>_ZKr!WJ#bbXL1^7$ zh)Z$bJBfMx4;-)2S*C~5(CKY>2^=a?^dvh z)EphiWv=`wJ>+@%i8B!5YQSNrIlLr+pZe?}m>$=qy3))+h`pXxNkB&-`6NW3d*&02 zi7Wl1B*bCq=c#_`h6bWD8wa1HUiG`8e-DlG7}h6ifxCPYZg{@~*$dc8DXTd~;1dr4 zx@F)Gk2aoenzHXhK)fzao&Fhhirzf9SAin?<|Fb2X`ZY87 z>h4QyUqJ#>-!sQXi96#yVL#|qhML^(c$GIAcTYuSG19pEgS*6N!mEraQ|=Ru>D88| z;!*p-#@!dgQL#Cd=V~f`9e(nZ%ZPkIs?{|=;pa))_e&A5@~Ksty&;otVRB`|ru-Zx zFKOr^u*iDo@~3!H2|HZ;umzVd#h2UvVklnfRjeW02#v{6i#KKcU7(Zm!1Dtc#1oEr z)BKZ_Q@g%FV@2;?h2AWA*}m;ZeqItb9*@Jl4p85TxjeFM7iOZF918Y2Vc5Iw@8oU1FEViHyfU{Q@ z4$lY-<;lxgW&Aui#}$i$l*4AFnEKMT-iw;%Q<}Pzb$H{f*`(eAb@Eh$&4Dg-3-^iL z%Mr_reczBW1U|xg#nSd$hUlrA4A^VJhzVae;mfAp4@~%76G|rhp{aMLN#A0^5)*!E z%2g9)2eh@)!2BlMW5TT5W9nC$aJ~u8nR=&9__7HPnebT?X8lxN$9gHP{fiBl<^NAX ze~saH%7j_JILqf0;%C1#(F@s!s+OT*|5M4z@D^sos=E;trz(}Kj6TuS(BTUi|C|iJ zT;`NH-+<;w{(D5h=jNI9rV3^pOqk_QLt2FYgVM6ryLRFIn?Ktfxpno6Z>`8}eLs_+ zp|rKF3AVTKSf|+L7316FSXWylyg4LAw^S{wu4(JOwXUs2jEKRw*hWlzuG*}Rp4P@( zGX3+{U8`Gt^xe#Z#nHc|xjLhy=)q%%K96O2u9_Ow0^Z}(yl%z>tUu9V`WEK;ZJ-Qs z)IY;j*VMFkY+>vY=mPMoSGV8^y1Ef4V@u}n?k85k9A2}oS2CBU>pAD}{JNenm#6Dh z&f&G{dNtr-s^(Z)bv+MwKd^vjejlWq-isVHXxGl2J42f>4c1+5j#-l1UCoWUzuq~# z`*nYNX7gGNy}fgIhTfCl?V^6Wjqix`{j{FOX2h}=NnZ{8dep3(=o@CqEqNA}cL%-(EXrq# z>g;Thbj^5a*VUWIKaG1GbQ^H@{t^5U)W_12}g%xgwydaMi zNO@hkn{sN*ao1REdCXQ<$ZWOw{zB@njIpf$I2Q9W)RXW}(2`?eIf${Pw1Aa{P+RA* zQuI+;The85SP{D;%wZX1`J|)ABcB!3=64z6!C307%zjmI7sZ0cuOc_H<#FUhuYjBd z%$B=x2OR*9awuEI=$;d@)ED9ZmwUE=1p=!B@h-7F+!1aMYz;<35m92S9Q?Q(3dlWO zVjvvthy_AoT$W-zfk-$mGj==c6g%T0u=T53)_tqBc}>%@x|-@xB*NIO3>INo7Nt)1 z%^5r(vQ;b)7qYVmkssY0Gx}k})j9!AN-Lv`CC@ z4@C11DzdCw0>ltO)cMQ^0{k`-1*{8{E9O>0;e_# zV;%pXY1uEF^jIZ1or84GEEUjDxnuz1e^q{SB2ZGo*fLalN7VuLq1JuFfKTCF@HF^O z0}8m)$AJ@Ei#xp@I`p6TW2h0}`v6B#_W~!lzZCB>;757a zntu&r6{v()0tQVy1o)(h6MW9Z4*{Mraf1J5;!}Wr{QYqfehBuWl23yBOq}4)P-&dw zfZOnXAUpz?_cgrbfD^1lB|gD>O*{nn!$q(I{}AAL)G6Qu@%vK8Zvi}ochINc7y$1Toqk3x%Uw8Y7sx#a!#p1CJxvD+Zd0Q~v zS-pK(g@7w5+<{lc`q{m~l_1p1QX(!lgKzx2wz)GwVgruwDRlY@Ev(rny7%w5DUrv&4-k$Pb20Ds2+%<_Z&gZCZ$a@DW6 Izj+b(FWb;AasU7T diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs deleted file mode 100644 index f46e09e7f..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/IAsyncDisposable.cs +++ /dev/null @@ -1,12 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - interface IAsyncDisposable - { - Task DisposeAsync(); - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs deleted file mode 100644 index 29fd48d9e..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ILibuvTrace.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs deleted file mode 100644 index bb8c4e7b5..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvAwaitable.cs +++ /dev/null @@ -1,78 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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); - } - } - - internal 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/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs deleted file mode 100644 index dd9dcecc8..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnection.cs +++ /dev/null @@ -1,313 +0,0 @@ -// 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 Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal partial class LibuvConnection : TransportConnection - { - private static readonly int MinAllocBufferSize = SlabMemoryPool.BlockSize / 2; - - private static readonly Action _readCallback = - (handle, status, state) => ReadCallback(handle, status, state); - - private static readonly Func _allocCallback = - (handle, suggestedSize, state) => AllocCallback(handle, suggestedSize, state); - - 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, - preferLocal: 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); - } - else if (LibuvConstants.IsConnectionReset(uvError.StatusCode)) - { - // Log connection resets at a lower (Debug) level. - Log.ConnectionReset(ConnectionId); - return new ConnectionResetException(uvError.Message, uvError); - } - else - { - // 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/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs deleted file mode 100644 index 99868a0fd..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConnectionListener.cs +++ /dev/null @@ -1,189 +0,0 @@ -// 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 Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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(); - } - - internal 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 = (Libuv.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, var 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/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs deleted file mode 100644 index 5352d5e8d..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvConstants.cs +++ /dev/null @@ -1,91 +0,0 @@ -// 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 System.Runtime.InteropServices; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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 (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4077; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -104; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -54; - return null; - } - - private static int? GetEPIPE() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4047; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -32; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -32; - return null; - } - - private static int? GetENOTCONN() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4053; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -107; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -57; - return null; - } - - private static int? GetEINVAL() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4071; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -22; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -22; - return null; - } - - private static int? GetEADDRINUSE() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4091; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -98; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -48; - return null; - } - - private static int? GetENOTSUP() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -95; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -45; - return null; - } - - private static int? GetECANCELED() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return -4081; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return -125; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) return -89; - return null; - } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs deleted file mode 100644 index bb0b95831..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvOutputConsumer.cs +++ /dev/null @@ -1,111 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs deleted file mode 100644 index c2a742a80..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvThread.cs +++ /dev/null @@ -1,415 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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 = false; - private bool _initCompleted = false; - 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; } - - 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(); - } - - 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); - } - } - - 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/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs deleted file mode 100644 index 1d3e2e794..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTrace.cs +++ /dev/null @@ -1,94 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs deleted file mode 100644 index d5f68e1f5..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportContext.cs +++ /dev/null @@ -1,16 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal class LibuvTransportContext - { - public LibuvTransportOptions Options { get; set; } - - public IHostApplicationLifetime AppLifetime { get; set; } - - public ILibuvTrace Log { get; set; } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs deleted file mode 100644 index 6b06c11bc..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/LibuvTransportFactory.cs +++ /dev/null @@ -1,64 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Connections; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal class LibuvTransportFactory : IConnectionListenerFactory - { - private readonly LibuvTransportContext _baseTransportContext; - - public LibuvTransportFactory( - IOptions options, - IHostApplicationLifetime applicationLifetime, - ILoggerFactory loggerFactory) - { - if (options == null) throw new ArgumentNullException(nameof(options)); - if (applicationLifetime == null) throw new ArgumentNullException(nameof(applicationLifetime)); - if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); - - var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv"); - var trace = new LibuvTrace(logger); - - var threadCount = options.Value.ThreadCount; - - if (threadCount <= 0) - throw new ArgumentOutOfRangeException(nameof(threadCount), - threadCount, - "ThreadCount must be positive."); - - if (!LibuvConstants.ECONNRESET.HasValue) trace.LogWarning("Unable to determine ECONNRESET value on this platform."); - - if (!LibuvConstants.EADDRINUSE.HasValue) trace.LogWarning("Unable to determine EADDRINUSE value on this platform."); - - _baseTransportContext = new LibuvTransportContext - { - Options = options.Value, - AppLifetime = applicationLifetime, - Log = trace, - }; - } - - public async ValueTask BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default) - { - var transportContext = new LibuvTransportContext - { - Options = _baseTransportContext.Options, - AppLifetime = _baseTransportContext.AppLifetime, - Log = _baseTransportContext.Log - }; - - var transport = new LibuvConnectionListener(transportContext, endpoint); - await transport.BindAsync(); - return transport; - } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs deleted file mode 100644 index aac5bf3f2..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Listener.cs +++ /dev/null @@ -1,209 +0,0 @@ -// 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 Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - /// - /// Base class for listeners in Kestrel. Listens for incoming connections - /// - internal 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() - { - switch (EndPoint) - { - case IPEndPoint _: - return ListenTcp(useFileHandle: false); - case UnixDomainSocketEndPoint _: - return ListenPipe(useFileHandle: false); - case FileHandleEndPoint _: - return ListenHandle(); - default: - throw new NotSupportedException(); - } - } - - private UvTcpHandle ListenTcp(bool useFileHandle) - { - var socket = new UvTcpHandle(Log); - - try - { - socket.Init(Thread.Loop, Thread.QueueCloseHandle); - socket.NoDelay(TransportContext.Options.NoDelay); - - 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(useFileHandle: true); - case FileHandleType.Pipe: - return ListenPipe(useFileHandle: true); - default: - throw new NotSupportedException(); - } - - UvStreamHandle handle; - try - { - handle = ListenTcp(useFileHandle: 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(useFileHandle: 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/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs deleted file mode 100644 index 32cfd42d8..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerContext.cs +++ /dev/null @@ -1,165 +0,0 @@ -// 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 Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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(TransportContext.Options.NoDelay); - } - 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; - - return fileHandleEndPoint.FileHandleType switch - { - FileHandleType.Auto => throw new InvalidOperationException( - "Cannot accept on a non-specific file handle, listen should be performed first."), - FileHandleType.Tcp => (UvStreamHandle)AcceptTcp(), - FileHandleType.Pipe => AcceptPipe(), - _ => throw new NotSupportedException() - }; - } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs deleted file mode 100644 index e172ad175..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerPrimary.cs +++ /dev/null @@ -1,264 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - /// - /// A primary listener waits for incoming connections on a specified socket. Incoming - /// connections may be passed to a secondary listener to handle. - /// - internal 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 = PlatformApis.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 - { -#pragma warning disable 169 - uint status; - ulong information; -#pragma warning restore 169 - } - - 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/dotnet/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/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs deleted file mode 100644 index e741c8c0c..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/ListenerSecondary.cs +++ /dev/null @@ -1,192 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - /// - /// A secondary listener is delegated requests from a primary listener via a named pipe or - /// UNIX domain socket. - /// - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs deleted file mode 100644 index 718bdc3cd..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/LibuvFunctions.cs +++ /dev/null @@ -1,619 +0,0 @@ -// 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; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal class LibuvFunctions - { - public LibuvFunctions() - { - IsWindows = PlatformApis.IsWindows; - - _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 readonly bool IsWindows; - - 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; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private UvException GetError(int statusCode) - { - // Note: method marked as NoInlining so it doesn't bloat either of the two preceding functions - // Check and ThrowError and alter their jit heuristics. - var errorName = err_name(statusCode); - var errorDescription = strerror(statusCode); - return new UvException("Error " + statusCode + " " + errorName + " " + errorDescription, statusCode); - } - - protected Func _uv_loop_init; - public void loop_init(UvLoopHandle handle) - { - ThrowIfErrored(_uv_loop_init(handle)); - } - - protected Func _uv_loop_close; - public void loop_close(UvLoopHandle handle) - { - handle.Validate(closed: true); - ThrowIfErrored(_uv_loop_close(handle.InternalGetHandle())); - } - - protected Func _uv_run; - public void run(UvLoopHandle handle, int mode) - { - handle.Validate(); - ThrowIfErrored(_uv_run(handle, mode)); - } - - protected Action _uv_stop; - public void stop(UvLoopHandle handle) - { - handle.Validate(); - _uv_stop(handle); - } - - protected Action _uv_ref; - public void @ref(UvHandle handle) - { - handle.Validate(); - _uv_ref(handle); - } - - protected Action _uv_unref; - public void unref(UvHandle handle) - { - handle.Validate(); - _uv_unref(handle); - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - protected delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket); - protected 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); - protected Action _uv_close; - public void close(UvHandle handle, uv_close_cb close_cb) - { - handle.Validate(closed: 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); - protected 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)); - } - - protected Func _uv_async_send; - public void async_send(UvAsyncHandle handle) - { - ThrowIfErrored(_uv_async_send(handle)); - } - - protected Func _uv_unsafe_async_send; - public void unsafe_async_send(IntPtr handle) - { - ThrowIfErrored(_uv_unsafe_async_send(handle)); - } - - protected Func _uv_tcp_init; - public void tcp_init(UvLoopHandle loop, UvTcpHandle handle) - { - loop.Validate(); - handle.Validate(); - ThrowIfErrored(_uv_tcp_init(loop, handle)); - } - - protected delegate int uv_tcp_bind_func(UvTcpHandle handle, ref SockAddr addr, int flags); - protected 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)); - } - - protected Func _uv_tcp_open; - public void tcp_open(UvTcpHandle handle, IntPtr hSocket) - { - handle.Validate(); - ThrowIfErrored(_uv_tcp_open(handle, hSocket)); - } - - protected Func _uv_tcp_nodelay; - public void tcp_nodelay(UvTcpHandle handle, bool enable) - { - handle.Validate(); - ThrowIfErrored(_uv_tcp_nodelay(handle, enable ? 1 : 0)); - } - - protected 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)); - } - - protected Func _uv_pipe_bind; - public void pipe_bind(UvPipeHandle handle, string name) - { - handle.Validate(); - ThrowIfErrored(_uv_pipe_bind(handle, name)); - } - - protected 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); - protected Func _uv_listen; - public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb) - { - handle.Validate(); - ThrowIfErrored(_uv_listen(handle, backlog, cb)); - } - - protected 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); - protected 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); - } - - protected 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); - protected 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)); - } - - protected Func _uv_read_stop; - public void read_stop(UvStreamHandle handle) - { - handle.Validate(); - ThrowIfErrored(_uv_read_stop(handle)); - } - - protected 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); - - protected unsafe delegate int uv_write_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb); - protected 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)); - } - - protected unsafe delegate int uv_write2_func(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); - protected 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)); - } - - protected Func _uv_err_name; - public string err_name(int err) - { - IntPtr ptr = _uv_err_name(err); - return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); - } - - protected Func _uv_strerror; - public string strerror(int err) - { - IntPtr ptr = _uv_strerror(err); - return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr); - } - - protected Func _uv_loop_size; - public int loop_size() => _uv_loop_size(); - - protected Func _uv_handle_size; - public int handle_size(HandleType handleType) => _uv_handle_size(handleType); - - protected Func _uv_req_size; - public int req_size(RequestType reqType) => _uv_req_size(reqType); - - protected delegate int uv_ip4_addr_func(string ip, int port, out SockAddr addr); - protected 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); - } - - protected delegate int uv_ip6_addr_func(string ip, int port, out SockAddr addr); - protected 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); - protected Func _uv_walk; - public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg) - { - loop.Validate(); - _uv_walk(loop, walk_cb, arg); - } - - protected Func _uv_timer_init; - public unsafe 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); - protected Func _uv_timer_start; - public unsafe void timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat) - { - handle.Validate(); - ThrowIfErrored(_uv_timer_start(handle, cb, timeout, repeat)); - } - - protected Func _uv_timer_stop; - public unsafe void timer_stop(UvTimerHandle handle) - { - handle.Validate(); - ThrowIfErrored(_uv_timer_stop(handle)); - } - - protected Func _uv_now; - public unsafe 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); - protected 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); - protected 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, 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 IsWindows) - { - if (IsWindows) - { - _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 unsafe int uv_timer_init(UvLoopHandle loop, UvTimerHandle handle); - - [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe int uv_timer_start(UvTimerHandle handle, uv_timer_cb cb, long timeout, long repeat); - - [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe int uv_timer_stop(UvTimerHandle handle); - - [DllImport("libuv", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe 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/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs deleted file mode 100644 index ebf59a240..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/PlatformApis.cs +++ /dev/null @@ -1,20 +0,0 @@ -// 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 System.Threading; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal static class PlatformApis - { - public static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - public static bool IsDarwin { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long VolatileRead(ref long value) => IntPtr.Size == 8 ? Volatile.Read(ref value) : Interlocked.Read(ref value); - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs deleted file mode 100644 index 9a01f5be3..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs +++ /dev/null @@ -1,114 +0,0 @@ -// 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; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - [StructLayout(LayoutKind.Sequential)] - internal 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 readonly long _field0; - private readonly long _field1; - private readonly 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 (PlatformApis.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); - } - else if (IsIPv4MappedToIPv6()) - { - var ipv4bits = (_field2 >> 32) & 0x00000000FFFFFFFF; - return new IPEndPoint(new IPAddress(ipv4bits), port); - } - else - { - // 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; - } - } - - private bool IsIPv4MappedToIPv6() - { - // 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 - if (_field1 != 0) return false; - - return (_field2 & 0xFFFFFFFF) == 0xFFFF0000; - } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs deleted file mode 100644 index 47552037c..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvAsyncHandle.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal 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". - var 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/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs deleted file mode 100644 index 8aa07fc8d..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvConnectRequest.cs +++ /dev/null @@ -1,75 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - /// - /// Summary description for UvWriteRequest - /// - internal class UvConnectRequest : UvRequest - { - private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = (req, status) => UvConnectCb(req, status); - - 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/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs deleted file mode 100644 index bdda17954..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvException.cs +++ /dev/null @@ -1,14 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal class UvException : Exception - { - public UvException(string message, int statusCode) : base(message) => StatusCode = statusCode; - - public int StatusCode { get; } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs deleted file mode 100644 index 4f738819e..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvHandle.cs +++ /dev/null @@ -1,66 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal abstract class UvHandle : UvMemory - { - private static readonly LibuvFunctions.uv_close_cb _destroyMemory = (handle) => DestroyMemory(handle); - 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() - { - 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". - var 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/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs deleted file mode 100644 index d24c1fac0..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvLoopHandle.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs deleted file mode 100644 index e42e420f8..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvMemory.cs +++ /dev/null @@ -1,78 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - /// - /// Summary description for UvMemory - /// - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs deleted file mode 100644 index 0ce7aedd3..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvPipeHandle.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs deleted file mode 100644 index c25a1eecd..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal class UvRequest : UvMemory - { - protected UvRequest(ILibuvTrace logger) : base(logger, GCHandleType.Normal) - { - } - - public virtual void Init(LibuvThread thread) - { - } - - protected override bool ReleaseHandle() - { - DestroyMemory(handle); - handle = IntPtr.Zero; - return true; - } - } -} - diff --git a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs deleted file mode 100644 index eb139c311..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvStreamHandle.cs +++ /dev/null @@ -1,149 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal abstract class UvStreamHandle : UvHandle - { - private static readonly LibuvFunctions.uv_connection_cb _uv_connection_cb = (handle, status) => UvConnectionCb(handle, status); - // 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/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs deleted file mode 100644 index 548ec53a0..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTcpHandle.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs deleted file mode 100644 index f15c8458c..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvTimerHandle.cs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - internal 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/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs deleted file mode 100644 index b3fa47ce6..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/UvWriteReq.cs +++ /dev/null @@ -1,237 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking -{ - /// - /// Summary description for UvWriteRequest - /// - internal class UvWriteReq : UvRequest - { - private static readonly LibuvFunctions.uv_write_cb _uv_write_cb = (IntPtr ptr, int status) => UvWriteCb(ptr, status); - - private IntPtr _bufs; - - private Action _callback; - private object _state; - private const int BUFFER_COUNT = 4; - - private readonly LibuvAwaitable _awaitable = new LibuvAwaitable(); - private readonly List _pins = new List(BUFFER_COUNT + 1); - private readonly 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, sendHandle: null, callback: callback, state: 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/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs b/Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs deleted file mode 100644 index 41f76afd7..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/Internal/WriteReqPool.cs +++ /dev/null @@ -1,63 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; - -namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal -{ - internal 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/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs b/Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs deleted file mode 100644 index 7ea005700..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/LibuvTransportOptions.cs +++ /dev/null @@ -1,66 +0,0 @@ -// 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 Microsoft.AspNetCore.Server.Kestrel.Transport.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; - - /// - /// Set to false to enable Nagle's algorithm for all connections. - /// - /// - /// Defaults to true. - /// - public bool NoDelay { get; set; } = true; - - /// - /// 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 to do webserving). - var threadCount = Environment.ProcessorCount >> 1; - - if (threadCount < 1) - // Ensure shifted value is at least one - return 1; - - if (threadCount > 16) - // 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 16; - - return threadCount; - } - } - } -} diff --git a/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs b/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs deleted file mode 100644 index 242695bb3..000000000 --- a/Projects/Server/Kestrel/Transport.LIbuv/WebHostBuilderLibuvExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -// 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.AspNetCore.Connections; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.AspNetCore.Hosting -{ - public static class WebHostBuilderLibuvExtensions - { - /// - /// Specify Libuv as the transport to be used by Kestrel. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. - /// - public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder) - { - return hostBuilder.ConfigureServices(services => - { - services.AddSingleton(); - }); - } - - /// - /// Specify Libuv as the transport to be used by Kestrel. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure. - /// - /// - /// A callback to configure Libuv options. - /// - /// - /// The Microsoft.AspNetCore.Hosting.IWebHostBuilder. - /// - public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder, Action configureOptions) - { - return hostBuilder.UseLibuv().ConfigureServices(services => - { - services.Configure(configureOptions); - }); - } - } -} diff --git a/Projects/Server/Main.cs b/Projects/Server/Main.cs index 9763edebb..3ec6eae73 100644 --- a/Projects/Server/Main.cs +++ b/Projects/Server/Main.cs @@ -357,9 +357,6 @@ namespace Server Console.WriteLine("Core: High resolution timing ({0})", Stopwatch.IsHighResolution ? "Supported" : "Unsupported"); - Console.WriteLine("SecureRandomImpl: {0} ({1})", SecureRandomImpl.Name, - SecureRandomImpl.IsHardwareRNG ? "Hardware" : "Software"); - ServerConfiguration.Load(); // Load UOContent.dll diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 5348b7128..47482c0be 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -34,7 +34,6 @@ - @@ -75,12 +74,6 @@ true ..\..\Rules.ruleset - - - libdrng.dll - Always - - @@ -88,15 +81,6 @@ - - - - - - - - - 1.1.118 diff --git a/Projects/Server/Utilities/DRng64.cs b/Projects/Server/Utilities/DRng64.cs deleted file mode 100644 index 76ace45e9..000000000 --- a/Projects/Server/Utilities/DRng64.cs +++ /dev/null @@ -1,115 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright (C) 2019-2020 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: DRng64.cs - Created: 2019/12/30 - Updated: 2019/12/30 * - * * - * 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.Runtime.InteropServices; -using System.Security.Cryptography; -using Server.Network; - -namespace Server -{ - public sealed class DRng64 : RandomNumberGenerator, IHardwareRNG - { - public enum RDRandError - { - Unknown = -4, - Unsupported = -3, - Supported = -2, - NotReady = -1, - - Failure = 0, - - Success = 1 - } - - public bool IsSupported() - { - ulong r = 0; - return SafeNativeMethods.rdrand_64(ref r, true) == RDRandError.Success; - } - - private static unsafe void GetBytes(byte* pBuffer, int count) - { - SafeNativeMethods.rdrand_get_bytes(count, pBuffer); - } - - public override void GetBytes(byte[] data) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - GetBytes(new Span(data)); - } - - public override void GetBytes(byte[] data, int offset, int count) - { - GetBytes(new Span(data, offset, count)); - } - - public override unsafe void GetBytes(Span data) - { - if (data.Length > 0) - fixed (byte* ptr = data) - { - GetBytes(ptr, data.Length); - } - } - - public override void GetNonZeroBytes(byte[] data) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - GetNonZeroBytes(new Span(data)); - } - - public override void GetNonZeroBytes(Span data) - { - while (data.Length > 0) - { - // Fill the remaining portion of the span with random bytes. - GetBytes(data); - - // Find the first zero in the remaining portion. - var indexOfFirst0Byte = data.Length; - for (var i = 0; i < data.Length; i++) - if (data[i] == 0) - { - indexOfFirst0Byte = i; - break; - } - - // If there were any zeros, shift down all non-zeros. - for (var i = indexOfFirst0Byte + 1; i < data.Length; i++) - if (data[i] != 0) - data[indexOfFirst0Byte++] = data[i]; - - // Request new random bytes if necessary; dont re-use - // existing bytes since they were shifted down. - data = data.Slice(indexOfFirst0Byte); - } - } - - internal class SafeNativeMethods - { - [DllImport("libdrng", CallingConvention = CallingConvention.Cdecl)] - internal static extern RDRandError rdrand_64(ref ulong rand, bool retry); - - [DllImport("libdrng", CallingConvention = CallingConvention.Cdecl)] - internal static extern unsafe RDRandError rdrand_get_bytes(int n, byte* buffer); - } - } -} diff --git a/Projects/Server/Utilities/RandomProviders.cs b/Projects/Server/Utilities/RandomProviders.cs new file mode 100644 index 000000000..b866d2214 --- /dev/null +++ b/Projects/Server/Utilities/RandomProviders.cs @@ -0,0 +1,42 @@ +/************************************************************************* + * ModernUO * + * Copyright (C) 2019-2020 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: RandomProviders.cs - Created: 2020/05/24 - Updated: 2020/05/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.Security.Cryptography; + +namespace Server +{ + public interface IRandomProvider + { + public uint Next(uint c); + public bool NextBool(); + public void GetBytes(Span b); + public double NextDouble(); + } + + public static class RandomProviders + { + private static IRandomProvider m_Provider; + private static IRandomProvider m_SecureProvider; + + public static IRandomProvider Provider => m_Provider ??= new Xoshiro256PlusPlus(); + public static IRandomProvider SecureProvider => m_SecureProvider ??= new SecureRandom(); + } +} diff --git a/Projects/Server/Utilities/Random.cs b/Projects/Server/Utilities/SecureRandom.cs similarity index 53% rename from Projects/Server/Utilities/Random.cs rename to Projects/Server/Utilities/SecureRandom.cs index e44ff0d4d..a0ff95fe2 100644 --- a/Projects/Server/Utilities/Random.cs +++ b/Projects/Server/Utilities/SecureRandom.cs @@ -2,7 +2,7 @@ * ModernUO * * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: Random.cs - Created: 2019/12/30 - Updated: 2019/01/05 * + * File: Random.cs - Created: 2019/12/30 - Updated: 2020/05/23 * * * * 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 * @@ -23,49 +23,18 @@ using System.Security.Cryptography; namespace Server { - /// - /// Handles random number generation. - /// - public static class RandomImpl + public class SecureRandom : RandomNumberGenerator, IRandomProvider { - private static readonly Xoshiro256PlusPlus _Random = new Xoshiro256PlusPlus(); + private readonly RandomNumberGenerator m_Random = new RNGCryptoServiceProvider(); - public static uint Next(uint c) => _Random.Next(c); + public override void GetBytes(byte[] data) => m_Random.GetBytes(data); - public static bool NextBool() => _Random.NextBool(); + public override void GetBytes(Span data) => m_Random.GetBytes(data); - public static void GetBytes(Span b) => _Random.GetBytes(b); + public uint Next(uint c) => throw new NotImplementedException(); - public static double NextDouble() => _Random.NextDouble(); - } + public bool NextBool() => throw new NotImplementedException(); - public static class SecureRandomImpl - { - public static readonly RandomNumberGenerator _Random; - - static SecureRandomImpl() - { - try - { - _Random = new DRng64(); - if (_Random is IHardwareRNG rng && rng?.IsSupported() != true) - _Random = new RNGCryptoServiceProvider(); - } - catch (Exception) - { - _Random = new RNGCryptoServiceProvider(); - } - } - - public static bool IsHardwareRNG => _Random is IHardwareRNG; - - public static string Name => _Random.GetType().Name; - - public static void GetBytes(Span buffer) => _Random.GetBytes(buffer); - } - - public interface IHardwareRNG - { - bool IsSupported(); + public double NextDouble() => throw new NotImplementedException(); } } diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 99fbd7cf8..11a91659b 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -875,7 +875,7 @@ namespace Server var sides = numSides; for (var i = 0; i < numDice; ++i) - total += (int)RandomImpl.Next(sides) + 1; + total += (int)RandomProviders.Provider.Next(sides) + 1; return total + bonus; } @@ -885,7 +885,7 @@ namespace Server var count = list.Count; for (var i = count - 1; i > 0; i--) { - var r = (int)RandomImpl.Next((uint)count); + var r = (int)RandomProviders.Provider.Next((uint)count); var swap = list[r]; list[r] = list[i]; list[i] = swap; @@ -896,7 +896,7 @@ namespace Server public static T RandomList(IList list) => list[Random(list.Count)]; - public static bool RandomBool() => RandomImpl.NextBool(); + public static bool RandomBool() => RandomProviders.Provider.NextBool(); public static int RandomMinMax(int min, int max) { @@ -911,27 +911,27 @@ namespace Server return min; } - return min + (int)RandomImpl.Next((uint)(max - min + 1)); + return min + (int)RandomProviders.Provider.Next((uint)(max - min + 1)); } public static int Random(int from, int count) { if (count == 0) return from; - if (count > 0) return (int)(from + RandomImpl.Next((uint)count)); + if (count > 0) return (int)(from + RandomProviders.Provider.Next((uint)count)); - return (int)(from - RandomImpl.Next((uint)-count)); + return (int)(from - RandomProviders.Provider.Next((uint)-count)); } - public static int Random(int count) => (int)RandomImpl.Next((uint)count); + public static int Random(int count) => (int)RandomProviders.Provider.Next((uint)count); - public static uint Random(uint count) => RandomImpl.Next(count); + public static uint Random(uint count) => RandomProviders.Provider.Next(count); - public static void RandomBytes(byte[] buffer) => RandomImpl.GetBytes(buffer); + public static void RandomBytes(byte[] buffer) => RandomProviders.Provider.GetBytes(buffer); - public static void RandomBytes(Span buffer) => RandomImpl.GetBytes(buffer); + public static void RandomBytes(Span buffer) => RandomProviders.Provider.GetBytes(buffer); - public static double RandomDouble() => RandomImpl.NextDouble(); + public static double RandomDouble() => RandomProviders.Provider.NextDouble(); /// /// Random pink, blue, green, orange, red or yellow hue diff --git a/Projects/Server/Utilities/Xoshiro256PlusPlus.cs b/Projects/Server/Utilities/Xoshiro256PlusPlus.cs index e0273f289..fce281e60 100644 --- a/Projects/Server/Utilities/Xoshiro256PlusPlus.cs +++ b/Projects/Server/Utilities/Xoshiro256PlusPlus.cs @@ -3,7 +3,7 @@ * Copyright (C) 2019-2020 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Xoshiro256PlusPlus.cs * - * Created: 2019/12/29 - Updated: 2019/12/30 * + * Created: 2019/12/29 - Updated: 2020/05/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 * @@ -22,10 +22,11 @@ using System; using System.Runtime.CompilerServices; using System.Security.Cryptography; +using Server.Utilities; namespace Server { - public class Xoshiro256PlusPlus : RandomNumberGenerator + public class Xoshiro256PlusPlus : RandomNumberGenerator, IRandomProvider { private ulong _s0, _s1, _s2, _s3; diff --git a/README.md b/README.md index ba614ac6f..ed8ca5289 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,6 @@ Windows #### Requirements - [.NET Core 3.1 Runtime](https://dotnet.microsoft.com/download/dotnet-core/3.1) -- Optional: compile and install [Intel DRNG](https://github.com/modernuo/libdrng) #### Windows - Run `ModernUO.exe` or `dotnet ModernUO.dll`