示例#1
0
        public bool TakeMessageHeaders(ReadOnlySequence <byte> buffer, out SequencePosition consumed, out SequencePosition examined)
        {
            // Make sure the buffer is limited
            bool overLength = false;

            if (buffer.Length >= _remainingRequestHeadersBytesAllowed)
            {
                buffer = buffer.Slice(buffer.Start, _remainingRequestHeadersBytesAllowed);

                // If we sliced it means the current buffer bigger than what we're
                // allowed to look at
                overLength = true;
            }

            var result = _parser.ParseHeaders(new Proto1ParsingHandler(this), buffer, out consumed, out examined, out var consumedBytes);

            _remainingRequestHeadersBytesAllowed -= consumedBytes;

            if (!result && overLength)
            {
                BadProtoRequestException.Throw(RequestRejectionReason.HeadersExceedMaxTotalSize);
            }
            if (result)
            {
                TimeoutControl.CancelTimeout();
            }

            return(result);
        }
示例#2
0
        public HttpConnection(HttpConnectionContext context)
        {
            _context     = context;
            _systemClock = _context.ServiceContext.SystemClock;

            _timeoutControl = new TimeoutControl(this);
        }
 public Http3Connection(Http3ConnectionContext context)
 {
     _multiplexedContext = context.ConnectionContext;
     _context            = context;
     DynamicTable        = new DynamicTable(0);
     _systemClock        = context.ServiceContext.SystemClock;
     _timeoutControl     = new TimeoutControl(this);
     _context.TimeoutControl ??= _timeoutControl;
 }
示例#4
0
        protected override void OnRequestProcessingEnded()
        {
            TimeoutControl.StartDrainTimeout(MinResponseDataRate, ServerOptions.Limits.MaxResponseBufferSize);

            // Prevent RequestAborted from firing. Free up unneeded feature references.
            Reset();

            _http1Output.Dispose();
        }
示例#5
0
    public HttpConnection(BaseHttpConnectionContext context)
    {
        _context     = context;
        _systemClock = _context.ServiceContext.SystemClock;

        _timeoutControl = new TimeoutControl(this);

        // Tests override the timeout control sometimes
        _context.TimeoutControl ??= _timeoutControl;
    }
示例#6
0
        public Http3InMemory(ServiceContext serviceContext, MockSystemClock mockSystemClock, ITimeoutHandler timeoutHandler, ILoggerFactory loggerFactory)
        {
            _serviceContext          = serviceContext;
            _timeoutControl          = new TimeoutControl(new TimeoutControlConnectionInvoker(this, timeoutHandler));
            _timeoutControl.Debugger = new TestDebugger();

            _mockSystemClock = mockSystemClock;

            _serverReceivedSettings = Channel.CreateUnbounded <KeyValuePair <Http3SettingType, long> >();
            Logger = loggerFactory.CreateLogger <Http3InMemory>();
        }
示例#7
0
        public Http3Connection(Http3ConnectionContext context)
        {
            _multiplexedContext = context.ConnectionContext;
            _context            = context;
            _systemClock        = context.ServiceContext.SystemClock;
            _timeoutControl     = new TimeoutControl(this);
            _context.TimeoutControl ??= _timeoutControl;

            var httpLimits = context.ServiceContext.ServerOptions.Limits;

            _serverSettings.HeaderTableSize           = (uint)httpLimits.Http3.HeaderTableSize;
            _serverSettings.MaxRequestHeaderFieldSize = (uint)httpLimits.Http3.MaxRequestHeaderFieldSize;
        }
示例#8
0
        public bool TakeMessageHeaders(ref SequenceReader <byte> reader, bool trailers)
        {
            // Make sure the buffer is limited
            if (reader.Remaining > _remainingRequestHeadersBytesAllowed)
            {
                // Input oversize, cap amount checked
                return(TrimAndTakeMessageHeaders(ref reader, trailers));
            }

            var alreadyConsumed = reader.Consumed;

            try
            {
                var result = _parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref reader);
                if (result)
                {
                    TimeoutControl.CancelTimeout();
                }

                return(result);
            }
            finally
            {
                _remainingRequestHeadersBytesAllowed -= reader.Consumed - alreadyConsumed;
            }

            bool TrimAndTakeMessageHeaders(ref SequenceReader <byte> reader, bool trailers)
            {
                var trimmedBuffer = reader.Sequence.Slice(reader.Position, _remainingRequestHeadersBytesAllowed);
                var trimmedReader = new SequenceReader <byte>(trimmedBuffer);

                try
                {
                    if (!_parser.ParseHeaders(new Http1ParsingHandler(this, trailers), ref trimmedReader))
                    {
                        // We read the maximum allowed but didn't complete the headers.
                        KestrelBadHttpRequestException.Throw(RequestRejectionReason.HeadersExceedMaxTotalSize);
                    }

                    TimeoutControl.CancelTimeout();

                    reader.Advance(trimmedReader.Consumed);

                    return(true);
                }
                finally
                {
                    _remainingRequestHeadersBytesAllowed -= trimmedReader.Consumed;
                }
            }
        }
示例#9
0
        public Http3TestBase()
        {
            _timeoutControl     = new TimeoutControl(_mockTimeoutHandler.Object);
            _mockTimeoutControl = new Mock <MockTimeoutControlBase>(_timeoutControl)
            {
                CallBase = true
            };
            _timeoutControl.Debugger = Mock.Of <IDebugger>();

            _mockKestrelTrace
            .Setup(m => m.Http3ConnectionClosed(It.IsAny <string>(), It.IsAny <long>()))
            .Callback(() => _closedStateReached.SetResult());


            _noopApplication = context => Task.CompletedTask;

            _echoApplication = async context =>
            {
                var buffer   = new byte[16 * 1024];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }
            };

            _echoMethod = context =>
            {
                context.Response.Headers["Method"] = context.Request.Method;

                return(Task.CompletedTask);
            };

            _echoPath = context =>
            {
                context.Response.Headers["path"]      = context.Request.Path.ToString();
                context.Response.Headers["rawtarget"] = context.Features.Get <IHttpRequestFeature>().RawTarget;

                return(Task.CompletedTask);
            };

            _echoHost = context =>
            {
                context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];

                return(Task.CompletedTask);
            };
        }
示例#10
0
        protected override void OnRequestProcessingEnded()
        {
            if (IsUpgraded)
            {
                KestrelEventSource.Log.RequestUpgradedStop(this);

                ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne();
            }

            TimeoutControl.StartDrainTimeout(MinResponseDataRate, ServerOptions.Limits.MaxResponseBufferSize);

            // Prevent RequestAborted from firing. Free up unneeded feature references.
            Reset();

            _http1Output.Dispose();
        }
示例#11
0
    public bool ParseRequest(ref SequenceReader <byte> reader)
    {
        switch (_requestProcessingStatus)
        {
        case RequestProcessingStatus.RequestPending:
            // Skip any empty lines (\r or \n) between requests.
            // Peek first as a minor performance optimization; it's a quick inlined check.
            if (reader.TryPeek(out byte b) && (b == ByteCR || b == ByteLF))
            {
                reader.AdvancePastAny(ByteCR, ByteLF);
            }

            if (reader.End)
            {
                break;
            }

            TimeoutControl.ResetTimeout(_requestHeadersTimeoutTicks, TimeoutReason.RequestHeaders);

            _requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
            goto case RequestProcessingStatus.ParsingRequestLine;

        case RequestProcessingStatus.ParsingRequestLine:
            if (TakeStartLine(ref reader))
            {
                _requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
                goto case RequestProcessingStatus.ParsingHeaders;
            }
            else
            {
                break;
            }

        case RequestProcessingStatus.ParsingHeaders:
            if (TakeMessageHeaders(ref reader, trailers: false))
            {
                _requestProcessingStatus = RequestProcessingStatus.AppStarted;
                // Consumed preamble
                return(true);
            }
            break;
        }

        // Haven't completed consuming preamble
        return(false);
    }
示例#12
0
        public Http3TestBase()
        {
            _timeoutControl     = new TimeoutControl(_mockTimeoutHandler.Object);
            _mockTimeoutControl = new Mock <MockTimeoutControlBase>(_timeoutControl)
            {
                CallBase = true
            };
            _timeoutControl.Debugger = Mock.Of <IDebugger>();

            _noopApplication = context => Task.CompletedTask;

            _echoApplication = async context =>
            {
                var buffer   = new byte[Http3PeerSettings.MinAllowedMaxFrameSize];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }
            };

            _echoMethod = context =>
            {
                context.Response.Headers["Method"] = context.Request.Method;

                return(Task.CompletedTask);
            };

            _echoPath = context =>
            {
                context.Response.Headers["path"]      = context.Request.Path.ToString();
                context.Response.Headers["rawtarget"] = context.Features.Get <IHttpRequestFeature>().RawTarget;

                return(Task.CompletedTask);
            };

            _echoHost = context =>
            {
                context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];

                return(Task.CompletedTask);
            };
        }
示例#13
0
        public Http3TestBase()
        {
            _timeoutControl     = new TimeoutControl(_mockTimeoutHandler.Object);
            _mockTimeoutControl = new Mock <MockTimeoutControlBase>(_timeoutControl)
            {
                CallBase = true
            };
            _timeoutControl.Debugger = Mock.Of <IDebugger>();
            _echoApplication         = async context =>
            {
                var buffer   = new byte[Http3PeerSettings.MinAllowedMaxFrameSize];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }
            };
        }
示例#14
0
        public bool ParseRequest(ref SequenceReader <byte> reader)
        {
            switch (_requestProcessingStatus)
            {
            case RequestProcessingStatus.RequestPending:
                if (reader.End)
                {
                    break;
                }

                TimeoutControl.ResetTimeout(_requestHeadersTimeoutTicks, TimeoutReason.RequestHeaders);

                _requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
                goto case RequestProcessingStatus.ParsingRequestLine;

            case RequestProcessingStatus.ParsingRequestLine:
                if (TakeStartLine(ref reader))
                {
                    _requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
                    goto case RequestProcessingStatus.ParsingHeaders;
                }
                else
                {
                    break;
                }

            case RequestProcessingStatus.ParsingHeaders:
                if (TakeMessageHeaders(ref reader, trailers: false))
                {
                    _requestProcessingStatus = RequestProcessingStatus.AppStarted;
                    // Consumed preamble
                    return(true);
                }
                break;
            }

            // Haven't completed consuming preamble
            return(false);
        }
示例#15
0
        public void ParseRequest(ReadOnlySequence <byte> buffer, out SequencePosition consumed, out SequencePosition examined)
        {
            consumed = buffer.Start;
            examined = buffer.End;

            switch (_requestProcessingStatus)
            {
            case RequestProcessingStatus.RequestPending:
                if (buffer.IsEmpty)
                {
                    break;
                }

                TimeoutControl.ResetTimeout(_requestHeadersTimeoutTicks, TimeoutReason.RequestHeaders);

                _requestProcessingStatus = RequestProcessingStatus.ParsingRequestLine;
                goto case RequestProcessingStatus.ParsingRequestLine;

            case RequestProcessingStatus.ParsingRequestLine:
                if (TakeStartLine(buffer, out consumed, out examined))
                {
                    buffer = buffer.Slice(consumed, buffer.End);

                    _requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
                    goto case RequestProcessingStatus.ParsingHeaders;
                }
                else
                {
                    break;
                }

            case RequestProcessingStatus.ParsingHeaders:
                if (TakeMessageHeaders(buffer, trailers: false, out consumed, out examined))
                {
                    _requestProcessingStatus = RequestProcessingStatus.AppStarted;
                }
                break;
            }
        }
示例#16
0
 protected override void BeginRequestProcessing()
 {
     // Reset the features and timeout.
     Reset();
     TimeoutControl.SetTimeout(_keepAliveTicks, TimeoutReason.KeepAlive);
 }
示例#17
0
        /// <summary>
        /// Primary loop which consumes socket input, parses it for protocol framing, and invokes the
        /// application delegate for as long as the socket is intended to remain open.
        /// The resulting Task from this loop is preserved in a field which is used when the server needs
        /// to drain and close all currently active connections.
        /// </summary>
        public override async Task ProcessRequestsAsync()
        {
            try
            {
                while (!_requestProcessingStopping)
                {
                    TimeoutControl.SetTimeout(_keepAliveTicks, TimeoutAction.CloseConnection);

                    Reset();

                    while (!_requestProcessingStopping)
                    {
                        var result = await Input.ReadAsync();

                        var examined = result.Buffer.End;
                        var consumed = result.Buffer.End;

                        try
                        {
                            ParseRequest(result.Buffer, out consumed, out examined);
                        }
                        catch (InvalidOperationException)
                        {
                            if (_requestProcessingStatus == RequestProcessingStatus.ParsingHeaders)
                            {
                                throw BadHttpRequestException.GetException(RequestRejectionReason
                                                                           .MalformedRequestInvalidHeaders);
                            }
                            throw;
                        }
                        finally
                        {
                            Input.Advance(consumed, examined);
                        }

                        if (_requestProcessingStatus == RequestProcessingStatus.AppStarted)
                        {
                            break;
                        }

                        if (result.IsCompleted)
                        {
                            switch (_requestProcessingStatus)
                            {
                            case RequestProcessingStatus.RequestPending:
                                return;

                            case RequestProcessingStatus.ParsingRequestLine:
                                throw BadHttpRequestException.GetException(
                                          RequestRejectionReason.InvalidRequestLine);

                            case RequestProcessingStatus.ParsingHeaders:
                                throw BadHttpRequestException.GetException(
                                          RequestRejectionReason.MalformedRequestInvalidHeaders);
                            }
                        }
                    }

                    if (!_requestProcessingStopping)
                    {
                        EnsureHostHeaderExists();

                        var messageBody = MessageBody.For(_httpVersion, FrameRequestHeaders, this);
                        _keepAlive        = messageBody.RequestKeepAlive;
                        _upgradeAvailable = messageBody.RequestUpgrade;

                        InitializeStreams(messageBody);

                        var context = _application.CreateContext(this);
                        try
                        {
                            try
                            {
                                KestrelEventSource.Log.RequestStart(this);

                                await _application.ProcessRequestAsync(context);

                                if (Volatile.Read(ref _requestAborted) == 0)
                                {
                                    VerifyResponseContentLength();
                                }
                            }
                            catch (Exception ex)
                            {
                                ReportApplicationError(ex);

                                if (ex is BadHttpRequestException)
                                {
                                    throw;
                                }
                            }
                            finally
                            {
                                KestrelEventSource.Log.RequestStop(this);

                                // Trigger OnStarting if it hasn't been called yet and the app hasn't
                                // already failed. If an OnStarting callback throws we can go through
                                // our normal error handling in ProduceEnd.
                                // https://github.com/aspnet/KestrelHttpServer/issues/43
                                if (!HasResponseStarted && _applicationException == null && _onStarting != null)
                                {
                                    await FireOnStarting();
                                }

                                PauseStreams();

                                if (_onCompleted != null)
                                {
                                    await FireOnCompleted();
                                }
                            }

                            // If _requestAbort is set, the connection has already been closed.
                            if (Volatile.Read(ref _requestAborted) == 0)
                            {
                                if (HasResponseStarted)
                                {
                                    // If the response has already started, call ProduceEnd() before
                                    // consuming the rest of the request body to prevent
                                    // delaying clients waiting for the chunk terminator:
                                    //
                                    // https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
                                    //
                                    // ProduceEnd() must be called before _application.DisposeContext(), to ensure
                                    // HttpContext.Response.StatusCode is correctly set when
                                    // IHttpContextFactory.Dispose(HttpContext) is called.
                                    await ProduceEnd();
                                }

                                // ForZeroContentLength does not complete the reader nor the writer
                                if (!messageBody.IsEmpty && _keepAlive)
                                {
                                    // Finish reading the request body in case the app did not.
                                    TimeoutControl.SetTimeout(Constants.RequestBodyDrainTimeout.Ticks, TimeoutAction.SendTimeoutResponse);
                                    await messageBody.ConsumeAsync();

                                    TimeoutControl.CancelTimeout();
                                }

                                if (!HasResponseStarted)
                                {
                                    await ProduceEnd();
                                }
                            }
                            else if (!HasResponseStarted)
                            {
                                // If the request was aborted and no response was sent, there's no
                                // meaningful status code to log.
                                StatusCode = 0;
                            }
                        }
                        catch (BadHttpRequestException ex)
                        {
                            // Handle BadHttpRequestException thrown during app execution or remaining message body consumption.
                            // This has to be caught here so StatusCode is set properly before disposing the HttpContext
                            // (DisposeContext logs StatusCode).
                            SetBadRequestState(ex);
                        }
                        finally
                        {
                            _application.DisposeContext(context, _applicationException);

                            // StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block
                            // to ensure InitializeStreams has been called.
                            StopStreams();

                            if (HasStartedConsumingRequestBody)
                            {
                                RequestBodyPipe.Reader.Complete();

                                // Wait for MessageBody.PumpAsync() to call RequestBodyPipe.Writer.Complete().
                                await messageBody.StopAsync();

                                // At this point both the request body pipe reader and writer should be completed.
                                RequestBodyPipe.Reset();
                            }
                        }
                    }

                    if (!_keepAlive)
                    {
                        // End the connection for non keep alive as data incoming may have been thrown off
                        return;
                    }
                }
            }
            catch (BadHttpRequestException ex)
            {
                // Handle BadHttpRequestException thrown during request line or header parsing.
                // SetBadRequestState logs the error.
                SetBadRequestState(ex);
            }
            catch (ConnectionResetException ex)
            {
                // Don't log ECONNRESET errors made between requests. Browsers like IE will reset connections regularly.
                if (_requestProcessingStatus != RequestProcessingStatus.RequestPending)
                {
                    Log.RequestProcessingError(ConnectionId, ex);
                }
            }
            catch (IOException ex)
            {
                Log.RequestProcessingError(ConnectionId, ex);
            }
            catch (Exception ex)
            {
                Log.LogWarning(0, ex, CoreStrings.RequestProcessingEndError);
            }
            finally
            {
                try
                {
                    Input.Complete();

                    // If _requestAborted is set, the connection has already been closed.
                    if (Volatile.Read(ref _requestAborted) == 0)
                    {
                        await TryProduceInvalidRequestResponse();

                        Output.Dispose();
                    }
                }
                catch (Exception ex)
                {
                    Log.LogWarning(0, ex, CoreStrings.ConnectionShutdownError);
                }
            }
        }
示例#18
0
        public Http3TestBase()
        {
            _timeoutControl     = new TimeoutControl(_mockTimeoutHandler.Object);
            _mockTimeoutControl = new Mock <MockTimeoutControlBase>(_timeoutControl)
            {
                CallBase = true
            };
            _timeoutControl.Debugger = Mock.Of <IDebugger>();

            _mockKestrelTrace
            .Setup(m => m.Http3ConnectionClosed(It.IsAny <string>(), It.IsAny <long>()))
            .Callback(() => _closedStateReached.SetResult());

            _serverReceivedSettings = Channel.CreateUnbounded <KeyValuePair <Internal.Http3.Http3SettingType, long> >();

            _noopApplication = context => Task.CompletedTask;

            _echoApplication = async context =>
            {
                var buffer   = new byte[16 * 1024];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }
            };

            _readRateApplication = async context =>
            {
                var expectedBytes = int.Parse(context.Request.Path.Value.Substring(1), CultureInfo.InvariantCulture);

                var buffer   = new byte[16 * 1024];
                var received = 0;

                while (received < expectedBytes)
                {
                    received += await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);
                }

                var stalledReadTask = context.Request.Body.ReadAsync(buffer, 0, buffer.Length);

                // Write to the response so the test knows the app started the stalled read.
                await context.Response.Body.WriteAsync(new byte[1], 0, 1);

                await stalledReadTask;
            };

            _echoMethod = context =>
            {
                context.Response.Headers["Method"] = context.Request.Method;

                return(Task.CompletedTask);
            };

            _echoPath = context =>
            {
                context.Response.Headers["path"]      = context.Request.Path.ToString();
                context.Response.Headers["rawtarget"] = context.Features.Get <IHttpRequestFeature>().RawTarget;

                return(Task.CompletedTask);
            };

            _echoHost = context =>
            {
                context.Response.Headers.Host = context.Request.Headers.Host;

                return(Task.CompletedTask);
            };
        }
 public TimeoutControlTests()
 {
     _mockTimeoutHandler = new Mock <ITimeoutHandler>();
     _timeoutControl     = new TimeoutControl(_mockTimeoutHandler.Object);
     _systemClock        = new MockSystemClock();
 }
示例#20
0
        public Http2TestBase()
        {
            // Always dispatch test code back to the ThreadPool. This prevents deadlocks caused by continuing
            // Http2Connection.ProcessRequestsAsync() loop with writer locks acquired. Run product code inline to make
            // it easier to verify request frames are processed correctly immediately after sending the them.
            var inputPipeOptions = new PipeOptions(
                pool: _memoryPool,
                readerScheduler: PipeScheduler.Inline,
                writerScheduler: PipeScheduler.ThreadPool,
                useSynchronizationContext: false
                );
            var outputPipeOptions = new PipeOptions(
                pool: _memoryPool,
                readerScheduler: PipeScheduler.ThreadPool,
                writerScheduler: PipeScheduler.Inline,
                useSynchronizationContext: false
                );

            _pair           = DuplexPipe.CreateConnectionPair(inputPipeOptions, outputPipeOptions);
            _hpackDecoder   = new HPackDecoder((int)_clientSettings.HeaderTableSize, MaxRequestHeaderFieldSize);
            _timeoutControl = new TimeoutControl(_mockTimeoutHandler.Object);

            _noopApplication = context => Task.CompletedTask;

            _readHeadersApplication = context =>
            {
                foreach (var header in context.Request.Headers)
                {
                    _receivedHeaders[header.Key] = header.Value.ToString();
                }

                return(Task.CompletedTask);
            };

            _readTrailersApplication = async context =>
            {
                using (var ms = new MemoryStream())
                {
                    // Consuming the entire request body guarantees trailers will be available
                    await context.Request.Body.CopyToAsync(ms);
                }

                foreach (var header in context.Request.Headers)
                {
                    _receivedHeaders[header.Key] = header.Value.ToString();
                }
            };

            _bufferingApplication = async context =>
            {
                var data     = new List <byte>();
                var buffer   = new byte[1024];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    data.AddRange(new ArraySegment <byte>(buffer, 0, received));
                }

                await context.Response.Body.WriteAsync(data.ToArray(), 0, data.Count);
            };

            _echoApplication = async context =>
            {
                var buffer   = new byte[Http2PeerSettings.MinAllowedMaxFrameSize];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }
            };

            _echoWaitForAbortApplication = async context =>
            {
                var buffer   = new byte[Http2PeerSettings.MinAllowedMaxFrameSize];
                var received = 0;

                while ((received = await context.Request.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await context.Response.Body.WriteAsync(buffer, 0, received);
                }

                var sem = new SemaphoreSlim(0);

                context.RequestAborted.Register(() =>
                {
                    sem.Release();
                });

                await sem.WaitAsync().DefaultTimeout();
            };

            _largeHeadersApplication = context =>
            {
                foreach (var name in new[] { "a", "b", "c", "d", "e", "f", "g", "h" })
                {
                    context.Response.Headers[name] = _4kHeaderValue;
                }

                return(Task.CompletedTask);
            };

            _waitForAbortApplication = async context =>
            {
                var streamIdFeature = context.Features.Get <IHttp2StreamIdFeature>();
                var sem             = new SemaphoreSlim(0);

                context.RequestAborted.Register(() =>
                {
                    lock (_abortedStreamIdsLock)
                    {
                        _abortedStreamIds.Add(streamIdFeature.StreamId);
                    }

                    sem.Release();
                });

                await sem.WaitAsync().DefaultTimeout();

                _runningStreams[streamIdFeature.StreamId].TrySetResult(null);
            };

            _waitForAbortFlushingApplication = async context =>
            {
                var streamIdFeature = context.Features.Get <IHttp2StreamIdFeature>();
                var sem             = new SemaphoreSlim(0);

                context.RequestAborted.Register(() =>
                {
                    lock (_abortedStreamIdsLock)
                    {
                        _abortedStreamIds.Add(streamIdFeature.StreamId);
                    }

                    sem.Release();
                });

                await sem.WaitAsync().DefaultTimeout();

                await context.Response.Body.FlushAsync();

                _runningStreams[streamIdFeature.StreamId].TrySetResult(null);
            };

            _waitForAbortWithDataApplication = async context =>
            {
                var streamIdFeature = context.Features.Get <IHttp2StreamIdFeature>();
                var sem             = new SemaphoreSlim(0);

                context.RequestAborted.Register(() =>
                {
                    lock (_abortedStreamIdsLock)
                    {
                        _abortedStreamIds.Add(streamIdFeature.StreamId);
                    }

                    sem.Release();
                });

                await sem.WaitAsync().DefaultTimeout();

                await context.Response.Body.WriteAsync(new byte[10], 0, 10);

                _runningStreams[streamIdFeature.StreamId].TrySetResult(null);
            };

            _echoMethod = context =>
            {
                context.Response.Headers["Method"] = context.Request.Method;

                return(Task.CompletedTask);
            };

            _echoHost = context =>
            {
                context.Response.Headers[HeaderNames.Host] = context.Request.Headers[HeaderNames.Host];

                return(Task.CompletedTask);
            };

            _echoPath = context =>
            {
                context.Response.Headers["path"]      = context.Request.Path.ToString();
                context.Response.Headers["rawtarget"] = context.Features.Get <IHttpRequestFeature>().RawTarget;

                return(Task.CompletedTask);
            };
        }