HEAD response can include Content-Length header.
This commit is contained in:
parent
ce6e65b1d7
commit
ffc3eb3afd
|
|
@ -617,6 +617,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
_responseBytesWritten += count;
|
_responseBytesWritten += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void VerifyResponseContentLength()
|
||||||
|
{
|
||||||
|
var responseHeaders = FrameResponseHeaders;
|
||||||
|
|
||||||
|
if (!HttpMethods.IsHead(Method) &&
|
||||||
|
!responseHeaders.HasTransferEncoding &&
|
||||||
|
responseHeaders.HeaderContentLengthValue.HasValue &&
|
||||||
|
_responseBytesWritten < responseHeaders.HeaderContentLengthValue.Value)
|
||||||
|
{
|
||||||
|
_keepAlive = false;
|
||||||
|
ReportApplicationError(new InvalidOperationException(
|
||||||
|
$"Response Content-Length mismatch: too few bytes written ({_responseBytesWritten} of {responseHeaders.HeaderContentLengthValue.Value})."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void WriteChunked(ArraySegment<byte> data)
|
private void WriteChunked(ArraySegment<byte> data)
|
||||||
{
|
{
|
||||||
SocketOutput.Write(data, chunk: true);
|
SocketOutput.Write(data, chunk: true);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
|
|
@ -92,16 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _application.ProcessRequestAsync(context).ConfigureAwait(false);
|
await _application.ProcessRequestAsync(context).ConfigureAwait(false);
|
||||||
|
VerifyResponseContentLength();
|
||||||
var responseHeaders = FrameResponseHeaders;
|
|
||||||
if (!responseHeaders.HasTransferEncoding &&
|
|
||||||
responseHeaders.HasContentLength &&
|
|
||||||
_responseBytesWritten < responseHeaders.HeaderContentLengthValue.Value)
|
|
||||||
{
|
|
||||||
_keepAlive = false;
|
|
||||||
ReportApplicationError(new InvalidOperationException(
|
|
||||||
$"Response Content-Length mismatch: too few bytes written ({_responseBytesWritten} of {responseHeaders.HeaderContentLengthValue.Value})."));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -607,6 +607,62 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||||
Assert.Equal(0, testLogger.ApplicationErrorsLogged);
|
Assert.Equal(0, testLogger.ApplicationErrorsLogged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HeadResponseCanContainContentLengthHeader()
|
||||||
|
{
|
||||||
|
var testLogger = new TestApplicationErrorLogger();
|
||||||
|
var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(testLogger) };
|
||||||
|
|
||||||
|
using (var server = new TestServer(httpContext =>
|
||||||
|
{
|
||||||
|
httpContext.Response.ContentLength = 42;
|
||||||
|
return TaskCache.CompletedTask;
|
||||||
|
}, serviceContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"HEAD / HTTP/1.1",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
$"HTTP/1.1 200 OK",
|
||||||
|
$"Date: {server.Context.DateHeaderValue}",
|
||||||
|
"Content-Length: 42",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HeadResponseCanContainContentLengthHeaderButBodyNotWritten()
|
||||||
|
{
|
||||||
|
var testLogger = new TestApplicationErrorLogger();
|
||||||
|
var serviceContext = new TestServiceContext { Log = new TestKestrelTrace(testLogger) };
|
||||||
|
|
||||||
|
using (var server = new TestServer(async httpContext =>
|
||||||
|
{
|
||||||
|
httpContext.Response.ContentLength = 12;
|
||||||
|
await httpContext.Response.WriteAsync("hello, world");
|
||||||
|
}, serviceContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"HEAD / HTTP/1.1",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
$"HTTP/1.1 200 OK",
|
||||||
|
$"Date: {server.Context.DateHeaderValue}",
|
||||||
|
"Content-Length: 12",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static TheoryData<string, StringValues, string> NullHeaderData
|
public static TheoryData<string, StringValues, string> NullHeaderData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue