Disable automatic chunking for all non keep-alive requests
- Fixes a bug where Upgrade requests (e.g. WebSockets) would be chunked - Allows chunking to be disabled by setting "Connection: close" on the response
This commit is contained in:
parent
16fbb94c44
commit
972be6e8c1
|
|
@ -723,14 +723,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
var end = SocketOutput.ProducingStart();
|
var end = SocketOutput.ProducingStart();
|
||||||
if (_keepAlive && hasConnection)
|
if (_keepAlive && hasConnection)
|
||||||
{
|
{
|
||||||
foreach (var connectionValue in responseHeaders.HeaderConnection)
|
var connectionValue = responseHeaders.HeaderConnection.ToString();
|
||||||
{
|
_keepAlive = connectionValue.Equals("keep-alive", StringComparison.OrdinalIgnoreCase);
|
||||||
if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
|
|
||||||
{
|
|
||||||
_keepAlive = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
|
if (!responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
|
||||||
|
|
@ -746,7 +740,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
|
||||||
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if(_keepAlive)
|
||||||
{
|
{
|
||||||
// Note for future reference: never change this to set _autoChunk to true on HTTP/1.0
|
// Note for future reference: never change this to set _autoChunk to true on HTTP/1.0
|
||||||
// connections, even if we were to infer the client supports it because an HTTP/1.0 request
|
// connections, even if we were to infer the client supports it because an HTTP/1.0 request
|
||||||
|
|
|
||||||
|
|
@ -63,42 +63,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
public async Task ResponsesAreChunkedAutomaticallyForHttp11NonKeepAliveRequests(TestServiceContext testContext)
|
public async Task ResponsesAreNotChunkedAutomaticallyForHttp10RequestsAndHttp11NonKeepAliveRequests(TestServiceContext testContext)
|
||||||
{
|
|
||||||
using (var server = new TestServer(async httpContext =>
|
|
||||||
{
|
|
||||||
var response = httpContext.Response;
|
|
||||||
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
|
|
||||||
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
|
|
||||||
}, testContext))
|
|
||||||
{
|
|
||||||
using (var connection = server.CreateConnection())
|
|
||||||
{
|
|
||||||
await connection.SendEnd(
|
|
||||||
"GET / HTTP/1.1",
|
|
||||||
"Connection: close",
|
|
||||||
"",
|
|
||||||
"");
|
|
||||||
await connection.ReceiveEnd(
|
|
||||||
"HTTP/1.1 200 OK",
|
|
||||||
"Connection: close",
|
|
||||||
$"Date: {testContext.DateHeaderValue}",
|
|
||||||
"Transfer-Encoding: chunked",
|
|
||||||
"",
|
|
||||||
"6",
|
|
||||||
"Hello ",
|
|
||||||
"6",
|
|
||||||
"World!",
|
|
||||||
"0",
|
|
||||||
"",
|
|
||||||
"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
|
||||||
public async Task ResponsesAreNotChunkedAutomaticallyForHttp10Requests(TestServiceContext testContext)
|
|
||||||
{
|
{
|
||||||
using (var server = new TestServer(async httpContext =>
|
using (var server = new TestServer(async httpContext =>
|
||||||
{
|
{
|
||||||
|
|
@ -111,6 +76,51 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
{
|
{
|
||||||
await connection.SendEnd(
|
await connection.SendEnd(
|
||||||
"GET / HTTP/1.0",
|
"GET / HTTP/1.0",
|
||||||
|
"Connection: keep-alive",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
"HTTP/1.1 200 OK",
|
||||||
|
"Connection: close",
|
||||||
|
$"Date: {testContext.DateHeaderValue}",
|
||||||
|
"",
|
||||||
|
"Hello World!");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
|
"Connection: close",
|
||||||
|
"",
|
||||||
|
"");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
"HTTP/1.1 200 OK",
|
||||||
|
"Connection: close",
|
||||||
|
$"Date: {testContext.DateHeaderValue}",
|
||||||
|
"",
|
||||||
|
"Hello World!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
|
public async Task SettingConnectionCloseHeaderInAppDisablesChunking(TestServiceContext testContext)
|
||||||
|
{
|
||||||
|
using (var server = new TestServer(async httpContext =>
|
||||||
|
{
|
||||||
|
var response = httpContext.Response;
|
||||||
|
response.Headers["Connection"] = "close";
|
||||||
|
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
|
||||||
|
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
|
||||||
|
}, testContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
"",
|
"",
|
||||||
"");
|
"");
|
||||||
await connection.ReceiveEnd(
|
await connection.ReceiveEnd(
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
engine.Dispose();
|
engine.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
public async Task Http10RequestReceivesHttp11Response(TestServiceContext testContext)
|
public async Task Http10RequestReceivesHttp11Response(TestServiceContext testContext)
|
||||||
|
|
@ -143,7 +142,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ConnectionFilterData))]
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
public async Task Http11(TestServiceContext testContext)
|
public async Task Http11(TestServiceContext testContext)
|
||||||
|
|
@ -1310,5 +1308,43 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ConnectionFilterData))]
|
||||||
|
public async Task UpgradeRequestIsNotKeptAliveOrChunked(TestServiceContext testContext)
|
||||||
|
{
|
||||||
|
using (var server = new TestServer(async context =>
|
||||||
|
{
|
||||||
|
var upgradeFeature = context.Features.Get<IHttpUpgradeFeature>();
|
||||||
|
var duplexStream = await upgradeFeature.UpgradeAsync();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
var count = await duplexStream.ReadAsync(buffer, 0, buffer.Length);
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await duplexStream.WriteAsync(buffer, 0, count);
|
||||||
|
}
|
||||||
|
}, testContext))
|
||||||
|
{
|
||||||
|
using (var connection = server.CreateConnection())
|
||||||
|
{
|
||||||
|
await connection.SendEnd(
|
||||||
|
"GET / HTTP/1.1",
|
||||||
|
"Connection: Upgrade",
|
||||||
|
"",
|
||||||
|
"Hello World");
|
||||||
|
await connection.ReceiveEnd(
|
||||||
|
"HTTP/1.1 101 Switching Protocols",
|
||||||
|
"Connection: Upgrade",
|
||||||
|
$"Date: {testContext.DateHeaderValue}",
|
||||||
|
"",
|
||||||
|
"Hello World");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue