Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit ffffbe0

Browse files
committed
Use a timer to generate the value for the Date header in responses:
- Doing it on each request is expensive - The Timer is started when the first request comes in and fires every second - Every request flips a bool so the Timer knows requests are coming in - The Timer stops itself after a period of no requests coming in (10 ticks) - #163
1 parent 2041e4d commit ffffbe0

File tree

11 files changed

+170
-48
lines changed

11 files changed

+170
-48
lines changed

KestrelHttpServer.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1313
build.cmd = build.cmd
1414
global.json = global.json
1515
makefile.shade = makefile.shade
16+
NuGet.Config = NuGet.Config
1617
EndProjectSection
1718
EndProject
1819
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleApp", "samples\SampleApp\SampleApp.xproj", "{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading;
6+
7+
namespace Microsoft.AspNet.Server.Kestrel.Http
8+
{
9+
public class DateHeaderValueManager : IDisposable
10+
{
11+
private readonly TimeSpan _timeWithoutRequestsUntilIdle = TimeSpan.FromSeconds(10);
12+
private readonly TimeSpan _timerInterval = TimeSpan.FromSeconds(1);
13+
private readonly uint _timerTicksWithoutRequestsUntilIdle;
14+
15+
private volatile string _dateValue;
16+
private bool _isDisposed = false;
17+
private bool _hadRequestsSinceLastTimerTick = false;
18+
private Timer _dateValueTimer;
19+
private object _timerLocker = new object();
20+
private int _timerTicksSinceLastRequest;
21+
22+
public DateHeaderValueManager()
23+
{
24+
_timerTicksWithoutRequestsUntilIdle = (uint)(_timeWithoutRequestsUntilIdle.TotalMilliseconds / _timerInterval.TotalMilliseconds);
25+
}
26+
27+
public string GetDateHeaderValue()
28+
{
29+
PumpTimer();
30+
31+
return _dateValue ?? DateTime.UtcNow.ToString("r");
32+
}
33+
34+
public void Dispose()
35+
{
36+
lock (_timerLocker)
37+
{
38+
if (_dateValueTimer != null)
39+
{
40+
DisposeTimer();
41+
_isDisposed = true;
42+
}
43+
}
44+
}
45+
46+
private void PumpTimer()
47+
{
48+
_hadRequestsSinceLastTimerTick = true;
49+
50+
if (!_isDisposed && _dateValueTimer == null)
51+
{
52+
lock (_timerLocker)
53+
{
54+
if (!_isDisposed && _dateValueTimer == null)
55+
{
56+
// Immediately assign the date value and start the timer again
57+
_dateValue = DateTime.UtcNow.ToString("r");
58+
_dateValueTimer = new Timer(UpdateDateValue, state: null, dueTime: _timerInterval, period: _timerInterval);
59+
}
60+
}
61+
}
62+
}
63+
64+
private void UpdateDateValue(object state)
65+
{
66+
_dateValue = DateTime.UtcNow.ToString("r");
67+
68+
if (_hadRequestsSinceLastTimerTick)
69+
{
70+
// We served requests since the last tick, just return
71+
_hadRequestsSinceLastTimerTick = false;
72+
_timerTicksSinceLastRequest = 0;
73+
return;
74+
}
75+
76+
// No requests since the last timer tick
77+
_timerTicksSinceLastRequest++;
78+
if (_timerTicksSinceLastRequest == _timerTicksWithoutRequestsUntilIdle)
79+
{
80+
// No requests since idle threshold so stop the timer if it's still running
81+
if (_dateValueTimer != null)
82+
{
83+
lock (_timerLocker)
84+
{
85+
if (_dateValueTimer != null)
86+
{
87+
DisposeTimer();
88+
}
89+
}
90+
}
91+
}
92+
}
93+
94+
private void DisposeTimer()
95+
{
96+
_dateValueTimer.Dispose();
97+
_dateValueTimer = null;
98+
_dateValue = null;
99+
}
100+
}
101+
}

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class Frame : FrameContext, IFrameControl
2626
private bool _keepAlive;
2727
private bool _autoChunk;
2828
private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders();
29-
private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders();
29+
private readonly FrameResponseHeaders _responseHeaders;
3030

3131
private List<KeyValuePair<Func<object, Task>, object>> _onStarting;
3232
private List<KeyValuePair<Func<object, Task>, object>> _onCompleted;
@@ -38,6 +38,7 @@ public Frame(ConnectionContext context) : base(context)
3838
FrameControl = this;
3939
StatusCode = 200;
4040
RequestHeaders = _requestHeaders;
41+
_responseHeaders = new FrameResponseHeaders(DateHeaderValueManager);
4142
ResponseHeaders = _responseHeaders;
4243
}
4344

@@ -388,7 +389,7 @@ public void ProduceEnd(Exception ex)
388389
// the app func has failed. https://github.com/aspnet/KestrelHttpServer/issues/43
389390
_onStarting = null;
390391

391-
ResponseHeaders = new FrameResponseHeaders();
392+
ResponseHeaders = new FrameResponseHeaders(DateHeaderValueManager);
392393
ResponseHeaders["Content-Length"] = new[] { "0" };
393394
}
394395
}

src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
77
{
88
public partial class FrameResponseHeaders
99
{
10-
public FrameResponseHeaders()
10+
public FrameResponseHeaders(string dateHeaderValue)
1111
{
1212
_Server = "Kestrel";
13-
_Date = DateTime.UtcNow.ToString("r");
13+
_Date = GetDateHeaderValue();
1414
_bits = 67108868L;
1515
}
1616
}

src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ StringValues IDictionary<string, StringValues>.this[string key]
2020
get
2121
{
2222
return GetValueFast(key);
23-
}
23+
}
2424

2525
set
2626
{
@@ -131,4 +131,10 @@ bool IDictionary<string, StringValues>.TryGetValue(string key, out StringValues
131131
return TryGetValueFast(key, out value);
132132
}
133133
}
134+
private static string GetDateHeaderValue()
135+
{
136+
PumpTimer();
137+
138+
return _dateValue;
139+
}
134140
}

src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ public abstract class Listener : ListenerContext, IDisposable
1515
{
1616
protected Listener(ServiceContext serviceContext) : base(serviceContext)
1717
{
18-
}
18+
}
1919

2020
protected UvStreamHandle ListenSocket { get; private set; }
21+
public Listener(IMemoryPool memory, DateHeaderValueManager dateHeaderValueManager)
22+
{
23+
Memory = memory;
24+
DateHeaderValueManager = dateHeaderValueManager;
25+
}
2126

2227
public Task StartAsync(
2328
string scheme,
@@ -55,7 +60,7 @@ protected static void ConnectionCallback(UvStreamHandle stream, int status, Exce
5560
if (error != null)
5661
{
5762
Trace.WriteLine("Listener.ConnectionCallback " + error.ToString());
58-
}
63+
}
5964
else
6065
{
6166
((Listener)state).OnConnection(stream, status);

src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,7 @@ public ListenerContext(ListenerContext listenerContext)
2727
public Func<Frame, Task> Application { get; set; }
2828

2929
public IMemoryPool Memory { get; set; }
30+
31+
public DateHeaderValueManager DateHeaderValueManager { get; set; }
3032
}
3133
}

src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public abstract class ListenerSecondary : ListenerContext, IDisposable
1818
{
1919
protected ListenerSecondary(ServiceContext serviceContext) : base(serviceContext)
2020
{
21+
DateHeaderValueManager = dateHeaderValueManager;
2122
}
2223

2324
UvPipeHandle DispatchPipe { get; set; }

src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,12 @@ private KestrelEngine(IApplicationShutdown appShutdownService)
7777
};
7878

7979
Threads = new List<KestrelThread>();
80+
DateHeaderValueManager = new DateHeaderValueManager();
8081
}
8182

8283
public Libuv Libuv { get; private set; }
8384
public List<KestrelThread> Threads { get; private set; }
85+
public DateHeaderValueManager DateHeaderValueManager { get; private set; }
8486

8587
public void Start(int count)
8688
{
@@ -102,6 +104,8 @@ public void Dispose()
102104
thread.Stop(TimeSpan.FromSeconds(2.5));
103105
}
104106
Threads.Clear();
107+
108+
DateHeaderValueManager.Dispose();
105109
}
106110

107111
public IDisposable CreateServer(string scheme, string host, int port, Func<Frame, Task> application)
Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,48 @@
11
{
2-
"version": "1.0.0-*",
3-
"description": "ASP.NET 5 cross platform development web server.",
4-
"repository": {
5-
"type": "git",
6-
"url": "git://github.com/aspnet/kestrelhttpserver"
7-
},
8-
"dependencies": {
9-
"Microsoft.AspNet.Hosting": "1.0.0-*",
2+
"version": "1.0.0-*",
3+
"description": "ASP.NET 5 cross platform development web server.",
4+
"repository": {
5+
"type": "git",
6+
"url": "git://github.com/aspnet/kestrelhttpserver"
7+
},
8+
"dependencies": {
9+
"Microsoft.AspNet.Hosting": "1.0.0-*",
1010
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*",
1111
"Microsoft.StandardsPolice": {
1212
"version": "1.0.0-*",
1313
"type": "build"
1414
}
15-
},
16-
"frameworks": {
17-
"dnx451": { },
18-
"dnxcore50": {
19-
"dependencies": {
20-
"System.Collections": "4.0.11-beta-*",
21-
"System.Diagnostics.Debug": "4.0.11-beta-*",
22-
"System.Diagnostics.TraceSource": "4.0.0-beta-*",
23-
"System.Diagnostics.Tracing": "4.0.21-beta-*",
24-
"System.Globalization": "4.0.11-beta-*",
25-
"System.IO": "4.0.11-beta-*",
26-
"System.Linq": "4.0.1-beta-*",
27-
"System.Net.Primitives": "4.0.11-beta-*",
28-
"System.Runtime.Extensions": "4.0.11-beta-*",
29-
"System.Runtime.InteropServices": "4.0.21-beta-*",
30-
"System.Text.Encoding": "4.0.11-beta-*",
31-
"System.Threading": "4.0.11-beta-*",
32-
"System.Threading.Tasks": "4.0.11-beta-*",
33-
"System.Threading.Thread": "4.0.0-beta-*",
34-
"System.Threading.ThreadPool": "4.0.10-beta-*"
35-
}
36-
}
37-
},
38-
"compilationOptions": {
39-
"allowUnsafe": true
40-
},
41-
"scripts": {
42-
"prepare": [
43-
"dnu restore ../../tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode",
44-
"dnx --appbase ../../tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode Microsoft.Dnx.ApplicationHost run Http/FrameHeaders.Generated.cs"
45-
]
15+
},
16+
"frameworks": {
17+
"dnx451": { },
18+
"dnxcore50": {
19+
"dependencies": {
20+
"System.Collections": "4.0.11-beta-*",
21+
"System.Diagnostics.Debug": "4.0.11-beta-*",
22+
"System.Diagnostics.TraceSource": "4.0.0-beta-*",
23+
"System.Diagnostics.Tracing": "4.0.21-beta-*",
24+
"System.Globalization": "4.0.11-beta-*",
25+
"System.IO": "4.0.11-beta-*",
26+
"System.Linq": "4.0.1-beta-*",
27+
"System.Net.Primitives": "4.0.11-beta-*",
28+
"System.Runtime.Extensions": "4.0.11-beta-*",
29+
"System.Runtime.InteropServices": "4.0.21-beta-*",
30+
"System.Text.Encoding": "4.0.11-beta-*",
31+
"System.Threading": "4.0.11-beta-*",
32+
"System.Threading.Tasks": "4.0.11-beta-*",
33+
"System.Threading.Thread": "4.0.0-beta-*",
34+
"System.Threading.ThreadPool": "4.0.10-beta-*",
35+
"System.Threading.Timer": "4.0.1-beta-*"
36+
}
4637
}
38+
},
39+
"compilationOptions": {
40+
"allowUnsafe": true
41+
},
42+
"scripts": {
43+
"prepare": [
44+
"dnu restore ../../tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode",
45+
"dnx --appbase ../../tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode Microsoft.Dnx.ApplicationHost run Http/FrameHeaders.Generated.cs"
46+
]
47+
}
4748
}

tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http
173173
{{
174174
public partial class FrameResponseHeaders
175175
{{
176-
public FrameResponseHeaders()
176+
public FrameResponseHeaders(string dateHeaderValue)
177177
{{
178178
_Server = ""Kestrel"";
179-
_Date = DateTime.UtcNow.ToString(""r"");
179+
_Date = GetDateHeaderValue();
180180
_bits = {
181181
1L << responseHeaders.First(header => header.Name == "Server").Index |
182182
1L << responseHeaders.First(header => header.Name == "Date").Index

0 commit comments

Comments
 (0)