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);
}
private readonly Func<object, Task> _app;
private readonly UvStreamHandle _socket;
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)
{
_socket = socket;
@ -64,41 +56,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
public void Start()
{
//_services.Trace.Event(TraceEventType.Start, TraceMessage.Connection);
SocketInput = new SocketInput(Memory);
SocketOutput = new SocketOutput(Thread, _socket);
_frame = new Frame(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)
@ -119,10 +80,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
SocketInput.RemoteIntakeFin = true;
}
if (_frame == null)
{
_frame = new Frame(this);
}
_frame.Consume();
}
@ -141,14 +98,26 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
switch (endType)
{
case ProduceEndType.SocketShutdownSend:
var shutdown = new UvShutdownReq();
shutdown.Init(Thread.Loop);
shutdown.Shutdown(_socket, (req, status, state) => req.Close(), null);
Thread.Post(
x =>
{
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;
case ProduceEndType.ConnectionKeepAlive:
_frame = new Frame(this);
Thread.Post(
x => ((Frame)x).Consume(),
_frame);
break;
case ProduceEndType.SocketDisconnect:
_socket.Close();
Thread.Post(
x => ((UvHandle)x).Close(),
_socket);
break;
}
}

View File

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

View File

@ -6,24 +6,6 @@ using System.Collections.Generic;
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
{
private Action _continuation = () => { };
@ -156,8 +138,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
var consumeLength = Math.Min(_neededLength, input.Buffer.Count);
_neededLength -= consumeLength;
var consumed = input.Take(consumeLength);
if (_neededLength != 0)
{
Intake(consumeLength);

View File

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

View File

@ -1,8 +1,10 @@
// 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.
#define TRACE
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace Microsoft.AspNet.Server.Kestrel.Networking
{
@ -12,6 +14,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
public abstract class UvMemory : SafeHandle
{
protected Libuv _uv;
int _threadId;
public UvMemory() : base(IntPtr.Zero, true)
{
}
@ -29,6 +33,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
unsafe protected void CreateHandle(Libuv uv, int size)
{
_uv = uv;
_threadId = Thread.CurrentThread.ManagedThreadId;
handle = Marshal.AllocCoTaskMem(size);
*(IntPtr*)handle = GCHandle.ToIntPtr(GCHandle.Alloc(this));
}
@ -36,8 +42,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
protected void CreateHandle(UvLoopHandle loop, int 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)
{
var gcHandlePtr = *(IntPtr*)memory;

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
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)

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
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)
@ -39,14 +39,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
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()
{
DestroyHandle(handle);

View File

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

View File

@ -1,6 +1,7 @@
using Microsoft.AspNet.HttpFeature;
using Microsoft.AspNet.Server.Kestrel;
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
@ -29,6 +30,25 @@ namespace Microsoft.AspNet.Server.KestralTests
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]
public async Task EngineCanStartAndStop()
@ -70,5 +90,187 @@ namespace Microsoft.AspNet.Server.KestralTests
started.Dispose();
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-*"
},
"configurations": {
"net45": {}
"net45": {
"compilationOptions": { "define": [ "TRACE" ] }
}
},
"commands": {
"run": "Xunit.KRunner",