Reuse headers to prevent values moving to higher GC gens
This commit is contained in:
parent
dfcd6a6227
commit
c293bbbd1a
|
|
@ -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,9 @@ 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();
|
private Headers _frameHeaders;
|
||||||
|
private Streams _frameStreams;
|
||||||
|
|
||||||
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
|
||||||
|
|
||||||
|
|
@ -60,10 +59,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 +87,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 +186,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 +236,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 +245,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 +261,26 @@ 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(),
|
RequestHeaders = null;
|
||||||
DateHeaderValueManager.GetDateHeaderValueBytes());
|
ResponseHeaders = null;
|
||||||
_responseHeaders.SetRawServer(
|
var frameHeaders = _frameHeaders;
|
||||||
"Kestrel",
|
_frameHeaders = null;
|
||||||
_bytesServer);
|
HttpComponentFactory.DisposeHeaders(frameHeaders, poolingPermitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_frameStreams != null)
|
||||||
|
{
|
||||||
|
RequestBody = null;
|
||||||
|
ResponseBody = null;
|
||||||
|
DuplexStream = null;
|
||||||
|
var frameStreams = _frameStreams;
|
||||||
|
_frameStreams = null;
|
||||||
|
HttpComponentFactory.DisposeStreams(frameStreams, poolingPermitted: (poolingPermitted && ReuseStreams));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -292,8 +325,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 +593,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -615,9 +657,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
bool appCompleted)
|
bool appCompleted)
|
||||||
{
|
{
|
||||||
var end = SocketOutput.ProducingStart();
|
var end = SocketOutput.ProducingStart();
|
||||||
|
var responseHeaders = _frameHeaders.ResponseHeaders;
|
||||||
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 +669,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 +679,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 +687,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 +696,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);
|
||||||
|
|
|
||||||
|
|
@ -3737,6 +3737,51 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
protected override void ClearFast()
|
protected override void ClearFast()
|
||||||
{
|
{
|
||||||
|
if (((_bits & 1L) != 0)) _CacheControl = default(StringValues);
|
||||||
|
if (((_bits & 2L) != 0)) _Connection = default(StringValues);
|
||||||
|
if (((_bits & 4L) != 0)) _Date = default(StringValues);
|
||||||
|
if (((_bits & 8L) != 0)) _KeepAlive = default(StringValues);
|
||||||
|
if (((_bits & 16L) != 0)) _Pragma = default(StringValues);
|
||||||
|
if (((_bits & 32L) != 0)) _Trailer = default(StringValues);
|
||||||
|
if (((_bits & 64L) != 0)) _TransferEncoding = default(StringValues);
|
||||||
|
if (((_bits & 128L) != 0)) _Upgrade = default(StringValues);
|
||||||
|
if (((_bits & 256L) != 0)) _Via = default(StringValues);
|
||||||
|
if (((_bits & 512L) != 0)) _Warning = default(StringValues);
|
||||||
|
if (((_bits & 1024L) != 0)) _Allow = default(StringValues);
|
||||||
|
if (((_bits & 2048L) != 0)) _ContentLength = default(StringValues);
|
||||||
|
if (((_bits & 4096L) != 0)) _ContentType = default(StringValues);
|
||||||
|
if (((_bits & 8192L) != 0)) _ContentEncoding = default(StringValues);
|
||||||
|
if (((_bits & 16384L) != 0)) _ContentLanguage = default(StringValues);
|
||||||
|
if (((_bits & 32768L) != 0)) _ContentLocation = default(StringValues);
|
||||||
|
if (((_bits & 65536L) != 0)) _ContentMD5 = default(StringValues);
|
||||||
|
if (((_bits & 131072L) != 0)) _ContentRange = default(StringValues);
|
||||||
|
if (((_bits & 262144L) != 0)) _Expires = default(StringValues);
|
||||||
|
if (((_bits & 524288L) != 0)) _LastModified = default(StringValues);
|
||||||
|
if (((_bits & 1048576L) != 0)) _Accept = default(StringValues);
|
||||||
|
if (((_bits & 2097152L) != 0)) _AcceptCharset = default(StringValues);
|
||||||
|
if (((_bits & 4194304L) != 0)) _AcceptEncoding = default(StringValues);
|
||||||
|
if (((_bits & 8388608L) != 0)) _AcceptLanguage = default(StringValues);
|
||||||
|
if (((_bits & 16777216L) != 0)) _Authorization = default(StringValues);
|
||||||
|
if (((_bits & 33554432L) != 0)) _Cookie = default(StringValues);
|
||||||
|
if (((_bits & 67108864L) != 0)) _Expect = default(StringValues);
|
||||||
|
if (((_bits & 134217728L) != 0)) _From = default(StringValues);
|
||||||
|
if (((_bits & 268435456L) != 0)) _Host = default(StringValues);
|
||||||
|
if (((_bits & 536870912L) != 0)) _IfMatch = default(StringValues);
|
||||||
|
if (((_bits & 1073741824L) != 0)) _IfModifiedSince = default(StringValues);
|
||||||
|
if (((_bits & 2147483648L) != 0)) _IfNoneMatch = default(StringValues);
|
||||||
|
if (((_bits & 4294967296L) != 0)) _IfRange = default(StringValues);
|
||||||
|
if (((_bits & 8589934592L) != 0)) _IfUnmodifiedSince = default(StringValues);
|
||||||
|
if (((_bits & 17179869184L) != 0)) _MaxForwards = default(StringValues);
|
||||||
|
if (((_bits & 34359738368L) != 0)) _ProxyAuthorization = default(StringValues);
|
||||||
|
if (((_bits & 68719476736L) != 0)) _Referer = default(StringValues);
|
||||||
|
if (((_bits & 137438953472L) != 0)) _Range = default(StringValues);
|
||||||
|
if (((_bits & 274877906944L) != 0)) _TE = default(StringValues);
|
||||||
|
if (((_bits & 549755813888L) != 0)) _Translate = default(StringValues);
|
||||||
|
if (((_bits & 1099511627776L) != 0)) _UserAgent = default(StringValues);
|
||||||
|
if (((_bits & 2199023255552L) != 0)) _Origin = default(StringValues);
|
||||||
|
if (((_bits & 4398046511104L) != 0)) _AccessControlRequestMethod = default(StringValues);
|
||||||
|
if (((_bits & 8796093022208L) != 0)) _AccessControlRequestHeaders = default(StringValues);
|
||||||
|
|
||||||
_bits = 0;
|
_bits = 0;
|
||||||
MaybeUnknown?.Clear();
|
MaybeUnknown?.Clear();
|
||||||
}
|
}
|
||||||
|
|
@ -8618,6 +8663,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
protected override void ClearFast()
|
protected override void ClearFast()
|
||||||
{
|
{
|
||||||
|
|
||||||
_bits = 0;
|
_bits = 0;
|
||||||
MaybeUnknown?.Clear();
|
MaybeUnknown?.Clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +135,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
// Error occurred, do not return components to pool
|
||||||
|
ResetComponents(poolingPermitted: false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_abortedCts = null;
|
_abortedCts = null;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Server.Kestrel.Http;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
|
||||||
|
{
|
||||||
|
class HttpComponentFactory : IHttpComponentFactory
|
||||||
|
{
|
||||||
|
private const int _maxPooledComponents = 128;
|
||||||
|
private ConcurrentQueue<Streams> _streamPool = new ConcurrentQueue<Streams>();
|
||||||
|
private ConcurrentQueue<Headers> _headerPool = new ConcurrentQueue<Headers>();
|
||||||
|
|
||||||
|
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 < _maxPooledComponents)
|
||||||
|
{
|
||||||
|
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 < _maxPooledComponents)
|
||||||
|
{
|
||||||
|
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,18 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
Streams CreateStreams(FrameContext owner);
|
||||||
|
|
||||||
|
void DisposeStreams(Streams streams, bool poolingPermitted);
|
||||||
|
|
||||||
|
Headers CreateHeaders(DateHeaderValueManager dateValueManager);
|
||||||
|
|
||||||
|
void DisposeHeaders(Headers headers, bool poolingPermitted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -56,6 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
var information = (KestrelServerInformation)Features.Get<IKestrelServerInformation>();
|
var information = (KestrelServerInformation)Features.Get<IKestrelServerInformation>();
|
||||||
var dateHeaderValueManager = new DateHeaderValueManager();
|
var dateHeaderValueManager = new DateHeaderValueManager();
|
||||||
var trace = new KestrelTrace(_logger);
|
var trace = new KestrelTrace(_logger);
|
||||||
|
var componentFactory = new HttpComponentFactory();
|
||||||
var engine = new KestrelEngine(new ServiceContext
|
var engine = new KestrelEngine(new ServiceContext
|
||||||
{
|
{
|
||||||
FrameFactory = (context, remoteEP, localEP, prepareRequest) =>
|
FrameFactory = (context, remoteEP, localEP, prepareRequest) =>
|
||||||
|
|
@ -68,7 +69,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
DateHeaderValueManager = dateHeaderValueManager,
|
DateHeaderValueManager = dateHeaderValueManager,
|
||||||
ConnectionFilter = information.ConnectionFilter,
|
ConnectionFilter = information.ConnectionFilter,
|
||||||
NoDelay = information.NoDelay,
|
NoDelay = information.NoDelay,
|
||||||
ReuseStreams = information.ReuseStreams
|
ReuseStreams = information.ReuseStreams,
|
||||||
|
HttpComponentFactory = componentFactory
|
||||||
});
|
});
|
||||||
|
|
||||||
_disposables.Push(engine);
|
_disposables.Push(engine);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
ConnectionFilter = context.ConnectionFilter;
|
ConnectionFilter = context.ConnectionFilter;
|
||||||
NoDelay = context.NoDelay;
|
NoDelay = context.NoDelay;
|
||||||
ReuseStreams = context.ReuseStreams;
|
ReuseStreams = context.ReuseStreams;
|
||||||
|
HttpComponentFactory = context.HttpComponentFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IApplicationLifetime AppLifetime { get; set; }
|
public IApplicationLifetime AppLifetime { get; set; }
|
||||||
|
|
@ -44,5 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
||||||
public bool NoDelay { get; set; }
|
public bool NoDelay { get; set; }
|
||||||
|
|
||||||
public bool ReuseStreams { get; set; }
|
public bool ReuseStreams { get; set; }
|
||||||
|
|
||||||
|
internal IHttpComponentFactory HttpComponentFactory { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ 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.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -18,9 +19,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
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"),
|
||||||
|
HttpComponentFactory = new HttpComponentFactory()
|
||||||
};
|
};
|
||||||
var frame = new Frame<object>(application: null, context: connectionContext);
|
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);
|
||||||
|
|
@ -46,10 +50,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
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"),
|
||||||
|
HttpComponentFactory = new HttpComponentFactory()
|
||||||
};
|
};
|
||||||
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();
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ 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();
|
||||||
|
HttpComponentFactory = new HttpComponentFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RequestDelegate App
|
public RequestDelegate App
|
||||||
|
|
|
||||||
|
|
@ -366,7 +366,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
|
||||||
return MaybeUnknown?.Remove(key) ?? false;
|
return MaybeUnknown?.Remove(key) ?? false;
|
||||||
}}
|
}}
|
||||||
protected override void ClearFast()
|
protected override void ClearFast()
|
||||||
{{
|
{{{(loop.ClassName != "FrameRequestHeaders" ? "" : Each(loop.Headers, header => $@"
|
||||||
|
if ({header.TestBit()}) _{header.Identifier} = default(StringValues);"))}
|
||||||
|
|
||||||
_bits = 0;
|
_bits = 0;
|
||||||
MaybeUnknown?.Clear();
|
MaybeUnknown?.Clear();
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue