Removes libdrng (rdrand) (#139)

This commit is contained in:
Kamron Batman 2020-05-24 10:47:47 -07:00 committed by GitHub
parent 739e77a973
commit 2770ab1d50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 64 additions and 4298 deletions

View file

@ -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();
}
}

View file

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

View file

@ -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<TRequest> : ICriticalNotifyCompletion where TRequest : UvRequest
{
private static readonly Action _callbackCompleted = () => { };
private Action _callback;
private UvException _exception;
private int _status;
public static readonly Action<TRequest, int, UvException, object> Callback = (req, status, error, state) =>
{
var awaitable = (LibuvAwaitable<TRequest>)state;
awaitable._exception = error;
awaitable._status = status;
var continuation = Interlocked.Exchange(ref awaitable._callback, _callbackCompleted);
continuation?.Invoke();
};
public LibuvAwaitable<TRequest> 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<TRequest>)}.{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;
}
}
}

View file

@ -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<UvStreamHandle, int, object> _readCallback =
(handle, status, state) => ReadCallback(handle, status, state);
private static readonly Func<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> _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<object> _waitForConnectionClosedTcs = new TaskCompletionSource<object>(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<byte> 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<FlushResult> 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)}.");
}
}
}
}

View file

@ -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<ListenerContext> _listeners = new List<ListenerContext>();
private IAsyncEnumerator<LibuvConnection> _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<LibuvThread> Threads { get; } = new List<LibuvThread>();
public IHostApplicationLifetime AppLifetime => TransportContext.AppLifetime;
public ILibuvTrace Log => TransportContext.Log;
public LibuvTransportOptions TransportOptions => TransportContext.Options;
public EndPoint EndPoint { get; set; }
public async ValueTask<ConnectionContext> 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<LibuvConnection> 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<bool> WaitAsync(Task task, TimeSpan timeout) => await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task;
}
}

View file

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

View file

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

View file

@ -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<object> _threadTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly UvLoopHandle _loop;
private readonly UvAsyncHandle _post;
private Queue<Work> _workAdding = new Queue<Work>(1024);
private Queue<Work> _workRunning = new Queue<Work>(1024);
private Queue<CloseHandle> _closeHandleAdding = new Queue<CloseHandle>(256);
private Queue<CloseHandle> _closeHandleRunning = new Queue<CloseHandle>(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<byte> 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<byte> MemoryPool { get; }
public WriteReqPool WriteReqPool { get; }
public Exception FatalError => _closeError;
public Action<Action<IntPtr>, IntPtr> QueueCloseHandle { get; }
private Action<Action<IntPtr>, IntPtr> QueueCloseAsyncHandle { get; }
public Task StartAsync()
{
var tcs = new TaskCompletionSource<int>(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<UvHandle>(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<T>(Action<T> callback, T state)
{
// Handle is closed to don't bother scheduling anything
if (_post.IsClosed) return;
var work = new Work
{
CallbackAdapter = CallbackAdapter<T>.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<LibuvThread> callback)
{
Post(callback, this);
}
public Task PostAsync<T>(Action<T> callback, T state)
{
// Handle is closed to don't bother scheduling anything
if (_post.IsClosed) return Task.CompletedTask;
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var work = new Work
{
CallbackAdapter = CallbackAdapter<T>.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<IntPtr> 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<IntPtr> callback, IntPtr handle)
{
EnqueueCloseHandle(callback, handle);
_post.Send();
}
private void EnqueueCloseHandle(Action<IntPtr> 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<int>)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<UvHandle>(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<Work> 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<CloseHandle> 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<bool> WaitAsync(Task task, TimeSpan timeout) => await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false) == task;
public override void Schedule(Action<object> action, object state)
{
Post(action, state);
}
private struct Work
{
public Action<object, object> CallbackAdapter;
public object Callback;
public object State;
public TaskCompletionSource<object> Completion;
}
private struct CloseHandle
{
public Action<IntPtr> Callback;
public IntPtr Handle;
}
private class CallbackAdapter<T>
{
public static readonly Action<object, object> PostCallbackAdapter = (callback, state) => ((Action<T>)callback).Invoke((T)state);
public static readonly Action<object, object> PostAsyncCallbackAdapter = (callback, state) => ((Action<T>)callback).Invoke((T)state);
}
}
}

View file

@ -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<ILogger, string, Exception> _connectionPause =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(4, nameof(ConnectionPause)), @"Connection id ""{ConnectionId}"" paused.");
private static readonly Action<ILogger, string, Exception> _connectionResume =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(5, nameof(ConnectionResume)), @"Connection id ""{ConnectionId}"" resumed.");
private static readonly Action<ILogger, string, Exception> _connectionReadFin =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(6, nameof(ConnectionReadFin)), @"Connection id ""{ConnectionId}"" received FIN.");
private static readonly Action<ILogger, string, string, Exception> _connectionWriteFin =
LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(7, nameof(ConnectionWriteFin)), @"Connection id ""{ConnectionId}"" sending FIN because: ""{Reason}""");
// ConnectionWrite: Reserved: 11
// ConnectionWriteCallback: Reserved: 12
private static readonly Action<ILogger, string, Exception> _connectionError =
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(14, nameof(ConnectionError)), @"Connection id ""{ConnectionId}"" communication error.");
private static readonly Action<ILogger, string, Exception> _connectionReset =
LoggerMessage.Define<string>(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>(TState state) => _logger.BeginScope(state);
public bool IsEnabled(LogLevel logLevel) => _logger.IsEnabled(logLevel);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
=> _logger.Log(logLevel, eventId, state, exception, formatter);
}
}

View file

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

View file

@ -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<LibuvTransportOptions> 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<IConnectionListener> 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;
}
}
}

View file

@ -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
{
/// <summary>
/// Base class for listeners in Kestrel. Listens for incoming connections
/// </summary>
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);
}
/// <summary>
/// Creates the socket used to listen for incoming connections
/// </summary>
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);
}
/// <summary>
/// Handles an incoming connection
/// </summary>
/// <param name="listenSocket">Socket being used to listen on</param>
/// <param name="status">Connection status</param>
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;
}
}
}

View file

@ -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<LibuvConnection> _acceptQueue = Channel.CreateUnbounded<LibuvConnection>(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<LibuvConnection> AcceptAsync(CancellationToken cancellationToken = default)
{
while (await _acceptQueue.Reader.WaitToReadAsync())
while (_acceptQueue.Reader.TryRead(out var connection))
return connection;
return null;
}
/// <summary>
/// Aborts all unaccepted connections in the queue
/// </summary>
/// <returns></returns>
public async Task AbortQueuedConnectionAsync()
{
while (await _acceptQueue.Reader.WaitToReadAsync())
while (_acceptQueue.Reader.TryRead(out var connection))
// REVIEW: Pass an abort reason?
connection.Abort();
}
/// <summary>
/// Creates a socket which can be used to accept an incoming connection.
/// </summary>
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()
};
}
}
}

View file

@ -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
{
/// <summary>
/// A primary listener waits for incoming connections on a specified socket. Incoming
/// connections may be passed to a secondary listener to handle.
/// </summary>
internal class ListenerPrimary : Listener
{
// The list of pipes that can be dispatched to (where we've confirmed the _pipeMessage)
private readonly List<UvPipeHandle> _dispatchPipes = new List<UvPipeHandle>();
// The list of pipes we've created but may not be part of _dispatchPipes
private readonly List<UvPipeHandle> _createdPipes = new List<UvPipeHandle>();
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<ArraySegment<byte>> _dummyMessage = new ArraySegment<ArraySegment<byte>>(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }) });
public ListenerPrimary(LibuvTransportContext transportContext) : base(transportContext)
{
}
/// <summary>
/// For testing purposes.
/// </summary>
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<FILE_COMPLETION_INFORMATION>(), 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");
}
}
}
}
}

View file

@ -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
{
/// <summary>
/// A secondary listener is delegated requests from a primary listener via a named pipe or
/// UNIX domain socket.
/// </summary>
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<int>(this, TaskCreationOptions.RunContinuationsAsynchronously);
Thread.Post(StartCallback, tcs);
return tcs.Task;
}
private static void StartCallback(TaskCompletionSource<int> tcs)
{
var listener = (ListenerSecondary)tcs.Task.AsyncState;
listener.StartedCallback(tcs);
}
private void StartedCallback(TaskCompletionSource<int> 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<int>)state),
tcs);
}
catch (Exception ex)
{
DispatchPipe.Dispose();
connect.Dispose();
tcs.SetException(ex);
}
}
private static void ConnectCallback(UvConnectRequest connect, int status, UvException error, TaskCompletionSource<int> tcs)
{
var listener = (ListenerSecondary)tcs.Task.AsyncState;
_ = listener.ConnectedCallback(connect, status, error, tcs);
}
private async Task ConnectedCallback(UvConnectRequest connect, int status, UvException error, TaskCompletionSource<int> 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<ArraySegment<byte>>(new[] { new ArraySegment<byte>(_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();
}
}
}
}

View file

@ -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<UvLoopHandle, int> _uv_loop_init;
public void loop_init(UvLoopHandle handle)
{
ThrowIfErrored(_uv_loop_init(handle));
}
protected Func<IntPtr, int> _uv_loop_close;
public void loop_close(UvLoopHandle handle)
{
handle.Validate(closed: true);
ThrowIfErrored(_uv_loop_close(handle.InternalGetHandle()));
}
protected Func<UvLoopHandle, int, int> _uv_run;
public void run(UvLoopHandle handle, int mode)
{
handle.Validate();
ThrowIfErrored(_uv_run(handle, mode));
}
protected Action<UvLoopHandle> _uv_stop;
public void stop(UvLoopHandle handle)
{
handle.Validate();
_uv_stop(handle);
}
protected Action<UvHandle> _uv_ref;
public void @ref(UvHandle handle)
{
handle.Validate();
_uv_ref(handle);
}
protected Action<UvHandle> _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<IntPtr, uv_close_cb> _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<UvLoopHandle, UvAsyncHandle, uv_async_cb, int> _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<UvAsyncHandle, int> _uv_async_send;
public void async_send(UvAsyncHandle handle)
{
ThrowIfErrored(_uv_async_send(handle));
}
protected Func<IntPtr, int> _uv_unsafe_async_send;
public void unsafe_async_send(IntPtr handle)
{
ThrowIfErrored(_uv_unsafe_async_send(handle));
}
protected Func<UvLoopHandle, UvTcpHandle, int> _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<UvTcpHandle, IntPtr, int> _uv_tcp_open;
public void tcp_open(UvTcpHandle handle, IntPtr hSocket)
{
handle.Validate();
ThrowIfErrored(_uv_tcp_open(handle, hSocket));
}
protected Func<UvTcpHandle, int, int> _uv_tcp_nodelay;
public void tcp_nodelay(UvTcpHandle handle, bool enable)
{
handle.Validate();
ThrowIfErrored(_uv_tcp_nodelay(handle, enable ? 1 : 0));
}
protected Func<UvLoopHandle, UvPipeHandle, int, int> _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<UvPipeHandle, string, int> _uv_pipe_bind;
public void pipe_bind(UvPipeHandle handle, string name)
{
handle.Validate();
ThrowIfErrored(_uv_pipe_bind(handle, name));
}
protected Func<UvPipeHandle, IntPtr, int> _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<UvStreamHandle, int, uv_connection_cb, int> _uv_listen;
public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb)
{
handle.Validate();
ThrowIfErrored(_uv_listen(handle, backlog, cb));
}
protected Func<UvStreamHandle, UvStreamHandle, int> _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<UvConnectRequest, UvPipeHandle, string, uv_connect_cb> _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<UvPipeHandle, int> _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<UvStreamHandle, uv_alloc_cb, uv_read_cb, int> _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<UvStreamHandle, int> _uv_read_stop;
public void read_stop(UvStreamHandle handle)
{
handle.Validate();
ThrowIfErrored(_uv_read_stop(handle));
}
protected Func<UvStreamHandle, uv_buf_t[], int, int> _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<int, IntPtr> _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<int, IntPtr> _uv_strerror;
public string strerror(int err)
{
IntPtr ptr = _uv_strerror(err);
return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr);
}
protected Func<int> _uv_loop_size;
public int loop_size() => _uv_loop_size();
protected Func<HandleType, int> _uv_handle_size;
public int handle_size(HandleType handleType) => _uv_handle_size(handleType);
protected Func<RequestType, int> _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<UvLoopHandle, uv_walk_cb, IntPtr, int> _uv_walk;
public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg)
{
loop.Validate();
_uv_walk(loop, walk_cb, arg);
}
protected Func<UvLoopHandle, UvTimerHandle, int> _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<UvTimerHandle, uv_timer_cb, long, long, int> _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<UvTimerHandle, int> _uv_timer_stop;
public unsafe void timer_stop(UvTimerHandle handle)
{
handle.Validate();
ThrowIfErrored(_uv_timer_stop(handle));
}
protected Func<UvLoopHandle, long> _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();
}
}
}

View file

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

View file

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

View file

@ -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<Action<IntPtr>, IntPtr> _queueCloseHandle;
public UvAsyncHandle(ILibuvTrace logger) : base(logger)
{
}
public void Init(UvLoopHandle loop, Action callback, Action<Action<IntPtr>, 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<UvAsyncHandle>(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;
}
}
}

View file

@ -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>
/// Summary description for UvWriteRequest
/// </summary>
internal class UvConnectRequest : UvRequest
{
private static readonly LibuvFunctions.uv_connect_cb _uv_connect_cb = (req, status) => UvConnectCb(req, status);
private Action<UvConnectRequest, int, UvException, object> _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<UvConnectRequest, int, UvException, object> 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<UvConnectRequest>(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;
}
}
}
}

View file

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

View file

@ -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<Action<IntPtr>, IntPtr> _queueCloseHandle;
protected UvHandle(ILibuvTrace logger) : base (logger)
{
}
protected void CreateHandle(
LibuvFunctions uv,
int threadId,
int size,
Action<Action<IntPtr>, 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);
}
}
}

View file

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

View file

@ -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>
/// Summary description for UvMemory
/// </summary>
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<THandle>(IntPtr handle)
{
GCHandle gcHandle = GCHandle.FromIntPtr(*(IntPtr*)handle);
return (THandle)gcHandle.Target;
}
}
}

View file

@ -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<Action<IntPtr>, 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);
}
}

View file

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

View file

@ -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<UvStreamHandle, int, UvException, object> _listenCallback;
private object _listenState;
private GCHandle _listenVitality;
private Func<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> _allocCallback;
private Action<UvStreamHandle, int, object> _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<UvStreamHandle, int, UvException, object> 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<UvStreamHandle, int, object, LibuvFunctions.uv_buf_t> allocCallback,
Action<UvStreamHandle, int, object> 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<UvStreamHandle>(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<UvStreamHandle>(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<UvStreamHandle>(handle);
try
{
stream._readCallback(stream, status, stream._readState);
}
catch (Exception ex)
{
stream._log.LogError(0, ex, "UbReadCb");
throw;
}
}
}
}

View file

@ -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<Action<IntPtr>, 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<SockAddr>();
_uv.tcp_getpeername(this, out var socketAddress, ref namelen);
return socketAddress.GetIPEndPoint();
}
public IPEndPoint GetSockIPEndPoint()
{
int namelen = Marshal.SizeOf<SockAddr>();
_uv.tcp_getsockname(this, out var socketAddress, ref namelen);
return socketAddress.GetIPEndPoint();
}
public void NoDelay(bool enable)
{
_uv.tcp_nodelay(this, enable);
}
}
}

View file

@ -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<UvTimerHandle> _callback;
public UvTimerHandle(ILibuvTrace logger) : base(logger)
{
}
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
{
CreateHandle(
loop.Libuv,
loop.ThreadId,
loop.Libuv.handle_size(LibuvFunctions.HandleType.TIMER),
queueCloseHandle);
_uv.timer_init(loop, this);
}
public void Start(Action<UvTimerHandle> 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<UvTimerHandle>(handle);
try
{
timer._callback(timer);
}
catch (Exception ex)
{
timer._log.LogError(0, ex, nameof(UvTimerCb));
throw;
}
}
}
}

View file

@ -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>
/// Summary description for UvWriteRequest
/// </summary>
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<UvWriteReq, int, UvException, object> _callback;
private object _state;
private const int BUFFER_COUNT = 4;
private readonly LibuvAwaitable<UvWriteReq> _awaitable = new LibuvAwaitable<UvWriteReq>();
private readonly List<GCHandle> _pins = new List<GCHandle>(BUFFER_COUNT + 1);
private readonly List<MemoryHandle> _handles = new List<MemoryHandle>(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<LibuvFunctions.uv_buf_t>() * BUFFER_COUNT;
CreateMemory(
loop.Libuv,
loop.ThreadId,
requestSize + bufferSize);
_bufs = handle + requestSize;
}
public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, in ReadOnlySequence<byte> buffer)
{
Write(handle, buffer, LibuvAwaitable<UvWriteReq>.Callback, _awaitable);
return _awaitable;
}
public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, ArraySegment<ArraySegment<byte>> bufs)
{
Write(handle, bufs, LibuvAwaitable<UvWriteReq>.Callback, _awaitable);
return _awaitable;
}
private unsafe void Write(
UvStreamHandle handle,
in ReadOnlySequence<byte> buffer,
Action<UvWriteReq, int, UvException, object> 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<ArraySegment<byte>> bufs,
Action<UvWriteReq, int, UvException, object> callback,
object state)
{
WriteArraySegmentInternal(handle, bufs, sendHandle: null, callback: callback, state: state);
}
public void Write2(
UvStreamHandle handle,
ArraySegment<ArraySegment<byte>> bufs,
UvStreamHandle sendHandle,
Action<UvWriteReq, int, UvException, object> callback,
object state)
{
WriteArraySegmentInternal(handle, bufs, sendHandle, callback, state);
}
private unsafe void WriteArraySegmentInternal(
UvStreamHandle handle,
ArraySegment<ArraySegment<byte>> bufs,
UvStreamHandle sendHandle,
Action<UvWriteReq, int, UvException, object> 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<UvWriteReq>(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;
}
}
}
}

View file

@ -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<UvWriteReq> _pool = new Queue<UvWriteReq>(_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();
}
}
}
}

View file

@ -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
{
/// <summary>
/// Provides programmatic configuration of Libuv transport features.
/// </summary>
public class LibuvTransportOptions
{
/// <summary>
/// The number of libuv I/O threads used to process requests.
/// </summary>
/// <remarks>
/// Defaults to half of <see cref="Environment.ProcessorCount" /> rounded down and clamped between 1 and 16.
/// </remarks>
public int ThreadCount { get; set; } = ProcessorThreadCount;
/// <summary>
/// Set to false to enable Nagle's algorithm for all connections.
/// </summary>
/// <remarks>
/// Defaults to true.
/// </remarks>
public bool NoDelay { get; set; } = true;
/// <summary>
/// The maximum length of the pending connection queue.
/// </summary>
/// <remarks>
/// Defaults to 128.
/// </remarks>
public int Backlog { get; set; } = 128;
public long? MaxReadBufferSize { get; set; } = 1024 * 1024;
public long? MaxWriteBufferSize { get; set; } = 64 * 1024;
internal Func<MemoryPool<byte>> 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;
}
}
}
}

View file

@ -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
{
/// <summary>
/// Specify Libuv as the transport to be used by Kestrel.
/// </summary>
/// <param name="hostBuilder">
/// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure.
/// </param>
/// <returns>
/// The Microsoft.AspNetCore.Hosting.IWebHostBuilder.
/// </returns>
public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder)
{
return hostBuilder.ConfigureServices(services =>
{
services.AddSingleton<IConnectionListenerFactory, LibuvTransportFactory>();
});
}
/// <summary>
/// Specify Libuv as the transport to be used by Kestrel.
/// </summary>
/// <param name="hostBuilder">
/// The Microsoft.AspNetCore.Hosting.IWebHostBuilder to configure.
/// </param>
/// <param name="configureOptions">
/// A callback to configure Libuv options.
/// </param>
/// <returns>
/// The Microsoft.AspNetCore.Hosting.IWebHostBuilder.
/// </returns>
public static IWebHostBuilder UseLibuv(this IWebHostBuilder hostBuilder, Action<LibuvTransportOptions> configureOptions)
{
return hostBuilder.UseLibuv().ConfigureServices(services =>
{
services.Configure(configureOptions);
});
}
}
}

View file

@ -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

View file

@ -34,7 +34,6 @@
<Delete Files="..\..\Distribution\zlib.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libz.dylib" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libz.so" ContinueOnError="true" />
<Delete Files="..\..\Distribution\libdrng.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Microsoft.AspNetCore.Connections.Abstractions.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Microsoft.AspNetCore.Http.Features.dll" ContinueOnError="true" />
<Delete Files="..\..\Distribution\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.dll" ContinueOnError="true" />
@ -75,12 +74,6 @@
<RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
<CodeAnalysisRuleSet>..\..\Rules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Content Include="Assemblies\libdrng.dll" Condition="$(DefineConstants.Contains('WINDOWS'))">
<Link>libdrng.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv" Version="3.1.3" />
@ -88,15 +81,6 @@
<PackageReference Include="System.IO.Pipelines" Version="4.7.1" />
<PackageReference Include="Zlib.Bindings" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Kestrel\Transport.Libuv\**" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Kestrel\Transport.Libuv\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Kestrel\Transport.Libuv\**" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Analyze'">
<PackageReference Include="StyleCop.Analyzers">
<Version>1.1.118</Version>

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
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<byte>(data));
}
public override void GetBytes(byte[] data, int offset, int count)
{
GetBytes(new Span<byte>(data, offset, count));
}
public override unsafe void GetBytes(Span<byte> 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<byte>(data));
}
public override void GetNonZeroBytes(Span<byte> 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);
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Security.Cryptography;
namespace Server
{
public interface IRandomProvider
{
public uint Next(uint c);
public bool NextBool();
public void GetBytes(Span<byte> 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();
}
}

View file

@ -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
{
/// <summary>
/// Handles random number generation.
/// </summary>
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<byte> data) => m_Random.GetBytes(data);
public static void GetBytes(Span<byte> 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<byte> buffer) => _Random.GetBytes(buffer);
}
public interface IHardwareRNG
{
bool IsSupported();
public double NextDouble() => throw new NotImplementedException();
}
}

View file

@ -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<T>(IList<T> 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<byte> buffer) => RandomImpl.GetBytes(buffer);
public static void RandomBytes(Span<byte> buffer) => RandomProviders.Provider.GetBytes(buffer);
public static double RandomDouble() => RandomImpl.NextDouble();
public static double RandomDouble() => RandomProviders.Provider.NextDouble();
/// <summary>
/// Random pink, blue, green, orange, red or yellow hue

View file

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

View file

@ -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`