Changing flow control for incoming data
SocketInput is now awaitable Input buffers are slab-allocated and pinned from large object heap Request frame parsing is offloaded from libuv thread
This commit is contained in:
parent
557f6d6993
commit
1f6aaebeda
|
|
@ -16,7 +16,7 @@ namespace SampleApp
|
||||||
|
|
||||||
loggerFactory.AddConsole(LogLevel.Debug);
|
loggerFactory.AddConsole(LogLevel.Debug);
|
||||||
|
|
||||||
app.Run(context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
Console.WriteLine("{0} {1}{2}{3}",
|
Console.WriteLine("{0} {1}{2}{3}",
|
||||||
context.Request.Method,
|
context.Request.Method,
|
||||||
|
|
@ -24,9 +24,11 @@ namespace SampleApp
|
||||||
context.Request.Path,
|
context.Request.Path,
|
||||||
context.Request.QueryString);
|
context.Request.QueryString);
|
||||||
|
|
||||||
|
await context.Request.Body.CopyToAsync(Console.OpenStandardOutput());
|
||||||
|
|
||||||
context.Response.ContentLength = 11;
|
context.Response.ContentLength = 11;
|
||||||
context.Response.ContentType = "text/plain";
|
context.Response.ContentType = "text/plain";
|
||||||
return context.Response.WriteAsync("Hello world");
|
await context.Response.WriteAsync("Hello world");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Networking;
|
using Microsoft.AspNet.Server.Kestrel.Networking;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
|
|
@ -11,13 +12,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
public class Connection : ConnectionContext, IConnectionControl
|
public class Connection : ConnectionContext, IConnectionControl
|
||||||
{
|
{
|
||||||
private static readonly Action<UvStreamHandle, int, Exception, object> _readCallback = ReadCallback;
|
private const int EOF = -4095;
|
||||||
|
private const int ECONNRESET = -4077;
|
||||||
|
|
||||||
|
private static readonly Action<UvStreamHandle, int, int, Exception, object> _readCallback = ReadCallback;
|
||||||
private static readonly Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback = AllocCallback;
|
private static readonly Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback = AllocCallback;
|
||||||
|
|
||||||
private static long _lastConnectionId;
|
private static long _lastConnectionId;
|
||||||
|
|
||||||
private readonly UvStreamHandle _socket;
|
private readonly UvStreamHandle _socket;
|
||||||
private Frame _frame;
|
private Frame _frame;
|
||||||
|
private Task _frameTask;
|
||||||
private long _connectionId = 0;
|
private long _connectionId = 0;
|
||||||
|
|
||||||
private readonly object _stateLock = new object();
|
private readonly object _stateLock = new object();
|
||||||
|
|
@ -35,9 +40,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
Log.ConnectionStart(_connectionId);
|
Log.ConnectionStart(_connectionId);
|
||||||
|
|
||||||
SocketInput = new SocketInput(Memory);
|
SocketInput = new SocketInput(Memory2);
|
||||||
SocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log);
|
SocketOutput = new SocketOutput(Thread, _socket, _connectionId, Log);
|
||||||
_frame = new Frame(this);
|
_frame = new Frame(this);
|
||||||
|
_frameTask = Task.Run(_frame.ProcessFraming);
|
||||||
_socket.ReadStart(_allocCallback, _readCallback, this);
|
_socket.ReadStart(_allocCallback, _readCallback, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,27 +54,29 @@ 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)
|
||||||
{
|
{
|
||||||
|
var result = SocketInput.Pin(2048);
|
||||||
|
|
||||||
return handle.Libuv.buf_init(
|
return handle.Libuv.buf_init(
|
||||||
SocketInput.Pin(2048),
|
result.DataPtr,
|
||||||
2048);
|
result.Data.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReadCallback(UvStreamHandle handle, int nread, Exception error, object state)
|
private static void ReadCallback(UvStreamHandle handle, int readCount, int errorCode, Exception error, object state)
|
||||||
{
|
{
|
||||||
((Connection)state).OnRead(handle, nread, error);
|
((Connection)state).OnRead(handle, readCount, errorCode, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRead(UvStreamHandle handle, int status, Exception error)
|
private void OnRead(UvStreamHandle handle, int readCount, int errorCode, Exception error)
|
||||||
{
|
{
|
||||||
SocketInput.Unpin(status);
|
SocketInput.Unpin(readCount);
|
||||||
|
|
||||||
var normalRead = error == null && status > 0;
|
var normalRead = readCount != 0 && errorCode == 0;
|
||||||
var normalDone = status == 0 || status == Constants.ECONNRESET || status == Constants.EOF;
|
var normalDone = readCount == 0 && (errorCode == 0 || errorCode == Constants.ECONNRESET || errorCode == Constants.EOF);
|
||||||
var errorDone = !(normalDone || normalRead);
|
var errorDone = !(normalDone || normalRead);
|
||||||
|
|
||||||
if (normalRead)
|
if (normalRead)
|
||||||
{
|
{
|
||||||
Log.ConnectionRead(_connectionId, status);
|
Log.ConnectionRead(_connectionId, readCount);
|
||||||
}
|
}
|
||||||
else if (normalDone || errorDone)
|
else if (normalDone || errorDone)
|
||||||
{
|
{
|
||||||
|
|
@ -85,16 +93,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SocketInput.SetCompleted(errorDone ? error : null);
|
||||||
try
|
|
||||||
{
|
|
||||||
_frame.Consume();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.LogError("Connection._frame.Consume ", ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IConnectionControl.Pause()
|
void IConnectionControl.Pause()
|
||||||
|
|
@ -124,17 +123,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
Log.ConnectionWriteFin(_connectionId);
|
Log.ConnectionWriteFin(_connectionId);
|
||||||
Thread.Post(
|
Thread.Post(
|
||||||
state =>
|
_this =>
|
||||||
{
|
{
|
||||||
var self = (Connection)state;
|
var self = (Connection)state;
|
||||||
var shutdown = new UvShutdownReq(self.Log);
|
var shutdown = new UvShutdownReq(self.Log);
|
||||||
shutdown.Init(self.Thread.Loop);
|
shutdown.Init(_this.Thread.Loop);
|
||||||
shutdown.Shutdown(self._socket, (req, status, state2) =>
|
shutdown.Shutdown(_this._socket, (req, status, state2) =>
|
||||||
{
|
{
|
||||||
var self2 = (Connection)state2;
|
var __this = (Connection)state2;
|
||||||
self2.Log.ConnectionWroteFin(_connectionId, status);
|
__this.Log.ConnectionWroteFin(__this._connectionId, status);
|
||||||
req.Dispose();
|
req.Dispose();
|
||||||
}, this);
|
}, _this);
|
||||||
},
|
},
|
||||||
this);
|
this);
|
||||||
break;
|
break;
|
||||||
|
|
@ -159,12 +158,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
Log.ConnectionDisconnect(_connectionId);
|
Log.ConnectionDisconnect(_connectionId);
|
||||||
Thread.Post(
|
Thread.Post(
|
||||||
state =>
|
_this =>
|
||||||
{
|
{
|
||||||
Log.ConnectionStop(_connectionId);
|
Log.ConnectionStop(_this._connectionId);
|
||||||
((UvHandle)state).Dispose();
|
_this._socket.Dispose();
|
||||||
},
|
},
|
||||||
_socket);
|
this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
|
||||||
using Microsoft.Framework.Logging;
|
using Microsoft.Framework.Logging;
|
||||||
using Microsoft.Framework.Primitives;
|
using Microsoft.Framework.Primitives;
|
||||||
|
using System.Numerics;
|
||||||
|
using Microsoft.AspNet.Hosting.Builder;
|
||||||
|
|
||||||
// ReSharper disable AccessToModifiedClosure
|
// ReSharper disable AccessToModifiedClosure
|
||||||
|
|
||||||
|
|
@ -25,24 +27,22 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
private static readonly ArraySegment<byte> _emptyData = new ArraySegment<byte>(new byte[0]);
|
||||||
private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef");
|
private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef");
|
||||||
|
|
||||||
private Mode _mode;
|
private readonly object _onStartingSync = new Object();
|
||||||
private bool _responseStarted;
|
private readonly object _onCompletedSync = new Object();
|
||||||
private bool _keepAlive;
|
|
||||||
private bool _autoChunk;
|
|
||||||
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
|
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
|
||||||
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
|
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
|
||||||
|
|
||||||
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
private List<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
private List<KeyValuePair<Func<object, Task>, object>> _onCompleted;
|
||||||
private object _onStartingSync = new Object();
|
|
||||||
private object _onCompletedSync = new Object();
|
private bool _responseStarted;
|
||||||
|
private bool _keepAlive;
|
||||||
|
private bool _autoChunk;
|
||||||
|
|
||||||
public Frame(ConnectionContext context) : base(context)
|
public Frame(ConnectionContext context) : base(context)
|
||||||
{
|
{
|
||||||
FrameControl = this;
|
FrameControl = this;
|
||||||
StatusCode = 200;
|
Reset();
|
||||||
RequestHeaders = _requestHeaders;
|
|
||||||
ResponseHeaders = _responseHeaders;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Method { get; set; }
|
public string Method { get; set; }
|
||||||
|
|
@ -66,95 +66,112 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
get { return _responseStarted; }
|
get { return _responseStarted; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Consume()
|
public void Reset()
|
||||||
{
|
{
|
||||||
var input = SocketInput;
|
_onStarting = null;
|
||||||
for (;;)
|
_onCompleted = null;
|
||||||
{
|
|
||||||
switch (_mode)
|
|
||||||
{
|
|
||||||
case Mode.StartLine:
|
|
||||||
if (input.Buffer.Count == 0 && input.RemoteIntakeFin)
|
|
||||||
{
|
|
||||||
_mode = Mode.Terminated;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TakeStartLine(input))
|
_responseStarted = false;
|
||||||
{
|
_keepAlive = false;
|
||||||
if (input.RemoteIntakeFin)
|
_autoChunk = false;
|
||||||
{
|
|
||||||
_mode = Mode.Terminated;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_mode = Mode.MessageHeader;
|
_requestHeaders.Reset();
|
||||||
break;
|
ResetResponseHeaders();
|
||||||
|
|
||||||
case Mode.MessageHeader:
|
Method = null;
|
||||||
if (input.Buffer.Count == 0 && input.RemoteIntakeFin)
|
RequestUri = null;
|
||||||
{
|
Path = null;
|
||||||
_mode = Mode.Terminated;
|
QueryString = null;
|
||||||
break;
|
HttpVersion = null;
|
||||||
}
|
RequestHeaders = _requestHeaders;
|
||||||
|
MessageBody = null;
|
||||||
|
RequestBody = null;
|
||||||
|
StatusCode = 200;
|
||||||
|
ReasonPhrase = null;
|
||||||
|
ResponseHeaders = _responseHeaders;
|
||||||
|
ResponseBody = null;
|
||||||
|
DuplexStream = null;
|
||||||
|
|
||||||
var endOfHeaders = false;
|
|
||||||
while (!endOfHeaders)
|
|
||||||
{
|
|
||||||
if (!TakeMessageHeader(input, out endOfHeaders))
|
|
||||||
{
|
|
||||||
if (input.RemoteIntakeFin)
|
|
||||||
{
|
|
||||||
_mode = Mode.Terminated;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_mode == Mode.Terminated)
|
|
||||||
{
|
|
||||||
// If we broke out of the above while loop in the Terminated
|
|
||||||
// state, we don't want to transition to the MessageBody state.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_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;
|
|
||||||
|
|
||||||
case Mode.Terminated:
|
|
||||||
ConnectionControl.End(ProduceEndType.SocketShutdownSend);
|
|
||||||
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Execute()
|
public void ResetResponseHeaders()
|
||||||
{
|
{
|
||||||
MessageBody = MessageBody.For(
|
_responseHeaders.Reset();
|
||||||
HttpVersion,
|
_responseHeaders.HeaderServer = "Kestrel";
|
||||||
RequestHeaders,
|
_responseHeaders.HeaderDate = DateTime.UtcNow.ToString("r");
|
||||||
this);
|
}
|
||||||
_keepAlive = MessageBody.RequestKeepAlive;
|
|
||||||
RequestBody = new FrameRequestStream(MessageBody);
|
public async Task ProcessFraming()
|
||||||
ResponseBody = new FrameResponseStream(this);
|
{
|
||||||
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
|
var terminated = false;
|
||||||
SocketInput.Free();
|
while (!terminated)
|
||||||
Task.Run(ExecuteAsync);
|
{
|
||||||
|
while (!terminated && !TakeStartLine(SocketInput))
|
||||||
|
{
|
||||||
|
terminated = SocketInput.RemoteIntakeFin;
|
||||||
|
if (!terminated)
|
||||||
|
{
|
||||||
|
await SocketInput;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var x = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!terminated && !TakeMessageHeader2(SocketInput))
|
||||||
|
{
|
||||||
|
terminated = SocketInput.RemoteIntakeFin;
|
||||||
|
if (!terminated)
|
||||||
|
{
|
||||||
|
await SocketInput;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var x = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!terminated)
|
||||||
|
{
|
||||||
|
MessageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
|
||||||
|
_keepAlive = MessageBody.RequestKeepAlive;
|
||||||
|
RequestBody = new FrameRequestStream(MessageBody);
|
||||||
|
ResponseBody = new FrameResponseStream(this);
|
||||||
|
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
|
||||||
|
|
||||||
|
Exception error = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Application.Invoke(this).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Trigger FireOnStarting if ProduceStart hasn't been called yet.
|
||||||
|
// We call it here, so it can go through our normal error handling
|
||||||
|
// and respond with a 500 if an OnStarting callback throws.
|
||||||
|
if (!_responseStarted)
|
||||||
|
{
|
||||||
|
FireOnStarting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = ex;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
FireOnCompleted();
|
||||||
|
ProduceEnd(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminated = !_keepAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connection Terminated!
|
||||||
|
ConnectionControl.End(ProduceEndType.SocketShutdownSend);
|
||||||
|
ConnectionControl.End(ProduceEndType.SocketDisconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnStarting(Func<object, Task> callback, object state)
|
public void OnStarting(Func<object, Task> callback, object state)
|
||||||
|
|
@ -222,32 +239,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteAsync()
|
|
||||||
{
|
|
||||||
Exception error = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Application.Invoke(this).ConfigureAwait(false);
|
|
||||||
|
|
||||||
// Trigger FireOnStarting if ProduceStart hasn't been called yet.
|
|
||||||
// We call it here, so it can go through our normal error handling
|
|
||||||
// and respond with a 500 if an OnStarting callback throws.
|
|
||||||
if (!_responseStarted)
|
|
||||||
{
|
|
||||||
FireOnStarting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
error = ex;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
FireOnCompleted();
|
|
||||||
ProduceEnd(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Flush()
|
public void Flush()
|
||||||
{
|
{
|
||||||
ProduceStart(immediate: false);
|
ProduceStart(immediate: false);
|
||||||
|
|
@ -402,8 +393,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
// the app func has failed. https://github.com/aspnet/KestrelHttpServer/issues/43
|
// the app func has failed. https://github.com/aspnet/KestrelHttpServer/issues/43
|
||||||
_onStarting = null;
|
_onStarting = null;
|
||||||
|
|
||||||
ResponseHeaders = new FrameResponseHeaders();
|
ResetResponseHeaders();
|
||||||
ResponseHeaders["Content-Length"] = new[] { "0" };
|
_responseHeaders.HeaderContentLength = "0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -522,58 +513,39 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return new Tuple<ArraySegment<byte>, IDisposable>(writer.Buffer, writer);
|
return new Tuple<ArraySegment<byte>, IDisposable>(writer.Buffer, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TakeStartLine(SocketInput baton)
|
private bool TakeStartLine(SocketInput input)
|
||||||
{
|
{
|
||||||
var remaining = baton.Buffer;
|
var begin = input.GetIterator();
|
||||||
if (remaining.Count < 2)
|
if (begin.IsDefault) return false;
|
||||||
|
|
||||||
|
var end = begin.IndexOf(' ');
|
||||||
|
var method = begin.GetString(end);
|
||||||
|
|
||||||
|
char chFound;
|
||||||
|
begin = end.Add(1);
|
||||||
|
end = begin.IndexOfAny(' ', '?', out chFound);
|
||||||
|
var requestUri = begin.GetString(end);
|
||||||
|
|
||||||
|
begin = end;
|
||||||
|
end = chFound == '?' ? begin.IndexOf(' ') : begin;
|
||||||
|
var queryString = begin.GetString(end);
|
||||||
|
|
||||||
|
begin = end.Add(1);
|
||||||
|
end = begin.IndexOf('\r');
|
||||||
|
var httpVersion = begin.GetString(end);
|
||||||
|
|
||||||
|
end = end.Add(1);
|
||||||
|
if (end.Peek() != '\n')
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var firstSpace = -1;
|
|
||||||
var secondSpace = -1;
|
|
||||||
var questionMark = -1;
|
|
||||||
var ch0 = remaining.Array[remaining.Offset];
|
|
||||||
for (var index = 0; index != remaining.Count - 1; ++index)
|
|
||||||
{
|
|
||||||
var ch1 = remaining.Array[remaining.Offset + index + 1];
|
|
||||||
if (ch0 == '\r' && ch1 == '\n')
|
|
||||||
{
|
|
||||||
if (secondSpace == -1)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("INVALID REQUEST FORMAT");
|
|
||||||
}
|
|
||||||
Method = GetString(remaining, 0, firstSpace);
|
|
||||||
RequestUri = GetString(remaining, firstSpace + 1, secondSpace);
|
|
||||||
if (questionMark == -1)
|
|
||||||
{
|
|
||||||
Path = RequestUri;
|
|
||||||
QueryString = string.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Path = GetString(remaining, firstSpace + 1, questionMark);
|
|
||||||
QueryString = GetString(remaining, questionMark, secondSpace);
|
|
||||||
}
|
|
||||||
HttpVersion = GetString(remaining, secondSpace + 1, index);
|
|
||||||
baton.Skip(index + 2);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch0 == ' ' && firstSpace == -1)
|
Method = method;
|
||||||
{
|
RequestUri = requestUri;
|
||||||
firstSpace = index;
|
QueryString = queryString;
|
||||||
}
|
HttpVersion = httpVersion;
|
||||||
else if (ch0 == ' ' && firstSpace != -1 && secondSpace == -1)
|
input.JumpTo(end.Add(1));
|
||||||
{
|
return true;
|
||||||
secondSpace = index;
|
|
||||||
}
|
|
||||||
else if (ch0 == '?' && firstSpace != -1 && questionMark == -1 && secondSpace == -1)
|
|
||||||
{
|
|
||||||
questionMark = index;
|
|
||||||
}
|
|
||||||
ch0 = ch1;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetString(ArraySegment<byte> range, int startIndex, int endIndex)
|
static string GetString(ArraySegment<byte> range, int startIndex, int endIndex)
|
||||||
|
|
@ -581,6 +553,68 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return Encoding.UTF8.GetString(range.Array, range.Offset + startIndex, endIndex - startIndex);
|
return Encoding.UTF8.GetString(range.Array, range.Offset + startIndex, endIndex - startIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TakeMessageHeader2(SocketInput baton)
|
||||||
|
{
|
||||||
|
char chFirst;
|
||||||
|
char chSecond;
|
||||||
|
var scan = baton.GetIterator();
|
||||||
|
while (!scan.IsDefault)
|
||||||
|
{
|
||||||
|
var beginName = scan;
|
||||||
|
scan = scan.IndexOfAny(':', '\r', out chFirst);
|
||||||
|
|
||||||
|
var endName = scan;
|
||||||
|
chSecond = scan.MoveNext();
|
||||||
|
|
||||||
|
if (chFirst == '\r' && chSecond == '\n')
|
||||||
|
{
|
||||||
|
baton.JumpTo(scan.Add(1));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (chFirst == char.MinValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (
|
||||||
|
chSecond == ' ' ||
|
||||||
|
chSecond == '\t' ||
|
||||||
|
chSecond == '\r' ||
|
||||||
|
chSecond == '\n')
|
||||||
|
{
|
||||||
|
chSecond = scan.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
var beginValue = scan;
|
||||||
|
var wrapping = false;
|
||||||
|
while (!scan.IsDefault)
|
||||||
|
{
|
||||||
|
var endValue = scan = scan.IndexOf('\r');
|
||||||
|
chFirst = scan.MoveNext();
|
||||||
|
if (chFirst != '\n')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
chSecond = scan.MoveNext();
|
||||||
|
if (chSecond == ' ' || chSecond == '\t')
|
||||||
|
{
|
||||||
|
wrapping = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var name = beginName.GetArraySegment(endName);
|
||||||
|
var value = beginValue.GetString(endValue);
|
||||||
|
if (wrapping)
|
||||||
|
{
|
||||||
|
value = value.Replace("\r\n", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestHeaders.Append(name.Array, name.Offset, name.Count, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private bool TakeMessageHeader(SocketInput baton, out bool endOfHeaders)
|
private bool TakeMessageHeader(SocketInput baton, out bool endOfHeaders)
|
||||||
{
|
{
|
||||||
|
|
@ -673,14 +707,5 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
statusCode != 205 &&
|
statusCode != 205 &&
|
||||||
statusCode != 304;
|
statusCode != 304;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private enum Mode
|
|
||||||
{
|
|
||||||
StartLine,
|
|
||||||
MessageHeader,
|
|
||||||
MessageBody,
|
|
||||||
Terminated,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -21,7 +21,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
return GetValueFast(key);
|
return GetValueFast(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
SetValueFast(key, value);
|
SetValueFast(key, value);
|
||||||
|
|
@ -36,6 +35,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
ICollection<StringValues> IDictionary<string, StringValues>.Values => ((IDictionary<string, StringValues>)this).Select(pair => pair.Value).ToList();
|
ICollection<StringValues> IDictionary<string, StringValues>.Values => ((IDictionary<string, StringValues>)this).Select(pair => pair.Value).ToList();
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
ClearFast();
|
||||||
|
}
|
||||||
|
|
||||||
protected static StringValues AppendValue(StringValues existing, string append)
|
protected static StringValues AppendValue(StringValues existing, string append)
|
||||||
{
|
{
|
||||||
return StringValues.Concat(existing, append);
|
return StringValues.Concat(existing, append);
|
||||||
|
|
|
||||||
|
|
@ -72,13 +72,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count));
|
return _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state)
|
public Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<int>(state);
|
var tcs = new TaskCompletionSource<int>(state);
|
||||||
var task = _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count));
|
var task = _body.ReadAsync(new ArraySegment<byte>(buffer, offset, count), cancellationToken);
|
||||||
task.ContinueWith((task2, state2) =>
|
task.ContinueWith((task2, state2) =>
|
||||||
{
|
{
|
||||||
var tcs2 = (TaskCompletionSource<int>)state2;
|
var tcs2 = (TaskCompletionSource<int>)state2;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
protected Listener(ServiceContext serviceContext) : base(serviceContext)
|
protected Listener(ServiceContext serviceContext) : base(serviceContext)
|
||||||
{
|
{
|
||||||
|
Memory2 = new MemoryPool2();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UvStreamHandle ListenSocket { get; private set; }
|
protected UvStreamHandle ListenSocket { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
Thread = listenerContext.Thread;
|
Thread = listenerContext.Thread;
|
||||||
Application = listenerContext.Application;
|
Application = listenerContext.Application;
|
||||||
Memory = listenerContext.Memory;
|
Memory = listenerContext.Memory;
|
||||||
|
Memory2 = listenerContext.Memory2;
|
||||||
Log = listenerContext.Log;
|
Log = listenerContext.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,6 +32,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public IMemoryPool Memory { get; set; }
|
public IMemoryPool Memory { get; set; }
|
||||||
|
|
||||||
|
public MemoryPool2 Memory2 { get; set; }
|
||||||
|
|
||||||
public IKestrelTrace Log { get; }
|
public IKestrelTrace Log { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
DispatchPipe.ReadStart(
|
DispatchPipe.ReadStart(
|
||||||
(_1, _2, _3) => buf,
|
(_1, _2, _3) => buf,
|
||||||
(_1, status2, error2, state2) =>
|
(_1, status2, errCode, error2, state2) =>
|
||||||
{
|
{
|
||||||
if (status2 < 0)
|
if (status2 < 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
{
|
||||||
|
public class MemoryPool2 : IDisposable
|
||||||
|
{
|
||||||
|
private const int blockStride = 4096;
|
||||||
|
private const int blockUnused = 64;
|
||||||
|
private const int blockCount = 32;
|
||||||
|
private const int blockLength = blockStride - blockUnused;
|
||||||
|
private const int slabLength = blockStride * blockCount;
|
||||||
|
|
||||||
|
private ConcurrentStack<MemoryPoolBlock2> _blocks = new ConcurrentStack<MemoryPoolBlock2>();
|
||||||
|
private ConcurrentStack<MemoryPoolSlab2> _slabs = new ConcurrentStack<MemoryPoolSlab2>();
|
||||||
|
|
||||||
|
public MemoryPoolBlock2 Lease(int minimumSize)
|
||||||
|
{
|
||||||
|
if (minimumSize > blockLength)
|
||||||
|
{
|
||||||
|
return MemoryPoolBlock2.Create(
|
||||||
|
new ArraySegment<byte>(new byte[minimumSize]),
|
||||||
|
dataPtr: IntPtr.Zero,
|
||||||
|
pool: null,
|
||||||
|
slab: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
MemoryPoolBlock2 block;
|
||||||
|
if (_blocks.TryPop(out block))
|
||||||
|
{
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
AllocateSlab();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AllocateSlab()
|
||||||
|
{
|
||||||
|
var slab = MemoryPoolSlab2.Create(slabLength);
|
||||||
|
_slabs.Push(slab);
|
||||||
|
|
||||||
|
var basePtr = slab.ArrayPtr;
|
||||||
|
var firstOffset = (blockStride - 1) - ((ushort)(basePtr + blockStride - 1) % blockStride);
|
||||||
|
|
||||||
|
for (var offset = firstOffset;
|
||||||
|
offset + blockLength <= slabLength;
|
||||||
|
offset += blockStride)
|
||||||
|
{
|
||||||
|
var block = MemoryPoolBlock2.Create(
|
||||||
|
new ArraySegment<byte>(slab.Array, offset, blockLength),
|
||||||
|
basePtr,
|
||||||
|
this,
|
||||||
|
slab);
|
||||||
|
Return(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(MemoryPoolBlock2 block)
|
||||||
|
{
|
||||||
|
block.Reset();
|
||||||
|
_blocks.Push(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
MemoryPoolSlab2 slab;
|
||||||
|
while (_slabs.TryPop(out slab))
|
||||||
|
{
|
||||||
|
// dispose managed state (managed objects).
|
||||||
|
slab.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// N/A: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||||
|
|
||||||
|
// N/A: set large fields to null.
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// N/A: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||||
|
// ~MemoryPool2() {
|
||||||
|
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
// Dispose(false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// This code added to correctly implement the disposable pattern.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(true);
|
||||||
|
// N/A: uncomment the following line if the finalizer is overridden above.
|
||||||
|
// GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,479 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
{
|
||||||
|
public class MemoryPoolBlock2
|
||||||
|
{
|
||||||
|
private static Vector<byte> _dotIndex = new Vector<byte>(Enumerable.Range(0, Vector<byte>.Count).Select(x => (byte)-x).ToArray());
|
||||||
|
private static Vector<byte> _dotCount = new Vector<byte>(Byte.MaxValue);
|
||||||
|
|
||||||
|
private GCHandle _pinHandle;
|
||||||
|
private IntPtr _dataArrayPtr;
|
||||||
|
|
||||||
|
public ArraySegment<byte> Data;
|
||||||
|
|
||||||
|
protected MemoryPoolBlock2()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryPool2 Pool { get; private set; }
|
||||||
|
|
||||||
|
public MemoryPoolSlab2 Slab { get; private set; }
|
||||||
|
|
||||||
|
public byte[] Array => Data.Array;
|
||||||
|
public int Start { get; set; }
|
||||||
|
public int End { get; set; }
|
||||||
|
public MemoryPoolBlock2 Next { get; set; }
|
||||||
|
|
||||||
|
~MemoryPoolBlock2()
|
||||||
|
{
|
||||||
|
if (_pinHandle.IsAllocated)
|
||||||
|
{
|
||||||
|
_pinHandle.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Slab != null && Slab.IsActive)
|
||||||
|
{
|
||||||
|
Pool.Return(new MemoryPoolBlock2
|
||||||
|
{
|
||||||
|
_dataArrayPtr = _dataArrayPtr,
|
||||||
|
Data = Data,
|
||||||
|
Pool = Pool,
|
||||||
|
Slab = Slab,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntPtr Pin()
|
||||||
|
{
|
||||||
|
Debug.Assert(!_pinHandle.IsAllocated);
|
||||||
|
|
||||||
|
if (_dataArrayPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return _dataArrayPtr + End;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pinHandle = GCHandle.Alloc(Data.Array, GCHandleType.Pinned);
|
||||||
|
return _pinHandle.AddrOfPinnedObject() + End;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unpin()
|
||||||
|
{
|
||||||
|
if (_dataArrayPtr == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Debug.Assert(_pinHandle.IsAllocated);
|
||||||
|
_pinHandle.Free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemoryPoolBlock2 Create(int size, MemoryPool2 pool)
|
||||||
|
{
|
||||||
|
return new MemoryPoolBlock2
|
||||||
|
{
|
||||||
|
Data = new ArraySegment<byte>(new byte[size]),
|
||||||
|
Pool = pool
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemoryPoolBlock2 Create(
|
||||||
|
ArraySegment<byte> data,
|
||||||
|
IntPtr dataPtr,
|
||||||
|
MemoryPool2 pool,
|
||||||
|
MemoryPoolSlab2 slab)
|
||||||
|
{
|
||||||
|
return new MemoryPoolBlock2
|
||||||
|
{
|
||||||
|
Data = data,
|
||||||
|
_dataArrayPtr = dataPtr,
|
||||||
|
Pool = pool,
|
||||||
|
Slab = slab,
|
||||||
|
Start = data.Offset,
|
||||||
|
End = data.Offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Next = null;
|
||||||
|
Start = Data.Offset;
|
||||||
|
End = Data.Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Encoding.ASCII.GetString(Array, Start, End - Start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator GetIterator()
|
||||||
|
{
|
||||||
|
return new Iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Iterator
|
||||||
|
{
|
||||||
|
private MemoryPoolBlock2 _block;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public Iterator(MemoryPoolBlock2 block)
|
||||||
|
{
|
||||||
|
_block = block;
|
||||||
|
_index = _block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
public Iterator(MemoryPoolBlock2 block, int index)
|
||||||
|
{
|
||||||
|
_block = block;
|
||||||
|
_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDefault => _block == null;
|
||||||
|
|
||||||
|
public MemoryPoolBlock2 Block => _block;
|
||||||
|
|
||||||
|
public int Index => _index;
|
||||||
|
|
||||||
|
public bool HasAtLeast(int count)
|
||||||
|
{
|
||||||
|
var scan = _block;
|
||||||
|
var index = _index;
|
||||||
|
while (scan != null)
|
||||||
|
{
|
||||||
|
if (count <= scan.End - index)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
count -= scan.End - index;
|
||||||
|
scan = scan.Next;
|
||||||
|
index = scan?.Start ?? 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator Add(int count)
|
||||||
|
{
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
while (block != null)
|
||||||
|
{
|
||||||
|
var tailCount = block.End - index;
|
||||||
|
if (count < tailCount)
|
||||||
|
{
|
||||||
|
return new Iterator(block, index + count);
|
||||||
|
}
|
||||||
|
count -= tailCount;
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
return new Iterator(block, index + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator CopyTo(byte[] array, int offset, int count, out int actual)
|
||||||
|
{
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
var remaining = count;
|
||||||
|
while (block != null && remaining != 0)
|
||||||
|
{
|
||||||
|
var copyLength = Math.Min(remaining, block.End - index);
|
||||||
|
Buffer.BlockCopy(block.Array, index, array, offset, copyLength);
|
||||||
|
index += copyLength;
|
||||||
|
offset += copyLength;
|
||||||
|
remaining -= copyLength;
|
||||||
|
if (index == block.End)
|
||||||
|
{
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual = count - remaining;
|
||||||
|
return new Iterator(block, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char MoveNext()
|
||||||
|
{
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
while (block != null && index == block.End)
|
||||||
|
{
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
if (block != null)
|
||||||
|
{
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
while (block != null && index == block.End)
|
||||||
|
{
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
_block = block;
|
||||||
|
_index = index;
|
||||||
|
return block != null ? (char)block.Array[index] : char.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Peek()
|
||||||
|
{
|
||||||
|
while (_block != null)
|
||||||
|
{
|
||||||
|
if (_index < _block.End)
|
||||||
|
{
|
||||||
|
return _block.Data.Array[_index];
|
||||||
|
}
|
||||||
|
_block = _block.Next;
|
||||||
|
_index = _block.Start;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator IndexOf(char char0)
|
||||||
|
{
|
||||||
|
var byte0 = (byte)char0;
|
||||||
|
var vectorStride = Vector<byte>.Count;
|
||||||
|
var ch0Vector = new Vector<byte>(byte0);
|
||||||
|
|
||||||
|
var scanBlock = _block;
|
||||||
|
var scanArray = scanBlock?.Array;
|
||||||
|
var scanIndex = _index;
|
||||||
|
while (scanBlock != null)
|
||||||
|
{
|
||||||
|
var tailCount = scanBlock.End - scanIndex;
|
||||||
|
if (tailCount == 0)
|
||||||
|
{
|
||||||
|
scanBlock = scanBlock.Next;
|
||||||
|
scanArray = scanBlock?.Array;
|
||||||
|
scanIndex = scanBlock?.Start ?? 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tailCount >= vectorStride)
|
||||||
|
{
|
||||||
|
var data = new Vector<byte>(scanBlock.Array, scanIndex);
|
||||||
|
var ch0Equals = Vector.Equals(data, ch0Vector);
|
||||||
|
var ch0Count = Vector.Dot(ch0Equals, _dotCount);
|
||||||
|
|
||||||
|
if (ch0Count == 0)
|
||||||
|
{
|
||||||
|
scanIndex += vectorStride;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (ch0Count == 1)
|
||||||
|
{
|
||||||
|
return new Iterator(scanBlock, scanIndex + Vector.Dot(ch0Equals, _dotIndex));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tailCount = vectorStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; tailCount != 0; tailCount--, scanIndex++)
|
||||||
|
{
|
||||||
|
var ch = scanBlock.Array[scanIndex];
|
||||||
|
if (ch == byte0)
|
||||||
|
{
|
||||||
|
return new Iterator(scanBlock, scanIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Iterator(null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator IndexOfAny(char char0, char char1, out char chFound)
|
||||||
|
{
|
||||||
|
var byte0 = (byte)char0;
|
||||||
|
var byte1 = (byte)char1;
|
||||||
|
var vectorStride = Vector<byte>.Count;
|
||||||
|
var ch0Vector = new Vector<byte>(byte0);
|
||||||
|
var ch1Vector = new Vector<byte>(byte1);
|
||||||
|
|
||||||
|
var scanBlock = _block;
|
||||||
|
var scanArray = scanBlock?.Array;
|
||||||
|
var scanIndex = _index;
|
||||||
|
while (scanBlock != null)
|
||||||
|
{
|
||||||
|
var tailCount = scanBlock.End - scanIndex;
|
||||||
|
if (tailCount == 0)
|
||||||
|
{
|
||||||
|
scanBlock = scanBlock.Next;
|
||||||
|
scanArray = scanBlock?.Array;
|
||||||
|
scanIndex = scanBlock?.Start ?? 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tailCount >= vectorStride)
|
||||||
|
{
|
||||||
|
var data = new Vector<byte>(scanBlock.Array, scanIndex);
|
||||||
|
var ch0Equals = Vector.Equals(data, ch0Vector);
|
||||||
|
var ch0Count = Vector.Dot(ch0Equals, _dotCount);
|
||||||
|
var ch1Equals = Vector.Equals(data, ch1Vector);
|
||||||
|
var ch1Count = Vector.Dot(ch1Equals, _dotCount);
|
||||||
|
|
||||||
|
if (ch0Count == 0 && ch1Count == 0)
|
||||||
|
{
|
||||||
|
scanIndex += vectorStride;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (ch0Count < 2 && ch1Count < 2)
|
||||||
|
{
|
||||||
|
var ch0Index = ch0Count == 1 ? Vector.Dot(ch0Equals, _dotIndex) : byte.MaxValue;
|
||||||
|
var ch1Index = ch1Count == 1 ? Vector.Dot(ch1Equals, _dotIndex) : byte.MaxValue;
|
||||||
|
if (ch0Index < ch1Index)
|
||||||
|
{
|
||||||
|
chFound = char0;
|
||||||
|
return new Iterator(scanBlock, scanIndex + ch0Index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chFound = char1;
|
||||||
|
return new Iterator(scanBlock, scanIndex + ch1Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tailCount = vectorStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; tailCount != 0; tailCount--, scanIndex++)
|
||||||
|
{
|
||||||
|
var chIndex = scanBlock.Array[scanIndex];
|
||||||
|
if (chIndex == byte0)
|
||||||
|
{
|
||||||
|
chFound = char0;
|
||||||
|
return new Iterator(scanBlock, scanIndex);
|
||||||
|
}
|
||||||
|
else if (chIndex == byte1)
|
||||||
|
{
|
||||||
|
chFound = char1;
|
||||||
|
return new Iterator(scanBlock, scanIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chFound = char.MinValue;
|
||||||
|
return new Iterator(null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetLength(Iterator end)
|
||||||
|
{
|
||||||
|
var length = 0;
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (block == end._block)
|
||||||
|
{
|
||||||
|
return length + end._index - index;
|
||||||
|
}
|
||||||
|
if (block == null)
|
||||||
|
{
|
||||||
|
throw new Exception("end was not after iterator");
|
||||||
|
}
|
||||||
|
length += block.End - index;
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetString(Iterator end)
|
||||||
|
{
|
||||||
|
if (IsDefault || end.IsDefault)
|
||||||
|
{
|
||||||
|
return default(string);
|
||||||
|
}
|
||||||
|
if (end._block == _block)
|
||||||
|
{
|
||||||
|
return Encoding.ASCII.GetString(_block.Array, _index, end._index - _index);
|
||||||
|
}
|
||||||
|
if (end._block == _block.Next && end._index == end._block.Start)
|
||||||
|
{
|
||||||
|
return Encoding.ASCII.GetString(_block.Array, _index, _block.End - _index);
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = GetLength(end);
|
||||||
|
var result = new char[length];
|
||||||
|
var offset = 0;
|
||||||
|
var decoder = Encoding.ASCII.GetDecoder();
|
||||||
|
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
while (length != 0)
|
||||||
|
{
|
||||||
|
if (block == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Unexpected end of data");
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = Math.Min(block.End - index, length);
|
||||||
|
|
||||||
|
int bytesUsed;
|
||||||
|
int textAdded;
|
||||||
|
bool completed;
|
||||||
|
decoder.Convert(
|
||||||
|
block.Array,
|
||||||
|
index,
|
||||||
|
count,
|
||||||
|
result,
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
count == length,
|
||||||
|
out bytesUsed,
|
||||||
|
out textAdded,
|
||||||
|
out completed);
|
||||||
|
|
||||||
|
Debug.Assert(bytesUsed == count);
|
||||||
|
Debug.Assert(textAdded == count);
|
||||||
|
offset += count;
|
||||||
|
length -= count;
|
||||||
|
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
return new string(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArraySegment<byte> GetArraySegment(Iterator end)
|
||||||
|
{
|
||||||
|
if (IsDefault || end.IsDefault)
|
||||||
|
{
|
||||||
|
return default(ArraySegment<byte>);
|
||||||
|
}
|
||||||
|
if (end._block == _block)
|
||||||
|
{
|
||||||
|
return new ArraySegment<byte>(_block.Array, _index, end._index - _index);
|
||||||
|
}
|
||||||
|
if (end._block == _block.Next && end._index == end._block.Start)
|
||||||
|
{
|
||||||
|
return new ArraySegment<byte>(_block.Array, _index, _block.End - _index);
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = GetLength(end);
|
||||||
|
var result = new ArraySegment<byte>(new byte[length]);
|
||||||
|
var offset = result.Offset;
|
||||||
|
|
||||||
|
var block = _block;
|
||||||
|
var index = _index;
|
||||||
|
while (length != 0)
|
||||||
|
{
|
||||||
|
if (block == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Unexpected end of data");
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = Math.Min(block.End - index, length);
|
||||||
|
Buffer.BlockCopy(block.Array, index, result.Array, offset, count);
|
||||||
|
offset += count;
|
||||||
|
length -= count;
|
||||||
|
|
||||||
|
block = block.Next;
|
||||||
|
index = block?.Start ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
{
|
||||||
|
public class MemoryPoolSlab2 : IDisposable
|
||||||
|
{
|
||||||
|
private GCHandle _gcHandle;
|
||||||
|
public byte[] Array;
|
||||||
|
public IntPtr ArrayPtr;
|
||||||
|
public bool IsActive;
|
||||||
|
|
||||||
|
public static MemoryPoolSlab2 Create(int length)
|
||||||
|
{
|
||||||
|
var array = new byte[length];
|
||||||
|
var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
||||||
|
return new MemoryPoolSlab2
|
||||||
|
{
|
||||||
|
Array = array,
|
||||||
|
_gcHandle = gcHandle,
|
||||||
|
ArrayPtr = gcHandle.AddrOfPinnedObject(),
|
||||||
|
IsActive = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// N/A: dispose managed state (managed objects).
|
||||||
|
}
|
||||||
|
|
||||||
|
// free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||||
|
IsActive = false;
|
||||||
|
_gcHandle.Free();
|
||||||
|
|
||||||
|
// set large fields to null.
|
||||||
|
Array = null;
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||||
|
~MemoryPoolSlab2()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This code added to correctly implement the disposable pattern.
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||||
|
Dispose(true);
|
||||||
|
// uncomment the following line if the finalizer is overridden above.
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Framework.Primitives;
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
@ -15,18 +17,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
public bool RequestKeepAlive { get; protected set; }
|
public bool RequestKeepAlive { get; protected set; }
|
||||||
|
|
||||||
public void Intake(int count)
|
|
||||||
{
|
|
||||||
Transfer(count, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IntakeFin(int count)
|
|
||||||
{
|
|
||||||
Transfer(count, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void Consume();
|
|
||||||
|
|
||||||
public static MessageBody For(
|
public static MessageBody For(
|
||||||
string httpVersion,
|
string httpVersion,
|
||||||
IDictionary<string, StringValues> headers,
|
IDictionary<string, StringValues> headers,
|
||||||
|
|
@ -93,48 +83,49 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Consume()
|
public override async Task<int> ReadAsyncImplementation(ArraySegment<byte> buffer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var input = _context.SocketInput;
|
var input = _context.SocketInput;
|
||||||
|
|
||||||
if (input.RemoteIntakeFin)
|
await input;
|
||||||
{
|
|
||||||
IntakeFin(input.Buffer.Count);
|
var begin = input.GetIterator();
|
||||||
}
|
int actual;
|
||||||
else
|
var end = begin.CopyTo(buffer.Array, buffer.Offset, buffer.Count, out actual);
|
||||||
{
|
input.JumpTo(end);
|
||||||
Intake(input.Buffer.Count);
|
return actual;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForContentLength : MessageBody
|
class ForContentLength : MessageBody
|
||||||
{
|
{
|
||||||
private readonly int _contentLength;
|
private readonly int _contentLength;
|
||||||
private int _neededLength;
|
private int _inputLength;
|
||||||
|
|
||||||
public ForContentLength(bool keepAlive, int contentLength, FrameContext context)
|
public ForContentLength(bool keepAlive, int contentLength, FrameContext context)
|
||||||
: base(context)
|
: base(context)
|
||||||
{
|
{
|
||||||
RequestKeepAlive = keepAlive;
|
RequestKeepAlive = keepAlive;
|
||||||
_contentLength = contentLength;
|
_contentLength = contentLength;
|
||||||
_neededLength = _contentLength;
|
_inputLength = _contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Consume()
|
public override async Task<int> ReadAsyncImplementation(ArraySegment<byte> buffer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var input = _context.SocketInput;
|
var input = _context.SocketInput;
|
||||||
var consumeLength = Math.Min(_neededLength, input.Buffer.Count);
|
|
||||||
_neededLength -= consumeLength;
|
|
||||||
|
|
||||||
if (_neededLength != 0)
|
var limit = Math.Min(buffer.Count, _inputLength);
|
||||||
|
if (limit != 0)
|
||||||
{
|
{
|
||||||
Intake(consumeLength);
|
await input;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IntakeFin(consumeLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var begin = input.GetIterator();
|
||||||
|
int actual;
|
||||||
|
var end = begin.CopyTo(buffer.Array, buffer.Offset, limit, out actual);
|
||||||
|
_inputLength -= actual;
|
||||||
|
input.JumpTo(end);
|
||||||
|
return actual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,9 +135,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ForChunkedEncoding : MessageBody
|
class ForChunkedEncoding : MessageBody
|
||||||
{
|
{
|
||||||
private int _neededLength;
|
private int _inputLength;
|
||||||
|
|
||||||
private Mode _mode = Mode.ChunkSizeLine;
|
private Mode _mode = Mode.ChunkPrefix;
|
||||||
|
|
||||||
public ForChunkedEncoding(bool keepAlive, FrameContext context)
|
public ForChunkedEncoding(bool keepAlive, FrameContext context)
|
||||||
: base(context)
|
: base(context)
|
||||||
|
|
@ -154,80 +145,83 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
RequestKeepAlive = keepAlive;
|
RequestKeepAlive = keepAlive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Consume()
|
public override async Task<int> ReadAsyncImplementation(ArraySegment<byte> buffer, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var input = _context.SocketInput;
|
var input = _context.SocketInput;
|
||||||
for (; ;)
|
|
||||||
|
while (_mode != Mode.Complete)
|
||||||
{
|
{
|
||||||
switch (_mode)
|
while (_mode == Mode.ChunkPrefix)
|
||||||
{
|
{
|
||||||
case Mode.ChunkSizeLine:
|
var chunkSize = 0;
|
||||||
var chunkSize = 0;
|
if (!TakeChunkedLine(input, ref chunkSize))
|
||||||
if (!TakeChunkedLine(input, ref chunkSize))
|
{
|
||||||
{
|
await input;
|
||||||
return;
|
}
|
||||||
}
|
else if (chunkSize == 0)
|
||||||
|
{
|
||||||
_neededLength = chunkSize;
|
_mode = Mode.Complete;
|
||||||
if (chunkSize == 0)
|
}
|
||||||
{
|
else
|
||||||
_mode = Mode.Complete;
|
{
|
||||||
IntakeFin(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_mode = Mode.ChunkData;
|
_mode = Mode.ChunkData;
|
||||||
break;
|
}
|
||||||
|
_inputLength = chunkSize;
|
||||||
|
}
|
||||||
|
while (_mode == Mode.ChunkData)
|
||||||
|
{
|
||||||
|
var limit = Math.Min(buffer.Count, _inputLength);
|
||||||
|
if (limit != 0)
|
||||||
|
{
|
||||||
|
await input;
|
||||||
|
}
|
||||||
|
|
||||||
case Mode.ChunkData:
|
var begin = input.GetIterator();
|
||||||
if (_neededLength == 0)
|
int actual;
|
||||||
{
|
var end = begin.CopyTo(buffer.Array, buffer.Offset, limit, out actual);
|
||||||
_mode = Mode.ChunkDataCRLF;
|
_inputLength -= actual;
|
||||||
break;
|
input.JumpTo(end);
|
||||||
}
|
|
||||||
if (input.Buffer.Count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var consumeLength = Math.Min(_neededLength, input.Buffer.Count);
|
if (_inputLength == 0)
|
||||||
_neededLength -= consumeLength;
|
{
|
||||||
|
_mode = Mode.ChunkSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
Intake(consumeLength);
|
return actual;
|
||||||
break;
|
}
|
||||||
|
while (_mode == Mode.ChunkSuffix)
|
||||||
case Mode.ChunkDataCRLF:
|
{
|
||||||
if (input.Buffer.Count < 2)
|
var begin = input.GetIterator();
|
||||||
{
|
var ch1 = begin.Peek();
|
||||||
return;
|
var ch2 = begin.MoveNext();
|
||||||
}
|
if (ch1 == char.MinValue || ch2 == char.MinValue)
|
||||||
var crlf = input.Take(2);
|
{
|
||||||
if (crlf.Array[crlf.Offset] != '\r' ||
|
await input;
|
||||||
crlf.Array[crlf.Offset + 1] != '\n')
|
}
|
||||||
{
|
else if (ch1 == '\r' && ch2 == '\n')
|
||||||
throw new NotImplementedException("INVALID REQUEST FORMAT");
|
{
|
||||||
}
|
input.JumpTo(begin.Add(1));
|
||||||
_mode = Mode.ChunkSizeLine;
|
_mode = Mode.ChunkPrefix;
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
default:
|
{
|
||||||
throw new NotImplementedException("INVALID REQUEST FORMAT");
|
throw new NotImplementedException("INVALID REQUEST FORMAT");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TakeChunkedLine(SocketInput baton, ref int chunkSizeOut)
|
private static bool TakeChunkedLine(SocketInput baton, ref int chunkSizeOut)
|
||||||
{
|
{
|
||||||
var remaining = baton.Buffer;
|
var remaining = baton.GetIterator();
|
||||||
if (remaining.Count < 2)
|
var ch0 = remaining.Peek();
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var ch0 = remaining.Array[remaining.Offset];
|
|
||||||
var chunkSize = 0;
|
var chunkSize = 0;
|
||||||
var mode = 0;
|
var mode = 0;
|
||||||
for (var index = 0; index != remaining.Count - 1; ++index)
|
while(ch0 != -1)
|
||||||
{
|
{
|
||||||
var ch1 = remaining.Array[remaining.Offset + index + 1];
|
var ch1 = remaining.MoveNext();
|
||||||
|
|
||||||
if (mode == 0)
|
if (mode == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -269,7 +263,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
else if (ch0 == '\r' && ch1 == '\n')
|
else if (ch0 == '\r' && ch1 == '\n')
|
||||||
{
|
{
|
||||||
baton.Skip(index + 2);
|
baton.JumpTo(remaining.Add(1));
|
||||||
chunkSizeOut = chunkSize;
|
chunkSizeOut = chunkSize;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +276,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
if (ch0 == '\r' && ch1 == '\n')
|
if (ch0 == '\r' && ch1 == '\n')
|
||||||
{
|
{
|
||||||
baton.Skip(index + 2);
|
baton.JumpTo(remaining.Add(1));
|
||||||
chunkSizeOut = chunkSize;
|
chunkSizeOut = chunkSize;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -299,9 +293,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
|
|
||||||
private enum Mode
|
private enum Mode
|
||||||
{
|
{
|
||||||
ChunkSizeLine,
|
ChunkPrefix,
|
||||||
ChunkData,
|
ChunkData,
|
||||||
ChunkDataCRLF,
|
ChunkSuffix,
|
||||||
Complete,
|
Complete,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,31 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Task<int> result = null;
|
||||||
|
var send100Continue = false;
|
||||||
|
result = ReadAsyncImplementation(buffer, cancellationToken);
|
||||||
|
if (!result.IsCompleted)
|
||||||
|
{
|
||||||
|
lock (_sync)
|
||||||
|
{
|
||||||
|
send100Continue = _send100Continue;
|
||||||
|
_send100Continue = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (send100Continue)
|
||||||
|
{
|
||||||
|
_context.FrameControl.ProduceContinue();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task<int> ReadAsyncImplementation(ArraySegment<byte> buffer, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
static void CompletePending(object state)
|
static void CompletePending(object state)
|
||||||
{
|
{
|
||||||
while (((MessageBodyExchanger)state).CompletePending())
|
while (((MessageBodyExchanger)state).CompletePending())
|
||||||
|
|
|
||||||
|
|
@ -3,25 +3,45 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
public class SocketInput
|
public class SocketInput : ICriticalNotifyCompletion
|
||||||
{
|
{
|
||||||
private readonly IMemoryPool _memory;
|
private static readonly Action _awaitableIsCompleted = () => { };
|
||||||
private GCHandle _gcHandle;
|
private static readonly Action _awaitableIsNotCompleted = () => { };
|
||||||
|
|
||||||
public SocketInput(IMemoryPool memory)
|
private readonly MemoryPool2 _memory;
|
||||||
|
|
||||||
|
private Action _awaitableState;
|
||||||
|
private Exception _awaitableError;
|
||||||
|
|
||||||
|
private MemoryPoolBlock2 _head;
|
||||||
|
private MemoryPoolBlock2 _tail;
|
||||||
|
private MemoryPoolBlock2 _pinned;
|
||||||
|
private readonly object _syncHeadAndTail = new Object();
|
||||||
|
|
||||||
|
public SocketInput(MemoryPool2 memory)
|
||||||
{
|
{
|
||||||
_memory = memory;
|
_memory = memory;
|
||||||
Buffer = new ArraySegment<byte>(_memory.Empty, 0, 0);
|
_awaitableState = _awaitableIsNotCompleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArraySegment<byte> Buffer { get; set; }
|
public ArraySegment<byte> Buffer { get; set; }
|
||||||
|
|
||||||
public bool RemoteIntakeFin { get; set; }
|
public bool RemoteIntakeFin { get; set; }
|
||||||
|
|
||||||
|
public bool IsCompleted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Equals(_awaitableState, _awaitableIsCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Skip(int count)
|
public void Skip(int count)
|
||||||
{
|
{
|
||||||
|
|
@ -35,80 +55,183 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
return taken;
|
return taken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Free()
|
public PinResult Pin(int minimumSize)
|
||||||
{
|
{
|
||||||
if (Buffer.Count == 0 && Buffer.Array.Length != 0)
|
lock (_syncHeadAndTail)
|
||||||
{
|
{
|
||||||
_memory.FreeByte(Buffer.Array);
|
if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End)
|
||||||
Buffer = new ArraySegment<byte>(_memory.Empty, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArraySegment<byte> Available(int minimumSize)
|
|
||||||
{
|
|
||||||
if (Buffer.Count == 0 && Buffer.Offset != 0)
|
|
||||||
{
|
|
||||||
Buffer = new ArraySegment<byte>(Buffer.Array, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
var availableSize = Buffer.Array.Length - Buffer.Offset - Buffer.Count;
|
|
||||||
|
|
||||||
if (availableSize < minimumSize)
|
|
||||||
{
|
|
||||||
if (availableSize + Buffer.Offset >= minimumSize)
|
|
||||||
{
|
{
|
||||||
Array.Copy(Buffer.Array, Buffer.Offset, Buffer.Array, 0, Buffer.Count);
|
_pinned = _tail;
|
||||||
if (Buffer.Count != 0)
|
var data = new ArraySegment<byte>(_pinned.Data.Array, _pinned.End, _pinned.Data.Offset + _pinned.Data.Count - _pinned.End);
|
||||||
|
var dataPtr = _pinned.Pin();
|
||||||
|
return new PinResult
|
||||||
{
|
{
|
||||||
Buffer = new ArraySegment<byte>(Buffer.Array, 0, Buffer.Count);
|
Data = data,
|
||||||
}
|
DataPtr = dataPtr,
|
||||||
availableSize = Buffer.Array.Length - Buffer.Offset - Buffer.Count;
|
};
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var largerSize = Buffer.Array.Length + Math.Max(Buffer.Array.Length, minimumSize);
|
|
||||||
var larger = new ArraySegment<byte>(_memory.AllocByte(largerSize), 0, Buffer.Count);
|
|
||||||
if (Buffer.Count != 0)
|
|
||||||
{
|
|
||||||
Array.Copy(Buffer.Array, Buffer.Offset, larger.Array, 0, Buffer.Count);
|
|
||||||
}
|
|
||||||
_memory.FreeByte(Buffer.Array);
|
|
||||||
Buffer = larger;
|
|
||||||
availableSize = Buffer.Array.Length - Buffer.Offset - Buffer.Count;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new ArraySegment<byte>(Buffer.Array, Buffer.Offset + Buffer.Count, availableSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Extend(int count)
|
_pinned = _memory.Lease(minimumSize);
|
||||||
{
|
return new PinResult
|
||||||
Debug.Assert(count >= 0);
|
{
|
||||||
Debug.Assert(Buffer.Offset >= 0);
|
Data = _pinned.Data,
|
||||||
Debug.Assert(Buffer.Offset <= Buffer.Array.Length);
|
DataPtr = _pinned.Pin()
|
||||||
Debug.Assert(Buffer.Offset + Buffer.Count <= Buffer.Array.Length);
|
};
|
||||||
Debug.Assert(Buffer.Offset + Buffer.Count + count <= Buffer.Array.Length);
|
|
||||||
|
|
||||||
Buffer = new ArraySegment<byte>(Buffer.Array, Buffer.Offset, Buffer.Count + count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr Pin(int minimumSize)
|
|
||||||
{
|
|
||||||
var segment = Available(minimumSize);
|
|
||||||
_gcHandle = GCHandle.Alloc(segment.Array, GCHandleType.Pinned);
|
|
||||||
return _gcHandle.AddrOfPinnedObject() + segment.Offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unpin(int count)
|
public void Unpin(int count)
|
||||||
{
|
{
|
||||||
// read_cb may called without an earlier alloc_cb
|
// Unpin may called without an earlier Pin
|
||||||
// this does not need to be thread-safe
|
if (_pinned != null)
|
||||||
// IsAllocated is checked only because Unpin can be called redundantly
|
|
||||||
if (_gcHandle.IsAllocated)
|
|
||||||
{
|
{
|
||||||
_gcHandle.Free();
|
lock (_syncHeadAndTail)
|
||||||
Extend(count);
|
{
|
||||||
|
_pinned.End += count;
|
||||||
|
if (_head == null)
|
||||||
|
{
|
||||||
|
_head = _tail = _pinned;
|
||||||
|
}
|
||||||
|
else if (_tail == _pinned)
|
||||||
|
{
|
||||||
|
// NO-OP: this was a read into unoccupied tail-space
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tail.Next = _pinned;
|
||||||
|
_tail = _pinned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pinned = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SocketInput GetAwaiter()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
var awaitableState = Interlocked.CompareExchange(
|
||||||
|
ref _awaitableState,
|
||||||
|
continuation,
|
||||||
|
_awaitableIsNotCompleted);
|
||||||
|
|
||||||
|
if (awaitableState == _awaitableIsNotCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (awaitableState == _awaitableIsCompleted)
|
||||||
|
{
|
||||||
|
Task.Run(continuation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// THIS IS AN ERROR STATE - ONLY ONE WAITER CAN WAIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
OnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCompleted(Exception error)
|
||||||
|
{
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
_awaitableError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var awaitableState = Interlocked.Exchange(
|
||||||
|
ref _awaitableState,
|
||||||
|
_awaitableIsCompleted);
|
||||||
|
|
||||||
|
if (awaitableState != _awaitableIsCompleted &&
|
||||||
|
awaitableState != _awaitableIsNotCompleted)
|
||||||
|
{
|
||||||
|
Task.Run(awaitableState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetNotCompleted()
|
||||||
|
{
|
||||||
|
if (RemoteIntakeFin || _awaitableError != null)
|
||||||
|
{
|
||||||
|
// TODO: Race condition - setting either of these can leave awaitable not completed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var awaitableState = Interlocked.CompareExchange(
|
||||||
|
ref _awaitableState,
|
||||||
|
_awaitableIsNotCompleted,
|
||||||
|
_awaitableIsCompleted);
|
||||||
|
|
||||||
|
if (awaitableState == _awaitableIsNotCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (awaitableState == _awaitableIsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// THIS IS AN ERROR STATE - ONLY ONE WAITER MAY EXIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
var error = _awaitableError;
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
throw new AggregateException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryPoolBlock2.Iterator GetIterator()
|
||||||
|
{
|
||||||
|
lock (_syncHeadAndTail)
|
||||||
|
{
|
||||||
|
return new MemoryPoolBlock2.Iterator(_head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JumpTo(MemoryPoolBlock2.Iterator iterator)
|
||||||
|
{
|
||||||
|
MemoryPoolBlock2 returnStart;
|
||||||
|
MemoryPoolBlock2 returnEnd;
|
||||||
|
lock (_syncHeadAndTail)
|
||||||
|
{
|
||||||
|
// TODO: leave _pinned intact
|
||||||
|
|
||||||
|
returnStart = _head;
|
||||||
|
returnEnd = iterator.Block;
|
||||||
|
_head = iterator.Block;
|
||||||
|
if (_head == null)
|
||||||
|
{
|
||||||
|
_tail = null;
|
||||||
|
SetNotCompleted();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_head.Start = iterator.Index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (returnStart != returnEnd)
|
||||||
|
{
|
||||||
|
var returnBlock = returnStart;
|
||||||
|
returnStart = returnStart.Next;
|
||||||
|
returnBlock.Pool.Return(returnBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PinResult
|
||||||
|
{
|
||||||
|
public ArraySegment<byte> Data;
|
||||||
|
public IntPtr DataPtr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
private GCHandle _listenVitality;
|
private GCHandle _listenVitality;
|
||||||
|
|
||||||
public Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback;
|
public Func<UvStreamHandle, int, object, Libuv.uv_buf_t> _allocCallback;
|
||||||
public Action<UvStreamHandle, int, Exception, object> _readCallback;
|
public Action<UvStreamHandle, int, int, Exception, object> _readCallback;
|
||||||
public object _readState;
|
public object _readState;
|
||||||
private GCHandle _readVitality;
|
private GCHandle _readVitality;
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
|
|
||||||
public void ReadStart(
|
public void ReadStart(
|
||||||
Func<UvStreamHandle, int, object, Libuv.uv_buf_t> allocCallback,
|
Func<UvStreamHandle, int, object, Libuv.uv_buf_t> allocCallback,
|
||||||
Action<UvStreamHandle, int, Exception, object> readCallback,
|
Action<UvStreamHandle, int, int, Exception, object> readCallback,
|
||||||
object state)
|
object state)
|
||||||
{
|
{
|
||||||
if (_readVitality.IsAllocated)
|
if (_readVitality.IsAllocated)
|
||||||
|
|
@ -163,11 +163,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking
|
||||||
{
|
{
|
||||||
Exception error;
|
Exception error;
|
||||||
stream._uv.Check(nread, out error);
|
stream._uv.Check(nread, out error);
|
||||||
stream._readCallback(stream, 0, error, stream._readState);
|
stream._readCallback(stream, 0, nread, error, stream._readState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stream._readCallback(stream, nread, null, stream._readState);
|
stream._readCallback(stream, nread, 0, null, stream._readState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
"Microsoft.AspNet.Hosting": "1.0.0-*",
|
"Microsoft.AspNet.Hosting": "1.0.0-*",
|
||||||
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*",
|
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*",
|
||||||
"Microsoft.Framework.Logging.Abstractions": "1.0.0-*",
|
"Microsoft.Framework.Logging.Abstractions": "1.0.0-*",
|
||||||
|
"System.Numerics.Vectors": "4.1.1-beta-*",
|
||||||
"Microsoft.StandardsPolice": {
|
"Microsoft.StandardsPolice": {
|
||||||
"version": "1.0.0-*",
|
"version": "1.0.0-*",
|
||||||
"type": "build"
|
"type": "build"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InitialDictionaryContainsServerAndDate()
|
public void InitialDictionaryContainsServerAndDate()
|
||||||
{
|
{
|
||||||
IDictionary<string, StringValues> headers = new FrameResponseHeaders();
|
var frame = new Frame(new ConnectionContext());
|
||||||
|
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
|
||||||
|
|
||||||
Assert.Equal(2, headers.Count);
|
Assert.Equal(2, headers.Count);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
using Microsoft.AspNet.Server.Kestrel.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
{
|
||||||
|
public class MemoryPoolBlock2Tests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void IndexOfAnyWorks()
|
||||||
|
{
|
||||||
|
using (var pool = new MemoryPool2())
|
||||||
|
{
|
||||||
|
var block = pool.Lease(256);
|
||||||
|
foreach (var ch in Enumerable.Range(0, 256).Select(x => (byte)x))
|
||||||
|
{
|
||||||
|
block.Array[block.End++] = ch;
|
||||||
|
}
|
||||||
|
var iterator = block.GetIterator();
|
||||||
|
foreach (var ch in Enumerable.Range(0, 256).Select(x => (char)x))
|
||||||
|
{
|
||||||
|
var hit = iterator.IndexOf(ch);
|
||||||
|
Assert.Equal(ch, iterator.GetLength(hit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetLengthBetweenIteratorsWorks()
|
||||||
|
{
|
||||||
|
using (var pool = new MemoryPool2())
|
||||||
|
{
|
||||||
|
var block = pool.Lease(256);
|
||||||
|
block.End += 256;
|
||||||
|
TestAllLengths(block, 256);
|
||||||
|
pool.Return(block);
|
||||||
|
block = null;
|
||||||
|
|
||||||
|
for (var fragment = 0; fragment != 256; fragment += 4)
|
||||||
|
{
|
||||||
|
var next = block;
|
||||||
|
block = pool.Lease(4);
|
||||||
|
block.Next = next;
|
||||||
|
block.End += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestAllLengths(block, 256);
|
||||||
|
|
||||||
|
while(block != null)
|
||||||
|
{
|
||||||
|
var next = block.Next;
|
||||||
|
pool.Return(block);
|
||||||
|
block = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestAllLengths(MemoryPoolBlock2 block, int lengths)
|
||||||
|
{
|
||||||
|
for (var firstIndex = 0; firstIndex <= lengths; ++firstIndex)
|
||||||
|
{
|
||||||
|
for (var lastIndex = firstIndex; lastIndex <= lengths; ++lastIndex)
|
||||||
|
{
|
||||||
|
var first = block.GetIterator().Add(firstIndex);
|
||||||
|
var last = block.GetIterator().Add(lastIndex);
|
||||||
|
Assert.Equal(firstIndex, block.GetIterator().GetLength(first));
|
||||||
|
Assert.Equal(lastIndex, block.GetIterator().GetLength(last));
|
||||||
|
Assert.Equal(lastIndex - firstIndex, first.GetLength(last));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var testInput = new TestInput();
|
var testInput = new TestInput();
|
||||||
var context = new ConnectionContext();
|
var context = new ConnectionContext();
|
||||||
context.SocketInput = new SocketInput(new MemoryPool());
|
context.SocketInput = new SocketInput(new MemoryPool2());
|
||||||
|
|
||||||
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var testInput = new TestInput();
|
var testInput = new TestInput();
|
||||||
var context = new ConnectionContext();
|
var context = new ConnectionContext();
|
||||||
context.SocketInput = new SocketInput(new MemoryPool());
|
context.SocketInput = new SocketInput(new MemoryPool2());
|
||||||
|
|
||||||
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var testInput = new TestInput();
|
var testInput = new TestInput();
|
||||||
var context = new ConnectionContext();
|
var context = new ConnectionContext();
|
||||||
context.SocketInput = new SocketInput(new MemoryPool());
|
context.SocketInput = new SocketInput(new MemoryPool2());
|
||||||
|
|
||||||
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
||||||
|
|
||||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var testInput = new TestInput();
|
var testInput = new TestInput();
|
||||||
var context = new ConnectionContext();
|
var context = new ConnectionContext();
|
||||||
context.SocketInput = new SocketInput(new MemoryPool());
|
context.SocketInput = new SocketInput(new MemoryPool2());
|
||||||
|
|
||||||
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
||||||
|
|
||||||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
|
||||||
var testInput = new TestInput();
|
var testInput = new TestInput();
|
||||||
var context = new ConnectionContext();
|
var context = new ConnectionContext();
|
||||||
context.SocketInput = new SocketInput(new MemoryPool());
|
context.SocketInput = new SocketInput(new MemoryPool2());
|
||||||
|
|
||||||
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
var exchanger = new MessageBodyExchanger(testInput.FrameContext);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
var stream = new FrameRequestStream(body);
|
var stream = new FrameRequestStream(body);
|
||||||
|
|
||||||
input.Add("Hello", true);
|
input.Add("Hello", true);
|
||||||
body.Consume();
|
|
||||||
|
|
||||||
var buffer1 = new byte[1024];
|
var buffer1 = new byte[1024];
|
||||||
var count1 = stream.Read(buffer1, 0, 1024);
|
var count1 = stream.Read(buffer1, 0, 1024);
|
||||||
|
|
@ -43,7 +42,6 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
var stream = new FrameRequestStream(body);
|
var stream = new FrameRequestStream(body);
|
||||||
|
|
||||||
input.Add("Hello", true);
|
input.Add("Hello", true);
|
||||||
body.Consume();
|
|
||||||
|
|
||||||
var buffer1 = new byte[1024];
|
var buffer1 = new byte[1024];
|
||||||
var count1 = await stream.ReadAsync(buffer1, 0, 1024);
|
var count1 = await stream.ReadAsync(buffer1, 0, 1024);
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
connect.Dispose();
|
connect.Dispose();
|
||||||
clientConnectionPipe.ReadStart(
|
clientConnectionPipe.ReadStart(
|
||||||
(_3, cb, _4) => buf,
|
(_3, cb, _4) => buf,
|
||||||
(_3, status2, error2, _4) =>
|
(_3, status2, errCode, error2, _4) =>
|
||||||
{
|
{
|
||||||
if (status2 == 0)
|
if (status2 == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -211,7 +211,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
|
|
||||||
clientConnectionPipe.ReadStart(
|
clientConnectionPipe.ReadStart(
|
||||||
(_3, cb, _4) => buf,
|
(_3, cb, _4) => buf,
|
||||||
(_3, status2, error2, _4) =>
|
(_3, status2, errCode2, error2, _4) =>
|
||||||
{
|
{
|
||||||
if (status2 == 0)
|
if (status2 == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +224,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
var buf2 = loop2.Libuv.buf_init(Marshal.AllocHGlobal(64), 64);
|
var buf2 = loop2.Libuv.buf_init(Marshal.AllocHGlobal(64), 64);
|
||||||
clientConnectionTcp.ReadStart(
|
clientConnectionTcp.ReadStart(
|
||||||
(_5, cb, _6) => buf2,
|
(_5, cb, _6) => buf2,
|
||||||
(_5, status3, error3, _6) =>
|
(_5, status3, errCode3, error3, _6) =>
|
||||||
{
|
{
|
||||||
if (status3 == 0)
|
if (status3 == 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
var data = Marshal.AllocCoTaskMem(500);
|
var data = Marshal.AllocCoTaskMem(500);
|
||||||
tcp2.ReadStart(
|
tcp2.ReadStart(
|
||||||
(a, b, c) => _uv.buf_init(data, 500),
|
(a, b, c) => _uv.buf_init(data, 500),
|
||||||
(__, nread, error2, state2) =>
|
(__, nread, errCode, error2, state2) =>
|
||||||
{
|
{
|
||||||
bytesRead += nread;
|
bytesRead += nread;
|
||||||
if (nread == 0)
|
if (nread == 0)
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
var data = Marshal.AllocCoTaskMem(500);
|
var data = Marshal.AllocCoTaskMem(500);
|
||||||
tcp2.ReadStart(
|
tcp2.ReadStart(
|
||||||
(a, b, c) => tcp2.Libuv.buf_init(data, 500),
|
(a, b, c) => tcp2.Libuv.buf_init(data, 500),
|
||||||
(__, nread, error2, state2) =>
|
(__, nread, errCode, error2, state2) =>
|
||||||
{
|
{
|
||||||
bytesRead += nread;
|
bytesRead += nread;
|
||||||
if (nread == 0)
|
if (nread == 0)
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
public TestInput()
|
public TestInput()
|
||||||
{
|
{
|
||||||
var memory = new MemoryPool();
|
var memory = new MemoryPool();
|
||||||
|
var memory2 = new MemoryPool2();
|
||||||
FrameContext = new FrameContext
|
FrameContext = new FrameContext
|
||||||
{
|
{
|
||||||
SocketInput = new SocketInput(memory),
|
SocketInput = new SocketInput(memory2),
|
||||||
Memory = memory,
|
Memory = memory,
|
||||||
ConnectionControl = this,
|
ConnectionControl = this,
|
||||||
FrameControl = this
|
FrameControl = this
|
||||||
|
|
@ -28,9 +29,10 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
||||||
{
|
{
|
||||||
var encoding = System.Text.Encoding.ASCII;
|
var encoding = System.Text.Encoding.ASCII;
|
||||||
var count = encoding.GetByteCount(text);
|
var count = encoding.GetByteCount(text);
|
||||||
var buffer = FrameContext.SocketInput.Available(text.Length);
|
var buffer = FrameContext.SocketInput.Pin(text.Length);
|
||||||
count = encoding.GetBytes(text, 0, text.Length, buffer.Array, buffer.Offset);
|
count = encoding.GetBytes(text, 0, text.Length, buffer.Data.Array, buffer.Data.Offset);
|
||||||
FrameContext.SocketInput.Extend(count);
|
FrameContext.SocketInput.Unpin(count);
|
||||||
|
FrameContext.SocketInput.SetCompleted(null);
|
||||||
if (fin)
|
if (fin)
|
||||||
{
|
{
|
||||||
FrameContext.SocketInput.RemoteIntakeFin = true;
|
FrameContext.SocketInput.RemoteIntakeFin = true;
|
||||||
|
|
|
||||||
|
|
@ -171,18 +171,6 @@ using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Server.Kestrel.Http
|
namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
public partial class FrameResponseHeaders
|
|
||||||
{{
|
|
||||||
public FrameResponseHeaders()
|
|
||||||
{{
|
|
||||||
_Server = ""Kestrel"";
|
|
||||||
_Date = DateTime.UtcNow.ToString(""r"");
|
|
||||||
_bits = {
|
|
||||||
1L << responseHeaders.First(header => header.Name == "Server").Index |
|
|
||||||
1L << responseHeaders.First(header => header.Name == "Date").Index
|
|
||||||
}L;
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
{Each(loops, loop => $@"
|
{Each(loops, loop => $@"
|
||||||
public partial class {loop.ClassName}
|
public partial class {loop.ClassName}
|
||||||
{{
|
{{
|
||||||
|
|
@ -190,6 +178,28 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
{Each(loop.Headers, header => @"
|
{Each(loop.Headers, header => @"
|
||||||
private StringValues _" + header.Identifier + ";")}
|
private StringValues _" + header.Identifier + ";")}
|
||||||
|
|
||||||
|
{Each(loop.Headers, header => $@"
|
||||||
|
public StringValues Header{header.Identifier}
|
||||||
|
{{
|
||||||
|
get
|
||||||
|
{{
|
||||||
|
if ({header.TestBit()})
|
||||||
|
{{
|
||||||
|
return _{header.Identifier};
|
||||||
|
}}
|
||||||
|
else
|
||||||
|
{{
|
||||||
|
return StringValues.Empty;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
set
|
||||||
|
{{
|
||||||
|
{header.SetBit()};
|
||||||
|
_{header.Identifier} = value;
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
")}
|
||||||
|
|
||||||
protected override int GetCountFast()
|
protected override int GetCountFast()
|
||||||
{{
|
{{
|
||||||
var count = MaybeUnknown?.Count ?? 0;
|
var count = MaybeUnknown?.Count ?? 0;
|
||||||
|
|
@ -322,6 +332,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
||||||
protected override void ClearFast()
|
protected override void ClearFast()
|
||||||
{{
|
{{
|
||||||
_bits = 0;
|
_bits = 0;
|
||||||
|
{Each(loop.Headers, header => $@"
|
||||||
|
_{header.Identifier} = StringValues.Empty;")}
|
||||||
MaybeUnknown?.Clear();
|
MaybeUnknown?.Clear();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue