diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/DateHeaderValueManager.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/DateHeaderValueManager.cs
index 45e91194d6..d7022ab2dd 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/DateHeaderValueManager.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/DateHeaderValueManager.cs
@@ -13,15 +13,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http
///
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
///
- /// The value.
- public virtual string GetDateHeaderValue()
+ /// The value in string and byte[] format.
+ public DateHeaderValues GetDateHeaderValues()
{
- _hadRequestsSinceLastTimerTick = true;
- PrepareDateValues();
- return _dateValue;
- }
+ if (!_hadRequestsSinceLastTimerTick)
+ {
+ PrepareDateValues();
+ }
- public byte[] GetDateHeaderValueBytes()
- {
- _hadRequestsSinceLastTimerTick = true;
- PrepareDateValues();
- return _activeDateBytes ? _dateBytes0 : _dateBytes1;
+ return _dateValues;
}
///
@@ -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
///
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;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
index 4ed675767c..bbae130ad0 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/Frame.cs
@@ -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;
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
index cacd005b55..2669471cd9 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel/Infrastructure/HttpComponentFactory.cs
@@ -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);
}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs
index d357ece6ae..0c221cba52 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs
@@ -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);
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestDateHeaderValueManager.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestDateHeaderValueManager.cs
deleted file mode 100644
index f558080e9d..0000000000
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestDateHeaderValueManager.cs
+++ /dev/null
@@ -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");
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs
index 7cfe7df885..0457345450 100644
--- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs
+++ b/test/Microsoft.AspNetCore.Server.KestrelTests/TestServiceContext.cs
@@ -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);