parent
ebca8db7dc
commit
75adbc18a2
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||
|
|
@ -11,7 +12,7 @@ using Microsoft.Extensions.Logging;
|
|||
namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||
{
|
||||
/// <summary>
|
||||
/// A primary listener waits for incoming connections on a specified socket. Incoming
|
||||
/// A primary listener waits for incoming connections on a specified socket. Incoming
|
||||
/// connections may be passed to a secondary listener to handle.
|
||||
/// </summary>
|
||||
public abstract class ListenerPrimary : Listener
|
||||
|
|
@ -19,8 +20,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
private readonly List<UvPipeHandle> _dispatchPipes = new List<UvPipeHandle>();
|
||||
private int _dispatchIndex;
|
||||
private string _pipeName;
|
||||
private IntPtr _fileCompletionInfoPtr;
|
||||
private bool _tryDetachFromIOCP = PlatformApis.IsWindows;
|
||||
|
||||
// this message is passed to write2 because it must be non-zero-length,
|
||||
// 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 }) });
|
||||
|
||||
|
|
@ -37,6 +40,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
{
|
||||
_pipeName = pipeName;
|
||||
|
||||
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(address, thread).ConfigureAwait(false);
|
||||
|
||||
await Thread.PostAsync(state => ((ListenerPrimary)state).PostCallback(),
|
||||
|
|
@ -85,6 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
}
|
||||
else
|
||||
{
|
||||
DetachFromIOCP(socket);
|
||||
var dispatchPipe = _dispatchPipes[index];
|
||||
var write = new UvWriteReq(Log);
|
||||
write.Init(Thread.Loop);
|
||||
|
|
@ -92,7 +103,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
dispatchPipe,
|
||||
_dummyMessage,
|
||||
socket,
|
||||
(write2, status, error, state) =>
|
||||
(write2, status, error, state) =>
|
||||
{
|
||||
write2.Dispose();
|
||||
((UvStreamHandle)state).Dispose();
|
||||
|
|
@ -101,12 +112,59 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
uint status;
|
||||
ulong information;
|
||||
}
|
||||
|
||||
private struct FILE_COMPLETION_INFORMATION
|
||||
{
|
||||
public IntPtr Port;
|
||||
public IntPtr Key;
|
||||
}
|
||||
|
||||
[DllImport("NtDll.dll")]
|
||||
private static extern uint NtSetInformationFile(IntPtr FileHandle,
|
||||
out IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, uint Length,
|
||||
int FileInformationClass);
|
||||
|
||||
public override async Task DisposeAsync()
|
||||
{
|
||||
// Call base first so the ListenSocket gets closed and doesn't
|
||||
// try to dispatch connections to closed pipes.
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
|
||||
if (_fileCompletionInfoPtr != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(_fileCompletionInfoPtr);
|
||||
_fileCompletionInfoPtr = IntPtr.Zero;
|
||||
}
|
||||
|
||||
if (Thread.FatalError == null && ListenPipe != null)
|
||||
{
|
||||
await Thread.PostAsync(state =>
|
||||
|
|
|
|||
Loading…
Reference in New Issue