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

Commit 911bd5e

Browse files
committed
Move RequestIdentifierFeature to HttpContext to allow lighter caching
Expose TraceIdentifier on Httpcontext Also resolves #412 Add tests
1 parent fdb3d54 commit 911bd5e

File tree

8 files changed

+161
-2
lines changed

8 files changed

+161
-2
lines changed

src/Microsoft.AspNet.Http.Abstractions/HttpContext.cs

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public abstract class HttpContext : IDisposable
3636

3737
public abstract ISession Session { get; set; }
3838

39+
public abstract string TraceIdentifier { get; set; }
40+
3941
public abstract void Abort();
4042

4143
public abstract void Dispose();

src/Microsoft.AspNet.Http/DefaultHttpContext.cs

+19
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ private ISessionFeature SessionFeature
117117
_session = value;
118118
}
119119
}
120+
private IHttpRequestIdentifierFeature RequestIdentifierFeature
121+
{
122+
get {
123+
return FeatureHelpers.GetOrCreate<IHttpRequestIdentifierFeature>(
124+
this,
125+
_features,
126+
() => new DefaultHttpRequestIdentifierFeature());
127+
}
128+
set
129+
{
130+
_features.Set(value);
131+
}
132+
}
120133

121134
public override IFeatureCollection Features { get { return _features; } }
122135

@@ -167,6 +180,12 @@ public override CancellationToken RequestAborted
167180
set { LifetimeFeature.RequestAborted = value; }
168181
}
169182

183+
public override string TraceIdentifier
184+
{
185+
get { return RequestIdentifierFeature.TraceIdentifier; }
186+
set { RequestIdentifierFeature.TraceIdentifier = value; }
187+
}
188+
170189
public override ISession Session
171190
{
172191
get
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.Http.Features.Internal
8+
{
9+
public class DefaultHttpRequestIdentifierFeature : HttpRequestIdentifierFeature, IHttpRequestIdentifierFeature
10+
{
11+
// Base64 encoding - but in ascii sort order for easy text based sorting
12+
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
13+
// Seed the _requestId for this application instance with
14+
// the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001
15+
// for a roughly increasing _requestId over restarts
16+
private static long _requestId = DateTime.UtcNow.Ticks;
17+
18+
private string _id = null;
19+
20+
public override string TraceIdentifier
21+
{
22+
get
23+
{
24+
// Don't incur the cost of generating the request ID until it's asked for
25+
if (_id == null)
26+
{
27+
_id = GenerateRequestId(Interlocked.Increment(ref _requestId));
28+
}
29+
return _id;
30+
}
31+
set
32+
{
33+
_id = value;
34+
}
35+
}
36+
37+
private static unsafe string GenerateRequestId(long id)
38+
{
39+
// The following routine is ~310% faster than calling long.ToString() on x64
40+
// and ~600% faster than calling long.ToString() on x86 in tight loops of 1 million+ iterations
41+
// See: https://github.com/aspnet/Hosting/pull/385
42+
43+
// stackalloc to allocate array on stack rather than heap
44+
char* charBuffer = stackalloc char[13];
45+
46+
charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
47+
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
48+
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
49+
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
50+
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
51+
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
52+
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
53+
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
54+
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
55+
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
56+
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
57+
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
58+
charBuffer[12] = _encode32Chars[(int)id & 31];
59+
60+
// string ctor overload that takes char*
61+
return new string(charBuffer, 0, 13);
62+
}
63+
}
64+
}

src/Microsoft.AspNet.Http/Features/FeatureHelpers.cs

+15
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ public static T GetAndCache<T>(
2323
return obj;
2424
}
2525

26+
public static T GetOrCreate<T>(
27+
IFeatureCache cache,
28+
IFeatureCollection features,
29+
Func<T> factory)
30+
{
31+
T obj = features.Get<T>();
32+
if (obj == null)
33+
{
34+
obj = factory();
35+
features.Set(obj);
36+
}
37+
38+
return obj;
39+
}
40+
2641
public static T GetOrCreateAndCache<T>(
2742
IFeatureCache cache,
2843
IFeatureCollection features,

src/Microsoft.AspNet.Http/Features/HttpRequestIdentifierFeature.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Http.Features.Internal
55
{
66
public class HttpRequestIdentifierFeature : IHttpRequestIdentifierFeature
77
{
8-
public string TraceIdentifier { get; set; }
8+
public virtual string TraceIdentifier { get; set; }
99
}
1010
}

src/Microsoft.AspNet.Http/project.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"url": "git://github.com/aspnet/httpabstractions"
77
},
88
"compilationOptions": {
9-
"warningsAsErrors": true
9+
"warningsAsErrors": true,
10+
"allowUnsafe": true
1011
},
1112
"dependencies": {
1213
"Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 Microsoft.AspNet.Http.Features.Internal;
5+
using Xunit;
6+
7+
namespace Microsoft.AspNet.Http.Tests
8+
{
9+
public class DeafultHttpRequestIdentifierFeatureTests
10+
{
11+
[Fact]
12+
public void TraceIdentifier_ReturnsId()
13+
{
14+
var feature = new DefaultHttpRequestIdentifierFeature();
15+
16+
var id = feature.TraceIdentifier;
17+
18+
Assert.NotNull(id);
19+
}
20+
21+
[Fact]
22+
public void TraceIdentifier_ReturnsStableId()
23+
{
24+
var feature = new DefaultHttpRequestIdentifierFeature();
25+
26+
var id1 = feature.TraceIdentifier;
27+
var id2 = feature.TraceIdentifier;
28+
29+
Assert.Equal(id1, id2);
30+
}
31+
32+
[Fact]
33+
public void TraceIdentifier_ReturnsUniqueIdForDifferentInstances()
34+
{
35+
var feature1 = new DefaultHttpRequestIdentifierFeature();
36+
var feature2 = new DefaultHttpRequestIdentifierFeature();
37+
38+
var id1 = feature1.TraceIdentifier;
39+
var id2 = feature2.TraceIdentifier;
40+
41+
Assert.NotEqual(id1, id2);
42+
}
43+
}
44+
}

test/Microsoft.AspNet.Http.Tests/DefaultHttpContextTests.cs

+14
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,20 @@ public void GetItems_DefaultCollectionProvided()
119119
Assert.Same(item, context.Items["foo"]);
120120
}
121121

122+
[Fact]
123+
public void GetItems_DefaultRequestIdentifierAvailable()
124+
{
125+
var context = new DefaultHttpContext(new FeatureCollection());
126+
Assert.Null(context.Features.Get<IHttpRequestIdentifierFeature>());
127+
var traceIdentifier = context.TraceIdentifier;
128+
Assert.NotNull(context.Features.Get<IHttpRequestIdentifierFeature>());
129+
Assert.NotNull(traceIdentifier);
130+
Assert.Same(traceIdentifier, context.TraceIdentifier);
131+
132+
context.TraceIdentifier = "Hello";
133+
Assert.Same("Hello", context.TraceIdentifier);
134+
}
135+
122136
[Fact]
123137
public void SetItems_NewCollectionUsed()
124138
{

0 commit comments

Comments
 (0)