| File: Internal\Infrastructure\BodyControl.cs | Web Access |
| Project: src\aspnetcore\src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj (Microsoft.AspNetCore.Server.Kestrel.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.IO.Pipelines; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; internal sealed class BodyControl { private static readonly ThrowingWasUpgradedWriteOnlyStream _throwingResponseStream = new(); private static readonly ThrowingPipeWriter _throwingUpgradedPipeWriter = new(CoreStrings.ResponseStreamWasUpgraded); private readonly HttpResponseStream _response; private readonly HttpResponsePipeWriter _responseWriter; private readonly HttpRequestPipeReader _requestReader; private readonly HttpRequestStream _request; private readonly HttpRequestPipeReader _emptyRequestReader; private readonly WrappingStream _upgradeableResponse; private readonly WrappingPipeWriter _upgradeablePipeWriter; private readonly StatusCheckPipeWriter _connectPipeWriter; private readonly StatusCheckWriteStream _connectResponse; private readonly HttpRequestStream _emptyRequest; private readonly Stream _upgradeStream; public BodyControl(IHttpBodyControlFeature bodyControl, IHttpResponseControl responseControl) { _requestReader = new HttpRequestPipeReader(); _request = new HttpRequestStream(bodyControl, _requestReader); _emptyRequestReader = new HttpRequestPipeReader(); _emptyRequest = new HttpRequestStream(bodyControl, _emptyRequestReader); _responseWriter = new HttpResponsePipeWriter(responseControl); _response = new HttpResponseStream(bodyControl, _responseWriter); _upgradeableResponse = new WrappingStream(_response); _upgradeablePipeWriter = new WrappingPipeWriter(_responseWriter); _upgradeStream = new HttpUpgradeStream(_request, _response); _connectPipeWriter = new(_responseWriter); _connectResponse = new(_response); } public bool CanHaveBody { get; private set; } public Stream Upgrade() { // causes writes to context.Response.Body to throw _upgradeableResponse.SetInnerStream(_throwingResponseStream); _upgradeablePipeWriter.SetInnerPipe(_throwingUpgradedPipeWriter); // _upgradeStream always uses _response return _upgradeStream; } public Stream AcceptConnect() { return _upgradeStream; } public (Stream request, Stream response, PipeReader reader, PipeWriter writer) Start(MessageBody body) { CanHaveBody = !body.IsEmpty; _requestReader.StartAcceptingReads(body); _emptyRequestReader.StartAcceptingReads(MessageBody.ZeroContentLengthClose); _responseWriter.StartAcceptingWrites(); if (body.RequestUpgrade) { // until Upgrade() is called, context.Response.Body should use the normal output stream _upgradeableResponse.SetInnerStream(_response); _upgradeablePipeWriter.SetInnerPipe(_responseWriter); // upgradeable requests should never have a request body return (_emptyRequest, _upgradeableResponse, _emptyRequestReader, _upgradeablePipeWriter); } else if (body.ExtendedConnect) { // CONNECT requests do not have a request or response body until after accepted, // unless it's a 300+ response. We set CanHaveBody to false here since it's only // for requests (see IHttpRequestBodyDetectionFeature). CanHaveBody = false; _connectResponse.SetRequest(body.Context); _connectPipeWriter.SetRequest(body.Context); return (_emptyRequest, _connectResponse, _emptyRequestReader, _connectPipeWriter); } else { return (_request, _response, _requestReader, _responseWriter); } } public Task StopAsync() { _requestReader.StopAcceptingReads(); _emptyRequestReader.StopAcceptingReads(); return _responseWriter.StopAcceptingWritesAsync(); } public void Abort(Exception error) { _requestReader.Abort(error); _emptyRequestReader.Abort(error); _responseWriter.Abort(); } }