From c293bbbd1a6e49d7942dc352f31966dbc6419fba Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 1 Feb 2016 09:40:10 +0000 Subject: [PATCH] Reuse headers to prevent values moving to higher GC gens --- .../Http/Frame.cs | 127 ++++++++++++------ .../Http/FrameHeaders.Generated.cs | 46 +++++++ .../Http/FrameOfT.cs | 31 ++--- .../Http/FrameResponseStream.cs | 16 ++- .../Infrastructure/HttpComponentFactory.cs | 116 ++++++++++++++++ .../Infrastructure/IHttpComponentFactory.cs | 18 +++ .../KestrelServer.cs | 4 +- .../ServiceContext.cs | 3 + .../FrameResponseHeadersTests.cs | 16 ++- .../TestServiceContext.cs | 1 + .../KnownHeaders.cs | 4 +- 11 files changed, 311 insertions(+), 71 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs create mode 100644 src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs index 81aa57bb5b..6ca68ad4e1 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs @@ -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[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0"); 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 Vector _vectorCRs = new Vector((byte)'\r'); @@ -46,8 +44,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http private readonly object _onStartingSync = 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, object>> _onStarting; @@ -60,10 +59,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http protected CancellationTokenSource _abortedCts; protected CancellationToken? _manuallySetRequestAbortToken; - internal FrameRequestStream _requestBody; - internal FrameResponseStream _responseBody; - internal FrameDuplexStream _duplexStream; - protected bool _responseStarted; protected bool _keepAlive; private bool _autoChunk; @@ -92,12 +87,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http _localEndPoint = localEndPoint; _prepareRequest = prepareRequest; _pathBase = context.ServerAddress.PathBase; - if (ReuseStreams) - { - _requestBody = new FrameRequestStream(); - _responseBody = new FrameResponseStream(this); - _duplexStream = new FrameDuplexStream(_requestBody, _responseBody); - } FrameControl = this; Reset(); @@ -197,8 +186,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http 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() { + ResetComponents(poolingPermitted: true); + _onStarting = null; _onCompleted = null; @@ -207,8 +236,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http _autoChunk = false; _applicationException = null; - _requestHeaders.Reset(); - ResetResponseHeaders(); ResetFeatureCollection(); Scheme = null; @@ -218,13 +245,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http Path = null; QueryString = null; _httpVersion = HttpVersionType.Unknown; - RequestHeaders = _requestHeaders; - RequestBody = null; StatusCode = 200; ReasonPhrase = null; - ResponseHeaders = _responseHeaders; - ResponseBody = null; - DuplexStream = null; var httpConnectionFeature = this as IHttpConnectionFeature; httpConnectionFeature.RemoteIpAddress = _remoteEndPoint?.Address; @@ -239,15 +261,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http _abortedCts = null; } - public void ResetResponseHeaders() + protected void ResetComponents(bool poolingPermitted) { - _responseHeaders.Reset(); - _responseHeaders.SetRawDate( - DateHeaderValueManager.GetDateHeaderValue(), - DateHeaderValueManager.GetDateHeaderValueBytes()); - _responseHeaders.SetRawServer( - "Kestrel", - _bytesServer); + if (_frameHeaders != null) + { + RequestHeaders = null; + ResponseHeaders = null; + var frameHeaders = _frameHeaders; + _frameHeaders = null; + HttpComponentFactory.DisposeHeaders(frameHeaders, poolingPermitted); + } + + if (_frameStreams != null) + { + RequestBody = null; + ResponseBody = null; + DuplexStream = null; + var frameStreams = _frameStreams; + _frameStreams = null; + HttpComponentFactory.DisposeStreams(frameStreams, poolingPermitted: (poolingPermitted && ReuseStreams)); + } } /// @@ -292,8 +325,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { _requestProcessingStopping = true; - _requestBody?.Abort(); - _responseBody?.Abort(); + _frameStreams?.RequestBody.Abort(); + _frameStreams?.ResponseBody.Abort(); try { @@ -560,8 +593,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http StatusCode = 500; ReasonPhrase = null; - ResetResponseHeaders(); - _responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); + var responseHeaders = _frameHeaders.ResponseHeaders; + 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) { var end = SocketOutput.ProducingStart(); + var responseHeaders = _frameHeaders.ResponseHeaders; if (_keepAlive) { - foreach (var connectionValue in _responseHeaders.HeaderConnection) + foreach (var connectionValue in responseHeaders.HeaderConnection) { 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) { @@ -636,7 +679,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { // Since the app has completed and we are only now generating // the headers we can safely set the Content-Length to 0. - _responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); + responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); } } else @@ -644,7 +687,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http if (_httpVersion == HttpVersionType.Http1_1) { _autoChunk = true; - _responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked); + responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked); } 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(statusBytes); - _responseHeaders.CopyTo(ref end); + responseHeaders.CopyTo(ref end); end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length); SocketOutput.ProducingComplete(end); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs index 8a36b78d50..de407741d7 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -3737,6 +3737,51 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } 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; MaybeUnknown?.Clear(); } @@ -8618,6 +8663,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } protected override void ClearFast() { + _bits = 0; MaybeUnknown?.Clear(); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs index 0d9ea1d673..1c0191c6cb 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameOfT.cs @@ -52,7 +52,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http await SocketInput; } - while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) + InitializeHeaders(); + + while (!_requestProcessingStopping && !TakeMessageHeaders(SocketInput, FrameRequestHeaders)) { if (SocketInput.RemoteIntakeFin) { @@ -63,20 +65,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http if (!_requestProcessingStopping) { - var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this); + var messageBody = MessageBody.For(HttpVersion, FrameRequestHeaders, this); _keepAlive = messageBody.RequestKeepAlive; - // _duplexStream may be null if flag switched while running - 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; + InitializeStreams(messageBody); _abortedCts = null; _manuallySetRequestAbortToken = null; @@ -101,8 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http await FireOnStarting(); } - _requestBody.PauseAcceptingReads(); - _responseBody.PauseAcceptingWrites(); + PauseStreams(); if (_onCompleted != null) { @@ -114,23 +105,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http // If _requestAbort is set, the connection has already been closed. if (Volatile.Read(ref _requestAborted) == 0) { - _responseBody.ResumeAcceptingWrites(); + ResumeStreams(); + await ProduceEnd(); if (_keepAlive) { - _requestBody.ResumeAcceptingReads(); // Finish reading the request body in case the app did not. await messageBody.Consume(); } } - _requestBody.StopAcceptingReads(); - _responseBody.StopAcceptingWrites(); + StopStreams(); } if (!_keepAlive) { + ResetComponents(poolingPermitted: true); return; } } @@ -144,6 +135,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } finally { + // Error occurred, do not return components to pool + ResetComponents(poolingPermitted: false); try { _abortedCts = null; diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs index 3cf9600ecc..145710af24 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameResponseStream.cs @@ -11,12 +11,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { class FrameResponseStream : Stream { - private readonly FrameContext _context; + private FrameContext _context; private FrameStreamState _state; - public FrameResponseStream(FrameContext context) + public FrameResponseStream() { - _context = context; _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) { switch (_state) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs new file mode 100644 index 0000000000..98cdfef311 --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs @@ -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 _streamPool = new ConcurrentQueue(); + private ConcurrentQueue _headerPool = new ConcurrentQueue(); + + 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(); + } + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs new file mode 100644 index 0000000000..b02300774a --- /dev/null +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/IHttpComponentFactory.cs @@ -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); + } +} diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs index fed5bea660..6e3826d971 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/KestrelServer.cs @@ -56,6 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel var information = (KestrelServerInformation)Features.Get(); var dateHeaderValueManager = new DateHeaderValueManager(); var trace = new KestrelTrace(_logger); + var componentFactory = new HttpComponentFactory(); var engine = new KestrelEngine(new ServiceContext { FrameFactory = (context, remoteEP, localEP, prepareRequest) => @@ -68,7 +69,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel DateHeaderValueManager = dateHeaderValueManager, ConnectionFilter = information.ConnectionFilter, NoDelay = information.NoDelay, - ReuseStreams = information.ReuseStreams + ReuseStreams = information.ReuseStreams, + HttpComponentFactory = componentFactory }); _disposables.Push(engine); diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs index a299ff9461..b0d26c0953 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/ServiceContext.cs @@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel ConnectionFilter = context.ConnectionFilter; NoDelay = context.NoDelay; ReuseStreams = context.ReuseStreams; + HttpComponentFactory = context.HttpComponentFactory; } public IApplicationLifetime AppLifetime { get; set; } @@ -44,5 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel public bool NoDelay { get; set; } public bool ReuseStreams { get; set; } + + internal IHttpComponentFactory HttpComponentFactory { get; set; } } } diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs index 5fa4f6bec2..8bdf782e9f 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Server.Kestrel; using Microsoft.AspNetCore.Server.Kestrel.Http; +using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Primitives; using Xunit; @@ -18,9 +19,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var connectionContext = new ConnectionContext { DateHeaderValueManager = new DateHeaderValueManager(), - ServerAddress = ServerAddress.FromUrl("http://localhost:5000") + ServerAddress = ServerAddress.FromUrl("http://localhost:5000"), + HttpComponentFactory = new HttpComponentFactory() }; - var frame = new Frame(application: null, context: connectionContext); + var frame = new Frame(application: null, context: connectionContext) + .InitializeHeaders(); + IDictionary headers = frame.ResponseHeaders; Assert.Equal(2, headers.Count); @@ -46,10 +50,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var connectionContext = new ConnectionContext { DateHeaderValueManager = new DateHeaderValueManager(), - ServerAddress = ServerAddress.FromUrl("http://localhost:5000") + ServerAddress = ServerAddress.FromUrl("http://localhost:5000"), + HttpComponentFactory = new HttpComponentFactory() }; - var frame = new Frame(application: null, context: connectionContext); - + var frame = new Frame(application: null, context: connectionContext) + .InitializeHeaders(); + Assert.True(frame.ResponseHeaders.Count > 0); frame.ResponseHeaders.Clear(); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs index 4224c776d3..679abaa3a7 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests Log = new TestKestrelTrace(); ThreadPool = new LoggingThreadPool(Log); DateHeaderValueManager = new TestDateHeaderValueManager(); + HttpComponentFactory = new HttpComponentFactory(); } public RequestDelegate App diff --git a/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs index 32a5f057e7..92e133db89 100644 --- a/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -366,7 +366,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http return MaybeUnknown?.Remove(key) ?? false; }} protected override void ClearFast() - {{ + {{{(loop.ClassName != "FrameRequestHeaders" ? "" : Each(loop.Headers, header => $@" + if ({header.TestBit()}) _{header.Identifier} = default(StringValues);"))} + _bits = 0; MaybeUnknown?.Clear(); }}