Merge branch 'release' into dev
This commit is contained in:
commit
40ca61b640
|
|
@ -254,11 +254,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
{
|
||||
FireOnStarting();
|
||||
}
|
||||
|
||||
if (_autoChunk)
|
||||
{
|
||||
WriteChunkedResponseSuffix();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -380,7 +375,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
public void ProduceStart(bool immediate = true)
|
||||
public void ProduceStart(bool immediate = true, bool appCompleted = false)
|
||||
{
|
||||
// ProduceStart shouldn't no-op in the future just b/c FireOnStarting throws.
|
||||
if (_responseStarted) return;
|
||||
|
|
@ -389,7 +384,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase);
|
||||
|
||||
var responseHeader = CreateResponseHeader(status, ResponseHeaders);
|
||||
var responseHeader = CreateResponseHeader(status, appCompleted, ResponseHeaders);
|
||||
SocketOutput.Write(
|
||||
responseHeader.Item1,
|
||||
(error, x) =>
|
||||
|
|
@ -428,7 +423,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
}
|
||||
|
||||
ProduceStart();
|
||||
ProduceStart(immediate: true, appCompleted: true);
|
||||
|
||||
// _autoChunk should be checked after we are sure ProduceStart() has been called
|
||||
// since ProduceStart() may set _autoChunk to true.
|
||||
if (_autoChunk)
|
||||
{
|
||||
WriteChunkedResponseSuffix();
|
||||
}
|
||||
|
||||
if (!_keepAlive)
|
||||
{
|
||||
|
|
@ -440,7 +442,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
}
|
||||
|
||||
private Tuple<ArraySegment<byte>, IDisposable> CreateResponseHeader(
|
||||
string status, IEnumerable<KeyValuePair<string, string[]>> headers)
|
||||
string status,
|
||||
bool appCompleted,
|
||||
IEnumerable<KeyValuePair<string, string[]>> headers)
|
||||
{
|
||||
var writer = new MemoryPoolTextWriter(Memory);
|
||||
writer.Write(HttpVersion);
|
||||
|
|
@ -492,16 +496,31 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
|
||||
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");
|
||||
|
|
@ -661,5 +680,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
|
|||
{
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
|
@ -71,5 +72,85 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite()
|
||||
{
|
||||
using (var server = new TestServer(async frame =>
|
||||
{
|
||||
frame.ResponseHeaders.Clear();
|
||||
await frame.ResponseBody.WriteAsync(new byte[0], 0, 0);
|
||||
}))
|
||||
{
|
||||
using (var connection = new TestConnection())
|
||||
{
|
||||
await connection.SendEnd(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Transfer-Encoding: chunked",
|
||||
"",
|
||||
"0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConnectionClosedIfExeptionThrownAfterWrite()
|
||||
{
|
||||
using (var server = new TestServer(async frame =>
|
||||
{
|
||||
frame.ResponseHeaders.Clear();
|
||||
await frame.ResponseBody.WriteAsync(Encoding.ASCII.GetBytes("Hello World!"), 0, 12);
|
||||
throw new Exception();
|
||||
}))
|
||||
{
|
||||
using (var connection = new TestConnection())
|
||||
{
|
||||
// SendEnd is not called, so it isn't the client closing the connection.
|
||||
// client closing the connection.
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.1 200 OK",
|
||||
"Transfer-Encoding: chunked",
|
||||
"",
|
||||
"c",
|
||||
"Hello World!",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite()
|
||||
{
|
||||
using (var server = new TestServer(async frame =>
|
||||
{
|
||||
frame.ResponseHeaders.Clear();
|
||||
await frame.ResponseBody.WriteAsync(new byte[0], 0, 0);
|
||||
throw new Exception();
|
||||
}))
|
||||
{
|
||||
using (var connection = new TestConnection())
|
||||
{
|
||||
// SendEnd is not called, so it isn't the client closing the connection.
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"",
|
||||
"");
|
||||
|
||||
// Nothing (not even headers) are written, but the connection is closed.
|
||||
await connection.ReceiveEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,12 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
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()
|
||||
{
|
||||
|
|
@ -235,6 +241,35 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet()
|
||||
{
|
||||
using (var server = new TestServer(App))
|
||||
{
|
||||
using (var connection = new TestConnection())
|
||||
{
|
||||
await connection.SendEnd(
|
||||
"GET / HTTP/1.0",
|
||||
"Connection: keep-alive",
|
||||
"",
|
||||
"POST / HTTP/1.0",
|
||||
"Connection: keep-alive",
|
||||
"Content-Length: 7",
|
||||
"",
|
||||
"Goodbye");
|
||||
await connection.Receive(
|
||||
"HTTP/1.0 200 OK",
|
||||
"Content-Length: 0",
|
||||
"Connection: keep-alive",
|
||||
"\r\n");
|
||||
await connection.ReceiveEnd(
|
||||
"HTTP/1.0 200 OK",
|
||||
"",
|
||||
"Goodbye");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Http10KeepAliveContentLength()
|
||||
{
|
||||
|
|
@ -346,6 +381,135 @@ namespace Microsoft.AspNet.Server.KestrelTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites()
|
||||
{
|
||||
using (var server = new TestServer(EmptyApp))
|
||||
{
|
||||
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",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ThrowingResultsIn500Response()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue