Make SocketOutput more testable

- Added a MockLibUv class
- Create a SocketOutputTests class
This commit is contained in:
Stephen Halter 2015-07-24 16:47:05 -07:00
parent c345849707
commit cce9d8f09c
6 changed files with 298 additions and 67 deletions

View File

@ -14,11 +14,8 @@ namespace Microsoft.AspNet.Server.Kestrel
public class KestrelEngine : IDisposable public class KestrelEngine : IDisposable
{ {
public KestrelEngine(ILibraryManager libraryManager, IApplicationShutdown appShutdownService) public KestrelEngine(ILibraryManager libraryManager, IApplicationShutdown appShutdownService)
: this(appShutdownService)
{ {
AppShutdown = appShutdownService;
Threads = new List<KestrelThread>();
Listeners = new List<Listener>();
Memory = new MemoryPool();
Libuv = new Libuv(); Libuv = new Libuv();
var libraryPath = default(string); var libraryPath = default(string);
@ -61,6 +58,21 @@ namespace Microsoft.AspNet.Server.Kestrel
Libuv.Load(libraryPath); Libuv.Load(libraryPath);
} }
// For testing
internal KestrelEngine(Libuv uv, IApplicationShutdown appShutdownService)
: this(appShutdownService)
{
Libuv = uv;
}
private KestrelEngine(IApplicationShutdown appShutdownService)
{
AppShutdown = appShutdownService;
Threads = new List<KestrelThread>();
Listeners = new List<Listener>();
Memory = new MemoryPool();
}
public Libuv Libuv { get; private set; } public Libuv Libuv { get; private set; }
public IMemoryPool Memory { get; set; } public IMemoryPool Memory { get; set; }
public IApplicationShutdown AppShutdown { get; private set; } public IApplicationShutdown AppShutdown { get; private set; }

View File

@ -84,16 +84,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_loop_init(UvLoopHandle a0); protected delegate int uv_loop_init(UvLoopHandle a0);
uv_loop_init _uv_loop_init = default(uv_loop_init); protected uv_loop_init _uv_loop_init = default(uv_loop_init);
public void loop_init(UvLoopHandle handle) public void loop_init(UvLoopHandle handle)
{ {
Check(_uv_loop_init(handle)); Check(_uv_loop_init(handle));
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_loop_close(IntPtr a0); protected delegate int uv_loop_close(IntPtr a0);
uv_loop_close _uv_loop_close = default(uv_loop_close); protected uv_loop_close _uv_loop_close = default(uv_loop_close);
public void loop_close(UvLoopHandle handle) public void loop_close(UvLoopHandle handle)
{ {
handle.Validate(closed: true); handle.Validate(closed: true);
@ -101,8 +101,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_run(UvLoopHandle handle, int mode); protected delegate int uv_run(UvLoopHandle handle, int mode);
uv_run _uv_run = default(uv_run); protected uv_run _uv_run = default(uv_run);
public int run(UvLoopHandle handle, int mode) public int run(UvLoopHandle handle, int mode)
{ {
handle.Validate(); handle.Validate();
@ -110,8 +110,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void uv_stop(UvLoopHandle handle); protected delegate void uv_stop(UvLoopHandle handle);
uv_stop _uv_stop = default(uv_stop); protected uv_stop _uv_stop = default(uv_stop);
public void stop(UvLoopHandle handle) public void stop(UvLoopHandle handle)
{ {
handle.Validate(); handle.Validate();
@ -119,8 +119,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void uv_ref(UvHandle handle); protected delegate void uv_ref(UvHandle handle);
uv_ref _uv_ref = default(uv_ref); protected uv_ref _uv_ref = default(uv_ref);
public void @ref(UvHandle handle) public void @ref(UvHandle handle)
{ {
handle.Validate(); handle.Validate();
@ -128,8 +128,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void uv_unref(UvHandle handle); protected delegate void uv_unref(UvHandle handle);
uv_unref _uv_unref = default(uv_unref); protected uv_unref _uv_unref = default(uv_unref);
public void unref(UvHandle handle) public void unref(UvHandle handle)
{ {
handle.Validate(); handle.Validate();
@ -140,8 +140,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_close_cb(IntPtr handle); public delegate void uv_close_cb(IntPtr handle);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void uv_close(IntPtr handle, uv_close_cb close_cb); protected delegate void uv_close(IntPtr handle, uv_close_cb close_cb);
uv_close _uv_close = default(uv_close); protected uv_close _uv_close = default(uv_close);
public void close(UvHandle handle, uv_close_cb close_cb) public void close(UvHandle handle, uv_close_cb close_cb)
{ {
handle.Validate(closed: true); handle.Validate(closed: true);
@ -155,8 +155,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_async_cb(IntPtr handle); public delegate void uv_async_cb(IntPtr handle);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb); protected delegate int uv_async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb);
uv_async_init _uv_async_init = default(uv_async_init); protected uv_async_init _uv_async_init = default(uv_async_init);
public void async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb) public void async_init(UvLoopHandle loop, UvAsyncHandle handle, uv_async_cb cb)
{ {
loop.Validate(); loop.Validate();
@ -165,16 +165,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_async_send(UvAsyncHandle handle); protected delegate int uv_async_send(UvAsyncHandle handle);
uv_async_send _uv_async_send = default(uv_async_send); protected uv_async_send _uv_async_send = default(uv_async_send);
public void async_send(UvAsyncHandle handle) public void async_send(UvAsyncHandle handle)
{ {
Check(_uv_async_send(handle)); Check(_uv_async_send(handle));
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_tcp_init(UvLoopHandle loop, UvTcpHandle handle); protected delegate int uv_tcp_init(UvLoopHandle loop, UvTcpHandle handle);
uv_tcp_init _uv_tcp_init = default(uv_tcp_init); protected uv_tcp_init _uv_tcp_init = default(uv_tcp_init);
public void tcp_init(UvLoopHandle loop, UvTcpHandle handle) public void tcp_init(UvLoopHandle loop, UvTcpHandle handle)
{ {
loop.Validate(); loop.Validate();
@ -183,8 +183,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_tcp_bind(UvTcpHandle handle, ref sockaddr addr, int flags); protected delegate int uv_tcp_bind(UvTcpHandle handle, ref sockaddr addr, int flags);
uv_tcp_bind _uv_tcp_bind = default(uv_tcp_bind); protected uv_tcp_bind _uv_tcp_bind = default(uv_tcp_bind);
public void tcp_bind(UvTcpHandle handle, ref sockaddr addr, int flags) public void tcp_bind(UvTcpHandle handle, ref sockaddr addr, int flags)
{ {
handle.Validate(); handle.Validate();
@ -192,8 +192,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_tcp_open(UvTcpHandle handle, IntPtr hSocket); protected delegate int uv_tcp_open(UvTcpHandle handle, IntPtr hSocket);
uv_tcp_open _uv_tcp_open = default(uv_tcp_open); protected uv_tcp_open _uv_tcp_open = default(uv_tcp_open);
public void tcp_open(UvTcpHandle handle, IntPtr hSocket) public void tcp_open(UvTcpHandle handle, IntPtr hSocket)
{ {
handle.Validate(); handle.Validate();
@ -201,8 +201,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_pipe_init(UvLoopHandle loop, UvPipeHandle handle, int ipc); protected delegate int uv_pipe_init(UvLoopHandle loop, UvPipeHandle handle, int ipc);
uv_pipe_init _uv_pipe_init = default(uv_pipe_init); protected uv_pipe_init _uv_pipe_init = default(uv_pipe_init);
public void pipe_init(UvLoopHandle loop, UvPipeHandle handle, bool ipc) public void pipe_init(UvLoopHandle loop, UvPipeHandle handle, bool ipc)
{ {
loop.Validate(); loop.Validate();
@ -211,8 +211,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
delegate int uv_pipe_bind(UvPipeHandle loop, string name); protected delegate int uv_pipe_bind(UvPipeHandle loop, string name);
uv_pipe_bind _uv_pipe_bind = default(uv_pipe_bind); protected uv_pipe_bind _uv_pipe_bind = default(uv_pipe_bind);
public void pipe_bind(UvPipeHandle handle, string name) public void pipe_bind(UvPipeHandle handle, string name)
{ {
handle.Validate(); handle.Validate();
@ -222,8 +222,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_connection_cb(IntPtr server, int status); public delegate void uv_connection_cb(IntPtr server, int status);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_listen(UvStreamHandle handle, int backlog, uv_connection_cb cb); protected delegate int uv_listen(UvStreamHandle handle, int backlog, uv_connection_cb cb);
uv_listen _uv_listen = default(uv_listen); protected uv_listen _uv_listen = default(uv_listen);
public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb) public void listen(UvStreamHandle handle, int backlog, uv_connection_cb cb)
{ {
handle.Validate(); handle.Validate();
@ -231,8 +231,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_accept(UvStreamHandle server, UvStreamHandle client); protected delegate int uv_accept(UvStreamHandle server, UvStreamHandle client);
uv_accept _uv_accept = default(uv_accept); protected uv_accept _uv_accept = default(uv_accept);
public void accept(UvStreamHandle server, UvStreamHandle client) public void accept(UvStreamHandle server, UvStreamHandle client)
{ {
server.Validate(); server.Validate();
@ -243,8 +243,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_connect_cb(IntPtr req, int status); public delegate void uv_connect_cb(IntPtr req, int status);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
unsafe delegate int uv_pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb); unsafe protected delegate int uv_pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb);
uv_pipe_connect _uv_pipe_connect = default(uv_pipe_connect); protected uv_pipe_connect _uv_pipe_connect = default(uv_pipe_connect);
unsafe public void pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb) unsafe public void pipe_connect(UvConnectRequest req, UvPipeHandle handle, string name, uv_connect_cb cb)
{ {
req.Validate(); req.Validate();
@ -257,8 +257,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_read_cb(IntPtr server, int nread, ref uv_buf_t buf); public delegate void uv_read_cb(IntPtr server, int nread, ref uv_buf_t buf);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); protected delegate int uv_read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
uv_read_start _uv_read_start = default(uv_read_start); protected uv_read_start _uv_read_start = default(uv_read_start);
public void read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) public void read_start(UvStreamHandle handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb)
{ {
handle.Validate(); handle.Validate();
@ -266,8 +266,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_read_stop(UvStreamHandle handle); protected delegate int uv_read_stop(UvStreamHandle handle);
uv_read_stop _uv_read_stop = default(uv_read_stop); protected uv_read_stop _uv_read_stop = default(uv_read_stop);
public void read_stop(UvStreamHandle handle) public void read_stop(UvStreamHandle handle)
{ {
handle.Validate(); handle.Validate();
@ -275,8 +275,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_try_write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs); protected delegate int uv_try_write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs);
uv_try_write _uv_try_write = default(uv_try_write); protected uv_try_write _uv_try_write = default(uv_try_write);
public int try_write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs) public int try_write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs)
{ {
handle.Validate(); handle.Validate();
@ -286,8 +286,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_write_cb(IntPtr req, int status); public delegate void uv_write_cb(IntPtr req, int status);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate int uv_write(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb); unsafe protected delegate int uv_write(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb);
uv_write _uv_write = default(uv_write); protected uv_write _uv_write = default(uv_write);
unsafe public void write(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb) unsafe public void write(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, uv_write_cb cb)
{ {
req.Validate(); req.Validate();
@ -296,8 +296,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate int uv_write2(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb); unsafe protected delegate int uv_write2(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb);
uv_write2 _uv_write2 = default(uv_write2); protected uv_write2 _uv_write2 = default(uv_write2);
unsafe public void write2(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb) unsafe public void write2(UvRequest req, UvStreamHandle handle, Libuv.uv_buf_t* bufs, int nbufs, UvStreamHandle sendHandle, uv_write_cb cb)
{ {
req.Validate(); req.Validate();
@ -308,8 +308,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_shutdown_cb(IntPtr req, int status); public delegate void uv_shutdown_cb(IntPtr req, int status);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_shutdown(UvShutdownReq req, UvStreamHandle handle, uv_shutdown_cb cb); protected delegate int uv_shutdown(UvShutdownReq req, UvStreamHandle handle, uv_shutdown_cb cb);
uv_shutdown _uv_shutdown = default(uv_shutdown); protected uv_shutdown _uv_shutdown = default(uv_shutdown);
public void shutdown(UvShutdownReq req, UvStreamHandle handle, uv_shutdown_cb cb) public void shutdown(UvShutdownReq req, UvStreamHandle handle, uv_shutdown_cb cb)
{ {
req.Validate(); req.Validate();
@ -318,8 +318,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr uv_err_name(int err); protected delegate IntPtr uv_err_name(int err);
uv_err_name _uv_err_name = default(uv_err_name); protected uv_err_name _uv_err_name = default(uv_err_name);
public unsafe String err_name(int err) public unsafe String err_name(int err)
{ {
IntPtr ptr = _uv_err_name(err); IntPtr ptr = _uv_err_name(err);
@ -327,8 +327,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr uv_strerror(int err); protected delegate IntPtr uv_strerror(int err);
uv_strerror _uv_strerror = default(uv_strerror); protected uv_strerror _uv_strerror = default(uv_strerror);
public unsafe String strerror(int err) public unsafe String strerror(int err)
{ {
IntPtr ptr = _uv_strerror(err); IntPtr ptr = _uv_strerror(err);
@ -336,42 +336,42 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_loop_size(); protected delegate int uv_loop_size();
uv_loop_size _uv_loop_size = default(uv_loop_size); protected uv_loop_size _uv_loop_size = default(uv_loop_size);
public int loop_size() public int loop_size()
{ {
return _uv_loop_size(); return _uv_loop_size();
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_handle_size(HandleType handleType); protected delegate int uv_handle_size(HandleType handleType);
uv_handle_size _uv_handle_size = default(uv_handle_size); protected uv_handle_size _uv_handle_size = default(uv_handle_size);
public int handle_size(HandleType handleType) public int handle_size(HandleType handleType)
{ {
return _uv_handle_size(handleType); return _uv_handle_size(handleType);
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_req_size(RequestType reqType); protected delegate int uv_req_size(RequestType reqType);
uv_req_size _uv_req_size = default(uv_req_size); protected uv_req_size _uv_req_size = default(uv_req_size);
public int req_size(RequestType reqType) public int req_size(RequestType reqType)
{ {
return _uv_req_size(reqType); return _uv_req_size(reqType);
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_ip4_addr(string ip, int port, out sockaddr addr); protected delegate int uv_ip4_addr(string ip, int port, out sockaddr addr);
uv_ip4_addr _uv_ip4_addr = default(uv_ip4_addr); protected uv_ip4_addr _uv_ip4_addr = default(uv_ip4_addr);
public int ip4_addr(string ip, int port, out sockaddr addr, out Exception error) public int ip4_addr(string ip, int port, out sockaddr addr, out Exception error)
{ {
return Check(_uv_ip4_addr(ip, port, out addr), out error); return Check(_uv_ip4_addr(ip, port, out addr), out error);
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int uv_ip6_addr(string ip, int port, out sockaddr addr); protected delegate int uv_ip6_addr(string ip, int port, out sockaddr addr);
uv_ip6_addr _uv_ip6_addr = default(uv_ip6_addr); protected uv_ip6_addr _uv_ip6_addr = default(uv_ip6_addr);
public int ip6_addr(string ip, int port, out sockaddr addr, out Exception error) public int ip6_addr(string ip, int port, out sockaddr addr, out Exception error)
{ {
return Check(_uv_ip6_addr(ip, port, out addr), out error); return Check(_uv_ip6_addr(ip, port, out addr), out error);
@ -380,8 +380,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void uv_walk_cb(IntPtr handle, IntPtr arg); public delegate void uv_walk_cb(IntPtr handle, IntPtr arg);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
unsafe delegate int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg); unsafe protected delegate int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg);
uv_walk _uv_walk = default(uv_walk); protected uv_walk _uv_walk = default(uv_walk);
unsafe public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg) unsafe public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg)
{ {
loop.Validate(); loop.Validate();

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public abstract class UvMemory : SafeHandle public abstract class UvMemory : SafeHandle
{ {
protected Libuv _uv; protected Libuv _uv;
private int _threadId; protected int _threadId;
public UvMemory() : base(IntPtr.Zero, true) public UvMemory() : base(IntPtr.Zero, true)
{ {

View File

@ -2,5 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNet.Server.KestrelTests")]
[assembly: AssemblyMetadata("Serviceable", "True")] [assembly: AssemblyMetadata("Serviceable", "True")]

View File

@ -0,0 +1,133 @@
// 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.Threading;
using Microsoft.AspNet.Server.Kestrel;
using Microsoft.AspNet.Server.Kestrel.Http;
using Microsoft.AspNet.Server.Kestrel.Networking;
using Microsoft.AspNet.Server.KestrelTests.TestHelpers;
using Xunit;
namespace Microsoft.AspNet.Server.KestrelTests
{
public class SocketOutputTests
{
[Fact]
public void CanWrite1MB()
{
// This test was added because when initially implementing write-behind buffering in
// SocketOutput, the write callback would never be invoked for writes larger than
// _maxBytesPreCompleted even after the write actually completed.
// Arrange
var mockLibuv = new MockLibuv
{
OnWrite = (socket, buffers, triggerCompleted) =>
{
triggerCompleted(0);
return 0;
}
};
using (var kestrelEngine = new KestrelEngine(mockLibuv, new ShutdownNotImplemented()))
{
kestrelEngine.Start(count: 1);
var kestrelThread = kestrelEngine.Threads[0];
var socket = new MockSocket(kestrelThread.Loop.ThreadId);
var socketOutput = new SocketOutput(kestrelThread, socket);
// I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test.
var bufferSize = 1048576;
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
var completedWh = new ManualResetEventSlim();
Action<Exception, object> onCompleted = (ex, state) =>
{
Assert.Null(ex);
Assert.Null(state);
completedWh.Set();
};
// Act
socketOutput.Write(buffer, onCompleted, state: null);
// Assert
Assert.True(completedWh.Wait(100));
}
}
[Fact]
public void WritesDontCompleteImmediatelyWhenTooManyBytesAreAlreadyPreCompleted()
{
// This should match _maxBytesPreCompleted in SocketOutput
var maxBytesPreCompleted = 65536;
var completeQueue = new Queue<Action<int>>();
// Arrange
var mockLibuv = new MockLibuv
{
OnWrite = (socket, buffers, triggerCompleted) =>
{
completeQueue.Enqueue(triggerCompleted);
return 0;
}
};
using (var kestrelEngine = new KestrelEngine(mockLibuv, new ShutdownNotImplemented()))
{
kestrelEngine.Start(count: 1);
var kestrelThread = kestrelEngine.Threads[0];
var socket = new MockSocket(kestrelThread.Loop.ThreadId);
var socketOutput = new SocketOutput(kestrelThread, socket);
var bufferSize = maxBytesPreCompleted;
var buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
var completedWh = new ManualResetEventSlim();
Action<Exception, object> onCompleted = (ex, state) =>
{
Assert.Null(ex);
Assert.Null(state);
completedWh.Set();
};
// Act
socketOutput.Write(buffer, onCompleted, state: null);
// Assert
// The first write should pre-complete since it is <= _maxBytesPreCompleted.
Assert.True(completedWh.Wait(100));
// Arrange
completedWh.Reset();
// Act
socketOutput.Write(buffer, onCompleted, state: null);
// Assert
// Too many bytes are already pre-completed for the second write to pre-complete.
Assert.False(completedWh.Wait(100));
// Act
completeQueue.Dequeue()(0);
// Assert
// Finishing the first write should allow the second write to pre-complete.
Assert.True(completedWh.Wait(100));
}
}
private class MockSocket : UvStreamHandle
{
public MockSocket(int threadId)
{
// Set the handle to something other than IntPtr.Zero
// so handle.Validate doesn't fail in Libuv.write
handle = (IntPtr)1;
_threadId = threadId;
}
protected override bool ReleaseHandle()
{
// No-op
return true;
}
}
}
}

View File

@ -0,0 +1,84 @@
using System;
using System.Threading;
using Microsoft.AspNet.Server.Kestrel.Networking;
namespace Microsoft.AspNet.Server.KestrelTests.TestHelpers
{
public class MockLibuv : Libuv
{
private UvAsyncHandle _postHandle;
private uv_async_cb _onPost;
private bool _stopLoop;
private readonly ManualResetEventSlim _loopWh = new ManualResetEventSlim();
private Func<UvStreamHandle, ArraySegment<ArraySegment<byte>>, Action<int>, int> _onWrite;
unsafe public MockLibuv()
{
_uv_write = UvWrite;
_uv_async_send = postHandle =>
{
_loopWh.Set();
return 0;
};
_uv_async_init = (loop, postHandle, callback) =>
{
_postHandle = postHandle;
_onPost = callback;
return 0;
};
_uv_run = (loopHandle, mode) =>
{
while (!_stopLoop)
{
_loopWh.Wait();
_loopWh.Reset();
_onPost(_postHandle.InternalGetHandle());
}
_postHandle.Dispose();
loopHandle.Dispose();
return 0;
};
_uv_stop = handle =>
{
_stopLoop = true;
_loopWh.Set();
};
_uv_req_size = reqType => IntPtr.Size;
_uv_loop_size = () => IntPtr.Size;
_uv_handle_size = handleType => IntPtr.Size;
_uv_loop_init = loop => 0;
_uv_tcp_init = (loopHandle, tcpHandle) => 0;
_uv_close = (handle, callback) => callback(handle);
_uv_loop_close = handle => 0;
_uv_unref = handle => { };
_uv_walk = (loop, callback, ignore) => 0;
}
public Func<UvStreamHandle, ArraySegment<ArraySegment<byte>>, Action<int>, int> OnWrite
{
get
{
return _onWrite;
}
set
{
_onWrite = value;
}
}
unsafe private int UvWrite(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb)
{
return _onWrite(handle, new ArraySegment<ArraySegment<byte>>(), status => cb(req.InternalGetHandle(), status));
}
}
}