Merge branch 'benaadams/pool-headers' into dev
This commit is contained in:
commit
87bd60746b
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't initialize _frame until SocketInput and SocketOutput are set to their final values.
|
// Don't initialize _frame until SocketInput and SocketOutput are set to their final values.
|
||||||
if (ConnectionFilter == null)
|
if (ServerInformation.ConnectionFilter == null)
|
||||||
{
|
{
|
||||||
SocketInput = _rawSocketInput;
|
SocketInput = _rawSocketInput;
|
||||||
SocketOutput = _rawSocketOutput;
|
SocketOutput = _rawSocketOutput;
|
||||||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) =>
|
ServerInformation.ConnectionFilter.OnConnectionAsync(_filterContext).ContinueWith((task, state) =>
|
||||||
{
|
{
|
||||||
var connection = (Connection)state;
|
var connection = (Connection)state;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
private static readonly byte[] _bytesHttpVersion1_1 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
|
private static readonly byte[] _bytesHttpVersion1_1 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
|
||||||
private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0");
|
private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0");
|
||||||
private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" ");
|
private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" ");
|
||||||
private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
|
|
||||||
private static readonly byte[] _bytesDate = Encoding.ASCII.GetBytes("Date: ");
|
|
||||||
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
|
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
|
||||||
|
|
||||||
private static Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r');
|
private static Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r');
|
||||||
|
|
@ -46,8 +44,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
private readonly object _onStartingSync = new Object();
|
private readonly object _onStartingSync = new Object();
|
||||||
private readonly object _onCompletedSync = new Object();
|
private readonly object _onCompletedSync = new Object();
|
||||||
protected readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
|
|
||||||
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
|
protected bool _poolingPermitted = true;
|
||||||
|
private Headers _frameHeaders;
|
||||||
|
private Streams _frameStreams;
|
||||||
|
|
||||||
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
|
|
||||||
|
|
@ -60,10 +60,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
protected CancellationTokenSource _abortedCts;
|
protected CancellationTokenSource _abortedCts;
|
||||||
protected CancellationToken? _manuallySetRequestAbortToken;
|
protected CancellationToken? _manuallySetRequestAbortToken;
|
||||||
|
|
||||||
internal FrameRequestStream _requestBody;
|
|
||||||
internal FrameResponseStream _responseBody;
|
|
||||||
internal FrameDuplexStream _duplexStream;
|
|
||||||
|
|
||||||
protected bool _responseStarted;
|
protected bool _responseStarted;
|
||||||
protected bool _keepAlive;
|
protected bool _keepAlive;
|
||||||
private bool _autoChunk;
|
private bool _autoChunk;
|
||||||
|
|
@ -92,12 +88,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
_localEndPoint = localEndPoint;
|
_localEndPoint = localEndPoint;
|
||||||
_prepareRequest = prepareRequest;
|
_prepareRequest = prepareRequest;
|
||||||
_pathBase = context.ServerAddress.PathBase;
|
_pathBase = context.ServerAddress.PathBase;
|
||||||
if (ReuseStreams)
|
|
||||||
{
|
|
||||||
_requestBody = new FrameRequestStream();
|
|
||||||
_responseBody = new FrameResponseStream(this);
|
|
||||||
_duplexStream = new FrameDuplexStream(_requestBody, _responseBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameControl = this;
|
FrameControl = this;
|
||||||
Reset();
|
Reset();
|
||||||
|
|
@ -197,8 +187,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
get { return _responseStarted; }
|
get { return _responseStarted; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected FrameRequestHeaders FrameRequestHeaders => _frameHeaders.RequestHeaders;
|
||||||
|
|
||||||
|
public Frame InitializeHeaders()
|
||||||
|
{
|
||||||
|
_frameHeaders = HttpComponentFactory.CreateHeaders(DateHeaderValueManager);
|
||||||
|
RequestHeaders = _frameHeaders.RequestHeaders;
|
||||||
|
ResponseHeaders = _frameHeaders.ResponseHeaders;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void InitializeStreams(MessageBody messageBody)
|
||||||
|
{
|
||||||
|
_frameStreams = HttpComponentFactory.CreateStreams(this);
|
||||||
|
|
||||||
|
RequestBody = _frameStreams.RequestBody.StartAcceptingReads(messageBody);
|
||||||
|
ResponseBody = _frameStreams.ResponseBody.StartAcceptingWrites();
|
||||||
|
DuplexStream = _frameStreams.DuplexStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PauseStreams()
|
||||||
|
{
|
||||||
|
_frameStreams.RequestBody.PauseAcceptingReads();
|
||||||
|
_frameStreams.ResponseBody.PauseAcceptingWrites();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResumeStreams()
|
||||||
|
{
|
||||||
|
_frameStreams.RequestBody.ResumeAcceptingReads();
|
||||||
|
_frameStreams.ResponseBody.ResumeAcceptingWrites();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopStreams()
|
||||||
|
{
|
||||||
|
_frameStreams.RequestBody.StopAcceptingReads();
|
||||||
|
_frameStreams.ResponseBody.StopAcceptingWrites();
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
ResetComponents(poolingPermitted: true);
|
||||||
|
|
||||||
_onStarting = null;
|
_onStarting = null;
|
||||||
_onCompleted = null;
|
_onCompleted = null;
|
||||||
|
|
||||||
|
|
@ -207,8 +237,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
_autoChunk = false;
|
_autoChunk = false;
|
||||||
_applicationException = null;
|
_applicationException = null;
|
||||||
|
|
||||||
_requestHeaders.Reset();
|
|
||||||
ResetResponseHeaders();
|
|
||||||
ResetFeatureCollection();
|
ResetFeatureCollection();
|
||||||
|
|
||||||
Scheme = null;
|
Scheme = null;
|
||||||
|
|
@ -218,13 +246,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
Path = null;
|
Path = null;
|
||||||
QueryString = null;
|
QueryString = null;
|
||||||
_httpVersion = HttpVersionType.Unknown;
|
_httpVersion = HttpVersionType.Unknown;
|
||||||
RequestHeaders = _requestHeaders;
|
|
||||||
RequestBody = null;
|
|
||||||
StatusCode = 200;
|
StatusCode = 200;
|
||||||
ReasonPhrase = null;
|
ReasonPhrase = null;
|
||||||
ResponseHeaders = _responseHeaders;
|
|
||||||
ResponseBody = null;
|
|
||||||
DuplexStream = null;
|
|
||||||
|
|
||||||
var httpConnectionFeature = this as IHttpConnectionFeature;
|
var httpConnectionFeature = this as IHttpConnectionFeature;
|
||||||
httpConnectionFeature.RemoteIpAddress = _remoteEndPoint?.Address;
|
httpConnectionFeature.RemoteIpAddress = _remoteEndPoint?.Address;
|
||||||
|
|
@ -239,15 +262,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
_abortedCts = null;
|
_abortedCts = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetResponseHeaders()
|
protected void ResetComponents(bool poolingPermitted)
|
||||||
{
|
{
|
||||||
_responseHeaders.Reset();
|
if (_frameHeaders != null)
|
||||||
_responseHeaders.SetRawDate(
|
{
|
||||||
DateHeaderValueManager.GetDateHeaderValue(),
|
var frameHeaders = _frameHeaders;
|
||||||
DateHeaderValueManager.GetDateHeaderValueBytes());
|
_frameHeaders = null;
|
||||||
_responseHeaders.SetRawServer(
|
|
||||||
"Kestrel",
|
RequestHeaders = null;
|
||||||
_bytesServer);
|
ResponseHeaders = null;
|
||||||
|
HttpComponentFactory.DisposeHeaders(frameHeaders, poolingPermitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_frameStreams != null)
|
||||||
|
{
|
||||||
|
var frameStreams = _frameStreams;
|
||||||
|
_frameStreams = null;
|
||||||
|
|
||||||
|
RequestBody = null;
|
||||||
|
ResponseBody = null;
|
||||||
|
DuplexStream = null;
|
||||||
|
HttpComponentFactory.DisposeStreams(frameStreams, poolingPermitted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -292,8 +328,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
_requestProcessingStopping = true;
|
_requestProcessingStopping = true;
|
||||||
|
|
||||||
_requestBody?.Abort();
|
_frameStreams?.RequestBody.Abort();
|
||||||
_responseBody?.Abort();
|
_frameStreams?.ResponseBody.Abort();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -560,8 +596,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
StatusCode = 500;
|
StatusCode = 500;
|
||||||
ReasonPhrase = null;
|
ReasonPhrase = null;
|
||||||
|
|
||||||
ResetResponseHeaders();
|
var responseHeaders = _frameHeaders.ResponseHeaders;
|
||||||
_responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
responseHeaders.Reset();
|
||||||
|
responseHeaders.SetRawDate(
|
||||||
|
DateHeaderValueManager.GetDateHeaderValue(),
|
||||||
|
DateHeaderValueManager.GetDateHeaderValueBytes());
|
||||||
|
responseHeaders.SetRawServer(
|
||||||
|
"Kestrel",
|
||||||
|
Headers.BytesServer);
|
||||||
|
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
||||||
|
|
||||||
|
ResponseHeaders = responseHeaders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,10 +659,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
byte[] statusBytes,
|
byte[] statusBytes,
|
||||||
bool appCompleted)
|
bool appCompleted)
|
||||||
{
|
{
|
||||||
|
var responseHeaders = _frameHeaders.ResponseHeaders;
|
||||||
|
responseHeaders.SetReadOnly();
|
||||||
|
|
||||||
var end = SocketOutput.ProducingStart();
|
var end = SocketOutput.ProducingStart();
|
||||||
if (_keepAlive)
|
if (_keepAlive)
|
||||||
{
|
{
|
||||||
foreach (var connectionValue in _responseHeaders.HeaderConnection)
|
foreach (var connectionValue in responseHeaders.HeaderConnection)
|
||||||
{
|
{
|
||||||
if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
|
if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
{
|
{
|
||||||
|
|
@ -626,7 +674,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength)
|
if (_keepAlive && !responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
|
||||||
{
|
{
|
||||||
if (appCompleted)
|
if (appCompleted)
|
||||||
{
|
{
|
||||||
|
|
@ -636,7 +684,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
// Since the app has completed and we are only now generating
|
// Since the app has completed and we are only now generating
|
||||||
// the headers we can safely set the Content-Length to 0.
|
// the headers we can safely set the Content-Length to 0.
|
||||||
_responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -644,7 +692,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
if (_httpVersion == HttpVersionType.Http1_1)
|
if (_httpVersion == HttpVersionType.Http1_1)
|
||||||
{
|
{
|
||||||
_autoChunk = true;
|
_autoChunk = true;
|
||||||
_responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked);
|
responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -653,18 +701,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_keepAlive == false && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_1)
|
if (_keepAlive == false && responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_1)
|
||||||
{
|
{
|
||||||
_responseHeaders.SetRawConnection("close", _bytesConnectionClose);
|
responseHeaders.SetRawConnection("close", _bytesConnectionClose);
|
||||||
}
|
}
|
||||||
else if (_keepAlive && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_0)
|
else if (_keepAlive && responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_0)
|
||||||
{
|
{
|
||||||
_responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive);
|
responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive);
|
||||||
}
|
}
|
||||||
|
|
||||||
end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0);
|
end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0);
|
||||||
end.CopyFrom(statusBytes);
|
end.CopyFrom(statusBytes);
|
||||||
_responseHeaders.CopyTo(ref end);
|
responseHeaders.CopyTo(ref end);
|
||||||
end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length);
|
end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length);
|
||||||
|
|
||||||
SocketOutput.ProducingComplete(end);
|
SocketOutput.ProducingComplete(end);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
public abstract class FrameHeaders : IHeaderDictionary
|
public abstract class FrameHeaders : IHeaderDictionary
|
||||||
{
|
{
|
||||||
|
protected bool _isReadOnly;
|
||||||
protected Dictionary<string, StringValues> MaybeUnknown;
|
protected Dictionary<string, StringValues> MaybeUnknown;
|
||||||
|
|
||||||
protected Dictionary<string, StringValues> Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase));
|
protected Dictionary<string, StringValues> Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase));
|
||||||
|
|
@ -26,6 +27,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
SetValueFast(key, value);
|
SetValueFast(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -38,20 +43,50 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
SetValueFast(key, value);
|
SetValueFast(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void ThrowReadOnlyException()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Headers are readonly, reponse has already started.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ThrowArgumentException()
|
||||||
|
{
|
||||||
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ThrowKeyNotFoundException()
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ThrowDuplicateKeyException()
|
||||||
|
{
|
||||||
|
throw new ArgumentException("An item with the same key has already been added.");
|
||||||
|
}
|
||||||
|
|
||||||
int ICollection<KeyValuePair<string, StringValues>>.Count => GetCountFast();
|
int ICollection<KeyValuePair<string, StringValues>>.Count => GetCountFast();
|
||||||
|
|
||||||
bool ICollection<KeyValuePair<string, StringValues>>.IsReadOnly => false;
|
bool ICollection<KeyValuePair<string, StringValues>>.IsReadOnly => _isReadOnly;
|
||||||
|
|
||||||
ICollection<string> IDictionary<string, StringValues>.Keys => ((IDictionary<string, StringValues>)this).Select(pair => pair.Key).ToList();
|
ICollection<string> IDictionary<string, StringValues>.Keys => ((IDictionary<string, StringValues>)this).Select(pair => pair.Key).ToList();
|
||||||
|
|
||||||
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 SetReadOnly()
|
||||||
|
{
|
||||||
|
_isReadOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
_isReadOnly = false;
|
||||||
ClearFast();
|
ClearFast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,16 +140,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item)
|
void ICollection<KeyValuePair<string, StringValues>>.Add(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
AddValueFast(item.Key, item.Value);
|
AddValueFast(item.Key, item.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDictionary<string, StringValues>.Add(string key, StringValues value)
|
void IDictionary<string, StringValues>.Add(string key, StringValues value)
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
AddValueFast(key, value);
|
AddValueFast(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICollection<KeyValuePair<string, StringValues>>.Clear()
|
void ICollection<KeyValuePair<string, StringValues>>.Clear()
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
ClearFast();
|
ClearFast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +205,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
bool IDictionary<string, StringValues>.Remove(string key)
|
bool IDictionary<string, StringValues>.Remove(string key)
|
||||||
{
|
{
|
||||||
|
if (_isReadOnly)
|
||||||
|
{
|
||||||
|
ThrowReadOnlyException();
|
||||||
|
}
|
||||||
return RemoveFast(key);
|
return RemoveFast(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
await SocketInput;
|
await SocketInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders))
|
InitializeHeaders();
|
||||||
|
|
||||||
|
while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, FrameRequestHeaders))
|
||||||
{
|
{
|
||||||
if (SocketInput.RemoteIntakeFin)
|
if (SocketInput.RemoteIntakeFin)
|
||||||
{
|
{
|
||||||
|
|
@ -63,20 +65,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
|
|
||||||
if (!_requestProcessingStopping)
|
if (!_requestProcessingStopping)
|
||||||
{
|
{
|
||||||
var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this);
|
var messageBody = MessageBody.For(HttpVersion, FrameRequestHeaders, this);
|
||||||
_keepAlive = messageBody.RequestKeepAlive;
|
_keepAlive = messageBody.RequestKeepAlive;
|
||||||
|
|
||||||
// _duplexStream may be null if flag switched while running
|
InitializeStreams(messageBody);
|
||||||
if (!ReuseStreams || _duplexStream == null)
|
|
||||||
{
|
|
||||||
_requestBody = new FrameRequestStream();
|
|
||||||
_responseBody = new FrameResponseStream(this);
|
|
||||||
_duplexStream = new FrameDuplexStream(_requestBody, _responseBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestBody = _requestBody.StartAcceptingReads(messageBody);
|
|
||||||
ResponseBody = _responseBody.StartAcceptingWrites();
|
|
||||||
DuplexStream = _duplexStream;
|
|
||||||
|
|
||||||
_abortedCts = null;
|
_abortedCts = null;
|
||||||
_manuallySetRequestAbortToken = null;
|
_manuallySetRequestAbortToken = null;
|
||||||
|
|
@ -101,8 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
await FireOnStarting();
|
await FireOnStarting();
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestBody.PauseAcceptingReads();
|
PauseStreams();
|
||||||
_responseBody.PauseAcceptingWrites();
|
|
||||||
|
|
||||||
if (_onCompleted != null)
|
if (_onCompleted != null)
|
||||||
{
|
{
|
||||||
|
|
@ -114,23 +105,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
// If _requestAbort is set, the connection has already been closed.
|
// If _requestAbort is set, the connection has already been closed.
|
||||||
if (Volatile.Read(ref _requestAborted) == 0)
|
if (Volatile.Read(ref _requestAborted) == 0)
|
||||||
{
|
{
|
||||||
_responseBody.ResumeAcceptingWrites();
|
ResumeStreams();
|
||||||
|
|
||||||
await ProduceEnd();
|
await ProduceEnd();
|
||||||
|
|
||||||
if (_keepAlive)
|
if (_keepAlive)
|
||||||
{
|
{
|
||||||
_requestBody.ResumeAcceptingReads();
|
|
||||||
// Finish reading the request body in case the app did not.
|
// Finish reading the request body in case the app did not.
|
||||||
await messageBody.Consume();
|
await messageBody.Consume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestBody.StopAcceptingReads();
|
StopStreams();
|
||||||
_responseBody.StopAcceptingWrites();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_keepAlive)
|
if (!_keepAlive)
|
||||||
{
|
{
|
||||||
|
ResetComponents(poolingPermitted: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,12 +131,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
// Error occurred, do not return components to pool
|
||||||
|
_poolingPermitted = false;
|
||||||
Log.LogWarning(0, ex, "Connection processing ended abnormally");
|
Log.LogWarning(0, ex, "Connection processing ended abnormally");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
ResetComponents(poolingPermitted: _poolingPermitted);
|
||||||
_abortedCts = null;
|
_abortedCts = null;
|
||||||
|
|
||||||
// If _requestAborted is set, the connection has already been closed.
|
// If _requestAborted is set, the connection has already been closed.
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
class FrameResponseStream : Stream
|
class FrameResponseStream : Stream
|
||||||
{
|
{
|
||||||
private readonly FrameContext _context;
|
private FrameContext _context;
|
||||||
private FrameStreamState _state;
|
private FrameStreamState _state;
|
||||||
|
|
||||||
public FrameResponseStream(FrameContext context)
|
public FrameResponseStream()
|
||||||
{
|
{
|
||||||
_context = context;
|
|
||||||
_state = FrameStreamState.Closed;
|
_state = FrameStreamState.Closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +124,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Initialize(FrameContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Uninitialize()
|
||||||
|
{
|
||||||
|
_context = null;
|
||||||
|
_state = FrameStreamState.Closed;
|
||||||
|
}
|
||||||
|
|
||||||
private Task ValidateState(CancellationToken cancellationToken)
|
private Task ValidateState(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
switch (_state)
|
switch (_state)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
var socket = new UvTcpHandle(Log);
|
var socket = new UvTcpHandle(Log);
|
||||||
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
socket.NoDelay(NoDelay);
|
socket.NoDelay(ServerInformation.NoDelay);
|
||||||
socket.Bind(ServerAddress);
|
socket.Bind(ServerAddress);
|
||||||
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
|
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
|
||||||
return socket;
|
return socket;
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
acceptSocket.NoDelay(NoDelay);
|
acceptSocket.NoDelay(ServerInformation.NoDelay);
|
||||||
listenSocket.Accept(acceptSocket);
|
listenSocket.Accept(acceptSocket);
|
||||||
DispatchConnection(acceptSocket);
|
DispatchConnection(acceptSocket);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
var socket = new UvTcpHandle(Log);
|
var socket = new UvTcpHandle(Log);
|
||||||
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
socket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
socket.NoDelay(NoDelay);
|
socket.NoDelay(ServerInformation.NoDelay);
|
||||||
socket.Bind(ServerAddress);
|
socket.Bind(ServerAddress);
|
||||||
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
|
socket.Listen(Constants.ListenBacklog, (stream, status, error, state) => ConnectionCallback(stream, status, error, state), this);
|
||||||
return socket;
|
return socket;
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
acceptSocket.NoDelay(NoDelay);
|
acceptSocket.NoDelay(ServerInformation.NoDelay);
|
||||||
listenSocket.Accept(acceptSocket);
|
listenSocket.Accept(acceptSocket);
|
||||||
DispatchConnection(acceptSocket);
|
DispatchConnection(acceptSocket);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{
|
{
|
||||||
var acceptSocket = new UvTcpHandle(Log);
|
var acceptSocket = new UvTcpHandle(Log);
|
||||||
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
acceptSocket.Init(Thread.Loop, Thread.QueueCloseHandle);
|
||||||
acceptSocket.NoDelay(NoDelay);
|
acceptSocket.NoDelay(ServerInformation.NoDelay);
|
||||||
return acceptSocket;
|
return acceptSocket;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,17 +12,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
bool NoDelay { get; set; }
|
bool NoDelay { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a flag that instructs <seealso cref="KestrelServer"/> whether it is safe to
|
/// Gets or sets values that instruct <seealso cref="KestrelServer"/> whether it is safe to
|
||||||
/// reuse the Request and Response <seealso cref="System.IO.Stream"/> objects
|
/// pool the Request and Response <seealso cref="System.IO.Stream"/> objects, Headers etc
|
||||||
/// for another request after the Response's OnCompleted callback has fired.
|
/// for another request after the Response's OnCompleted callback has fired.
|
||||||
/// When this is set to true it is not safe to retain references to these streams after this event has fired.
|
/// When these values are greater than zero it is not safe to retain references to feature components after this event has fired.
|
||||||
/// It is false by default.
|
/// They are zero by default.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
KestrelServerPoolingParameters PoolingParameters { get; }
|
||||||
/// When this is set to true it is not safe to retain references to these streams after this event has fired.
|
|
||||||
/// It is false by default.
|
|
||||||
/// </remarks>
|
|
||||||
bool ReuseStreams { get; set; }
|
|
||||||
|
|
||||||
IConnectionFilter ConnectionFilter { get; set; }
|
IConnectionFilter ConnectionFilter { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
||||||
|
{
|
||||||
|
class HttpComponentFactory : IHttpComponentFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
private ConcurrentQueue<Streams> _streamPool = new ConcurrentQueue<Streams>();
|
||||||
|
private ConcurrentQueue<Headers> _headerPool = new ConcurrentQueue<Headers>();
|
||||||
|
|
||||||
|
public IKestrelServerInformation ServerInformation { get; set; }
|
||||||
|
|
||||||
|
public HttpComponentFactory(IKestrelServerInformation serverInformation)
|
||||||
|
{
|
||||||
|
ServerInformation = serverInformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Streams CreateStreams(FrameContext owner)
|
||||||
|
{
|
||||||
|
Streams streams;
|
||||||
|
|
||||||
|
if (!_streamPool.TryDequeue(out streams))
|
||||||
|
{
|
||||||
|
streams = new Streams();
|
||||||
|
}
|
||||||
|
|
||||||
|
streams.Initialize(owner);
|
||||||
|
|
||||||
|
return streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeStreams(Streams streams, bool poolingPermitted)
|
||||||
|
{
|
||||||
|
if (poolingPermitted && _streamPool.Count < ServerInformation.PoolingParameters.MaxPooledStreams)
|
||||||
|
{
|
||||||
|
streams.Uninitialize();
|
||||||
|
|
||||||
|
_streamPool.Enqueue(streams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Headers CreateHeaders(DateHeaderValueManager dateValueManager)
|
||||||
|
{
|
||||||
|
Headers headers;
|
||||||
|
|
||||||
|
if (!_headerPool.TryDequeue(out headers))
|
||||||
|
{
|
||||||
|
headers = new Headers();
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.Initialize(dateValueManager);
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisposeHeaders(Headers headers, bool poolingPermitted)
|
||||||
|
{
|
||||||
|
if (poolingPermitted && _headerPool.Count < ServerInformation.PoolingParameters.MaxPooledHeaders)
|
||||||
|
{
|
||||||
|
headers.Uninitialize();
|
||||||
|
|
||||||
|
_headerPool.Enqueue(headers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Headers
|
||||||
|
{
|
||||||
|
public static readonly byte[] BytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
|
||||||
|
|
||||||
|
public readonly FrameRequestHeaders RequestHeaders = new FrameRequestHeaders();
|
||||||
|
public readonly FrameResponseHeaders ResponseHeaders = new FrameResponseHeaders();
|
||||||
|
|
||||||
|
public Headers()
|
||||||
|
{
|
||||||
|
RequestHeaders = new FrameRequestHeaders();
|
||||||
|
ResponseHeaders = new FrameResponseHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(DateHeaderValueManager dateValueManager)
|
||||||
|
{
|
||||||
|
ResponseHeaders.SetRawDate(
|
||||||
|
dateValueManager.GetDateHeaderValue(),
|
||||||
|
dateValueManager.GetDateHeaderValueBytes());
|
||||||
|
ResponseHeaders.SetRawServer("Kestrel", BytesServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Uninitialize()
|
||||||
|
{
|
||||||
|
RequestHeaders.Reset();
|
||||||
|
ResponseHeaders.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Streams
|
||||||
|
{
|
||||||
|
public readonly FrameRequestStream RequestBody;
|
||||||
|
public readonly FrameResponseStream ResponseBody;
|
||||||
|
public readonly FrameDuplexStream DuplexStream;
|
||||||
|
|
||||||
|
public Streams()
|
||||||
|
{
|
||||||
|
RequestBody = new FrameRequestStream();
|
||||||
|
ResponseBody = new FrameResponseStream();
|
||||||
|
DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(FrameContext renter)
|
||||||
|
{
|
||||||
|
ResponseBody.Initialize(renter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Uninitialize()
|
||||||
|
{
|
||||||
|
ResponseBody.Uninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
||||||
|
{
|
||||||
|
interface IHttpComponentFactory
|
||||||
|
{
|
||||||
|
IKestrelServerInformation ServerInformation { get; set; }
|
||||||
|
|
||||||
|
Streams CreateStreams(FrameContext owner);
|
||||||
|
|
||||||
|
void DisposeStreams(Streams streams, bool poolingPermitted);
|
||||||
|
|
||||||
|
Headers CreateHeaders(DateHeaderValueManager dateValueManager);
|
||||||
|
|
||||||
|
void DisposeHeaders(Headers headers, bool poolingPermitted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -54,6 +54,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var information = (KestrelServerInformation)Features.Get<IKestrelServerInformation>();
|
var information = (KestrelServerInformation)Features.Get<IKestrelServerInformation>();
|
||||||
|
var componentFactory = Features.Get<IHttpComponentFactory>();
|
||||||
var dateHeaderValueManager = new DateHeaderValueManager();
|
var dateHeaderValueManager = new DateHeaderValueManager();
|
||||||
var trace = new KestrelTrace(_logger);
|
var trace = new KestrelTrace(_logger);
|
||||||
var engine = new KestrelEngine(new ServiceContext
|
var engine = new KestrelEngine(new ServiceContext
|
||||||
|
|
@ -66,9 +67,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
Log = trace,
|
Log = trace,
|
||||||
ThreadPool = new LoggingThreadPool(trace),
|
ThreadPool = new LoggingThreadPool(trace),
|
||||||
DateHeaderValueManager = dateHeaderValueManager,
|
DateHeaderValueManager = dateHeaderValueManager,
|
||||||
ConnectionFilter = information.ConnectionFilter,
|
ServerInformation = information,
|
||||||
NoDelay = information.NoDelay,
|
HttpComponentFactory = componentFactory
|
||||||
ReuseStreams = information.ReuseStreams
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_disposables.Push(engine);
|
_disposables.Push(engine);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
Addresses = GetAddresses(configuration);
|
Addresses = GetAddresses(configuration);
|
||||||
ThreadCount = GetThreadCount(configuration);
|
ThreadCount = GetThreadCount(configuration);
|
||||||
NoDelay = GetNoDelay(configuration);
|
NoDelay = GetNoDelay(configuration);
|
||||||
ReuseStreams = GetReuseStreams(configuration);
|
PoolingParameters = new KestrelServerPoolingParameters(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<string> Addresses { get; }
|
public ICollection<string> Addresses { get; }
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
public bool NoDelay { get; set; }
|
public bool NoDelay { get; set; }
|
||||||
|
|
||||||
public bool ReuseStreams { get; set; }
|
public KestrelServerPoolingParameters PoolingParameters { get; }
|
||||||
|
|
||||||
public IConnectionFilter ConnectionFilter { get; set; }
|
public IConnectionFilter ConnectionFilter { get; set; }
|
||||||
|
|
||||||
|
|
@ -110,18 +110,5 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetReuseStreams(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
var reuseStreamsString = configuration["kestrel.reuseStreams"];
|
|
||||||
|
|
||||||
bool reuseStreams;
|
|
||||||
if (bool.TryParse(reuseStreamsString, out reuseStreams))
|
|
||||||
{
|
|
||||||
return reuseStreams;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
{
|
||||||
|
public class KestrelServerPoolingParameters
|
||||||
|
{
|
||||||
|
public KestrelServerPoolingParameters(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
if (configuration == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
MaxPooledStreams = GetPooledCount(configuration["kestrel.maxPooledStreams"]);
|
||||||
|
MaxPooledHeaders = GetPooledCount(configuration["kestrel.maxPooledHeaders"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxPooledStreams { get; set; }
|
||||||
|
|
||||||
|
public int MaxPooledHeaders { get; set; }
|
||||||
|
|
||||||
|
private static int GetPooledCount(string countString)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(countString))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count;
|
||||||
|
if (int.TryParse(countString, NumberStyles.Integer, CultureInfo.InvariantCulture, out count))
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Features;
|
using Microsoft.AspNetCore.Server.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -28,8 +29,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
public IServer CreateServer(IConfiguration configuration)
|
public IServer CreateServer(IConfiguration configuration)
|
||||||
{
|
{
|
||||||
var information = new KestrelServerInformation(configuration);
|
var information = new KestrelServerInformation(configuration);
|
||||||
|
var componentFactory = new HttpComponentFactory(information);
|
||||||
var serverFeatures = new FeatureCollection();
|
var serverFeatures = new FeatureCollection();
|
||||||
serverFeatures.Set<IKestrelServerInformation>(information);
|
serverFeatures.Set<IKestrelServerInformation>(information);
|
||||||
|
serverFeatures.Set<IHttpComponentFactory>(componentFactory);
|
||||||
serverFeatures.Set<IServerAddressesFeature>(information);
|
serverFeatures.Set<IServerAddressesFeature>(information);
|
||||||
return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
|
return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
ThreadPool = context.ThreadPool;
|
ThreadPool = context.ThreadPool;
|
||||||
FrameFactory = context.FrameFactory;
|
FrameFactory = context.FrameFactory;
|
||||||
DateHeaderValueManager = context.DateHeaderValueManager;
|
DateHeaderValueManager = context.DateHeaderValueManager;
|
||||||
ConnectionFilter = context.ConnectionFilter;
|
ServerInformation = context.ServerInformation;
|
||||||
NoDelay = context.NoDelay;
|
HttpComponentFactory = context.HttpComponentFactory;
|
||||||
ReuseStreams = context.ReuseStreams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IApplicationLifetime AppLifetime { get; set; }
|
public IApplicationLifetime AppLifetime { get; set; }
|
||||||
|
|
@ -39,10 +38,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
|
|
||||||
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
public DateHeaderValueManager DateHeaderValueManager { get; set; }
|
||||||
|
|
||||||
public IConnectionFilter ConnectionFilter { get; set; }
|
public IKestrelServerInformation ServerInformation { get; set; }
|
||||||
|
|
||||||
public bool NoDelay { get; set; }
|
internal IHttpComponentFactory HttpComponentFactory { get; set; }
|
||||||
|
|
||||||
public bool ReuseStreams { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
||||||
public async Task RequestPathIsNormalized()
|
public void RequestPathIsNormalized()
|
||||||
{
|
{
|
||||||
var port = PortManager.GetPort();
|
var port = PortManager.GetPort();
|
||||||
var config = new ConfigurationBuilder().AddInMemoryCollection(
|
var config = new ConfigurationBuilder().AddInMemoryCollection(
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
public async Task CanReadAndWriteWithRewritingConnectionFilter()
|
public async Task CanReadAndWriteWithRewritingConnectionFilter()
|
||||||
{
|
{
|
||||||
var filter = new RewritingConnectionFilter();
|
var filter = new RewritingConnectionFilter();
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(filter);
|
||||||
{
|
|
||||||
ConnectionFilter = filter
|
|
||||||
};
|
|
||||||
var sendString = "POST / HTTP/1.0\r\n\r\nHello World?";
|
var sendString = "POST / HTTP/1.0\r\n\r\nHello World?";
|
||||||
|
|
||||||
using (var server = new TestServer(App, serviceContext))
|
using (var server = new TestServer(App, serviceContext))
|
||||||
|
|
@ -62,10 +60,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
||||||
public async Task CanReadAndWriteWithAsyncConnectionFilter()
|
public async Task CanReadAndWriteWithAsyncConnectionFilter()
|
||||||
{
|
{
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new AsyncConnectionFilter());
|
||||||
{
|
|
||||||
ConnectionFilter = new AsyncConnectionFilter()
|
|
||||||
};
|
|
||||||
|
|
||||||
using (var server = new TestServer(App, serviceContext))
|
using (var server = new TestServer(App, serviceContext))
|
||||||
{
|
{
|
||||||
|
|
@ -87,10 +82,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
[FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")]
|
||||||
public async Task ThrowingSynchronousConnectionFilterDoesNotCrashServer()
|
public async Task ThrowingSynchronousConnectionFilterDoesNotCrashServer()
|
||||||
{
|
{
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new ThrowingConnectionFilter());
|
||||||
{
|
|
||||||
ConnectionFilter = new ThrowingConnectionFilter()
|
|
||||||
};
|
|
||||||
|
|
||||||
using (var server = new TestServer(App, serviceContext))
|
using (var server = new TestServer(App, serviceContext))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
new TestServiceContext()
|
new TestServiceContext()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TestServiceContext
|
new TestServiceContext(new PassThroughConnectionFilter())
|
||||||
{
|
|
||||||
ConnectionFilter = new PassThroughConnectionFilter()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +185,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
public async Task ReuseStreamsOn(ServiceContext testContext)
|
public async Task ReuseStreamsOn(ServiceContext testContext)
|
||||||
{
|
{
|
||||||
testContext.ReuseStreams = true;
|
testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 120;
|
||||||
|
|
||||||
var streamCount = 0;
|
var streamCount = 0;
|
||||||
var loopCount = 20;
|
var loopCount = 20;
|
||||||
|
|
@ -231,7 +228,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
public async Task ReuseStreamsOff(ServiceContext testContext)
|
public async Task ReuseStreamsOff(ServiceContext testContext)
|
||||||
{
|
{
|
||||||
testContext.ReuseStreams = false;
|
testContext.ServerInformation.PoolingParameters.MaxPooledStreams = 0;
|
||||||
|
|
||||||
var streamCount = 0;
|
var streamCount = 0;
|
||||||
var loopCount = 20;
|
var loopCount = 20;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
using Microsoft.AspNetCore.Server.Kestrel;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -15,12 +17,18 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InitialDictionaryContainsServerAndDate()
|
public void InitialDictionaryContainsServerAndDate()
|
||||||
{
|
{
|
||||||
|
var configuration = new ConfigurationBuilder().Build();
|
||||||
|
var serverInformation = new KestrelServerInformation(configuration);
|
||||||
var connectionContext = new ConnectionContext
|
var connectionContext = new ConnectionContext
|
||||||
{
|
{
|
||||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
|
||||||
};
|
ServerInformation = serverInformation,
|
||||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
HttpComponentFactory = new HttpComponentFactory(serverInformation)
|
||||||
|
};
|
||||||
|
var frame = new Frame<object>(application: null, context: connectionContext)
|
||||||
|
.InitializeHeaders();
|
||||||
|
|
||||||
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
|
IDictionary<string, StringValues> headers = frame.ResponseHeaders;
|
||||||
|
|
||||||
Assert.Equal(2, headers.Count);
|
Assert.Equal(2, headers.Count);
|
||||||
|
|
@ -43,13 +51,18 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InitialEntriesCanBeCleared()
|
public void InitialEntriesCanBeCleared()
|
||||||
{
|
{
|
||||||
|
var configuration = new ConfigurationBuilder().Build();
|
||||||
|
var serverInformation = new KestrelServerInformation(configuration);
|
||||||
var connectionContext = new ConnectionContext
|
var connectionContext = new ConnectionContext
|
||||||
{
|
{
|
||||||
DateHeaderValueManager = new DateHeaderValueManager(),
|
DateHeaderValueManager = new DateHeaderValueManager(),
|
||||||
ServerAddress = ServerAddress.FromUrl("http://localhost:5000")
|
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
|
||||||
|
ServerInformation = serverInformation,
|
||||||
|
HttpComponentFactory = new HttpComponentFactory(serverInformation)
|
||||||
};
|
};
|
||||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
var frame = new Frame<object>(application: null, context: connectionContext)
|
||||||
|
.InitializeHeaders();
|
||||||
|
|
||||||
Assert.True(frame.ResponseHeaders.Count > 0);
|
Assert.True(frame.ResponseHeaders.Count > 0);
|
||||||
|
|
||||||
frame.ResponseHeaders.Clear();
|
frame.ResponseHeaders.Clear();
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
handler.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
|
handler.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
|
||||||
#endif
|
#endif
|
||||||
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new HttpsConnectionFilter(
|
||||||
{
|
|
||||||
ConnectionFilter = new HttpsConnectionFilter(
|
|
||||||
new HttpsConnectionFilterOptions
|
new HttpsConnectionFilterOptions
|
||||||
{ ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")},
|
{ ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword") },
|
||||||
new NoOpConnectionFilter())
|
new NoOpConnectionFilter())
|
||||||
};
|
);
|
||||||
|
|
||||||
using (var server = new TestServer(App, serviceContext, serverAddress))
|
using (var server = new TestServer(App, serviceContext, serverAddress))
|
||||||
{
|
{
|
||||||
|
|
@ -108,16 +106,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new HttpsConnectionFilter(
|
||||||
{
|
|
||||||
ConnectionFilter = new HttpsConnectionFilter(
|
|
||||||
new HttpsConnectionFilterOptions
|
new HttpsConnectionFilterOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
||||||
ClientCertificateMode = ClientCertificateMode.RequireCertificate
|
ClientCertificateMode = ClientCertificateMode.RequireCertificate
|
||||||
},
|
},
|
||||||
new NoOpConnectionFilter())
|
new NoOpConnectionFilter())
|
||||||
};
|
);
|
||||||
|
|
||||||
using (var server = new TestServer(App, serviceContext, serverAddress))
|
using (var server = new TestServer(App, serviceContext, serverAddress))
|
||||||
{
|
{
|
||||||
|
|
@ -157,16 +153,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new HttpsConnectionFilter(
|
||||||
{
|
new HttpsConnectionFilterOptions
|
||||||
ConnectionFilter = new HttpsConnectionFilter(
|
{
|
||||||
new HttpsConnectionFilterOptions
|
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
||||||
{
|
ClientCertificateMode = ClientCertificateMode.AllowCertificate
|
||||||
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
},
|
||||||
ClientCertificateMode = ClientCertificateMode.AllowCertificate
|
new NoOpConnectionFilter())
|
||||||
},
|
);
|
||||||
new NoOpConnectionFilter())
|
|
||||||
};
|
|
||||||
|
|
||||||
RequestDelegate app = context =>
|
RequestDelegate app = context =>
|
||||||
{
|
{
|
||||||
|
|
@ -209,17 +203,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(new HttpsConnectionFilter(
|
||||||
{
|
new HttpsConnectionFilterOptions
|
||||||
ConnectionFilter = new HttpsConnectionFilter(
|
{
|
||||||
new HttpsConnectionFilterOptions
|
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
||||||
{
|
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
|
||||||
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword"),
|
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
||||||
ClientCertificateMode = ClientCertificateMode.RequireCertificate,
|
},
|
||||||
ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => true
|
new NoOpConnectionFilter())
|
||||||
},
|
);
|
||||||
new NoOpConnectionFilter())
|
|
||||||
};
|
|
||||||
|
|
||||||
RequestDelegate app = context =>
|
RequestDelegate app = context =>
|
||||||
{
|
{
|
||||||
|
|
@ -281,15 +273,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
var serverAddress = $"https://localhost:{TestServer.GetNextPort()}/";
|
||||||
var serviceContext = new TestServiceContext()
|
var serviceContext = new TestServiceContext(
|
||||||
{
|
new HttpsConnectionFilter(
|
||||||
ConnectionFilter = new HttpsConnectionFilter(
|
|
||||||
new HttpsConnectionFilterOptions
|
new HttpsConnectionFilterOptions
|
||||||
{
|
{
|
||||||
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")
|
ServerCertificate = new X509Certificate2(@"TestResources/testCert.pfx", "testPassword")
|
||||||
},
|
},
|
||||||
new NoOpConnectionFilter())
|
new NoOpConnectionFilter())
|
||||||
};
|
);
|
||||||
|
|
||||||
RequestDelegate app = context => context.Response.WriteAsync(context.Request.Scheme);
|
RequestDelegate app = context => context.Response.WriteAsync(context.Request.Scheme);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,19 +86,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null, false)]
|
[InlineData(null, 0)]
|
||||||
[InlineData("", false)]
|
[InlineData("", 0)]
|
||||||
[InlineData("false", false)]
|
[InlineData("0", 0)]
|
||||||
[InlineData("False", false)]
|
[InlineData("00", 0)]
|
||||||
[InlineData("true", true)]
|
[InlineData("0.0", 0)]
|
||||||
[InlineData("True", true)]
|
[InlineData("1", 1)]
|
||||||
[InlineData("Foo", false)]
|
[InlineData("16", 16)]
|
||||||
[InlineData("FooBar", false)]
|
[InlineData("1000", 1000)]
|
||||||
public void SetReuseStreamsUsingConfiguration(string input, bool expected)
|
public void SetMaxPooledStreamsUsingConfiguration(string input, int expected)
|
||||||
{
|
{
|
||||||
var values = new Dictionary<string, string>
|
var values = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "kestrel.reuseStreams", input }
|
{ "kestrel.maxPooledStreams", input }
|
||||||
};
|
};
|
||||||
|
|
||||||
var configuration = new ConfigurationBuilder()
|
var configuration = new ConfigurationBuilder()
|
||||||
|
|
@ -107,7 +107,33 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
|
|
||||||
var information = new KestrelServerInformation(configuration);
|
var information = new KestrelServerInformation(configuration);
|
||||||
|
|
||||||
Assert.Equal(expected, information.ReuseStreams);
|
Assert.Equal(expected, information.PoolingParameters.MaxPooledStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null, 0)]
|
||||||
|
[InlineData("", 0)]
|
||||||
|
[InlineData("0", 0)]
|
||||||
|
[InlineData("00", 0)]
|
||||||
|
[InlineData("0.0", 0)]
|
||||||
|
[InlineData("1", 1)]
|
||||||
|
[InlineData("16", 16)]
|
||||||
|
[InlineData("1000", 1000)]
|
||||||
|
public void SetMaxPooledHeadersUsingConfiguration(string input, int expected)
|
||||||
|
{
|
||||||
|
var values = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "kestrel.maxPooledHeaders", input }
|
||||||
|
};
|
||||||
|
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(values)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var information = new KestrelServerInformation(configuration);
|
||||||
|
|
||||||
|
Assert.Equal(expected, information.PoolingParameters.MaxPooledHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int Clamp(int value, int min, int max)
|
private static int Clamp(int value, int min, int max)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel;
|
using Microsoft.AspNetCore.Server.Kestrel;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Filter;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
using Microsoft.AspNetCore.Server.Kestrel.Http;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.KestrelTests
|
namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
|
|
@ -18,6 +20,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
Log = new TestKestrelTrace();
|
Log = new TestKestrelTrace();
|
||||||
ThreadPool = new LoggingThreadPool(Log);
|
ThreadPool = new LoggingThreadPool(Log);
|
||||||
DateHeaderValueManager = new TestDateHeaderValueManager();
|
DateHeaderValueManager = new TestDateHeaderValueManager();
|
||||||
|
|
||||||
|
var configuration = new ConfigurationBuilder().Build();
|
||||||
|
ServerInformation = new KestrelServerInformation(configuration);
|
||||||
|
HttpComponentFactory = new HttpComponentFactory(ServerInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestServiceContext(IConnectionFilter filter)
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
ServerInformation.ConnectionFilter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RequestDelegate App
|
public RequestDelegate App
|
||||||
|
|
|
||||||
|
|
@ -216,10 +216,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
: "")}
|
: "")}
|
||||||
|
|
||||||
private long _bits = 0;
|
private long _bits = 0;
|
||||||
{Each(loop.Headers, header => @"
|
private HeaderReferences _headers;
|
||||||
private StringValues _" + header.Identifier + ";")}
|
|
||||||
{Each(loop.Headers.Where(header => header.EnhancedSetter), header => @"
|
|
||||||
private byte[] _raw" + header.Identifier + ";")}
|
|
||||||
{Each(loop.Headers, header => $@"
|
{Each(loop.Headers, header => $@"
|
||||||
public StringValues Header{header.Identifier}
|
public StringValues Header{header.Identifier}
|
||||||
{{
|
{{
|
||||||
|
|
@ -227,23 +224,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
return _{header.Identifier};
|
return _headers._{header.Identifier};
|
||||||
}}
|
}}
|
||||||
return StringValues.Empty;
|
return StringValues.Empty;
|
||||||
}}
|
}}
|
||||||
set
|
set
|
||||||
{{
|
{{
|
||||||
{header.SetBit()};
|
{header.SetBit()};
|
||||||
_{header.Identifier} = value; {(header.EnhancedSetter == false ? "" : $@"
|
_headers._{header.Identifier} = value; {(header.EnhancedSetter == false ? "" : $@"
|
||||||
_raw{header.Identifier} = null;")}
|
_headers._raw{header.Identifier} = null;")}
|
||||||
}}
|
}}
|
||||||
}}")}
|
}}")}
|
||||||
{Each(loop.Headers.Where(header => header.EnhancedSetter), header => $@"
|
{Each(loop.Headers.Where(header => header.EnhancedSetter), header => $@"
|
||||||
public void SetRaw{header.Identifier}(StringValues value, byte[] raw)
|
public void SetRaw{header.Identifier}(StringValues value, byte[] raw)
|
||||||
{{
|
{{
|
||||||
{header.SetBit()};
|
{header.SetBit()};
|
||||||
_{header.Identifier} = value;
|
_headers._{header.Identifier} = value;
|
||||||
_raw{header.Identifier} = raw;
|
_headers._raw{header.Identifier} = raw;
|
||||||
}}")}
|
}}")}
|
||||||
protected override int GetCountFast()
|
protected override int GetCountFast()
|
||||||
{{
|
{{
|
||||||
|
|
@ -259,11 +256,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
return _{header.Identifier};
|
return _headers._{header.Identifier};
|
||||||
}}
|
}}
|
||||||
else
|
else
|
||||||
{{
|
{{
|
||||||
throw new System.Collections.Generic.KeyNotFoundException();
|
ThrowKeyNotFoundException();
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
")}}}
|
")}}}
|
||||||
|
|
@ -271,7 +268,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
")}}}
|
")}}}
|
||||||
if (MaybeUnknown == null)
|
if (MaybeUnknown == null)
|
||||||
{{
|
{{
|
||||||
throw new System.Collections.Generic.KeyNotFoundException();
|
ThrowKeyNotFoundException();
|
||||||
}}
|
}}
|
||||||
return MaybeUnknown[key];
|
return MaybeUnknown[key];
|
||||||
}}
|
}}
|
||||||
|
|
@ -285,7 +282,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
value = _{header.Identifier};
|
value = _headers._{header.Identifier};
|
||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
else
|
else
|
||||||
|
|
@ -309,8 +306,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
|
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase))
|
||||||
{{
|
{{
|
||||||
{header.SetBit()};
|
{header.SetBit()};
|
||||||
_{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
|
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
|
||||||
_raw{header.Identifier} = null;")}
|
_headers._raw{header.Identifier} = null;")}
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
")}}}
|
")}}}
|
||||||
|
|
@ -328,11 +325,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
throw new ArgumentException(""An item with the same key has already been added."");
|
ThrowDuplicateKeyException();
|
||||||
}}
|
}}
|
||||||
{header.SetBit()};
|
{header.SetBit()};
|
||||||
_{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
|
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
|
||||||
_raw{header.Identifier} = null;")}
|
_headers._raw{header.Identifier} = null;")}
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
")}}}
|
")}}}
|
||||||
|
|
@ -351,8 +348,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
{header.ClearBit()};
|
{header.ClearBit()};
|
||||||
_{header.Identifier} = StringValues.Empty;{(header.EnhancedSetter == false ? "" : $@"
|
_headers._{header.Identifier} = StringValues.Empty;{(header.EnhancedSetter == false ? "" : $@"
|
||||||
_raw{header.Identifier} = null;")}
|
_headers._raw{header.Identifier} = null;")}
|
||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
else
|
else
|
||||||
|
|
@ -368,6 +365,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
protected override void ClearFast()
|
protected override void ClearFast()
|
||||||
{{
|
{{
|
||||||
_bits = 0;
|
_bits = 0;
|
||||||
|
_headers = default(HeaderReferences);
|
||||||
MaybeUnknown?.Clear();
|
MaybeUnknown?.Clear();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
@ -375,17 +373,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if (arrayIndex < 0)
|
if (arrayIndex < 0)
|
||||||
{{
|
{{
|
||||||
throw new ArgumentException();
|
ThrowArgumentException();
|
||||||
}}
|
}}
|
||||||
{Each(loop.Headers, header => $@"
|
{Each(loop.Headers, header => $@"
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
if (arrayIndex == array.Length)
|
if (arrayIndex == array.Length)
|
||||||
{{
|
{{
|
||||||
throw new ArgumentException();
|
ThrowArgumentException();
|
||||||
}}
|
}}
|
||||||
|
|
||||||
array[arrayIndex] = new KeyValuePair<string, StringValues>(""{header.Name}"", _{header.Identifier});
|
array[arrayIndex] = new KeyValuePair<string, StringValues>(""{header.Name}"", _headers._{header.Identifier});
|
||||||
++arrayIndex;
|
++arrayIndex;
|
||||||
}}
|
}}
|
||||||
")}
|
")}
|
||||||
|
|
@ -397,12 +395,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{Each(loop.Headers, header => $@"
|
{Each(loop.Headers, header => $@"
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{ {(header.EnhancedSetter == false ? "" : $@"
|
{{ {(header.EnhancedSetter == false ? "" : $@"
|
||||||
if (_raw{header.Identifier} != null)
|
if (_headers._raw{header.Identifier} != null)
|
||||||
{{
|
{{
|
||||||
output.CopyFrom(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length);
|
output.CopyFrom(_headers._raw{header.Identifier}, 0, _headers._raw{header.Identifier}.Length);
|
||||||
}}
|
}}
|
||||||
else ")}
|
else ")}
|
||||||
foreach (var value in _{header.Identifier})
|
foreach (var value in _headers._{header.Identifier})
|
||||||
{{
|
{{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{{
|
{{
|
||||||
|
|
@ -413,6 +411,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}}
|
}}
|
||||||
")}
|
")}
|
||||||
}}" : "")}
|
}}" : "")}
|
||||||
|
{(loop.ClassName == "FrameRequestHeaders" ? $@"
|
||||||
public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value)
|
public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value)
|
||||||
{{
|
{{
|
||||||
fixed (byte* ptr = &keyBytes[keyOffset])
|
fixed (byte* ptr = &keyBytes[keyOffset])
|
||||||
|
|
@ -429,13 +428,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
{{
|
{{
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
_{header.Identifier} = AppendValue(_{header.Identifier}, value);
|
_headers._{header.Identifier} = AppendValue(_headers._{header.Identifier}, value);
|
||||||
}}
|
}}
|
||||||
else
|
else
|
||||||
{{
|
{{
|
||||||
{header.SetBit()};
|
{header.SetBit()};
|
||||||
_{header.Identifier} = new StringValues(value);{(header.EnhancedSetter == false ? "" : $@"
|
_headers._{header.Identifier} = new StringValues(value);{(header.EnhancedSetter == false ? "" : $@"
|
||||||
_raw{header.Identifier} = null;")}
|
_headers._raw{header.Identifier} = null;")}
|
||||||
}}
|
}}
|
||||||
return;
|
return;
|
||||||
}}
|
}}
|
||||||
|
|
@ -447,7 +446,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
StringValues existing;
|
StringValues existing;
|
||||||
Unknown.TryGetValue(key, out existing);
|
Unknown.TryGetValue(key, out existing);
|
||||||
Unknown[key] = AppendValue(existing, value);
|
Unknown[key] = AppendValue(existing, value);
|
||||||
|
}}" : "")}
|
||||||
|
private struct HeaderReferences
|
||||||
|
{{{Each(loop.Headers, header => @"
|
||||||
|
public StringValues _" + header.Identifier + ";")}
|
||||||
|
{Each(loop.Headers.Where(header => header.EnhancedSetter), header => @"
|
||||||
|
public byte[] _raw" + header.Identifier + ";")}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
public partial struct Enumerator
|
public partial struct Enumerator
|
||||||
{{
|
{{
|
||||||
public bool MoveNext()
|
public bool MoveNext()
|
||||||
|
|
@ -465,7 +471,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
state{header.Index}:
|
state{header.Index}:
|
||||||
if ({header.TestBit()})
|
if ({header.TestBit()})
|
||||||
{{
|
{{
|
||||||
_current = new KeyValuePair<string, StringValues>(""{header.Name}"", _collection._{header.Identifier});
|
_current = new KeyValuePair<string, StringValues>(""{header.Name}"", _collection._headers._{header.Identifier});
|
||||||
_state = {header.Index + 1};
|
_state = {header.Index + 1};
|
||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue