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>
public class DateHeaderValueManager : IDisposable
{
private static readonly byte[] _datePreambleBytes = Encoding.ASCII.GetBytes("\r\nDate: ");
private readonly ISystemClock _systemClock;
private readonly TimeSpan _timeWithoutRequestsUntilIdle;
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 _hadRequestsSinceLastTimerTick = false;
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
/// in accordance with http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18
/// </summary>
/// <returns>The value.</returns>
public virtual string GetDateHeaderValue()
/// <returns>The value in string and byte[] format.</returns>
public DateHeaderValues GetDateHeaderValues()
{
_hadRequestsSinceLastTimerTick = true;
PrepareDateValues();
return _dateValue;
}
if (!_hadRequestsSinceLastTimerTick)
{
PrepareDateValues();
}
public byte[] GetDateHeaderValueBytes()
{
_hadRequestsSinceLastTimerTick = true;
PrepareDateValues();
return _activeDateBytes ? _dateBytes0 : _dateBytes1;
return _dateValues;
}
/// <summary>
@ -78,6 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
if (!_isDisposed)
{
_isDisposed = true;
_hadRequestsSinceLastTimerTick = false;
lock (_timerLocker)
{
@ -125,6 +122,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
{
_timerIsRunning = false;
_dateValueTimer.Change(Timeout.Infinite, Timeout.Infinite);
_hadRequestsSinceLastTimerTick = false;
}
}
}
@ -158,16 +156,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
/// </summary>
private void PrepareDateValues()
{
if (_isDisposed)
_hadRequestsSinceLastTimerTick = !_isDisposed;
if (!_timerIsRunning)
{
SetDateValues(_systemClock.UtcNow);
}
else
{
if (!_timerIsRunning)
{
StartTimer();
}
StartTimer();
}
}
@ -178,9 +170,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
private void SetDateValues(DateTimeOffset value)
{
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 for required format of Date header
_dateValue = value.ToString(Constants.RFC1123DateFormat);
Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, "\r\nDate: ".Length);
_activeDateBytes = !_activeDateBytes;
var dateValue = value.ToString(Constants.RFC1123DateFormat);
var dateBytes = new byte[_datePreambleBytes.Length + dateValue.Length];
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;
responseHeaders.Reset();
responseHeaders.SetRawDate(
DateHeaderValueManager.GetDateHeaderValue(),
DateHeaderValueManager.GetDateHeaderValueBytes());
responseHeaders.SetRawServer(
"Kestrel",
Headers.BytesServer);
var dateHeaderValues = DateHeaderValueManager.GetDateHeaderValues();
responseHeaders.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
responseHeaders.SetRawServer("Kestrel", Headers.BytesServer);
responseHeaders.SetRawContentLength("0", _bytesContentLengthZero);
ResponseHeaders = responseHeaders;

View File

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

View File

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