Getting request body variations to work

This commit is contained in:
Louis DeJardin 2014-06-07 02:49:25 -07:00
parent 313db3f0cf
commit 044bbb83e6
11 changed files with 280 additions and 85 deletions

View File

@ -45,17 +45,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
((Connection)state).OnRead(handle, nread); ((Connection)state).OnRead(handle, nread);
} }
private readonly Func<object, Task> _app;
private readonly UvStreamHandle _socket; private readonly UvStreamHandle _socket;
private Frame _frame; private Frame _frame;
private Action<Exception> _fault;
private Action<Frame, Exception> _frameConsumeCallback;
private Action _receiveAsyncCompleted;
private Frame _receiveAsyncCompletedFrame;
public Connection(ListenerContext context, UvStreamHandle socket) : base(context) public Connection(ListenerContext context, UvStreamHandle socket) : base(context)
{ {
_socket = socket; _socket = socket;
@ -64,41 +56,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public void Start() public void Start()
{ {
//_services.Trace.Event(TraceEventType.Start, TraceMessage.Connection);
SocketInput = new SocketInput(Memory); SocketInput = new SocketInput(Memory);
SocketOutput = new SocketOutput(Thread, _socket); SocketOutput = new SocketOutput(Thread, _socket);
_frame = new Frame(this);
_socket.ReadStart(_allocCallback, _readCallback, this); _socket.ReadStart(_allocCallback, _readCallback, this);
//_fault = ex => { Debug.WriteLine(ex.Message); };
//_frameConsumeCallback = (frame, error) =>
//{
// if (error != null)
// {
// _fault(error);
// }
// try
// {
// Go(false, frame);
// }
// catch (Exception ex)
// {
// _fault(ex);
// }
//};
//try
//{
// //_socket.Blocking = false;
// //_socket.NoDelay = true;
// Go(true, null);
//}
//catch (Exception ex)
//{
// _fault(ex);
//}
} }
private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize) private Libuv.uv_buf_t OnAlloc(UvStreamHandle handle, int suggestedSize)
@ -119,10 +80,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
SocketInput.RemoteIntakeFin = true; SocketInput.RemoteIntakeFin = true;
} }
if (_frame == null)
{
_frame = new Frame(this);
}
_frame.Consume(); _frame.Consume();
} }
@ -141,14 +98,26 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
switch (endType) switch (endType)
{ {
case ProduceEndType.SocketShutdownSend: case ProduceEndType.SocketShutdownSend:
var shutdown = new UvShutdownReq(); Thread.Post(
shutdown.Init(Thread.Loop); x =>
shutdown.Shutdown(_socket, (req, status, state) => req.Close(), null); {
var self = (Connection)x;
var shutdown = new UvShutdownReq();
shutdown.Init(self.Thread.Loop);
shutdown.Shutdown(self._socket, (req, status, state) => req.Close(), null);
},
this);
break; break;
case ProduceEndType.ConnectionKeepAlive: case ProduceEndType.ConnectionKeepAlive:
_frame = new Frame(this);
Thread.Post(
x => ((Frame)x).Consume(),
_frame);
break; break;
case ProduceEndType.SocketDisconnect: case ProduceEndType.SocketDisconnect:
_socket.Close(); Thread.Post(
x => ((UvHandle)x).Close(),
_socket);
break; break;
} }
} }

View File

@ -87,7 +87,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
switch (_mode) switch (_mode)
{ {
case Mode.StartLine: case Mode.StartLine:
if (input.RemoteIntakeFin) if (input.Buffer.Count == 0 && input.RemoteIntakeFin)
{ {
_mode = Mode.Terminated; _mode = Mode.Terminated;
return; return;
@ -95,6 +95,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
if (!TakeStartLine(input)) if (!TakeStartLine(input))
{ {
if (input.RemoteIntakeFin)
{
_mode = Mode.Terminated;
}
return; return;
} }
@ -102,7 +106,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
break; break;
case Mode.MessageHeader: case Mode.MessageHeader:
if (input.RemoteIntakeFin) if (input.Buffer.Count == 0 && input.RemoteIntakeFin)
{ {
_mode = Mode.Terminated; _mode = Mode.Terminated;
return; return;
@ -113,16 +117,25 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
if (!TakeMessageHeader(input, out endOfHeaders)) if (!TakeMessageHeader(input, out endOfHeaders))
{ {
if (input.RemoteIntakeFin)
{
_mode = Mode.Terminated;
}
return; return;
} }
} }
//var resumeBody = HandleExpectContinue(callback); //var resumeBody = HandleExpectContinue(callback);
Execute();
_mode = Mode.MessageBody; _mode = Mode.MessageBody;
Execute();
break; break;
case Mode.MessageBody: case Mode.MessageBody:
if (_messageBody.LocalIntakeFin)
{
// NOTE: stop reading and resume on keepalive?
return;
}
_messageBody.Consume(); _messageBody.Consume();
// NOTE: keep looping? // NOTE: keep looping?
return; return;

View File

@ -6,24 +6,6 @@ using System.Collections.Generic;
namespace Microsoft.AspNet.Server.Kestrel.Http namespace Microsoft.AspNet.Server.Kestrel.Http
{ {
public static class DelegateExtensions
{
public static void InvokeNoThrow(this Action d)
{
try
{ d.Invoke(); }
catch
{ }
}
public static void InvokeNoThrow<T>(this Action<T> d, T arg1)
{
try
{ d.Invoke(arg1); }
catch
{ }
}
}
public abstract class MessageBody : MessageBodyExchanger public abstract class MessageBody : MessageBodyExchanger
{ {
private Action _continuation = () => { }; private Action _continuation = () => { };
@ -156,8 +138,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
var consumeLength = Math.Min(_neededLength, input.Buffer.Count); var consumeLength = Math.Min(_neededLength, input.Buffer.Count);
_neededLength -= consumeLength; _neededLength -= consumeLength;
var consumed = input.Take(consumeLength);
if (_neededLength != 0) if (_neededLength != 0)
{ {
Intake(consumeLength); Intake(consumeLength);

View File

@ -59,6 +59,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_loop_close _uv_loop_close; uv_loop_close _uv_loop_close;
public void loop_close(UvLoopHandle handle) public void loop_close(UvLoopHandle handle)
{ {
handle.Validate(closed: true);
Check(_uv_loop_close(handle.DangerousGetHandle())); Check(_uv_loop_close(handle.DangerousGetHandle()));
} }
@ -67,6 +68,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_run _uv_run; uv_run _uv_run;
public int run(UvLoopHandle handle, int mode) public int run(UvLoopHandle handle, int mode)
{ {
handle.Validate();
return Check(_uv_run(handle, mode)); return Check(_uv_run(handle, mode));
} }
@ -75,6 +77,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_stop _uv_stop; uv_stop _uv_stop;
public void stop(UvLoopHandle handle) public void stop(UvLoopHandle handle)
{ {
handle.Validate();
_uv_stop(handle); _uv_stop(handle);
} }
@ -83,6 +86,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_ref _uv_ref; uv_ref _uv_ref;
public void @ref(UvHandle handle) public void @ref(UvHandle handle)
{ {
handle.Validate();
_uv_ref(handle); _uv_ref(handle);
} }
@ -91,6 +95,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_unref _uv_unref; uv_unref _uv_unref;
public void unref(UvHandle handle) public void unref(UvHandle handle)
{ {
handle.Validate();
_uv_unref(handle); _uv_unref(handle);
} }
@ -102,6 +107,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_close _uv_close; uv_close _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);
_uv_close(handle.DangerousGetHandle(), close_cb); _uv_close(handle.DangerousGetHandle(), close_cb);
} }
@ -112,6 +118,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_async_init _uv_async_init; uv_async_init _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();
handle.Validate();
Check(_uv_async_init(loop, handle, cb)); Check(_uv_async_init(loop, handle, cb));
} }
@ -128,6 +136,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_tcp_init _uv_tcp_init; uv_tcp_init _uv_tcp_init;
public void tcp_init(UvLoopHandle loop, UvTcpHandle handle) public void tcp_init(UvLoopHandle loop, UvTcpHandle handle)
{ {
loop.Validate();
handle.Validate();
Check(_uv_tcp_init(loop, handle)); Check(_uv_tcp_init(loop, handle));
} }
@ -136,6 +146,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_tcp_bind _uv_tcp_bind; uv_tcp_bind _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();
Check(_uv_tcp_bind(handle, ref addr, flags)); Check(_uv_tcp_bind(handle, ref addr, flags));
} }
@ -146,6 +157,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_listen _uv_listen; uv_listen _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();
Check(_uv_listen(handle, backlog, cb)); Check(_uv_listen(handle, backlog, cb));
} }
@ -154,6 +166,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_accept _uv_accept; uv_accept _uv_accept;
public void accept(UvStreamHandle server, UvStreamHandle client) public void accept(UvStreamHandle server, UvStreamHandle client)
{ {
server.Validate();
client.Validate();
Check(_uv_accept(server, client)); Check(_uv_accept(server, client));
} }
@ -166,6 +180,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_read_start _uv_read_start; uv_read_start _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();
Check(_uv_read_start(handle, alloc_cb, read_cb)); Check(_uv_read_start(handle, alloc_cb, read_cb));
} }
@ -174,6 +189,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_read_stop _uv_read_stop; uv_read_stop _uv_read_stop;
public void read_stop(UvStreamHandle handle) public void read_stop(UvStreamHandle handle)
{ {
handle.Validate();
Check(_uv_read_stop(handle)); Check(_uv_read_stop(handle));
} }
@ -182,6 +198,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_try_write _uv_try_write; uv_try_write _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();
return Check(_uv_try_write(handle, bufs, nbufs)); return Check(_uv_try_write(handle, bufs, nbufs));
} }
@ -192,6 +209,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_write _uv_write; uv_write _uv_write;
public void write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, uv_write_cb cb) public void write(UvWriteReq req, UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, uv_write_cb cb)
{ {
req.Validate();
handle.Validate();
Check(_uv_write(req, handle, bufs, nbufs, cb)); Check(_uv_write(req, handle, bufs, nbufs, cb));
} }
@ -202,6 +221,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
uv_shutdown _uv_shutdown; uv_shutdown _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();
handle.Validate();
Check(_uv_shutdown(req, handle, cb)); Check(_uv_shutdown(req, handle, cb));
} }

View File

@ -9,8 +9,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
{ {
static Libuv.uv_close_cb _close_cb = DestroyHandle; static Libuv.uv_close_cb _close_cb = DestroyHandle;
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
var memory = handle; var memory = handle;

View File

@ -1,8 +1,10 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// 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.
#define TRACE
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Microsoft.AspNet.Server.Kestrel.Networking namespace Microsoft.AspNet.Server.Kestrel.Networking
{ {
@ -12,6 +14,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public abstract class UvMemory : SafeHandle public abstract class UvMemory : SafeHandle
{ {
protected Libuv _uv; protected Libuv _uv;
int _threadId;
public UvMemory() : base(IntPtr.Zero, true) public UvMemory() : base(IntPtr.Zero, true)
{ {
} }
@ -29,6 +33,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
unsafe protected void CreateHandle(Libuv uv, int size) unsafe protected void CreateHandle(Libuv uv, int size)
{ {
_uv = uv; _uv = uv;
_threadId = Thread.CurrentThread.ManagedThreadId;
handle = Marshal.AllocCoTaskMem(size); handle = Marshal.AllocCoTaskMem(size);
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this)); *(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this));
} }
@ -36,8 +42,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
protected void CreateHandle(UvLoopHandle loop, int size) protected void CreateHandle(UvLoopHandle loop, int size)
{ {
CreateHandle(loop._uv, size); CreateHandle(loop._uv, size);
_threadId = loop._threadId;
} }
public void Validate(bool closed = false)
{
Trace.Assert(IsClosed == closed, "Handle is closed");
Trace.Assert(!IsInvalid, "Handle is invalid");
Trace.Assert(_threadId == Thread.CurrentThread.ManagedThreadId, "ThreadId is correct");
}
unsafe protected static void DestroyHandle(IntPtr memory) unsafe protected static void DestroyHandle(IntPtr memory)
{ {
var gcHandlePtr = *(IntPtr*)memory; var gcHandlePtr = *(IntPtr*)memory;

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public void Init(UvLoopHandle loop) public void Init(UvLoopHandle loop)
{ {
CreateHandle(loop.Libuv, loop.Libuv.req_size(3)); CreateHandle(loop, loop.Libuv.req_size(3));
} }
public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, object> callback, object state) public void Shutdown(UvStreamHandle handle, Action<UvShutdownReq, int, object> callback, object state)

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public void Init(UvLoopHandle loop) public void Init(UvLoopHandle loop)
{ {
CreateHandle(loop.Libuv, loop.Libuv.req_size(2)); CreateHandle(loop, loop.Libuv.req_size(2));
} }
public void Write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, Action<UvWriteReq, int, object> callback, object state) public void Write(UvStreamHandle handle, Libuv.uv_buf_t[] bufs, int nbufs, Action<UvWriteReq, int, object> callback, object state)
@ -39,14 +39,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public abstract class UvReq : UvMemory public abstract class UvReq : UvMemory
{ {
unsafe protected void CreateHandle(Libuv uv, int size)
{
_uv = uv;
handle = Marshal.AllocCoTaskMem(size);
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this));
}
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
DestroyHandle(handle); DestroyHandle(handle);

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace SampleApp namespace SampleApp
@ -17,6 +18,8 @@ namespace SampleApp
engine.Stop(); engine.Stop();
} }
private static async Task App(object arg) private static async Task App(object arg)
{ {
var httpContext = new Microsoft.AspNet.PipelineCore.DefaultHttpContext( var httpContext = new Microsoft.AspNet.PipelineCore.DefaultHttpContext(

View File

@ -1,6 +1,7 @@
using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.Server.Kestrel; using Microsoft.AspNet.Server.Kestrel;
using System; using System;
using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
@ -29,6 +30,25 @@ namespace Microsoft.AspNet.Server.KestralTests
await response.Body.WriteAsync(buffer, 0, count); await response.Body.WriteAsync(buffer, 0, count);
} }
} }
private async Task AppChunked(object callContext)
{
var request = callContext as IHttpRequestFeature;
var response = callContext as IHttpResponseFeature;
response.Headers["Transfer-Encoding"] = new[] { "chunked" };
for (; ;)
{
var buffer = new byte[8192];
var count = await request.Body.ReadAsync(buffer, 0, buffer.Length);
var hex = Encoding.ASCII.GetBytes(count.ToString("x") + "\r\n");
await response.Body.WriteAsync(hex, 0, hex.Length);
if (count == 0)
{
break;
}
await response.Body.WriteAsync(buffer, 0, count);
await response.Body.WriteAsync(new[] { (byte)'\r', (byte)'\n' }, 0, 2);
}
}
[Fact] [Fact]
public async Task EngineCanStartAndStop() public async Task EngineCanStartAndStop()
@ -70,5 +90,187 @@ namespace Microsoft.AspNet.Server.KestralTests
started.Dispose(); started.Dispose();
engine.Stop(); engine.Stop();
} }
[Fact]
public async Task Http10()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(App);
Transceive(
@"POST / HTTP/1.0
Hello World",
@"HTTP/1.0 200 OK
Hello World");
started.Dispose();
engine.Stop();
}
[Fact]
public async Task Http10ContentLength()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(App);
Transceive(
@"POST / HTTP/1.0
Content-Length: 5
Hello World",
@"HTTP/1.0 200 OK
Hello");
started.Dispose();
engine.Stop();
}
[Fact]
public async Task Http10TransferEncoding()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(App);
Transceive(
@"POST / HTTP/1.0
Transfer-Encoding: chunked
5
Hello
6
World
0
ignored",
@"HTTP/1.0 200 OK
Hello World");
started.Dispose();
engine.Stop();
}
[Fact]
public async Task Http10KeepAlive()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(AppChunked);
Transceive(
@"GET / HTTP/1.0
Connection: Keep-Alive
POST / HTTP/1.0
Goodbye",
@"HTTP/1.0 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
0
HTTP/1.0 200 OK
Transfer-Encoding: chunked
7
Goodbye
0
");
started.Dispose();
engine.Stop();
}
[Fact]
public async Task Http10KeepAliveContentLength()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(AppChunked);
Transceive(
@"POST / HTTP/1.0
Connection: Keep-Alive
Content-Length: 11
Hello WorldPOST / HTTP/1.0
Goodbye",
@"HTTP/1.0 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
b
Hello World
0
HTTP/1.0 200 OK
Transfer-Encoding: chunked
7
Goodbye
0
");
started.Dispose();
engine.Stop();
}
[Fact]
public async Task Http10KeepAliveTransferEncoding()
{
var engine = new KestrelEngine();
engine.Start(1);
var started = engine.CreateServer(AppChunked);
Transceive(
@"POST / HTTP/1.0
Transfer-Encoding: chunked
Connection: keep-alive
5
Hello
6
World
0
POST / HTTP/1.0
Goodbye",
@"HTTP/1.0 200 OK
Transfer-Encoding: chunked
Connection: keep-alive
b
Hello World
0
HTTP/1.0 200 OK
Transfer-Encoding: chunked
7
Goodbye
0
");
started.Dispose();
engine.Stop();
}
private void Transceive(string send, string expected)
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(IPAddress.Loopback, 4001));
var stream = new NetworkStream(socket, false);
var writer = new StreamWriter(stream, Encoding.ASCII);
writer.Write(send);
writer.Flush();
stream.Flush();
socket.Shutdown(SocketShutdown.Send);
var reader = new StreamReader(stream, Encoding.ASCII);
var actual = reader.ReadToEnd();
Assert.Equal(expected, actual);
}
} }
} }

View File

@ -7,7 +7,9 @@
"Microsoft.AspNet.Server.Kestrel": "0.1-*" "Microsoft.AspNet.Server.Kestrel": "0.1-*"
}, },
"configurations": { "configurations": {
"net45": {} "net45": {
"compilationOptions": { "define": [ "TRACE" ] }
}
}, },
"commands": { "commands": {
"run": "Xunit.KRunner", "run": "Xunit.KRunner",