Merge branch 'release/3.1' => 'master' (#16834)
This commit is contained in:
commit
9abf4bfe3f
|
|
@ -2,9 +2,6 @@
|
|||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
|
||||
<add key="darc-pub-dotnet-corefx-4ac4c03" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-corefx-4ac4c036/nuget/v3/index.json" />
|
||||
<!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
|
||||
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
|
||||
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
|
||||
<add key="aspnet-blazor" value="https://dotnetfeed.blob.core.windows.net/aspnet-blazor/index.json" />
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href class="reload">Reload</a>
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
<script src="_framework/blazor.webassembly.js"></script>
|
||||
|
|
|
|||
|
|
@ -122,8 +122,9 @@ namespace Microsoft.AspNetCore.HttpsPolicy
|
|||
// 1. Set in the HttpsRedirectionOptions
|
||||
// 2. HTTPS_PORT environment variable
|
||||
// 3. IServerAddressesFeature
|
||||
// 4. Fail if not set
|
||||
var nullablePort = _config.GetValue<int?>("HTTPS_PORT");
|
||||
// 4. Fail if not sets
|
||||
|
||||
var nullablePort = _config.GetValue<int?>("HTTPS_PORT") ?? _config.GetValue<int?>("ANCM_HTTPS_PORT");
|
||||
if (nullablePort.HasValue)
|
||||
{
|
||||
var port = nullablePort.Value;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
|||
{
|
||||
response.ContentLength = resolvedContentTypeEncoding.GetByteCount(result.Content);
|
||||
|
||||
using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
await using (var textWriter = _httpResponseStreamWriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
{
|
||||
await textWriter.WriteAsync(result.Content);
|
||||
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
|
|||
{
|
||||
// This means we're writing to a 'real' writer, probably to the actual output stream.
|
||||
// We're using PagedBufferedTextWriter here to 'smooth' synchronous writes of IHtmlContent values.
|
||||
using (var writer = _bufferScope.CreateWriter(context.Writer))
|
||||
await using (var writer = _bufferScope.CreateWriter(context.Writer))
|
||||
{
|
||||
await bodyWriter.Buffer.WriteToAsync(writer, _htmlEncoder);
|
||||
await writer.FlushAsync();
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
|
||||
_writerFactory ??= context.HttpContext.RequestServices.GetRequiredService<IHttpResponseStreamWriterFactory>();
|
||||
|
||||
using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
await using (var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
{
|
||||
var viewContext = new ViewContext(
|
||||
context,
|
||||
|
|
@ -149,8 +149,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
else
|
||||
{
|
||||
using var bufferingStream = new FileBufferingWriteStream();
|
||||
using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
|
||||
await using var bufferingStream = new FileBufferingWriteStream();
|
||||
await using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding))
|
||||
{
|
||||
viewComponentResult.WriteTo(intermediateWriter, _htmlEncoder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
|
||||
OnExecuting(viewContext);
|
||||
|
||||
using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
await using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
{
|
||||
var view = viewContext.View;
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,44 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_ExceptionInSyncContext()
|
||||
{
|
||||
// Arrange
|
||||
var view = CreateView((v) =>
|
||||
{
|
||||
v.Writer.Write("xyz");
|
||||
throw new NotImplementedException("This should be raw!");
|
||||
});
|
||||
|
||||
var context = new DefaultHttpContext();
|
||||
var stream = new Mock<Stream>();
|
||||
stream.Setup(s => s.CanWrite).Returns(true);
|
||||
|
||||
context.Response.Body = stream.Object;
|
||||
var actionContext = new ActionContext(
|
||||
context,
|
||||
new RouteData(),
|
||||
new ActionDescriptor());
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider());
|
||||
|
||||
var viewExecutor = CreateViewExecutor();
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<NotImplementedException>(async () => await viewExecutor.ExecuteAsync(
|
||||
actionContext,
|
||||
view,
|
||||
viewData,
|
||||
Mock.Of<ITempDataDictionary>(),
|
||||
contentType: null,
|
||||
statusCode: null)
|
||||
);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("This should be raw!", exception.Message);
|
||||
stream.Verify(s => s.Write(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<int>()), Times.Never);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ViewExecutorSetsContentTypeAndEncodingData))]
|
||||
public async Task ExecuteAsync_SetsContentTypeAndEncoding(
|
||||
|
|
|
|||
|
|
@ -64,6 +64,17 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GivesCorrectCallstackForSyncronousCalls()
|
||||
{
|
||||
// Regression test for https://github.com/aspnet/AspNetCore/issues/15367
|
||||
// Arrange
|
||||
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await Client.GetAsync("http://localhost/Home/MyHtml"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Should be visible", exception.InnerException.InnerException.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanRenderViewsWithTagHelpersAndUnboundDynamicAttributes_Encoded()
|
||||
{
|
||||
|
|
@ -318,4 +329,4 @@ page:<root>root-content</root>"
|
|||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ namespace TagHelpersWebSite.Controllers
|
|||
return View();
|
||||
}
|
||||
|
||||
public IActionResult MyHtml()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ViewComponentTagHelpers()
|
||||
{
|
||||
return View();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// 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.IO;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace TagHelpersWebSite
|
||||
{
|
||||
public class MyHtmlContent : IHtmlContent
|
||||
{
|
||||
private IHtmlHelper Html { get; }
|
||||
|
||||
public MyHtmlContent(IHtmlHelper html)
|
||||
{
|
||||
Html = html;
|
||||
}
|
||||
|
||||
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
|
||||
{
|
||||
#pragma warning disable MVC1000 // Use of IHtmlHelper.{0} should be avoided.
|
||||
Html.Partial("_Test").WriteTo(writer, encoder);
|
||||
#pragma warning restore MVC1000 // Use of IHtmlHelper.{0} should be avoided.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\RazorPagesClassLibrary\RazorPagesClassLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
@(new TagHelpersWebSite.MyHtmlContent(Html))
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@{
|
||||
throw new Exception("Should be visible");
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
@page "/"
|
||||
@namespace BlazorServerWeb_CSharp.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@{
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<environment include="Development">
|
||||
An unhandled exception has occurred. See browser dev tools for details.
|
||||
</environment>
|
||||
<a href class="reload">Reload</a>
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ tmp/
|
|||
CustomHive/
|
||||
angular/
|
||||
blazorserver/
|
||||
blazorwasm/
|
||||
mvc/
|
||||
razor/
|
||||
react/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env pwsh
|
||||
#requires -version 4
|
||||
|
||||
# This script packages, installs and creates a template to help with rapid iteration in the templating area.
|
||||
[CmdletBinding(PositionalBinding = $false)]
|
||||
param()
|
||||
|
||||
Set-StrictMode -Version 2
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
. $PSScriptRoot\Test-Template.ps1
|
||||
|
||||
Test-Template "blazorwasm" "blazorwasm" "Microsoft.AspnetCore.Blazor.Templates.3.1.0-dev.nupkg" $false
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
#define STARTUP_TIME_LIMIT_INCREMENT_IN_MILLISECONDS 5000
|
||||
|
||||
|
||||
HRESULT
|
||||
SERVER_PROCESS::Initialize(
|
||||
PROCESS_MANAGER *pProcessManager,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH"
|
||||
#define ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR L"ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED"
|
||||
#define ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR L"ASPNETCORE_IIS_PHYSICAL_PATH"
|
||||
#define ASPNETCORE_HTTPS_PORT_ENV_STR L"ASPNETCORE_HTTPS_PORT"
|
||||
#define ASPNETCORE_ANCM_HTTPS_PORT_ENV_STR L"ASPNETCORE_ANCM_HTTPS_PORT"
|
||||
#define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;"
|
||||
#define ASPNETCORE_IIS_AUTH_BASIC L"basic;"
|
||||
#define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
environmentVariables.insert_or_assign(ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR, pApplicationPhysicalPath);
|
||||
if (pHttpsPort)
|
||||
{
|
||||
environmentVariables.try_emplace(ASPNETCORE_HTTPS_PORT_ENV_STR, pHttpsPort);
|
||||
environmentVariables.try_emplace(ASPNETCORE_ANCM_HTTPS_PORT_ENV_STR, pHttpsPort);
|
||||
}
|
||||
|
||||
std::wstring strIisAuthEnvValue;
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
|||
if (DeployerSelector.HasNewHandler &&
|
||||
DeployerSelector.HasNewShim)
|
||||
{
|
||||
// We expect ServerAddress to be set for InProcess and HTTPS_PORT for OutOfProcess
|
||||
// We expect ServerAddress to be set for InProcess and ANCM_HTTPS_PORT for OutOfProcess
|
||||
if (variant.HostingModel == HostingModel.InProcess)
|
||||
{
|
||||
Assert.Equal(deploymentParameters.ApplicationBaseUriHint, await client.GetStringAsync("/ServerAddresses"));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(port.ToString(), await client.GetStringAsync("/HTTPS_PORT"));
|
||||
Assert.Equal(port.ToString(), await client.GetStringAsync("/ANCM_HTTPS_PORT"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
|||
[ConditionalFact]
|
||||
[RequiresNewHandler]
|
||||
[RequiresNewShim]
|
||||
public async Task HttpsPortCanBeOverriden()
|
||||
public async Task AncmHttpsPortCanBeOverriden()
|
||||
{
|
||||
var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess);
|
||||
|
||||
|
|
@ -125,12 +125,57 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
|||
.SetAttributeValue("bindingInformation", $":{TestPortHelper.GetNextSSLPort()}:localhost");
|
||||
});
|
||||
|
||||
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_HTTPS_PORT"] = "123";
|
||||
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_ANCM_HTTPS_PORT"] = "123";
|
||||
|
||||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
var client = CreateNonValidatingClient(deploymentResult);
|
||||
|
||||
Assert.Equal("123", await client.GetStringAsync("/HTTPS_PORT"));
|
||||
Assert.Equal("123", await client.GetStringAsync("/ANCM_HTTPS_PORT"));
|
||||
Assert.Equal("NOVALUE", await client.GetStringAsync("/HTTPS_PORT"));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[RequiresNewShim]
|
||||
public async Task HttpsRedirectionWorksIn30AndNot22()
|
||||
{
|
||||
var port = TestPortHelper.GetNextSSLPort();
|
||||
var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess);
|
||||
deploymentParameters.WebConfigBasedEnvironmentVariables["ENABLE_HTTPS_REDIRECTION"] = "true";
|
||||
deploymentParameters.ApplicationBaseUriHint = $"http://localhost:{TestPortHelper.GetNextPort()}/";
|
||||
|
||||
deploymentParameters.AddServerConfigAction(
|
||||
element => {
|
||||
element.Descendants("bindings")
|
||||
.Single()
|
||||
.AddAndGetInnerElement("binding", "protocol", "https")
|
||||
.SetAttributeValue("bindingInformation", $":{port}:localhost");
|
||||
|
||||
element.Descendants("access")
|
||||
.Single()
|
||||
.SetAttributeValue("sslFlags", "None");
|
||||
});
|
||||
|
||||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
|
||||
AllowAutoRedirect = false
|
||||
};
|
||||
var client = new HttpClient(handler)
|
||||
{
|
||||
BaseAddress = new Uri(deploymentParameters.ApplicationBaseUriHint)
|
||||
};
|
||||
|
||||
if (DeployerSelector.HasNewHandler)
|
||||
{
|
||||
var response = await client.GetAsync("/ANCM_HTTPS_PORT");
|
||||
Assert.Equal(307, (int)response.StatusCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
var response = await client.GetAsync("/ANCM_HTTPS_PORT");
|
||||
Assert.Equal(200, (int)response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
|
|
@ -159,7 +204,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
|||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
var client = CreateNonValidatingClient(deploymentResult);
|
||||
|
||||
Assert.Equal("NOVALUE", await client.GetStringAsync("/HTTPS_PORT"));
|
||||
Assert.Equal("NOVALUE", await client.GetStringAsync("/ANCM_HTTPS_PORT"));
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" >
|
||||
<AllowExplicitReference>true</AllowExplicitReference>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.HttpsPolicy" Version="2.2.0" >
|
||||
<AllowExplicitReference>true</AllowExplicitReference>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.0" >
|
||||
<AllowExplicitReference>true</AllowExplicitReference>
|
||||
</PackageReference>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
<Reference Include="Microsoft.AspNetCore.Server.IIS" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
||||
<Reference Include="Microsoft.AspNetCore.HttpsPolicy" />
|
||||
<Reference Include="Microsoft.AspNetCore.WebUtilities" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ namespace TestSite
|
|||
{
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("ENABLE_HTTPS_REDIRECTION") != null)
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
TestStartup.Register(app, this);
|
||||
}
|
||||
|
||||
|
|
@ -992,6 +996,13 @@ namespace TestSite
|
|||
await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString());
|
||||
}
|
||||
|
||||
public async Task ANCM_HTTPS_PORT(HttpContext context)
|
||||
{
|
||||
var httpsPort = context.RequestServices.GetService<IConfiguration>().GetValue<int?>("ANCM_HTTPS_PORT");
|
||||
|
||||
await context.Response.WriteAsync(httpsPort.HasValue ? httpsPort.Value.ToString() : "NOVALUE");
|
||||
}
|
||||
|
||||
public async Task HTTPS_PORT(HttpContext context)
|
||||
{
|
||||
var httpsPort = context.RequestServices.GetService<IConfiguration>().GetValue<int?>("HTTPS_PORT");
|
||||
|
|
|
|||
|
|
@ -43,5 +43,13 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
|
|||
|
||||
return existing;
|
||||
}
|
||||
|
||||
public static XElement AddAndGetInnerElement(this XElement element, string name, string attribute, string attributeValue)
|
||||
{
|
||||
var innerElement = new XElement(name, new XAttribute(attribute, attributeValue));
|
||||
element.Add(innerElement);
|
||||
|
||||
return innerElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
}
|
||||
|
||||
// Read() will have already have greedily consumed the entire request body if able.
|
||||
CheckCompletedReadResult(result);
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
ThrowUnexpectedEndOfRequestContent();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
if (_readCompleted)
|
||||
{
|
||||
_isReading = true;
|
||||
return _readResult;
|
||||
return new ReadResult(_readResult.Buffer, Interlocked.Exchange(ref _userCanceled, 0) == 1, _readResult.IsCompleted);
|
||||
}
|
||||
|
||||
TryStart();
|
||||
|
|
@ -70,44 +70,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
}
|
||||
catch (ConnectionAbortedException ex)
|
||||
{
|
||||
_isReading = false;
|
||||
throw new TaskCanceledException("The request was aborted", ex);
|
||||
}
|
||||
|
||||
void ResetReadingState()
|
||||
{
|
||||
_isReading = false;
|
||||
// Reset the timing read here for the next call to read.
|
||||
StopTimingRead(0);
|
||||
_context.Input.AdvanceTo(_readResult.Buffer.Start);
|
||||
}
|
||||
|
||||
if (_context.RequestTimedOut)
|
||||
{
|
||||
ResetReadingState();
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTimeout);
|
||||
}
|
||||
|
||||
// Make sure to handle when this is canceled here.
|
||||
if (_readResult.IsCanceled)
|
||||
if (_readResult.IsCompleted)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _userCanceled, 0) == 1)
|
||||
{
|
||||
// Ignore the readResult if it wasn't by the user.
|
||||
CreateReadResultFromConnectionReadResult();
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the timing read here for the next call to read.
|
||||
StopTimingRead(0);
|
||||
continue;
|
||||
}
|
||||
ResetReadingState();
|
||||
ThrowUnexpectedEndOfRequestContent();
|
||||
}
|
||||
|
||||
var readableBuffer = _readResult.Buffer;
|
||||
var readableBufferLength = readableBuffer.Length;
|
||||
StopTimingRead(readableBufferLength);
|
||||
|
||||
CheckCompletedReadResult(_readResult);
|
||||
|
||||
if (readableBufferLength > 0)
|
||||
// Ignore the canceled readResult if it wasn't canceled by the user.
|
||||
if (!_readResult.IsCanceled || Interlocked.Exchange(ref _userCanceled, 0) == 1)
|
||||
{
|
||||
CreateReadResultFromConnectionReadResult();
|
||||
var returnedReadResultLength = CreateReadResultFromConnectionReadResult();
|
||||
|
||||
// Don't count bytes belonging to the next request, since read rate timeouts are done on a per-request basis.
|
||||
StopTimingRead(returnedReadResultLength);
|
||||
|
||||
if (_readResult.IsCompleted)
|
||||
{
|
||||
TryStop();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ResetReadingState();
|
||||
}
|
||||
|
||||
return _readResult;
|
||||
|
|
@ -129,66 +132,69 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
if (_readCompleted)
|
||||
{
|
||||
_isReading = true;
|
||||
readResult = _readResult;
|
||||
readResult = new ReadResult(_readResult.Buffer, Interlocked.Exchange(ref _userCanceled, 0) == 1, _readResult.IsCompleted);
|
||||
return true;
|
||||
}
|
||||
|
||||
TryStart();
|
||||
|
||||
if (!_context.Input.TryRead(out _readResult))
|
||||
// The while(true) because we don't want to return a canceled ReadResult if the user themselves didn't cancel it.
|
||||
while (true)
|
||||
{
|
||||
readResult = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_readResult.IsCanceled)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _userCanceled, 0) == 0)
|
||||
if (!_context.Input.TryRead(out _readResult))
|
||||
{
|
||||
// Cancellation wasn't by the user, return default ReadResult
|
||||
readResult = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only set _isReading if we are returing true.
|
||||
_isReading = true;
|
||||
if (!_readResult.IsCanceled || Interlocked.Exchange(ref _userCanceled, 0) == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
CreateReadResultFromConnectionReadResult();
|
||||
|
||||
readResult = _readResult;
|
||||
CountBytesRead(readResult.Buffer.Length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Task ConsumeAsync()
|
||||
{
|
||||
TryStart();
|
||||
|
||||
if (!_readResult.Buffer.IsEmpty && _inputLength == 0)
|
||||
{
|
||||
_context.Input.AdvanceTo(_readResult.Buffer.End);
|
||||
}
|
||||
|
||||
return OnConsumeAsync();
|
||||
}
|
||||
|
||||
private void CreateReadResultFromConnectionReadResult()
|
||||
{
|
||||
if (_readResult.Buffer.Length >= _inputLength + _examinedUnconsumedBytes)
|
||||
{
|
||||
_readCompleted = true;
|
||||
_readResult = new ReadResult(
|
||||
_readResult.Buffer.Slice(0, _inputLength + _examinedUnconsumedBytes),
|
||||
_readResult.IsCanceled && Interlocked.Exchange(ref _userCanceled, 0) == 1,
|
||||
_readCompleted);
|
||||
_context.Input.AdvanceTo(_readResult.Buffer.Start);
|
||||
}
|
||||
|
||||
if (_readResult.IsCompleted)
|
||||
{
|
||||
_context.Input.AdvanceTo(_readResult.Buffer.Start);
|
||||
ThrowUnexpectedEndOfRequestContent();
|
||||
}
|
||||
|
||||
var returnedReadResultLength = CreateReadResultFromConnectionReadResult();
|
||||
|
||||
// Don't count bytes belonging to the next request, since read rate timeouts are done on a per-request basis.
|
||||
CountBytesRead(returnedReadResultLength);
|
||||
|
||||
// Only set _isReading if we are returning true.
|
||||
_isReading = true;
|
||||
readResult = _readResult;
|
||||
|
||||
if (readResult.IsCompleted)
|
||||
{
|
||||
TryStop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private long CreateReadResultFromConnectionReadResult()
|
||||
{
|
||||
var initialLength = _readResult.Buffer.Length;
|
||||
var maxLength = _inputLength + _examinedUnconsumedBytes;
|
||||
|
||||
if (initialLength < maxLength)
|
||||
{
|
||||
return initialLength;
|
||||
}
|
||||
|
||||
_readCompleted = true;
|
||||
_readResult = new ReadResult(
|
||||
_readResult.Buffer.Slice(0, maxLength),
|
||||
_readResult.IsCanceled,
|
||||
isCompleted: true);
|
||||
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
public override void AdvanceTo(SequencePosition consumed)
|
||||
|
|
@ -207,9 +213,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
if (_readCompleted)
|
||||
{
|
||||
_readResult = new ReadResult(_readResult.Buffer.Slice(consumed, _readResult.Buffer.End), Interlocked.Exchange(ref _userCanceled, 0) == 1, _readCompleted);
|
||||
// If the old stored _readResult was canceled, it's already been observed. Do not store a canceled read result permanently.
|
||||
_readResult = new ReadResult(_readResult.Buffer.Slice(consumed, _readResult.Buffer.End), isCanceled: false, _readCompleted);
|
||||
|
||||
if (_readResult.Buffer.Length == 0 && !_finalAdvanceCalled)
|
||||
if (!_finalAdvanceCalled && _readResult.Buffer.Length == 0)
|
||||
{
|
||||
_context.Input.AdvanceTo(consumed);
|
||||
_finalAdvanceCalled = true;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -21,19 +22,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
_context = context;
|
||||
}
|
||||
|
||||
protected void CheckCompletedReadResult(ReadResult result)
|
||||
[StackTraceHidden]
|
||||
protected void ThrowUnexpectedEndOfRequestContent()
|
||||
{
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
// OnInputOrOutputCompleted() is an idempotent method that closes the connection. Sometimes
|
||||
// input completion is observed here before the Input.OnWriterCompleted() callback is fired,
|
||||
// so we call OnInputOrOutputCompleted() now to prevent a race in our tests where a 400
|
||||
// response is written after observing the unexpected end of request content instead of just
|
||||
// closing the connection without a response as expected.
|
||||
_context.OnInputOrOutputCompleted();
|
||||
// OnInputOrOutputCompleted() is an idempotent method that closes the connection. Sometimes
|
||||
// input completion is observed here before the Input.OnWriterCompleted() callback is fired,
|
||||
// so we call OnInputOrOutputCompleted() now to prevent a race in our tests where a 400
|
||||
// response is written after observing the unexpected end of request content instead of just
|
||||
// closing the connection without a response as expected.
|
||||
_context.OnInputOrOutputCompleted();
|
||||
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.UnexpectedEndOfRequestContent);
|
||||
}
|
||||
BadHttpRequestException.Throw(RequestRejectionReason.UnexpectedEndOfRequestContent);
|
||||
}
|
||||
|
||||
public abstract bool TryReadInternal(out ReadResult readResult);
|
||||
|
|
|
|||
|
|
@ -25,19 +25,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
public override ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_completed)
|
||||
{
|
||||
throw new InvalidOperationException("Reading is not allowed after the reader was completed.");
|
||||
}
|
||||
ThrowIfCompleted();
|
||||
return _context.Input.ReadAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public override bool TryRead(out ReadResult result)
|
||||
{
|
||||
if (_completed)
|
||||
{
|
||||
throw new InvalidOperationException("Reading is not allowed after the reader was completed.");
|
||||
}
|
||||
ThrowIfCompleted();
|
||||
return _context.Input.TryRead(out result);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
|||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogError(2, ex, CoreStrings.BadDeveloperCertificateState);
|
||||
_logger?.LogError(3, ex, CoreStrings.BadDeveloperCertificateState);
|
||||
}
|
||||
|
||||
await sslStream.DisposeAsync();
|
||||
|
|
|
|||
|
|
@ -1189,6 +1189,56 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UnexpectedEndOfRequestContentIsRepeatedlyThrownForContentLengthBody()
|
||||
{
|
||||
using (var input = new TestInput())
|
||||
{
|
||||
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderContentLength = "5" }, input.Http1Connection);
|
||||
var reader = new HttpRequestPipeReader();
|
||||
reader.StartAcceptingReads(body);
|
||||
|
||||
input.Application.Output.Complete();
|
||||
|
||||
var ex0 = Assert.Throws<BadHttpRequestException>(() => reader.TryRead(out var readResult));
|
||||
var ex1 = Assert.Throws<BadHttpRequestException>(() => reader.TryRead(out var readResult));
|
||||
var ex2 = await Assert.ThrowsAsync<BadHttpRequestException>(() => reader.ReadAsync().AsTask());
|
||||
var ex3 = await Assert.ThrowsAsync<BadHttpRequestException>(() => reader.ReadAsync().AsTask());
|
||||
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex0.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex1.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex2.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex3.Reason);
|
||||
|
||||
await body.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UnexpectedEndOfRequestContentIsRepeatedlyThrownForChunkedBody()
|
||||
{
|
||||
using (var input = new TestInput())
|
||||
{
|
||||
var body = Http1MessageBody.For(HttpVersion.Http11, new HttpRequestHeaders { HeaderTransferEncoding = "chunked" }, input.Http1Connection);
|
||||
var reader = new HttpRequestPipeReader();
|
||||
reader.StartAcceptingReads(body);
|
||||
|
||||
input.Application.Output.Complete();
|
||||
|
||||
var ex0 = Assert.Throws<BadHttpRequestException>(() => reader.TryRead(out var readResult));
|
||||
var ex1 = Assert.Throws<BadHttpRequestException>(() => reader.TryRead(out var readResult));
|
||||
var ex2 = await Assert.ThrowsAsync<BadHttpRequestException>(() => reader.ReadAsync().AsTask());
|
||||
var ex3 = await Assert.ThrowsAsync<BadHttpRequestException>(() => reader.ReadAsync().AsTask());
|
||||
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex0.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex1.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex2.Reason);
|
||||
Assert.Equal(RequestRejectionReason.UnexpectedEndOfRequestContent, ex3.Reason);
|
||||
|
||||
await body.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompleteForChunkedAllowsConsumeToWork()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
}
|
||||
|
||||
await loggerProvider.FilterLogger.LogTcs.Task.DefaultTimeout();
|
||||
Assert.Equal(2, loggerProvider.FilterLogger.LastEventId);
|
||||
Assert.Equal(3, loggerProvider.FilterLogger.LastEventId);
|
||||
Assert.Equal(LogLevel.Error, loggerProvider.FilterLogger.LastLogLevel);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,12 @@ using Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests.TestTransport
|
|||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Serilog;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
||||
{
|
||||
public class RequestTests : LoggedTest
|
||||
public class RequestTests : TestApplicationErrorLoggerLoggedTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task StreamsAreNotPersistedAcrossRequests()
|
||||
|
|
@ -1440,6 +1441,39 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContentLengthSwallowedUnexpectedEndOfRequestContentDoesNotResultInWarnings()
|
||||
{
|
||||
var testContext = new TestServiceContext(LoggerFactory);
|
||||
|
||||
await using (var server = new TestServer(async httpContext =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await httpContext.Request.Body.ReadAsync(new byte[1], 0, 1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}, testContext))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"POST / HTTP/1.1",
|
||||
"Host:",
|
||||
"Content-Length: 5",
|
||||
"",
|
||||
"");
|
||||
connection.ShutdownSend();
|
||||
|
||||
await connection.ReceiveEnd();
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Empty(TestApplicationErrorLogger.Messages.Where(m => m.LogLevel >= LogLevel.Warning));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ContentLengthRequestCallCancelPendingReadWorks()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue