Adjusting Kestrel to run cross platform
This commit is contained in:
parent
9c29ccdd32
commit
e4b9bd265c
|
|
@ -74,11 +74,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize)
|
private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize)
|
||||||
{
|
{
|
||||||
return new Libuv.uv_buf_t
|
return handle.Libuv.buf_init(
|
||||||
{
|
SocketInput.Pin(2048),
|
||||||
memory = SocketInput.Pin(2048),
|
2048);
|
||||||
len = 2048
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRead(UvStreamHandle handle, int nread)
|
private void OnRead(UvStreamHandle handle, int nread)
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
ListenSocket = new UvTcpHandle();
|
ListenSocket = new UvTcpHandle();
|
||||||
ListenSocket.Init(Thread.Loop);
|
ListenSocket.Init(Thread.Loop);
|
||||||
ListenSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
|
ListenSocket.Bind(new IPEndPoint(IPAddress.Any, port));
|
||||||
ListenSocket.Listen(10, _connectionCallback, this);
|
ListenSocket.Listen(10, _connectionCallback, this);
|
||||||
tcs.SetResult(0);
|
tcs.SetResult(0);
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +86,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Listener.Dispose");
|
|
||||||
var tcs = new TaskCompletionSource<int>();
|
var tcs = new TaskCompletionSource<int>();
|
||||||
Thread.Post(
|
Thread.Post(
|
||||||
_ =>
|
_ =>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public void Write(ArraySegment<byte> buffer, Action<object> callback, object state)
|
public void Write(ArraySegment<byte> buffer, Action<object> callback, object state)
|
||||||
{
|
{
|
||||||
|
//TODO: need buffering that works
|
||||||
|
var copy = new byte[buffer.Count];
|
||||||
|
Array.Copy(buffer.Array, buffer.Offset, copy, 0, buffer.Count);
|
||||||
|
buffer = new ArraySegment<byte>(copy);
|
||||||
|
|
||||||
KestrelTrace.Log.ConnectionWrite(0, buffer.Count);
|
KestrelTrace.Log.ConnectionWrite(0, buffer.Count);
|
||||||
var req = new ThisWriteReq();
|
var req = new ThisWriteReq();
|
||||||
req.Init(_thread.Loop);
|
req.Init(_thread.Loop);
|
||||||
|
|
@ -70,24 +75,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public void Write()
|
public void Write()
|
||||||
{
|
{
|
||||||
_pin = GCHandle.Alloc(_buffer.Array, GCHandleType.Pinned);
|
|
||||||
var buf = new Libuv.uv_buf_t
|
|
||||||
{
|
|
||||||
len = (uint)_buffer.Count,
|
|
||||||
memory = _pin.AddrOfPinnedObject() + _buffer.Offset
|
|
||||||
};
|
|
||||||
|
|
||||||
Write(
|
Write(
|
||||||
_socket,
|
_socket,
|
||||||
new[] { buf },
|
new ArraySegment<ArraySegment<byte>>(
|
||||||
1,
|
new[]{_buffer}),
|
||||||
_writeCallback,
|
_writeCallback,
|
||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWrite(UvWriteReq req, int status)
|
private void OnWrite(UvWriteReq req, int status)
|
||||||
{
|
{
|
||||||
_pin.Free();
|
|
||||||
KestrelTrace.Log.ConnectionWriteCallback(0, status);
|
KestrelTrace.Log.ConnectionWriteCallback(0, status);
|
||||||
//NOTE: pool this?
|
//NOTE: pool this?
|
||||||
Dispose();
|
Dispose();
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,17 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
_post.Send();
|
_post.Send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task PostAsync(Action<object> callback, object state)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
lock (_workSync)
|
||||||
|
{
|
||||||
|
_workAdding.Enqueue(new Work { Callback = callback, State = state, Completion = tcs });
|
||||||
|
}
|
||||||
|
_post.Send();
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
private void ThreadStart(object parameter)
|
private void ThreadStart(object parameter)
|
||||||
{
|
{
|
||||||
var tcs = (TaskCompletionSource<int>)parameter;
|
var tcs = (TaskCompletionSource<int>)parameter;
|
||||||
|
|
@ -103,12 +114,19 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the loop one more time to delete the _post handle
|
// run the loop one more time to delete the open handles
|
||||||
_post.Reference();
|
_post.Reference();
|
||||||
_post.DangerousClose();
|
_post.DangerousClose();
|
||||||
|
_engine.Libuv.walk(
|
||||||
|
_loop,
|
||||||
|
(ptr, arg) =>
|
||||||
|
{
|
||||||
|
var handle = UvMemory.FromIntPtr<UvHandle>(ptr);
|
||||||
|
handle.Dispose();
|
||||||
|
},
|
||||||
|
IntPtr.Zero);
|
||||||
var ran2 = _loop.Run();
|
var ran2 = _loop.Run();
|
||||||
|
|
||||||
// delete the last of the unmanaged memory
|
|
||||||
_loop.Dispose();
|
_loop.Dispose();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -131,10 +149,26 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
work.Callback(work.State);
|
work.Callback(work.State);
|
||||||
|
if (work.Completion != null)
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(
|
||||||
|
tcs =>
|
||||||
|
{
|
||||||
|
((TaskCompletionSource<int>)tcs).SetResult(0);
|
||||||
|
},
|
||||||
|
work.Completion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
//TODO: unhandled exceptions
|
if (work.Completion != null)
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(_ => work.Completion.SetException(ex), null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: unobserved exception?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +177,7 @@ namespace Microsoft.AspNet.Server.Kestrel
|
||||||
{
|
{
|
||||||
public Action<object> Callback;
|
public Action<object> Callback;
|
||||||
public object State;
|
public object State;
|
||||||
|
public TaskCompletionSource<int> Completion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,83 +1,83 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.Tracing;
|
//using System.Diagnostics.Tracing;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel
|
namespace Microsoft.AspNet.Server.Kestrel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Summary description for KestrelTrace
|
/// Summary description for KestrelTrace
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class KestrelTrace : EventSource
|
public class KestrelTrace //: EventSource
|
||||||
{
|
{
|
||||||
public static KestrelTrace Log = new KestrelTrace();
|
public static KestrelTrace Log = new KestrelTrace();
|
||||||
static EventTask Connection = (EventTask)1;
|
// static EventTask Connection = (EventTask)1;
|
||||||
static EventTask Frame = (EventTask)1;
|
// static EventTask Frame = (EventTask)1;
|
||||||
|
|
||||||
|
|
||||||
[Event(13, Level = EventLevel.Informational, Message = "Id {0}")]
|
// [Event(13, Level = EventLevel.Informational, Message = "Id {0}")]
|
||||||
public void ConnectionStart(long connectionId)
|
public void ConnectionStart(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(13, connectionId);
|
// WriteEvent(13, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(14, Level = EventLevel.Informational, Message = "Id {0}")]
|
// [Event(14, Level = EventLevel.Informational, Message = "Id {0}")]
|
||||||
public void ConnectionStop(long connectionId)
|
public void ConnectionStop(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(14, connectionId);
|
// WriteEvent(14, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Event(4, Message = "Id {0} Status {1}")]
|
// [Event(4, Message = "Id {0} Status {1}")]
|
||||||
internal void ConnectionRead(long connectionId, int status)
|
internal void ConnectionRead(long connectionId, int status)
|
||||||
{
|
{
|
||||||
WriteEvent(4, connectionId, status);
|
// WriteEvent(4, connectionId, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(5, Message = "Id {0}")]
|
// [Event(5, Message = "Id {0}")]
|
||||||
internal void ConnectionPause(long connectionId)
|
internal void ConnectionPause(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(5, connectionId);
|
// WriteEvent(5, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(6, Message = "Id {0}")]
|
// [Event(6, Message = "Id {0}")]
|
||||||
internal void ConnectionResume(long connectionId)
|
internal void ConnectionResume(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(6, connectionId);
|
// WriteEvent(6, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(7, Message = "Id {0}")]
|
// [Event(7, Message = "Id {0}")]
|
||||||
internal void ConnectionReadFin(long connectionId)
|
internal void ConnectionReadFin(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(7, connectionId);
|
// WriteEvent(7, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(8, Message = "Id {0} Step {1}")]
|
// [Event(8, Message = "Id {0} Step {1}")]
|
||||||
internal void ConnectionWriteFin(long connectionId, int step)
|
internal void ConnectionWriteFin(long connectionId, int step)
|
||||||
{
|
{
|
||||||
WriteEvent(8, connectionId, step);
|
// WriteEvent(8, connectionId, step);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(9, Message = "Id {0}")]
|
// [Event(9, Message = "Id {0}")]
|
||||||
internal void ConnectionKeepAlive(long connectionId)
|
internal void ConnectionKeepAlive(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(9, connectionId);
|
// WriteEvent(9, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(10, Message = "Id {0}")]
|
// [Event(10, Message = "Id {0}")]
|
||||||
internal void ConnectionDisconnect(long connectionId)
|
internal void ConnectionDisconnect(long connectionId)
|
||||||
{
|
{
|
||||||
WriteEvent(10, connectionId);
|
// WriteEvent(10, connectionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(11, Message = "Id {0} Count {1}")]
|
// [Event(11, Message = "Id {0} Count {1}")]
|
||||||
internal void ConnectionWrite(long connectionId, int count)
|
internal void ConnectionWrite(long connectionId, int count)
|
||||||
{
|
{
|
||||||
WriteEvent(11, connectionId, count);
|
// WriteEvent(11, connectionId, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(12, Message = "Id {0} Status {1}")]
|
// [Event(12, Message = "Id {0} Status {1}")]
|
||||||
internal void ConnectionWriteCallback(long connectionId, int status)
|
internal void ConnectionWriteCallback(long connectionId, int status)
|
||||||
{
|
{
|
||||||
WriteEvent(12, connectionId, status);
|
// WriteEvent(12, connectionId, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,23 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var services = CallContextServiceLocator.Locator.ServiceProvider;
|
try
|
||||||
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
{
|
||||||
|
var locator = CallContextServiceLocator.Locator;
|
||||||
|
if (locator == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var services = locator.ServiceProvider;
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
||||||
|
} catch (NullReferenceException) { return null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task AppChunked(Frame frame)
|
private async Task AppChunked(Frame frame)
|
||||||
{
|
{
|
||||||
var data = new MemoryStream();
|
var data = new MemoryStream();
|
||||||
|
|
@ -85,6 +96,7 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
engine.Start(1);
|
engine.Start(1);
|
||||||
var started = engine.CreateServer("http", "localhost", 54321, App);
|
var started = engine.CreateServer("http", "localhost", 54321, App);
|
||||||
|
|
||||||
|
Console.WriteLine("Started");
|
||||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||||
socket.Connect(new IPEndPoint(IPAddress.Loopback, 54321));
|
socket.Connect(new IPEndPoint(IPAddress.Loopback, 54321));
|
||||||
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"));
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,16 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var services = CallContextServiceLocator.Locator.ServiceProvider;
|
var locator = CallContextServiceLocator.Locator;
|
||||||
|
if (locator == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var services = locator.ServiceProvider;
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,12 +132,13 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
tcp.Bind(new IPEndPoint(IPAddress.Loopback, 54321));
|
tcp.Bind(new IPEndPoint(IPAddress.Loopback, 54321));
|
||||||
tcp.Listen(10, (_, status, state) =>
|
tcp.Listen(10, (_, status, state) =>
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("Connected");
|
||||||
var tcp2 = new UvTcpHandle();
|
var tcp2 = new UvTcpHandle();
|
||||||
tcp2.Init(loop);
|
tcp2.Init(loop);
|
||||||
tcp.Accept(tcp2);
|
tcp.Accept(tcp2);
|
||||||
var data = Marshal.AllocCoTaskMem(500);
|
var data = Marshal.AllocCoTaskMem(500);
|
||||||
tcp2.ReadStart(
|
tcp2.ReadStart(
|
||||||
(a, b, c) => new Libuv.uv_buf_t { memory = data, len = 500 },
|
(a, b, c) => _uv.buf_init(data, 500),
|
||||||
(__, nread, state2) =>
|
(__, nread, state2) =>
|
||||||
{
|
{
|
||||||
bytesRead += nread;
|
bytesRead += nread;
|
||||||
|
|
@ -140,6 +150,7 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
null);
|
null);
|
||||||
tcp.Dispose();
|
tcp.Dispose();
|
||||||
}, null);
|
}, null);
|
||||||
|
Console.WriteLine("Task.Run");
|
||||||
var t = Task.Run(async () =>
|
var t = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var socket = new Socket(
|
var socket = new Socket(
|
||||||
|
|
@ -165,5 +176,92 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
loop.Dispose();
|
loop.Dispose();
|
||||||
await t;
|
await t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SocketCanReadAndWrite()
|
||||||
|
{
|
||||||
|
int bytesRead = 0;
|
||||||
|
var loop = new UvLoopHandle();
|
||||||
|
loop.Init(_uv);
|
||||||
|
var tcp = new UvTcpHandle();
|
||||||
|
tcp.Init(loop);
|
||||||
|
tcp.Bind(new IPEndPoint(IPAddress.Loopback, 54321));
|
||||||
|
tcp.Listen(10, (_, status, state) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine("Connected");
|
||||||
|
var tcp2 = new UvTcpHandle();
|
||||||
|
tcp2.Init(loop);
|
||||||
|
tcp.Accept(tcp2);
|
||||||
|
var data = Marshal.AllocCoTaskMem(500);
|
||||||
|
tcp2.ReadStart(
|
||||||
|
(a, b, c) => tcp2.Libuv.buf_init(data, 500),
|
||||||
|
(__, nread, state2) =>
|
||||||
|
{
|
||||||
|
bytesRead += nread;
|
||||||
|
if (nread == 0)
|
||||||
|
{
|
||||||
|
tcp2.Dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (var x = 0; x != 2; ++x)
|
||||||
|
{
|
||||||
|
var req = new UvWriteReq();
|
||||||
|
req.Init(loop);
|
||||||
|
req.Write(
|
||||||
|
tcp2,
|
||||||
|
new ArraySegment<ArraySegment<byte>>(
|
||||||
|
new[]{new ArraySegment<byte>(new byte[]{65,66,67,68,69})}
|
||||||
|
),
|
||||||
|
(___,____,_____)=>{},
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
tcp.Dispose();
|
||||||
|
}, null);
|
||||||
|
Console.WriteLine("Task.Run");
|
||||||
|
var t = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var socket = new Socket(
|
||||||
|
AddressFamily.InterNetwork,
|
||||||
|
SocketType.Stream,
|
||||||
|
ProtocolType.Tcp);
|
||||||
|
await Task.Factory.FromAsync(
|
||||||
|
socket.BeginConnect,
|
||||||
|
socket.EndConnect,
|
||||||
|
new IPEndPoint(IPAddress.Loopback, 54321),
|
||||||
|
null,
|
||||||
|
TaskCreationOptions.None);
|
||||||
|
await Task.Factory.FromAsync(
|
||||||
|
socket.BeginSend,
|
||||||
|
socket.EndSend,
|
||||||
|
new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4, 5 }) },
|
||||||
|
SocketFlags.None,
|
||||||
|
null,
|
||||||
|
TaskCreationOptions.None);
|
||||||
|
socket.Shutdown(SocketShutdown.Send);
|
||||||
|
var buffer = new ArraySegment<byte>(new byte[2048]);
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
var count = await Task.Factory.FromAsync(
|
||||||
|
socket.BeginReceive,
|
||||||
|
socket.EndReceive,
|
||||||
|
new[] { buffer },
|
||||||
|
SocketFlags.None,
|
||||||
|
null,
|
||||||
|
TaskCreationOptions.None);
|
||||||
|
Console.WriteLine("count {0} {1}",
|
||||||
|
count,
|
||||||
|
System.Text.Encoding.ASCII.GetString(buffer.Array, 0, count));
|
||||||
|
if (count <= 0) break;
|
||||||
|
}
|
||||||
|
socket.Dispose();
|
||||||
|
});
|
||||||
|
loop.Run();
|
||||||
|
loop.Dispose();
|
||||||
|
await t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -24,8 +24,19 @@ namespace Microsoft.AspNet.Server.KestralTests
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var services = CallContextServiceLocator.Locator.ServiceProvider;
|
try{
|
||||||
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
var locator = CallContextServiceLocator.Locator;
|
||||||
|
if (locator == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var services = locator.ServiceProvider;
|
||||||
|
if (services == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (ILibraryManager)services.GetService(typeof(ILibraryManager));
|
||||||
|
} catch (NullReferenceException) { return null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue