Enable TCP Loopback Fast Path (Windows)
Enable TCP Loopback Fast Path; where the IP layer is skipped, for lower latency for localhost comms, like HttpPlatformHandler+IIS reverse proxy 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 Have to do it this way due to open issue in libuv Loopback fast path: libuv/libuv#489 Related: "Confirm HttpPlatformHandler uses Fast TCP Loopback" aspnet/IISIntegration#29
This commit is contained in:
parent
f89c959f4f
commit
020e0b9dc5
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
_uv_stop = NativeDarwinMonoMethods.uv_stop;
|
_uv_stop = NativeDarwinMonoMethods.uv_stop;
|
||||||
_uv_ref = NativeDarwinMonoMethods.uv_ref;
|
_uv_ref = NativeDarwinMonoMethods.uv_ref;
|
||||||
_uv_unref = NativeDarwinMonoMethods.uv_unref;
|
_uv_unref = NativeDarwinMonoMethods.uv_unref;
|
||||||
|
_uv_fileno = NativeDarwinMonoMethods.uv_fileno;
|
||||||
_uv_close = NativeDarwinMonoMethods.uv_close;
|
_uv_close = NativeDarwinMonoMethods.uv_close;
|
||||||
_uv_async_init = NativeDarwinMonoMethods.uv_async_init;
|
_uv_async_init = NativeDarwinMonoMethods.uv_async_init;
|
||||||
_uv_async_send = NativeDarwinMonoMethods.uv_async_send;
|
_uv_async_send = NativeDarwinMonoMethods.uv_async_send;
|
||||||
|
|
@ -68,6 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
_uv_stop = NativeMethods.uv_stop;
|
_uv_stop = NativeMethods.uv_stop;
|
||||||
_uv_ref = NativeMethods.uv_ref;
|
_uv_ref = NativeMethods.uv_ref;
|
||||||
_uv_unref = NativeMethods.uv_unref;
|
_uv_unref = NativeMethods.uv_unref;
|
||||||
|
_uv_fileno = NativeMethods.uv_fileno;
|
||||||
_uv_close = NativeMethods.uv_close;
|
_uv_close = NativeMethods.uv_close;
|
||||||
_uv_async_init = NativeMethods.uv_async_init;
|
_uv_async_init = NativeMethods.uv_async_init;
|
||||||
_uv_async_send = NativeMethods.uv_async_send;
|
_uv_async_send = NativeMethods.uv_async_send;
|
||||||
|
|
@ -177,6 +179,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
_uv_unref(handle);
|
_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)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
public delegate void uv_close_cb(IntPtr handle);
|
public delegate void uv_close_cb(IntPtr handle);
|
||||||
protected Action<IntPtr, uv_close_cb> _uv_close;
|
protected Action<IntPtr, uv_close_cb> _uv_close;
|
||||||
|
|
@ -221,6 +232,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
handle.Validate();
|
handle.Validate();
|
||||||
Check(_uv_tcp_bind(handle, ref addr, flags));
|
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;
|
protected Func<UvTcpHandle, IntPtr, int> _uv_tcp_open;
|
||||||
|
|
@ -501,6 +546,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void uv_unref(UvHandle handle);
|
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)]
|
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
||||||
|
|
||||||
|
|
@ -587,6 +635,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
|
|
||||||
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("libuv", CallingConvention = CallingConvention.Cdecl)]
|
||||||
unsafe public static extern int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg);
|
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
|
private static class NativeDarwinMonoMethods
|
||||||
|
|
@ -609,6 +673,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Networking
|
||||||
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void uv_unref(UvHandle handle);
|
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)]
|
[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
public static extern void uv_close(IntPtr handle, uv_close_cb close_cb);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
using Microsoft.AspNetCore.Server.Kestrel;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Filter;
|
using Microsoft.AspNetCore.Server.Kestrel.Filter;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -118,6 +119,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
|
|
||||||
Console.WriteLine("Started");
|
Console.WriteLine("Started");
|
||||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
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.NoDelay = true;
|
||||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||||
socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World"));
|
socket.Send(Encoding.ASCII.GetBytes("POST / HTTP/1.0\r\n\r\nHello World"));
|
||||||
socket.Shutdown(SocketShutdown.Send);
|
socket.Shutdown(SocketShutdown.Send);
|
||||||
|
|
@ -475,6 +492,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
using (var server = new TestServer(App, testContext))
|
using (var server = new TestServer(App, testContext))
|
||||||
{
|
{
|
||||||
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
var socket = new Socket(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.NoDelay = true;
|
||||||
socket.Connect(IPAddress.Loopback, server.Port);
|
socket.Connect(IPAddress.Loopback, server.Port);
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
socket.Dispose();
|
socket.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
serverConnectionPipeAcceptedEvent.WaitOne();
|
serverConnectionPipeAcceptedEvent.WaitOne();
|
||||||
|
|
||||||
var socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
var socket = new Socket(SocketType.Stream, ProtocolType.IP);
|
||||||
|
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.NoDelay = true;
|
||||||
socket.Connect(IPAddress.Loopback, 54321);
|
socket.Connect(IPAddress.Loopback, 54321);
|
||||||
socket.Send(new byte[] { 6, 7, 8, 9 });
|
socket.Send(new byte[] { 6, 7, 8, 9 });
|
||||||
socket.Shutdown(SocketShutdown.Send);
|
socket.Shutdown(SocketShutdown.Send);
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
AddressFamily.InterNetwork,
|
AddressFamily.InterNetwork,
|
||||||
SocketType.Stream,
|
SocketType.Stream,
|
||||||
ProtocolType.Tcp);
|
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.NoDelay = true;
|
||||||
#if DNX451
|
#if DNX451
|
||||||
await Task.Factory.FromAsync(
|
await Task.Factory.FromAsync(
|
||||||
socket.BeginConnect,
|
socket.BeginConnect,
|
||||||
|
|
@ -147,6 +163,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
AddressFamily.InterNetwork,
|
AddressFamily.InterNetwork,
|
||||||
SocketType.Stream,
|
SocketType.Stream,
|
||||||
ProtocolType.Tcp);
|
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.NoDelay = true;
|
||||||
#if DNX451
|
#if DNX451
|
||||||
await Task.Factory.FromAsync(
|
await Task.Factory.FromAsync(
|
||||||
socket.BeginConnect,
|
socket.BeginConnect,
|
||||||
|
|
@ -234,6 +266,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
AddressFamily.InterNetwork,
|
AddressFamily.InterNetwork,
|
||||||
SocketType.Stream,
|
SocketType.Stream,
|
||||||
ProtocolType.Tcp);
|
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.NoDelay = true;
|
||||||
#if DNX451
|
#if DNX451
|
||||||
await Task.Factory.FromAsync(
|
await Task.Factory.FromAsync(
|
||||||
socket.BeginConnect,
|
socket.BeginConnect,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Networking;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
|
|
@ -29,6 +30,22 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
public void Create(int port)
|
public void Create(int port)
|
||||||
{
|
{
|
||||||
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
_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.NoDelay = true;
|
||||||
_socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
_socket.Connect(new IPEndPoint(IPAddress.Loopback, port));
|
||||||
|
|
||||||
_stream = new NetworkStream(_socket, false);
|
_stream = new NetworkStream(_socket, false);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue