Skip to content
This repository has been archived by the owner on Dec 18, 2018. It is now read-only.

Commit

Permalink
Don't automatically set Content-Length: 0 in some circumstances
Browse files Browse the repository at this point in the history
- When in response to a HEAD Request
- For 101, 204, 205 and 304 responses
- For non keep-alive connections
  • Loading branch information
halter73 committed Aug 26, 2015
1 parent 6975923 commit 2642c84
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 18 deletions.
40 changes: 28 additions & 12 deletions src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -494,26 +494,33 @@ public void ProduceEnd(Exception ex)
}
}

if (appCompleted && !hasTransferEncoding && !hasContentLength)
{
// Since the app has completed and we are only now generating
// the headers we can safely set the Content-Length to 0.
writer.Write("Content-Length: 0\r\n");
hasContentLength = true;
}

if (_keepAlive && !hasTransferEncoding && !hasContentLength)
{
if (HttpVersion == "HTTP/1.1")
if (appCompleted)
{
_autoChunk = true;
writer.Write("Transfer-Encoding: chunked\r\n");
// Don't set the Content-Length or Transfer-Encoding headers
// automatically for HEAD requests or 101, 204, 205, 304 responses.
if (Method != "HEAD" && StatusCanHaveBody(StatusCode))
{
// Since the app has completed and we are only now generating
// the headers we can safely set the Content-Length to 0.
writer.Write("Content-Length: 0\r\n");
}
}
else
{
_keepAlive = false;
if (HttpVersion == "HTTP/1.1")
{
_autoChunk = true;
writer.Write("Transfer-Encoding: chunked\r\n");
}
else
{
_keepAlive = false;
}
}
}

if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1")
{
writer.Write("Connection: close\r\n\r\n");
Expand Down Expand Up @@ -673,5 +680,14 @@ private void AddRequestHeader(byte[] keyBytes, int keyOffset, int keyLength, str
{
_requestHeaders.Append(keyBytes, keyOffset, keyLength, value);
}

public bool StatusCanHaveBody(int statusCode)
{
// List of status codes taken from Microsoft.Net.Http.Server.Response
return statusCode != 101 &&
statusCode != 204 &&
statusCode != 205 &&
statusCode != 304;
}
}
}
122 changes: 116 additions & 6 deletions test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ private async Task AppChunked(Frame frame)
await frame.ResponseBody.WriteAsync(bytes, 0, bytes.Length);
}

private Task EmptyApp(Frame frame)
{
frame.ResponseHeaders.Clear();
return Task.FromResult<object>(null);
}

[Fact]
public void EngineCanStartAndStop()
{
Expand Down Expand Up @@ -370,28 +376,132 @@ public async Task DisconnectingClient()
"\r\n");
await connection.ReceiveEnd(
"HTTP/1.0 200 OK",
"Content-Length: 0",
"\r\n");
}
}
}

[Fact]
public async Task EmptyResponseBodyHandledCorrectlyWithoutAnyWrites()
public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites()
{
using (var server = new TestServer(frame =>
using (var server = new TestServer(EmptyApp))
{
frame.ResponseHeaders.Clear();
return Task.FromResult<object>(null);
}))
using (var connection = new TestConnection())
{
await connection.SendEnd(
"GET / HTTP/1.1",
"",
"GET / HTTP/1.0",
"Connection: keep-alive",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Content-Length: 0",
"",
"HTTP/1.0 200 OK",
"Content-Length: 0",
"Connection: keep-alive",
"",
"");
}
}
}

[Fact]
public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests()
{
using (var server = new TestServer(EmptyApp))
{
using (var connection = new TestConnection())
{
await connection.SendEnd(
"GET / HTTP/1.1",
"Connection: close",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"Connection: close",
"",
"");
}

using (var connection = new TestConnection())
{
await connection.SendEnd(
"GET / HTTP/1.0",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.0 200 OK",
"",
"");
}
}
}

[Fact]
public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests()
{
using (var server = new TestServer(EmptyApp))
{
using (var connection = new TestConnection())
{
await connection.SendEnd(
"HEAD / HTTP/1.1",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
"",
"");
}
}
}

[Fact]
public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes()
{
using (var server = new TestServer(async frame =>
{
frame.ResponseHeaders.Clear();
using (var reader = new StreamReader(frame.RequestBody, Encoding.ASCII))
{
var statusString = await reader.ReadLineAsync();
frame.StatusCode = int.Parse(statusString);
}
}))
{
using (var connection = new TestConnection())
{
await connection.SendEnd(
"POST / HTTP/1.1",
"Content-Length: 3",
"",
"101POST / HTTP/1.1",
"Content-Length: 3",
"",
"204POST / HTTP/1.1",
"Content-Length: 3",
"",
"205POST / HTTP/1.1",
"Content-Length: 3",
"",
"304POST / HTTP/1.1",
"Content-Length: 3",
"",
"200");
await connection.ReceiveEnd(
"HTTP/1.1 101 Switching Protocols",
"",
"HTTP/1.1 204 No Content",
"",
"HTTP/1.1 205 Reset Content",
"",
"HTTP/1.1 304 Not Modified",
"",
"HTTP/1.1 200 OK",
"Content-Length: 0",
"",
Expand Down

0 comments on commit 2642c84

Please sign in to comment.