Ignore zero length writes when automatically chunking responses

- Zero length writes would previously be interpreted as the end of response
- Optimize writing the chunked response suffix
- Add tests for automatic response chunking
This commit is contained in:
Stephen Halter 2015-08-14 15:04:00 -07:00
parent 3fb33119ee
commit 753d64660d
2 changed files with 97 additions and 2 deletions

View File

@ -257,8 +257,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
if (_autoChunk)
{
WriteChunkPrefix(numOctets: 0);
WriteChunkSuffix();
WriteChunkedResponseSuffix();
}
}
catch (Exception ex)
@ -278,6 +277,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
if (_autoChunk)
{
if (data.Count == 0)
{
callback(null, state);
return;
}
WriteChunkPrefix(data.Count);
}
@ -306,6 +311,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
}
private static readonly ArraySegment<byte> _endChunkBytes = CreateAsciiByteArraySegment("\r\n");
private static readonly ArraySegment<byte> _endChunkedResponseBytes = CreateAsciiByteArraySegment("0\r\n\r\n");
private void WriteChunkSuffix()
{
@ -321,6 +327,20 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
immediate: true);
}
private void WriteChunkedResponseSuffix()
{
SocketOutput.Write(_endChunkedResponseBytes,
(error, _) =>
{
if (error != null)
{
Trace.WriteLine("WriteChunkedResponseSuffix" + error.ToString());
}
},
null,
immediate: true);
}
public void Upgrade(IDictionary<string, object> options, Func<object, Task> callback)
{
_keepAlive = false;

View File

@ -0,0 +1,75 @@
// 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.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Server.KestrelTests
{
public class ChunkedResponseTests
{
[Fact]
public async Task ResponsesAreChunkedAutomatically()
{
using (var server = new TestServer(async frame =>
{
frame.ResponseHeaders.Clear();
await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
}))
{
using (var connection = new TestConnection())
{
await connection.SendEnd(
"GET / HTTP/1.1",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
[Fact]
public async Task ZeroLengthWritesAreIgnored()
{
using (var server = new TestServer(async frame =>
{
frame.ResponseHeaders.Clear();
await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
await frame.ResponseBody.WriteAsync(new byte[0], 0, 0);
await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
}))
{
using (var connection = new TestConnection())
{
await connection.SendEnd(
"GET / HTTP/1.1",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Transfer-Encoding: chunked",
"",
"6",
"Hello ",
"6",
"World!",
"0",
"",
"");
}
}
}
}
}