Set Date and Server headers at response start

Closes #223
This commit is contained in:
Kristian Hellang 2016-05-21 02:14:31 +02:00
parent 71ecf5612f
commit 72cc0ffbd5
15 changed files with 274 additions and 296 deletions

View File

@ -32,22 +32,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
/// Initializes a new instance of the <see cref="DateHeaderValueManager"/> class. /// Initializes a new instance of the <see cref="DateHeaderValueManager"/> class.
/// </summary> /// </summary>
public DateHeaderValueManager() public DateHeaderValueManager()
: this( : this(systemClock: new SystemClock())
systemClock: new SystemClock(),
timeWithoutRequestsUntilIdle: TimeSpan.FromSeconds(10),
timerInterval: TimeSpan.FromSeconds(1))
{ {
} }
// Internal for testing // Internal for testing
internal DateHeaderValueManager( internal DateHeaderValueManager(
ISystemClock systemClock, ISystemClock systemClock,
TimeSpan timeWithoutRequestsUntilIdle, TimeSpan? timeWithoutRequestsUntilIdle = null,
TimeSpan timerInterval) TimeSpan? timerInterval = null)
{ {
if (systemClock == null)
{
throw new ArgumentNullException(nameof(systemClock));
}
_systemClock = systemClock; _systemClock = systemClock;
_timeWithoutRequestsUntilIdle = timeWithoutRequestsUntilIdle; _timeWithoutRequestsUntilIdle = timeWithoutRequestsUntilIdle ?? TimeSpan.FromSeconds(10);
_timerInterval = timerInterval; _timerInterval = timerInterval ?? TimeSpan.FromSeconds(1);
_dateValueTimer = new Timer(TimerLoop, state: null, dueTime: Timeout.Infinite, period: Timeout.Infinite); _dateValueTimer = new Timer(TimerLoop, state: null, dueTime: Timeout.Infinite, period: Timeout.Infinite);
} }

View File

@ -33,6 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private static readonly byte[] _bytesHttpVersion11 = Encoding.ASCII.GetBytes("HTTP/1.1 "); private static readonly byte[] _bytesHttpVersion11 = Encoding.ASCII.GetBytes("HTTP/1.1 ");
private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0"); private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0");
private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n"); private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n");
private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
private static Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r'); private static Vector<byte> _vectorCRs = new Vector<byte>((byte)'\r');
private static Vector<byte> _vectorColons = new Vector<byte>((byte)':'); private static Vector<byte> _vectorColons = new Vector<byte>((byte)':');
@ -44,7 +45,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private readonly object _onCompletedSync = new Object(); private readonly object _onCompletedSync = new Object();
private bool _requestRejected; private bool _requestRejected;
private Headers _frameHeaders;
private Streams _frameStreams; private Streams _frameStreams;
protected List<KeyValuePair<Func<object, Task>, object>> _onStarting; protected List<KeyValuePair<Func<object, Task>, object>> _onStarting;
@ -210,21 +210,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
get { return _requestProcessingStatus == RequestProcessingStatus.ResponseStarted; } get { return _requestProcessingStatus == RequestProcessingStatus.ResponseStarted; }
} }
protected FrameRequestHeaders FrameRequestHeaders => _frameHeaders.RequestHeaders; protected FrameRequestHeaders FrameRequestHeaders { get; private set; }
protected FrameResponseHeaders FrameResponseHeaders { get; private set; }
public void InitializeHeaders() public void InitializeHeaders()
{ {
if (_frameHeaders == null) if (FrameRequestHeaders == null)
{ {
_frameHeaders = new Headers(ServerOptions); RequestHeaders = FrameRequestHeaders = new FrameRequestHeaders();
RequestHeaders = _frameHeaders.RequestHeaders;
ResponseHeaders = _frameHeaders.ResponseHeaders;
} }
_frameHeaders.Initialize(DateHeaderValueManager); if (FrameResponseHeaders == null)
{
ResponseHeaders = FrameResponseHeaders = new FrameResponseHeaders();
}
} }
public void InitializeStreams(MessageBody messageBody) public void InitializeStreams(MessageBody messageBody)
{ {
if (_frameStreams == null) if (_frameStreams == null)
@ -259,7 +261,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public void Reset() public void Reset()
{ {
_frameHeaders?.Reset(); FrameRequestHeaders?.Reset();
FrameResponseHeaders?.Reset();
_onStarting = null; _onStarting = null;
_onCompleted = null; _onCompleted = null;
@ -598,7 +601,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{ {
if (_requestProcessingStatus == RequestProcessingStatus.RequestStarted && _requestRejected) if (_requestProcessingStatus == RequestProcessingStatus.RequestStarted && _requestRejected)
{ {
if (_frameHeaders == null) if (FrameRequestHeaders == null || FrameResponseHeaders == null)
{ {
InitializeHeaders(); InitializeHeaders();
} }
@ -634,7 +637,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
ReasonPhrase = null; ReasonPhrase = null;
var responseHeaders = _frameHeaders.ResponseHeaders; var responseHeaders = FrameResponseHeaders;
responseHeaders.Reset(); responseHeaders.Reset();
var dateHeaderValues = DateHeaderValueManager.GetDateHeaderValues(); var dateHeaderValues = DateHeaderValueManager.GetDateHeaderValues();
@ -643,7 +646,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
if (ServerOptions.AddServerHeader) if (ServerOptions.AddServerHeader)
{ {
responseHeaders.SetRawServer(Constants.ServerName, Headers.BytesServer); responseHeaders.SetRawServer(Constants.ServerName, _bytesServer);
} }
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
@ -698,7 +701,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
byte[] statusBytes, byte[] statusBytes,
bool appCompleted) bool appCompleted)
{ {
var responseHeaders = _frameHeaders.ResponseHeaders; var responseHeaders = FrameResponseHeaders;
responseHeaders.SetReadOnly(); responseHeaders.SetReadOnly();
var hasConnection = responseHeaders.HasConnection; var hasConnection = responseHeaders.HasConnection;
@ -759,6 +762,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive); responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive);
} }
if (ServerOptions.AddServerHeader && !responseHeaders.HasServer)
{
responseHeaders.SetRawServer(Constants.ServerName, _bytesServer);
}
if (!responseHeaders.HasDate)
{
var dateHeaderValues = DateHeaderValueManager.GetDateHeaderValues();
responseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
}
end.CopyFrom(_bytesHttpVersion11); end.CopyFrom(_bytesHttpVersion11);
end.CopyFrom(statusBytes); end.CopyFrom(statusBytes);
responseHeaders.CopyTo(ref end); responseHeaders.CopyTo(ref end);

View File

@ -19,6 +19,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
public bool HasContentLength => HeaderContentLength.Count != 0; public bool HasContentLength => HeaderContentLength.Count != 0;
public bool HasServer => HeaderServer.Count != 0;
public bool HasDate => HeaderDate.Count != 0;
public Enumerator GetEnumerator() public Enumerator GetEnumerator()
{ {

View File

@ -1,40 +0,0 @@
// 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 Microsoft.AspNetCore.Server.Kestrel.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
{
class Headers
{
public static readonly byte[] BytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
private readonly KestrelServerOptions _options;
public Headers(KestrelServerOptions options)
{
_options = options;
}
public void Initialize(DateHeaderValueManager dateValueManager)
{
var dateHeaderValues = dateValueManager.GetDateHeaderValues();
ResponseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
if (_options.AddServerHeader)
{
ResponseHeaders.SetRawServer("Kestrel", BytesServer);
}
}
public FrameRequestHeaders RequestHeaders { get; } = new FrameRequestHeaders();
public FrameResponseHeaders ResponseHeaders { get; } = new FrameResponseHeaders();
public void Reset()
{
RequestHeaders.Reset();
ResponseHeaders.Reset();
}
}
}

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
using (var server = new TestServer(context => { return Task.FromResult(0); })) using (var server = new TestServer(context => { return Task.FromResult(0); }))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd(request); await connection.SendEnd(request);
await ReceiveBadRequestResponse(connection); await ReceiveBadRequestResponse(connection);
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
using (var server = new TestServer(context => { return Task.FromResult(0); })) using (var server = new TestServer(context => { return Task.FromResult(0); }))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send(request); await connection.Send(request);
await ReceiveBadRequestResponse(connection); await ReceiveBadRequestResponse(connection);
@ -103,10 +103,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"Connection: close", "Connection: close",
""); "");
await connection.ReceiveStartsWith("Date: ");
await connection.ReceiveEnd( await connection.ReceiveEnd(
$"Date: {connection.Server.Context.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
} }

View File

@ -14,11 +14,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
public class ChunkedRequestTests public class ChunkedRequestTests
{ {
public static TheoryData<ServiceContext> ConnectionFilterData public static TheoryData<TestServiceContext> ConnectionFilterData
{ {
get get
{ {
return new TheoryData<ServiceContext> return new TheoryData<TestServiceContext>
{ {
{ {
new TestServiceContext() new TestServiceContext()
@ -34,7 +34,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var request = httpContext.Request; var request = httpContext.Request;
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
while (true) while (true)
{ {
var buffer = new byte[8192]; var buffer = new byte[8192];
@ -55,18 +54,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await request.Body.CopyToAsync(data); await request.Body.CopyToAsync(data);
var bytes = data.ToArray(); var bytes = data.ToArray();
response.Headers.Clear();
response.Headers["Content-Length"] = bytes.Length.ToString(); response.Headers["Content-Length"] = bytes.Length.ToString();
await response.Body.WriteAsync(bytes, 0, bytes.Length); await response.Body.WriteAsync(bytes, 0, bytes.Length);
} }
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10TransferEncoding(ServiceContext testContext) public async Task Http10TransferEncoding(TestServiceContext testContext)
{ {
using (var server = new TestServer(App, testContext)) using (var server = new TestServer(App, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -78,6 +76,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
"Hello World"); "Hello World");
} }
@ -86,11 +85,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10KeepAliveTransferEncoding(ServiceContext testContext) public async Task Http10KeepAliveTransferEncoding(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -107,11 +106,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: keep-alive", "Connection: keep-alive",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -121,7 +122,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task RequestBodyIsConsumedAutomaticallyIfAppDoesntConsumeItFully(ServiceContext testContext) public async Task RequestBodyIsConsumedAutomaticallyIfAppDoesntConsumeItFully(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
@ -130,13 +131,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
Assert.Equal("POST", request.Method); Assert.Equal("POST", request.Method);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -154,12 +154,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Goodbye"); "Goodbye");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello WorldHTTP/1.1 200 OK", "Hello WorldHTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello WorldHTTP/1.1 200 OK", "Hello WorldHTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
@ -169,7 +172,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task TrailingHeadersAreParsed(ServiceContext testContext) public async Task TrailingHeadersAreParsed(TestServiceContext testContext)
{ {
var requestCount = 10; var requestCount = 10;
var requestsReceived = 0; var requestsReceived = 0;
@ -199,7 +202,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
requestsReceived++; requestsReceived++;
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
@ -207,6 +209,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var response = string.Join("\r\n", new string[] { var response = string.Join("\r\n", new string[] {
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"}); "Hello World"});
@ -244,7 +247,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var fullRequest = sendSequence.ToArray(); var fullRequest = sendSequence.ToArray();
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd(fullRequest); await connection.SendEnd(fullRequest);
@ -255,7 +258,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ExtensionsAreIgnored(ServiceContext testContext) public async Task ExtensionsAreIgnored(TestServiceContext testContext)
{ {
var requestCount = 10; var requestCount = 10;
var requestsReceived = 0; var requestsReceived = 0;
@ -285,7 +288,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
requestsReceived++; requestsReceived++;
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
@ -293,6 +295,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var response = string.Join("\r\n", new string[] { var response = string.Join("\r\n", new string[] {
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"}); "Hello World"});
@ -330,7 +333,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var fullRequest = sendSequence.ToArray(); var fullRequest = sendSequence.ToArray();
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd(fullRequest); await connection.SendEnd(fullRequest);
@ -341,7 +344,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task InvalidLengthResultsIn400(ServiceContext testContext) public async Task InvalidLengthResultsIn400(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
@ -355,13 +358,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
;// read to end ;// read to end
} }
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -373,10 +375,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"HTTP/1.1 400 Bad Request", "HTTP/1.1 400 Bad Request",
"Connection: close", "Connection: close",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
} }
@ -385,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task InvalidSizedDataResultsIn400(ServiceContext testContext) public async Task InvalidSizedDataResultsIn400(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
@ -399,13 +400,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
;// read to end ;// read to end
} }
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -418,10 +418,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"HTTP/1.1 400 Bad Request", "HTTP/1.1 400 Bad Request",
"Connection: close", "Connection: close",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
} }

View File

@ -12,11 +12,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
public class ChunkedResponseTests public class ChunkedResponseTests
{ {
public static TheoryData<ServiceContext> ConnectionFilterData public static TheoryData<TestServiceContext> ConnectionFilterData
{ {
get get
{ {
return new TheoryData<ServiceContext> return new TheoryData<TestServiceContext>
{ {
{ {
new TestServiceContext() new TestServiceContext()
@ -30,17 +30,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ResponsesAreChunkedAutomatically(ServiceContext testContext) public async Task ResponsesAreChunkedAutomatically(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -48,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"6", "6",
@ -63,18 +63,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ZeroLengthWritesAreIgnored(ServiceContext testContext) public async Task ZeroLengthWritesAreIgnored(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
await response.Body.WriteAsync(new byte[0], 0, 0); await response.Body.WriteAsync(new byte[0], 0, 0);
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -82,6 +81,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"6", "6",
@ -97,16 +97,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite(ServiceContext testContext) public async Task EmptyResponseBodyHandledCorrectlyWithZeroLengthWrite(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(new byte[0], 0, 0); await response.Body.WriteAsync(new byte[0], 0, 0);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -114,6 +113,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"0", "0",
@ -125,17 +125,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ConnectionClosedIfExeptionThrownAfterWrite(ServiceContext testContext) public async Task ConnectionClosedIfExeptionThrownAfterWrite(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World!"), 0, 12); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World!"), 0, 12);
throw new Exception(); throw new Exception();
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
// SendEnd is not called, so it isn't the client closing the connection. // SendEnd is not called, so it isn't the client closing the connection.
// client closing the connection. // client closing the connection.
@ -145,6 +144,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"c", "c",
@ -156,17 +156,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite(ServiceContext testContext) public async Task ConnectionClosedIfExeptionThrownAfterZeroLengthWrite(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(new byte[0], 0, 0); await response.Body.WriteAsync(new byte[0], 0, 0);
throw new Exception(); throw new Exception();
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
// SendEnd is not called, so it isn't the client closing the connection. // SendEnd is not called, so it isn't the client closing the connection.
await connection.Send( await connection.Send(
@ -177,6 +176,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
// Headers are sent before connection is closed, but chunked body terminator isn't sent // Headers are sent before connection is closed, but chunked body terminator isn't sent
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
""); "");
@ -186,14 +186,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task WritesAreFlushedPriorToResponseCompletion(ServiceContext testContext) public async Task WritesAreFlushedPriorToResponseCompletion(TestServiceContext testContext)
{ {
var flushWh = new ManualResetEventSlim(); var flushWh = new ManualResetEventSlim();
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello "), 0, 6);
// Don't complete response until client has received the first chunk. // Don't complete response until client has received the first chunk.
@ -202,7 +201,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("World!"), 0, 6);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -210,6 +209,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Transfer-Encoding: chunked", "Transfer-Encoding: chunked",
"", "",
"6", "6",

View File

@ -16,7 +16,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var request = httpContext.Request; var request = httpContext.Request;
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
while (true) while (true)
{ {
var buffer = new byte[8192]; var buffer = new byte[8192];
@ -39,12 +38,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
using (var server = new TestServer(App, serviceContext)) using (var server = new TestServer(App, serviceContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
// "?" changes to "!" // "?" changes to "!"
await connection.SendEnd(sendString); await connection.SendEnd(sendString);
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {serviceContext.DateHeaderValue}",
"", "",
"Hello World!"); "Hello World!");
} }
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
using (var server = new TestServer(App, serviceContext)) using (var server = new TestServer(App, serviceContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -68,6 +68,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Hello World?"); "Hello World?");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {serviceContext.DateHeaderValue}",
"", "",
"Hello World!"); "Hello World!");
} }
@ -81,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
using (var server = new TestServer(App, serviceContext)) using (var server = new TestServer(App, serviceContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
try try
{ {

View File

@ -0,0 +1,41 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
using Xunit;
namespace Microsoft.AspNetCore.Server.KestrelTests
{
public class DefaultHeaderTests
{
[Fact]
public async Task TestDefaultHeaders()
{
var testContext = new TestServiceContext()
{
ServerOptions = { AddServerHeader = true }
};
using (var server = new TestServer(ctx => TaskUtilities.CompletedTask, testContext))
{
using (var connection = server.CreateConnection())
{
await connection.SendEnd(
"GET / HTTP/1.0",
"",
"");
await connection.ReceiveEnd(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Server: Kestrel",
"",
"");
}
}
}
}
}

View File

@ -20,11 +20,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
/// </summary> /// </summary>
public class EngineTests public class EngineTests
{ {
public static TheoryData<ServiceContext> ConnectionFilterData public static TheoryData<TestServiceContext> ConnectionFilterData
{ {
get get
{ {
return new TheoryData<ServiceContext> return new TheoryData<TestServiceContext>
{ {
{ {
new TestServiceContext() new TestServiceContext()
@ -40,7 +40,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var request = httpContext.Request; var request = httpContext.Request;
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
while (true) while (true)
{ {
var buffer = new byte[8192]; var buffer = new byte[8192];
@ -61,20 +60,18 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await request.Body.CopyToAsync(data); await request.Body.CopyToAsync(data);
var bytes = data.ToArray(); var bytes = data.ToArray();
response.Headers.Clear();
response.Headers["Content-Length"] = bytes.Length.ToString(); response.Headers["Content-Length"] = bytes.Length.ToString();
await response.Body.WriteAsync(bytes, 0, bytes.Length); await response.Body.WriteAsync(bytes, 0, bytes.Length);
} }
private Task EmptyApp(HttpContext httpContext) private Task EmptyApp(HttpContext httpContext)
{ {
httpContext.Response.Headers.Clear();
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
} }
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public void EngineCanStartAndStop(ServiceContext testContext) public void EngineCanStartAndStop(TestServiceContext testContext)
{ {
var engine = new KestrelEngine(testContext); var engine = new KestrelEngine(testContext);
engine.Start(1); engine.Start(1);
@ -121,11 +118,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10RequestReceivesHttp11Response(ServiceContext testContext) public async Task Http10RequestReceivesHttp11Response(TestServiceContext testContext)
{ {
using (var server = new TestServer(App, testContext)) using (var server = new TestServer(App, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -133,6 +130,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Hello World"); "Hello World");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
"Hello World"); "Hello World");
} }
@ -142,11 +140,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http11(ServiceContext testContext) public async Task Http11(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -157,10 +155,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Goodbye"); "Goodbye");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: close", "Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task HeadersAndStreamsAreReused(ServiceContext testContext) public async Task HeadersAndStreamsAreReused(TestServiceContext testContext)
{ {
var streamCount = 0; var streamCount = 0;
var requestHeadersCount = 0; var requestHeadersCount = 0;
@ -198,21 +198,35 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
lastResponseHeaders = context.Response.Headers; lastResponseHeaders = context.Response.Headers;
responseHeadersCount++; responseHeadersCount++;
} }
context.Response.Headers.Clear();
return context.Request.Body.CopyToAsync(context.Response.Body); return context.Request.Body.CopyToAsync(context.Response.Body);
}, },
testContext)) testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
var requestData = var requestData =
Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount) Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount)
.Concat(new[] { "GET / HTTP/1.1\r\nConnection: close\r\n\r\nGoodbye" }); .Concat(new[] { "GET / HTTP/1.1\r\nConnection: close\r\n\r\nGoodbye" });
var response = string.Join("\r\n", new string[] {
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0",
""});
var lastResponse = string.Join("\r\n", new string[]
{
"HTTP/1.1 200 OK",
"Connection: close",
$"Date: {testContext.DateHeaderValue}",
"",
"Goodbye"
});
var responseData = var responseData =
Enumerable.Repeat("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n", loopCount) Enumerable.Repeat(response, loopCount)
.Concat(new[] { "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nGoodbye" }); .Concat(new[] { lastResponse });
await connection.SendEnd(requestData.ToArray()); await connection.SendEnd(requestData.ToArray());
@ -227,11 +241,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10ContentLength(ServiceContext testContext) public async Task Http10ContentLength(TestServiceContext testContext)
{ {
using (var server = new TestServer(App, testContext)) using (var server = new TestServer(App, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -240,6 +254,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Hello World"); "Hello World");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
"Hello World"); "Hello World");
} }
@ -248,11 +263,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10KeepAlive(ServiceContext testContext) public async Task Http10KeepAlive(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.0", "GET / HTTP/1.0",
@ -264,10 +279,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: keep-alive", "Connection: keep-alive",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"\r\n"); "\r\n");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -277,11 +294,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet(ServiceContext testContext) public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet(TestServiceContext testContext)
{ {
using (var server = new TestServer(App, testContext)) using (var server = new TestServer(App, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.0", "GET / HTTP/1.0",
@ -295,10 +312,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: keep-alive", "Connection: keep-alive",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"\r\n"); "\r\n");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
"Goodbye"); "Goodbye");
} }
@ -307,11 +326,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Http10KeepAliveContentLength(ServiceContext testContext) public async Task Http10KeepAliveContentLength(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.0", "POST / HTTP/1.0",
@ -324,11 +343,13 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: keep-alive", "Connection: keep-alive",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -338,11 +359,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task Expect100ContinueForBody(ServiceContext testContext) public async Task Expect100ContinueForBody(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -355,6 +376,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: close", "Connection: close",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
@ -364,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task DisconnectingClient(ServiceContext testContext) public async Task DisconnectingClient(TestServiceContext testContext)
{ {
using (var server = new TestServer(App, testContext)) using (var server = new TestServer(App, testContext))
{ {
@ -373,13 +395,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
socket.Dispose(); socket.Dispose();
await Task.Delay(200); await Task.Delay(200);
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.0", "GET / HTTP/1.0",
"\r\n"); "\r\n");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"\r\n"); "\r\n");
} }
} }
@ -387,11 +410,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites(ServiceContext testContext) public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites(TestServiceContext testContext)
{ {
using (var server = new TestServer(EmptyApp, testContext)) using (var server = new TestServer(EmptyApp, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -402,10 +425,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: keep-alive", "Connection: keep-alive",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
""); "");
@ -415,11 +440,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests(ServiceContext testContext) public async Task ZeroContentLengthNotSetAutomaticallyForNonKeepAliveRequests(TestServiceContext testContext)
{ {
using (var server = new TestServer(EmptyApp, testContext)) using (var server = new TestServer(EmptyApp, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -429,11 +454,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
"Connection: close", "Connection: close",
$"Date: {testContext.DateHeaderValue}",
"", "",
""); "");
} }
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.0", "GET / HTTP/1.0",
@ -441,6 +467,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
""); "");
} }
@ -449,11 +476,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests(ServiceContext testContext) public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests(TestServiceContext testContext)
{ {
using (var server = new TestServer(EmptyApp, testContext)) using (var server = new TestServer(EmptyApp, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"HEAD / HTTP/1.1", "HEAD / HTTP/1.1",
@ -461,6 +488,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"", "",
""); "");
} }
@ -469,13 +497,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(ServiceContext testContext) public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(TestServiceContext testContext)
{ {
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var request = httpContext.Request; var request = httpContext.Request;
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
using (var reader = new StreamReader(request.Body, Encoding.ASCII)) using (var reader = new StreamReader(request.Body, Encoding.ASCII))
{ {
@ -484,7 +511,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
} }
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -505,14 +532,19 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"200"); "200");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 101 Switching Protocols", "HTTP/1.1 101 Switching Protocols",
$"Date: {testContext.DateHeaderValue}",
"", "",
"HTTP/1.1 204 No Content", "HTTP/1.1 204 No Content",
$"Date: {testContext.DateHeaderValue}",
"", "",
"HTTP/1.1 205 Reset Content", "HTTP/1.1 205 Reset Content",
$"Date: {testContext.DateHeaderValue}",
"", "",
"HTTP/1.1 304 Not Modified", "HTTP/1.1 304 Not Modified",
$"Date: {testContext.DateHeaderValue}",
"", "",
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
""); "");
@ -522,7 +554,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingResultsIn500Response(ServiceContext testContext) public async Task ThrowingResultsIn500Response(TestServiceContext testContext)
{ {
bool onStartingCalled = false; bool onStartingCalled = false;
@ -539,12 +571,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
}, null); }, null);
// Anything added to the ResponseHeaders dictionary is ignored // Anything added to the ResponseHeaders dictionary is ignored
response.Headers.Clear();
response.Headers["Content-Length"] = "11"; response.Headers["Content-Length"] = "11";
throw new Exception(); throw new Exception();
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -556,19 +587,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 500 Internal Server Error", "HTTP/1.1 500 Internal Server Error",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.Receive( await connection.Receive(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
"HTTP/1.1 500 Internal Server Error", "HTTP/1.1 500 Internal Server Error",
""); "");
await connection.Receive("Connection: close", await connection.Receive("Connection: close",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveEnd( await connection.ReceiveEnd(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
@ -580,7 +609,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingAfterWritingKillsConnection(ServiceContext testContext) public async Task ThrowingAfterWritingKillsConnection(TestServiceContext testContext)
{ {
bool onStartingCalled = false; bool onStartingCalled = false;
@ -596,13 +625,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
}, null); }, null);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
throw new Exception(); throw new Exception();
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -610,6 +638,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
@ -622,7 +651,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingAfterPartialWriteKillsConnection(ServiceContext testContext) public async Task ThrowingAfterPartialWriteKillsConnection(TestServiceContext testContext)
{ {
bool onStartingCalled = false; bool onStartingCalled = false;
@ -638,13 +667,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
return Task.FromResult<object>(null); return Task.FromResult<object>(null);
}, null); }, null);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5);
throw new Exception(); throw new Exception();
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -652,6 +680,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello"); "Hello");
@ -664,11 +693,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ConnectionClosesWhenFinReceived(ServiceContext testContext) public async Task ConnectionClosesWhenFinReceived(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -679,9 +708,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
"Goodbye"); "Goodbye");
await connection.ReceiveEnd( await connection.ReceiveEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 7", "Content-Length: 7",
"", "",
"Goodbye"); "Goodbye");
@ -691,49 +722,45 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes(ServiceContext testContext) public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes(TestServiceContext testContext)
{ {
using (var server = new TestServer(AppChunked, testContext)) using (var server = new TestServer(AppChunked, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"", "",
"POST / HTTP/1.1"); "POST / HTTP/1.1");
await connection.Receive( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
"HTTP/1.1 400 Bad Request", "HTTP/1.1 400 Bad Request",
"Connection: close", "Connection: close",
""); $"Date: {testContext.DateHeaderValue}",
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveForcedEnd(
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
} }
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
"", "",
"POST / HTTP/1.1", "POST / HTTP/1.1",
"Content-Length: 7"); "Content-Length: 7");
await connection.Receive( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"", "",
"HTTP/1.1 400 Bad Request", "HTTP/1.1 400 Bad Request",
"Connection: close", "Connection: close",
""); $"Date: {testContext.DateHeaderValue}",
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveForcedEnd(
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
} }
@ -742,7 +769,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ServiceContext testContext) public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(TestServiceContext testContext)
{ {
var onStartingCallCount1 = 0; var onStartingCallCount1 = 0;
var onStartingCallCount2 = 0; var onStartingCallCount2 = 0;
@ -767,7 +794,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
throw onStartingException; throw onStartingException;
}, null); }, null);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
var writeException = await Assert.ThrowsAsync<ObjectDisposedException>(async () => var writeException = await Assert.ThrowsAsync<ObjectDisposedException>(async () =>
@ -778,7 +804,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
failedWriteCount++; failedWriteCount++;
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.SendEnd( await connection.SendEnd(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -790,18 +816,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.Receive( await connection.Receive(
"HTTP/1.1 500 Internal Server Error", "HTTP/1.1 500 Internal Server Error",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.Receive( await connection.Receive(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
"HTTP/1.1 500 Internal Server Error", "HTTP/1.1 500 Internal Server Error",
"Connection: close", "Connection: close",
""); "");
await connection.ReceiveStartsWith("Date:");
await connection.ReceiveEnd( await connection.ReceiveEnd(
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 0", "Content-Length: 0",
"Server: Kestrel",
"", "",
""); "");
@ -815,7 +839,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ServiceContext testContext) public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(TestServiceContext testContext)
{ {
var onCompletedCalled1 = false; var onCompletedCalled1 = false;
var onCompletedCalled2 = false; var onCompletedCalled2 = false;
@ -837,13 +861,12 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
throw new Exception(); throw new Exception();
}, null); }, null);
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.1", "GET / HTTP/1.1",
@ -851,6 +874,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
@ -865,7 +889,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task RequestsCanBeAbortedMidRead(ServiceContext testContext) public async Task RequestsCanBeAbortedMidRead(TestServiceContext testContext)
{ {
var readTcs = new TaskCompletionSource<object>(); var readTcs = new TaskCompletionSource<object>();
var registrationTcs = new TaskCompletionSource<int>(); var registrationTcs = new TaskCompletionSource<int>();
@ -883,7 +907,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
if (requestId == 1) if (requestId == 1)
{ {
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "5" }; response.Headers["Content-Length"] = new[] { "5" };
await response.WriteAsync("World"); await response.WriteAsync("World");
@ -908,7 +931,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
} }
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
// Never send the body so CopyToAsync always fails. // Never send the body so CopyToAsync always fails.
await connection.Send( await connection.Send(
@ -922,6 +945,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 5", "Content-Length: 5",
"", "",
"World"); "World");
@ -937,7 +961,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task FailedWritesResultInAbortedRequest(ServiceContext testContext) public async Task FailedWritesResultInAbortedRequest(TestServiceContext testContext)
{ {
// This should match _maxBytesPreCompleted in SocketOutput // This should match _maxBytesPreCompleted in SocketOutput
var maxBytesPreCompleted = 65536; var maxBytesPreCompleted = 65536;
@ -959,8 +983,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
await request.Body.CopyToAsync(Stream.Null); await request.Body.CopyToAsync(Stream.Null);
connectionCloseWh.Wait(); connectionCloseWh.Wait();
response.Headers.Clear();
try try
{ {
// Ensure write is long enough to disable write-behind buffering // Ensure write is long enough to disable write-behind buffering
@ -979,7 +1001,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
writeTcs.SetException(new Exception("This shouldn't be reached.")); writeTcs.SetException(new Exception("This shouldn't be reached."));
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.1", "POST / HTTP/1.1",
@ -1000,7 +1022,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task NoErrorsLoggedWhenServerEndsConnectionBeforeClient(ServiceContext testContext) public async Task NoErrorsLoggedWhenServerEndsConnectionBeforeClient(TestServiceContext testContext)
{ {
var testLogger = new TestApplicationErrorLogger(); var testLogger = new TestApplicationErrorLogger();
testContext.Log = new KestrelTrace(testLogger); testContext.Log = new KestrelTrace(testLogger);
@ -1008,12 +1030,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
using (var server = new TestServer(async httpContext => using (var server = new TestServer(async httpContext =>
{ {
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
response.Headers["Content-Length"] = new[] { "11" }; response.Headers["Content-Length"] = new[] { "11" };
await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"GET / HTTP/1.0", "GET / HTTP/1.0",
@ -1021,6 +1042,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
""); "");
await connection.ReceiveForcedEnd( await connection.ReceiveForcedEnd(
"HTTP/1.1 200 OK", "HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 11", "Content-Length: 11",
"", "",
"Hello World"); "Hello World");
@ -1032,7 +1054,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
[Theory] [Theory]
[MemberData(nameof(ConnectionFilterData))] [MemberData(nameof(ConnectionFilterData))]
public async Task NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest(ServiceContext testContext) public async Task NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest(TestServiceContext testContext)
{ {
var testLogger = new TestApplicationErrorLogger(); var testLogger = new TestApplicationErrorLogger();
testContext.Log = new KestrelTrace(testLogger); testContext.Log = new KestrelTrace(testLogger);
@ -1043,7 +1065,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
return Task.FromResult(0); return Task.FromResult(0);
}, testContext)) }, testContext))
{ {
using (var connection = new TestConnection(server.Port)) using (var connection = server.CreateConnection())
{ {
await connection.Send( await connection.Send(
"POST / HTTP/1.0", "POST / HTTP/1.0",

View File

@ -13,12 +13,10 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
public class FrameResponseHeadersTests public class FrameResponseHeadersTests
{ {
[Theory] [Fact]
[InlineData(true)] public void InitialDictionaryIsEmpty()
[InlineData(false)]
public void InitialDictionaryContainsServerAndDate(bool addServerHeader)
{ {
var serverOptions = new KestrelServerOptions { AddServerHeader = addServerHeader }; var serverOptions = new KestrelServerOptions();
var connectionContext = new ConnectionContext var connectionContext = new ConnectionContext
{ {
@ -26,60 +24,17 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"), ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
ServerOptions = serverOptions, ServerOptions = serverOptions,
}; };
var frame = new Frame<object>(application: null, context: connectionContext); var frame = new Frame<object>(application: null, context: connectionContext);
frame.InitializeHeaders(); frame.InitializeHeaders();
IDictionary<string, StringValues> headers = frame.ResponseHeaders; IDictionary<string, StringValues> headers = frame.ResponseHeaders;
if (addServerHeader) Assert.Equal(0, headers.Count);
{
Assert.Equal(2, headers.Count);
StringValues serverHeader;
Assert.True(headers.TryGetValue("Server", out serverHeader));
Assert.Equal(1, serverHeader.Count);
Assert.Equal("Kestrel", serverHeader[0]);
}
else
{
Assert.Equal(1, headers.Count);
StringValues serverHeader;
Assert.False(headers.TryGetValue("Server", out serverHeader));
}
StringValues dateHeader;
DateTime date;
Assert.True(headers.TryGetValue("Date", out dateHeader));
Assert.Equal(1, dateHeader.Count);
Assert.True(DateTime.TryParse(dateHeader[0], out date));
Assert.True(DateTime.Now - date <= TimeSpan.FromMinutes(1));
Assert.False(headers.IsReadOnly); Assert.False(headers.IsReadOnly);
} }
[Fact]
public void InitialEntriesCanBeCleared()
{
var serverOptions = new KestrelServerOptions();
var connectionContext = new ConnectionContext
{
DateHeaderValueManager = new DateHeaderValueManager(),
ServerAddress = ServerAddress.FromUrl("http://localhost:5000"),
ServerOptions = serverOptions,
};
var frame = new Frame<object>(application: null, context: connectionContext);
frame.InitializeHeaders();
Assert.True(frame.ResponseHeaders.Count > 0);
frame.ResponseHeaders.Clear();
Assert.Equal(0, frame.ResponseHeaders.Count);
Assert.False(frame.ResponseHeaders.ContainsKey("Server"));
Assert.False(frame.ResponseHeaders.ContainsKey("Date"));
}
[Theory] [Theory]
[InlineData("Server", "\r\nData")] [InlineData("Server", "\r\nData")]
[InlineData("Server", "\0Data")] [InlineData("Server", "\0Data")]

View File

@ -403,7 +403,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
var request = httpContext.Request; var request = httpContext.Request;
var response = httpContext.Response; var response = httpContext.Response;
response.Headers.Clear();
while (true) while (true)
{ {
var buffer = new byte[8192]; var buffer = new byte[8192];

View File

@ -22,11 +22,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
private NetworkStream _stream; private NetworkStream _stream;
private StreamReader _reader; private StreamReader _reader;
public TestConnection(int port) public TestConnection(TestServer server)
{ {
Create(port); Server = server;
Create(server.Port);
} }
public TestServer Server { get; }
public void Create(int port) public void Create(int port)
{ {
_socket = CreateConnectedLoopbackSocket(port); _socket = CreateConnectedLoopbackSocket(port);
@ -85,38 +88,6 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
Assert.Equal(expected, new String(actual, 0, offset)); Assert.Equal(expected, new String(actual, 0, offset));
} }
public async Task ReceiveStartsWith(string prefix, int maxLineLength = 1024)
{
var actual = new char[maxLineLength];
var offset = 0;
while (offset < maxLineLength)
{
// Read one char at a time so we don't read past the end of the line.
var task = _reader.ReadAsync(actual, offset, 1);
if (!Debugger.IsAttached)
{
Assert.True(task.Wait(4000), "timeout");
}
var count = await task;
if (count == 0)
{
break;
}
Assert.True(count == 1);
offset++;
if (actual[offset - 1] == '\n')
{
break;
}
}
var actualLine = new string(actual, 0, offset);
Assert.StartsWith(prefix, actualLine);
}
public async Task ReceiveEnd(params string[] lines) public async Task ReceiveEnd(params string[] lines)
{ {
await Receive(lines); await Receive(lines);

View File

@ -22,15 +22,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
} }
public TestServer(RequestDelegate app, ServiceContext context) public TestServer(RequestDelegate app, TestServiceContext context)
: this(app, context, "http://127.0.0.1:0/") : this(app, context, "http://127.0.0.1:0/")
{ {
} }
public int Port => _address.Port; public TestServer(RequestDelegate app, TestServiceContext context, string serverAddress)
public TestServer(RequestDelegate app, ServiceContext context, string serverAddress)
{ {
Context = context;
context.FrameFactory = connectionContext => context.FrameFactory = connectionContext =>
{ {
return new Frame<HttpContext>(new DummyApplication(app), connectionContext); return new Frame<HttpContext>(new DummyApplication(app), connectionContext);
@ -51,6 +51,15 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
} }
} }
public int Port => _address.Port;
public TestServiceContext Context { get; }
public TestConnection CreateConnection()
{
return new TestConnection(this);
}
public void Dispose() public void Dispose()
{ {
_server.Dispose(); _server.Dispose();

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Filter; using Microsoft.AspNetCore.Server.Kestrel.Filter;
using Microsoft.AspNetCore.Server.Kestrel.Http; using Microsoft.AspNetCore.Server.Kestrel.Http;
using Microsoft.AspNetCore.Server.Kestrel.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Infrastructure;
using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers;
namespace Microsoft.AspNetCore.Server.KestrelTests namespace Microsoft.AspNetCore.Server.KestrelTests
{ {
@ -19,9 +20,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
AppLifetime = new LifetimeNotImplemented(); AppLifetime = new LifetimeNotImplemented();
Log = new TestKestrelTrace(); Log = new TestKestrelTrace();
ThreadPool = new LoggingThreadPool(Log); ThreadPool = new LoggingThreadPool(Log);
DateHeaderValueManager = new DateHeaderValueManager(); DateHeaderValueManager = new DateHeaderValueManager(systemClock: new MockSystemClock());
DateHeaderValue = DateHeaderValueManager.GetDateHeaderValues().String;
ServerOptions = new KestrelServerOptions(); ServerOptions = new KestrelServerOptions { AddServerHeader = false };
ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5); ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5);
} }
@ -31,6 +32,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
ServerOptions.ConnectionFilter = filter; ServerOptions.ConnectionFilter = filter;
} }
public string DateHeaderValue { get; }
public RequestDelegate App public RequestDelegate App
{ {
get get