Merge branch 'benaadams/loopback-fastpath' into dev
This commit is contained in:
commit
aa48ad2933
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
_uv_stop = NativeDarwinMonoMethods.uv_stop;
|
||||
_uv_ref = NativeDarwinMonoMethods.uv_ref;
|
||||
_uv_unref = NativeDarwinMonoMethods.uv_unref;
|
||||
_uv_fileno = NativeDarwinMonoMethods.uv_fileno;
|
||||
_uv_close = NativeDarwinMonoMethods.uv_close;
|
||||
_uv_async_init = NativeDarwinMonoMethods.uv_async_init;
|
||||
_uv_async_send = NativeDarwinMonoMethods.uv_async_send;
|
||||
|
|
@ -69,6 +70,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
_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;
|
||||
|
|
@ -179,6 +181,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
_uv_unref(handle);
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
protected delegate int uv_fileno_func(UvHandle handle, ref IntPtr socket);
|
||||
protected uv_fileno_func _uv_fileno;
|
||||
public int uv_fileno(UvHandle handle, ref IntPtr socket)
|
||||
{
|
||||
handle.Validate();
|
||||
return Check(_uv_fileno(handle, ref socket));
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void uv_close_cb(IntPtr handle);
|
||||
protected Action<IntPtr, uv_close_cb> _uv_close;
|
||||
|
|
@ -229,6 +240,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
{
|
||||
handle.Validate();
|
||||
Check(_uv_tcp_bind(handle, ref addr, flags));
|
||||
if (PlatformApis.IsWindows)
|
||||
{
|
||||
tcp_bind_windows_extras(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void tcp_bind_windows_extras(UvTcpHandle handle)
|
||||
{
|
||||
const int SIO_LOOPBACK_FAST_PATH = -1744830448; // IOC_IN | IOC_WS2 | 16;
|
||||
const int WSAEOPNOTSUPP = 10000 + 45; // (WSABASEERR+45)
|
||||
const int SOCKET_ERROR = -1;
|
||||
|
||||
var socket = IntPtr.Zero;
|
||||
Check(_uv_fileno(handle, ref socket));
|
||||
|
||||
// Enable loopback fast-path for lower latency for localhost comms, like HttpPlatformHandler fronting
|
||||
// http://blogs.technet.com/b/wincat/archive/2012/12/05/fast-tcp-loopback-performance-and-low-latency-with-windows-server-2012-tcp-loopback-fast-path.aspx
|
||||
// https://github.com/libuv/libuv/issues/489
|
||||
var optionValue = 1;
|
||||
uint dwBytes = 0u;
|
||||
|
||||
var result = NativeMethods.WSAIoctl(socket, SIO_LOOPBACK_FAST_PATH, &optionValue, sizeof(int), null, 0, out dwBytes, IntPtr.Zero, IntPtr.Zero);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
var errorId = NativeMethods.WSAGetLastError();
|
||||
if (errorId == WSAEOPNOTSUPP)
|
||||
{
|
||||
// This system is not >= Windows Server 2012, and the call is not supported.
|
||||
}
|
||||
else
|
||||
{
|
||||
Check(errorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Func<UvTcpHandle, IntPtr, int> _uv_tcp_open;
|
||||
|
|
@ -509,6 +554,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
[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);
|
||||
|
||||
|
|
@ -598,6 +646,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
|
||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||
unsafe public static extern int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg);
|
||||
|
||||
[DllImport("WS2_32.dll", CallingConvention = CallingConvention.Winapi)]
|
||||
unsafe public static extern 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();
|
||||
}
|
||||
|
||||
private static class NativeDarwinMonoMethods
|
||||
|
|
@ -620,6 +684,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
|||
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_unref(UvHandle handle);
|
||||
|
||||
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int uv_fileno(UvHandle handle, ref IntPtr socket);
|
||||
|
||||
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
||||
|
||||
|
|
|
|||
|
|
@ -152,9 +152,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
host.Start();
|
||||
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||
using (var socket = TestConnection.CreateConnectedLoopbackSocket(port))
|
||||
{
|
||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||
socket.Send(Encoding.ASCII.GetBytes("GET /%41%CC%8A/A/../B/%41%CC%8A HTTP/1.1\r\n\r\n"));
|
||||
socket.Shutdown(SocketShutdown.Send);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
// 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 Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
public class TestConnection
|
||||
{
|
||||
public static Socket CreateConnectedLoopbackSocket(int port)
|
||||
{
|
||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
if (PlatformApis.IsWindows)
|
||||
{
|
||||
const int SIO_LOOPBACK_FAST_PATH = -1744830448;
|
||||
var optionInValue = BitConverter.GetBytes(1);
|
||||
try
|
||||
{
|
||||
socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the operating system version on this machine did
|
||||
// not support SIO_LOOPBACK_FAST_PATH (i.e. version
|
||||
// prior to Windows 8 / Windows Server 2012), handle the exception
|
||||
}
|
||||
}
|
||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ using Microsoft.AspNetCore.Http.Features;
|
|||
using Microsoft.AspNetCore.Server.Kestrel;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Filter;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -109,8 +111,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
var address = ServerAddress.FromUrl($"http://localhost:{port}/");
|
||||
var started = engine.CreateServer(address);
|
||||
|
||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(port);
|
||||
socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World"));
|
||||
socket.Shutdown(SocketShutdown.Send);
|
||||
var buffer = new byte[8192];
|
||||
|
|
@ -456,8 +457,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
using (var server = new TestServer(App, testContext))
|
||||
{
|
||||
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
socket.Connect(IPAddress.Loopback, server.Port);
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(server.Port);
|
||||
await Task.Delay(200);
|
||||
socket.Dispose();
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
var pipeName = @"\\.\pipe\ServerPipeDispatchConnections" + Guid.NewGuid().ToString("n");
|
||||
|
||||
var port = TestServer.GetNextPort();
|
||||
var loop = new UvLoopHandle(_logger);
|
||||
loop.Init(_uv);
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
var serverListenTcp = new UvTcpHandle(_logger);
|
||||
serverListenTcp.Init(loop, (a, b) => { });
|
||||
var address = ServerAddress.FromUrl("http://localhost:54321/");
|
||||
var address = ServerAddress.FromUrl($"http://localhost:{port}/");
|
||||
serverListenTcp.Bind(address);
|
||||
serverListenTcp.Listen(128, (_1, status, error, _2) =>
|
||||
{
|
||||
|
|
@ -237,8 +238,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
{
|
||||
serverConnectionPipeAcceptedEvent.WaitOne();
|
||||
|
||||
var socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
||||
socket.Connect(IPAddress.Loopback, 54321);
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(port);
|
||||
socket.Send(new byte[] { 6, 7, 8, 9 });
|
||||
socket.Shutdown(SocketShutdown.Send);
|
||||
var cb = socket.Receive(new byte[64]);
|
||||
|
|
|
|||
|
|
@ -87,22 +87,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
tcp2.Dispose();
|
||||
stream.Dispose();
|
||||
}, null);
|
||||
var t = Task.Run(async () =>
|
||||
var t = Task.Run(() =>
|
||||
{
|
||||
var socket = new Socket(
|
||||
AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
#if DNX451
|
||||
await Task.Factory.FromAsync(
|
||||
socket.BeginConnect,
|
||||
socket.EndConnect,
|
||||
new IPEndPoint(IPAddress.Loopback, port),
|
||||
null,
|
||||
TaskCreationOptions.None);
|
||||
#else
|
||||
await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, port));
|
||||
#endif
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(port);
|
||||
socket.Dispose();
|
||||
});
|
||||
loop.Run();
|
||||
|
|
@ -141,17 +128,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
}, null);
|
||||
var t = Task.Run(async () =>
|
||||
{
|
||||
var socket = new Socket(
|
||||
AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(port);
|
||||
#if DNX451
|
||||
await Task.Factory.FromAsync(
|
||||
socket.BeginConnect,
|
||||
socket.EndConnect,
|
||||
new IPEndPoint(IPAddress.Loopback, port),
|
||||
null,
|
||||
TaskCreationOptions.None);
|
||||
await Task.Factory.FromAsync(
|
||||
socket.BeginSend,
|
||||
socket.EndSend,
|
||||
|
|
@ -160,7 +138,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
null,
|
||||
TaskCreationOptions.None);
|
||||
#else
|
||||
await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, port));
|
||||
await socket.SendAsync(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4, 5 }) },
|
||||
SocketFlags.None);
|
||||
#endif
|
||||
|
|
@ -226,17 +203,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
}, null);
|
||||
var t = Task.Run(async () =>
|
||||
{
|
||||
var socket = new Socket(
|
||||
AddressFamily.InterNetwork,
|
||||
SocketType.Stream,
|
||||
ProtocolType.Tcp);
|
||||
var socket = TestConnection.CreateConnectedLoopbackSocket(port);
|
||||
#if DNX451
|
||||
await Task.Factory.FromAsync(
|
||||
socket.BeginConnect,
|
||||
socket.EndConnect,
|
||||
new IPEndPoint(IPAddress.Loopback, port),
|
||||
null,
|
||||
TaskCreationOptions.None);
|
||||
await Task.Factory.FromAsync(
|
||||
socket.BeginSend,
|
||||
socket.EndSend,
|
||||
|
|
@ -245,7 +213,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
null,
|
||||
TaskCreationOptions.None);
|
||||
#else
|
||||
await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, port));
|
||||
await socket.SendAsync(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4, 5 }) },
|
||||
SocketFlags.None);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||
|
|
@ -28,8 +29,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
|
||||
public void Create(int port)
|
||||
{
|
||||
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
_socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||
_socket = CreateConnectedLoopbackSocket(port);
|
||||
|
||||
_stream = new NetworkStream(_socket, false);
|
||||
_reader = new StreamReader(_stream, Encoding.ASCII);
|
||||
|
|
@ -125,5 +125,27 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
|||
var text = new string(ch, 0, count);
|
||||
Assert.Equal("", text);
|
||||
}
|
||||
|
||||
public static Socket CreateConnectedLoopbackSocket(int port)
|
||||
{
|
||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
if (PlatformApis.IsWindows)
|
||||
{
|
||||
const int SIO_LOOPBACK_FAST_PATH = -1744830448;
|
||||
var optionInValue = BitConverter.GetBytes(1);
|
||||
try
|
||||
{
|
||||
socket.IOControl(SIO_LOOPBACK_FAST_PATH, optionInValue, null);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the operating system version on this machine did
|
||||
// not support SIO_LOOPBACK_FAST_PATH (i.e. version
|
||||
// prior to Windows 8 / Windows Server 2012), handle the exception
|
||||
}
|
||||
}
|
||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue