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:
Stephen Halter 2016-08-17 11:10:39 -07:00
parent 16fbb94c44
commit 972be6e8c1
3 changed files with 87 additions and 47 deletions

View File

@ -723,14 +723,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
var end = SocketOutput.ProducingStart();
if (_keepAlive && hasConnection)
{
foreach (var connectionValue in responseHeaders.HeaderConnection)
{
if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1)
{
_keepAlive = false;
break;
}
}
var connectionValue = responseHeaders.HeaderConnection.ToString();
_keepAlive = connectionValue.Equals("keep-alive", StringComparison.OrdinalIgnoreCase);
}
if (!responseHeaders.HasTransferEncoding && !responseHeaders.HasContentLength)
@ -746,7 +740,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Http
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
}
}
else
else if(_keepAlive)
{
// 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

View File

@ -63,42 +63,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory]
[MemberData(nameof(ConnectionFilterData))]
public async Task ResponsesAreChunkedAutomaticallyForHttp11NonKeepAliveRequests(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)
public async Task ResponsesAreNotChunkedAutomaticallyForHttp10RequestsAndHttp11NonKeepAliveRequests(TestServiceContext testContext)
{
using (var server = new TestServer(async httpContext =>
{
@ -111,6 +76,51 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{
await connection.SendEnd(
"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(

View File

@ -120,7 +120,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
engine.Dispose();
}
[Theory]
[MemberData(nameof(ConnectionFilterData))]
public async Task Http10RequestReceivesHttp11Response(TestServiceContext testContext)
@ -143,7 +142,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
}
}
[Theory]
[MemberData(nameof(ConnectionFilterData))]
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");
}
}
}
}
}