Skip to content

Commit 8ae8104

Browse files
committed
improve port convert + enumerators for headers
1 parent a61aff4 commit 8ae8104

File tree

4 files changed

+179
-31
lines changed

4 files changed

+179
-31
lines changed

src/Http/Owin/benchmarks/Microsoft.AspNetCore.Owin.Microbenchmarks/Benchmarks/OwinEnvironmentBenchmark.cs

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,97 @@ namespace Microsoft.AspNetCore.Owin.Microbenchmarks.Benchmarks;
1111
[MemoryDiagnoser]
1212
public class OwinEnvironmentBenchmark
1313
{
14-
private RequestDelegate _requestDelegate;
15-
private readonly HttpContext _httpContext = new DefaultHttpContext();
14+
const int RequestCount = 10000;
15+
16+
RequestDelegate _noOperationRequestDelegate;
17+
RequestDelegate _accessPortsRequestDelegate;
18+
RequestDelegate _accessHeadersRequestDelegate;
19+
20+
HttpContext _defaultHttpContext;
21+
HttpContext _httpContextWithHeaders;
1622

1723
[GlobalSetup]
1824
public void GlobalSetup()
25+
{
26+
_noOperationRequestDelegate = BuildRequestDelegate();
27+
_accessPortsRequestDelegate = BuildRequestDelegate(beforeOwinInvokeAction: env =>
28+
{
29+
_ = env.TryGetValue("server.LocalPort", out var localPort);
30+
_ = env.TryGetValue("server.RemotePort", out var remotePort);
31+
});
32+
_accessHeadersRequestDelegate = BuildRequestDelegate(
33+
beforeOwinInvokeAction: env =>
34+
{
35+
_ = env.TryGetValue("owin.RequestHeaders", out var requestHeaders);
36+
},
37+
afterOwinInvokeAction: env =>
38+
{
39+
_ = env.TryGetValue("owin.ResponseHeaders", out var responseHeaders);
40+
}
41+
);
42+
43+
_defaultHttpContext = new DefaultHttpContext();
44+
45+
_httpContextWithHeaders = new DefaultHttpContext();
46+
_httpContextWithHeaders.Request.Headers["CustomRequestHeader1"] = "CustomRequestValue";
47+
_httpContextWithHeaders.Request.Headers["CustomRequestHeader2"] = "CustomRequestValue";
48+
_httpContextWithHeaders.Response.Headers["CustomResponseHeader1"] = "CustomResponseValue";
49+
_httpContextWithHeaders.Response.Headers["CustomResponseHeader2"] = "CustomResponseValue";
50+
}
51+
52+
[Benchmark]
53+
public async Task OwinRequest_NoOperation()
54+
{
55+
foreach (var i in Enumerable.Range(0, RequestCount))
56+
{
57+
await _noOperationRequestDelegate(_defaultHttpContext);
58+
}
59+
}
60+
61+
[Benchmark]
62+
public async Task OwinRequest_AccessPorts()
63+
{
64+
foreach (var i in Enumerable.Range(0, RequestCount))
65+
{
66+
await _accessPortsRequestDelegate(_defaultHttpContext);
67+
}
68+
}
69+
70+
[Benchmark]
71+
public async Task OwinRequest_AccessHeaders()
72+
{
73+
foreach (var i in Enumerable.Range(0, RequestCount))
74+
{
75+
await _accessHeadersRequestDelegate(_httpContextWithHeaders);
76+
}
77+
}
78+
79+
private static RequestDelegate BuildRequestDelegate(
80+
Action<IDictionary<string, object>> beforeOwinInvokeAction = null,
81+
Action<IDictionary<string, object>> afterOwinInvokeAction = null)
1982
{
2083
var serviceProvider = new ServiceCollection().BuildServiceProvider();
2184
var builder = new ApplicationBuilder(serviceProvider);
22-
IDictionary<string, object> environment = null;
2385

24-
builder.UseOwin(addToPipeline =>
86+
return builder.UseOwin(addToPipeline =>
2587
{
2688
addToPipeline(next =>
2789
{
2890
return async env =>
2991
{
30-
environment = env;
92+
if (beforeOwinInvokeAction is not null)
93+
{
94+
beforeOwinInvokeAction(env);
95+
}
96+
3197
await next(env);
98+
99+
if (afterOwinInvokeAction is not null)
100+
{
101+
afterOwinInvokeAction(env);
102+
}
32103
};
33104
});
34-
});
35-
36-
_requestDelegate = builder.Build();
37-
}
38-
39-
[Benchmark]
40-
public async Task ProcessMultipleRequests()
41-
{
42-
foreach (var i in Enumerable.Range(0, 10000))
43-
{
44-
await _requestDelegate(_httpContext);
45-
}
105+
}).Build();
46106
}
47107
}

src/Http/Owin/src/DictionaryStringArrayWrapper.cs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ namespace Microsoft.AspNetCore.Owin;
1010

1111
internal sealed class DictionaryStringArrayWrapper : IDictionary<string, string[]>
1212
{
13+
public readonly IHeaderDictionary Inner;
14+
1315
public DictionaryStringArrayWrapper(IHeaderDictionary inner)
1416
{
1517
Inner = inner;
1618
}
1719

18-
public readonly IHeaderDictionary Inner;
19-
20-
private static KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new KeyValuePair<string, StringValues>(item.Key, item.Value);
20+
private static KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new(item.Key, item.Value);
2121

22-
private static KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new KeyValuePair<string, string[]>(item.Key, item.Value);
22+
private static KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new(item.Key, item.Value);
2323

2424
private string[] Convert(StringValues item) => item;
2525

@@ -55,9 +55,11 @@ void ICollection<KeyValuePair<string, string[]>>.CopyTo(KeyValuePair<string, str
5555
}
5656
}
5757

58-
IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
58+
public Enumerator GetEnumerator() => new Enumerator(Inner);
59+
60+
IEnumerator<KeyValuePair<string, string[]>> IEnumerable<KeyValuePair<string, string[]>>.GetEnumerator() => new Enumerator(Inner);
5961

60-
IEnumerator<KeyValuePair<string, string[]>> IEnumerable<KeyValuePair<string, string[]>>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
62+
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(Inner);
6163

6264
bool ICollection<KeyValuePair<string, string[]>>.Remove(KeyValuePair<string, string[]> item) => Inner.Remove(Convert(item));
6365

@@ -74,4 +76,40 @@ bool IDictionary<string, string[]>.TryGetValue(string key, out string[] value)
7476
value = default(StringValues);
7577
return false;
7678
}
79+
80+
public struct Enumerator : IEnumerator<KeyValuePair<string, string[]>>, IEnumerator
81+
{
82+
private IEnumerator<KeyValuePair<string, StringValues>> _inner;
83+
private KeyValuePair<string, string[]> _current;
84+
85+
internal Enumerator(IDictionary<string, StringValues> inner)
86+
{
87+
_inner = inner.GetEnumerator();
88+
_current = default;
89+
}
90+
91+
public void Dispose()
92+
{
93+
_inner?.Dispose();
94+
_inner = null;
95+
}
96+
97+
public bool MoveNext()
98+
{
99+
if (!_inner.MoveNext())
100+
{
101+
_current = default;
102+
return false;
103+
}
104+
105+
_current = Convert(_inner.Current);
106+
return true;
107+
}
108+
109+
public KeyValuePair<string, string[]> Current => _current;
110+
111+
object? IEnumerator.Current => Current;
112+
113+
void IEnumerator.Reset() => throw new NotImplementedException();
114+
}
77115
}

src/Http/Owin/src/DictionaryStringValuesWrapper.cs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ namespace Microsoft.AspNetCore.Owin;
1111

1212
internal sealed class DictionaryStringValuesWrapper : IHeaderDictionary
1313
{
14+
public readonly IDictionary<string, string[]> Inner;
15+
1416
public DictionaryStringValuesWrapper(IDictionary<string, string[]> inner)
1517
{
1618
Inner = inner;
1719
}
1820

19-
public readonly IDictionary<string, string[]> Inner;
20-
21-
private static KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new KeyValuePair<string, StringValues>(item.Key, item.Value);
21+
private static KeyValuePair<string, StringValues> Convert(KeyValuePair<string, string[]> item) => new(item.Key, item.Value);
2222

23-
private static KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new KeyValuePair<string, string[]>(item.Key, item.Value);
23+
private static KeyValuePair<string, string[]> Convert(KeyValuePair<string, StringValues> item) => new(item.Key, item.Value);
2424

2525
private StringValues Convert(string[] item) => item;
2626

@@ -100,9 +100,11 @@ void ICollection<KeyValuePair<string, StringValues>>.CopyTo(KeyValuePair<string,
100100
}
101101
}
102102

103-
IEnumerator IEnumerable.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
103+
public Enumerator GetEnumerator() => new Enumerator(Inner);
104+
105+
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator() => new Enumerator(Inner);
104106

105-
IEnumerator<KeyValuePair<string, StringValues>> IEnumerable<KeyValuePair<string, StringValues>>.GetEnumerator() => Inner.Select(Convert).GetEnumerator();
107+
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(Inner);
106108

107109
bool ICollection<KeyValuePair<string, StringValues>>.Remove(KeyValuePair<string, StringValues> item) => Inner.Remove(Convert(item));
108110

@@ -119,4 +121,40 @@ bool IDictionary<string, StringValues>.TryGetValue(string key, out StringValues
119121
value = default(StringValues);
120122
return false;
121123
}
124+
125+
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>, IEnumerator
126+
{
127+
private IEnumerator<KeyValuePair<string, string[]>> _inner;
128+
private KeyValuePair<string, StringValues> _current;
129+
130+
internal Enumerator(IDictionary<string, string[]> inner)
131+
{
132+
_inner = inner.GetEnumerator();
133+
_current = default;
134+
}
135+
136+
public void Dispose()
137+
{
138+
_inner?.Dispose();
139+
_inner = null;
140+
}
141+
142+
public bool MoveNext()
143+
{
144+
if (!_inner.MoveNext())
145+
{
146+
_current = default;
147+
return false;
148+
}
149+
150+
_current = Convert(_inner.Current);
151+
return true;
152+
}
153+
154+
public KeyValuePair<string, StringValues> Current => _current;
155+
156+
object? IEnumerator.Current => Current;
157+
158+
void IEnumerator.Reset() => throw new NotImplementedException();
159+
}
122160
}

src/Http/Owin/src/OwinEnvironment.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,8 @@ private sealed class OwinEntries : IEnumerable<KeyValuePair<string, FeatureMap>>
429429

430430
{ OwinConstants.CommonKeys.ConnectionId, new FeatureMap<IHttpConnectionFeature>(feature => feature.ConnectionId, (feature, value) => feature.ConnectionId = Convert.ToString(value, CultureInfo.InvariantCulture)) },
431431

432-
{ OwinConstants.CommonKeys.LocalPort, new FeatureMap<IHttpConnectionFeature>(feature => feature.LocalPort.ToString(CultureInfo.InvariantCulture), (feature, value) => feature.LocalPort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
433-
{ OwinConstants.CommonKeys.RemotePort, new FeatureMap<IHttpConnectionFeature>(feature => feature.RemotePort.ToString(CultureInfo.InvariantCulture), (feature, value) => feature.RemotePort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
432+
{ OwinConstants.CommonKeys.LocalPort, new FeatureMap<IHttpConnectionFeature>(feature => PortToString(feature.LocalPort), (feature, value) => feature.LocalPort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
433+
{ OwinConstants.CommonKeys.RemotePort, new FeatureMap<IHttpConnectionFeature>(feature => PortToString(feature.RemotePort), (feature, value) => feature.RemotePort = Convert.ToInt32(value, CultureInfo.InvariantCulture)) },
434434

435435
{ OwinConstants.CommonKeys.LocalIpAddress, new FeatureMap<IHttpConnectionFeature>(feature => feature.LocalIpAddress.ToString(), (feature, value) => feature.LocalIpAddress = IPAddress.Parse(Convert.ToString(value, CultureInfo.InvariantCulture))) },
436436
{ OwinConstants.CommonKeys.RemoteIpAddress, new FeatureMap<IHttpConnectionFeature>(feature => feature.RemoteIpAddress.ToString(), (feature, value) => feature.RemoteIpAddress = IPAddress.Parse(Convert.ToString(value, CultureInfo.InvariantCulture))) },
@@ -472,6 +472,18 @@ public OwinEntries(HttpContext context)
472472
};
473473
}
474474

475+
static readonly string[] PortStrings = CreatePortStrings();
476+
static string[] CreatePortStrings()
477+
{
478+
var ports = new string[65535]; // limit of ephemeral ports https://en.wikipedia.org/wiki/Ephemeral_port
479+
for (var i = 0; i < ports.Length; i++)
480+
{
481+
ports[i] = (i + 1).ToString(CultureInfo.InvariantCulture);
482+
}
483+
return ports;
484+
}
485+
static string PortToString(int port) => PortStrings[port - 1];
486+
475487
public int Count
476488
{
477489
get

0 commit comments

Comments
 (0)