Only call PrepareDateValues if not already called for tick

This commit is contained in:
Ben Adams 2016-05-07 01:28:24 +01:00
parent 388841c1d8
commit a3b0f809de
6 changed files with 47 additions and 61 deletions

View File

@ -13,15 +13,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
/// </summary> /// </summary>
public class DateHeaderValueManager : IDisposable public class DateHeaderValueManager : IDisposable
{ {
private static readonly byte[] _datePreambleBytes = Encoding.ASCII.GetBytes("\r\nDate: ");
private readonly ISystemClock _systemClock; private readonly ISystemClock _systemClock;
private readonly TimeSpan _timeWithoutRequestsUntilIdle; private readonly TimeSpan _timeWithoutRequestsUntilIdle;
private readonly TimeSpan _timerInterval; private readonly TimeSpan _timerInterval;
private readonly object _timerLocker = new object();
private DateHeaderValues _dateValues;
private volatile string _dateValue;
private volatile bool _activeDateBytes;
private readonly byte[] _dateBytes0 = Encoding.ASCII.GetBytes("\r\nDate: DDD, dd mmm yyyy hh:mm:ss GMT");
private readonly byte[] _dateBytes1 = Encoding.ASCII.GetBytes("\r\nDate: DDD, dd mmm yyyy hh:mm:ss GMT");
private object _timerLocker = new object();
private volatile bool _isDisposed = false; private volatile bool _isDisposed = false;
private volatile bool _hadRequestsSinceLastTimerTick = false; private volatile bool _hadRequestsSinceLastTimerTick = false;
private Timer _dateValueTimer; private Timer _dateValueTimer;
@ -55,19 +55,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
/// Returns a value representing the current server date/time for use in the HTTP "Date" response header /// Returns a value representing the current server date/time for use in the HTTP "Date" response header
/// in accordance with http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 /// in accordance with http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18
/// </summary> /// </summary>
/// <returns>The value.</returns> /// <returns>The value in string and byte[] format.</returns>
public virtual string GetDateHeaderValue() public DateHeaderValues GetDateHeaderValues()
{ {
_hadRequestsSinceLastTimerTick = true; if (!_hadRequestsSinceLastTimerTick)
PrepareDateValues(); {
return _dateValue; PrepareDateValues();
} }
public byte[] GetDateHeaderValueBytes() return _dateValues;
{
_hadRequestsSinceLastTimerTick = true;
PrepareDateValues();
return _activeDateBytes ? _dateBytes0 : _dateBytes1;
} }
/// <summary> /// <summary>
@ -78,6 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
if (!_isDisposed) if (!_isDisposed)
{ {
_isDisposed = true; _isDisposed = true;
_hadRequestsSinceLastTimerTick = false;
lock (_timerLocker) lock (_timerLocker)
{ {
@ -125,6 +122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{ {
_timerIsRunning = false; _timerIsRunning = false;
_dateValueTimer.Change(Timeout.Infinite, Timeout.Infinite); _dateValueTimer.Change(Timeout.Infinite, Timeout.Infinite);
_hadRequestsSinceLastTimerTick = false;
} }
} }
} }
@ -158,16 +156,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
/// </summary> /// </summary>
private void PrepareDateValues() private void PrepareDateValues()
{ {
if (_isDisposed) _hadRequestsSinceLastTimerTick = !_isDisposed;
if (!_timerIsRunning)
{ {
SetDateValues(_systemClock.UtcNow); StartTimer();
}
else
{
if (!_timerIsRunning)
{
StartTimer();
}
} }
} }
@ -178,9 +170,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private void SetDateValues(DateTimeOffset value) private void SetDateValues(DateTimeOffset value)
{ {
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 for required format of Date header // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 for required format of Date header
_dateValue = value.ToString(Constants.RFC1123DateFormat); var dateValue = value.ToString(Constants.RFC1123DateFormat);
Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, "\r\nDate: ".Length); var dateBytes = new byte[_datePreambleBytes.Length + dateValue.Length];
_activeDateBytes = !_activeDateBytes; Buffer.BlockCopy(_datePreambleBytes, 0, dateBytes, 0, _datePreambleBytes.Length);
Encoding.ASCII.GetBytes(dateValue, 0, dateValue.Length, dateBytes, _datePreambleBytes.Length);
var dateValues = new DateHeaderValues()
{
Bytes = dateBytes,
String = dateValue
};
Volatile.Write(ref _dateValues, dateValues);
}
public class DateHeaderValues
{
public byte[] Bytes;
public string String;
} }
} }
} }

View File

@ -589,12 +589,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
var responseHeaders = _frameHeaders.ResponseHeaders; var responseHeaders = _frameHeaders.ResponseHeaders;
responseHeaders.Reset(); responseHeaders.Reset();
responseHeaders.SetRawDate( var dateHeaderValues = DateHeaderValueManager.GetDateHeaderValues();
DateHeaderValueManager.GetDateHeaderValue(), responseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
DateHeaderValueManager.GetDateHeaderValueBytes()); responseHeaders.SetRawServer("Kestrel", Headers.BytesServer);
responseHeaders.SetRawServer(
"Kestrel",
Headers.BytesServer);
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;

View File

@ -78,9 +78,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Infrastructure
public void Initialize(DateHeaderValueManager dateValueManager) public void Initialize(DateHeaderValueManager dateValueManager)
{ {
ResponseHeaders.SetRawDate( var dateHeaderValues = dateValueManager.GetDateHeaderValues();
dateValueManager.GetDateHeaderValue(), ResponseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
dateValueManager.GetDateHeaderValueBytes());
ResponseHeaders.SetRawServer("Kestrel", BytesServer); ResponseHeaders.SetRawServer("Kestrel", BytesServer);
} }

View File

@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try try
{ {
result = dateHeaderValueManager.GetDateHeaderValue(); result = dateHeaderValueManager.GetDateHeaderValues().String;
} }
finally finally
{ {
@ -54,9 +54,9 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try try
{ {
result1 = dateHeaderValueManager.GetDateHeaderValue(); result1 = dateHeaderValueManager.GetDateHeaderValues().String;
systemClock.UtcNow = future; systemClock.UtcNow = future;
result2 = dateHeaderValueManager.GetDateHeaderValue(); result2 = dateHeaderValueManager.GetDateHeaderValues().String;
} }
finally finally
{ {
@ -85,11 +85,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
try try
{ {
result1 = dateHeaderValueManager.GetDateHeaderValue(); result1 = dateHeaderValueManager.GetDateHeaderValues().String;
systemClock.UtcNow = future; systemClock.UtcNow = future;
// Wait for longer than the idle timeout to ensure the timer is stopped // Wait for longer than the idle timeout to ensure the timer is stopped
await Task.Delay(TimeSpan.FromSeconds(1)); await Task.Delay(TimeSpan.FromSeconds(1));
result2 = dateHeaderValueManager.GetDateHeaderValue(); result2 = dateHeaderValueManager.GetDateHeaderValues().String;
} }
finally finally
{ {
@ -114,10 +114,10 @@ namespace Microsoft.AspNetCore.Server.KestrelTests
var timerInterval = TimeSpan.FromSeconds(10); var timerInterval = TimeSpan.FromSeconds(10);
var dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval); var dateHeaderValueManager = new DateHeaderValueManager(systemClock, timeWithoutRequestsUntilIdle, timerInterval);
var result1 = dateHeaderValueManager.GetDateHeaderValue(); var result1 = dateHeaderValueManager.GetDateHeaderValues().String;
dateHeaderValueManager.Dispose(); dateHeaderValueManager.Dispose();
systemClock.UtcNow = future; systemClock.UtcNow = future;
var result2 = dateHeaderValueManager.GetDateHeaderValue(); var result2 = dateHeaderValueManager.GetDateHeaderValues().String;
Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result1); Assert.Equal(now.ToString(Constants.RFC1123DateFormat), result1);
Assert.Equal(future.ToString(Constants.RFC1123DateFormat), result2); Assert.Equal(future.ToString(Constants.RFC1123DateFormat), result2);

View File

@ -1,16 +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;
using Microsoft.AspNetCore.Server.Kestrel.Http;
namespace Microsoft.AspNetCore.Server.KestrelTests
{
public class TestDateHeaderValueManager : DateHeaderValueManager
{
public override string GetDateHeaderValue()
{
return DateTimeOffset.UtcNow.ToString("r");
}
}
}

View File

@ -20,7 +20,7 @@ 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 TestDateHeaderValueManager(); DateHeaderValueManager = new DateHeaderValueManager();
ServerOptions = new KestrelServerOptions(); ServerOptions = new KestrelServerOptions();
ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5); ServerOptions.ShutdownTimeout = TimeSpan.FromSeconds(5);