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

Commit e8e4f7c

Browse files
committed
Don't allocate for ResponseCookiesFeature
1 parent 30a4b09 commit e8e4f7c

File tree

7 files changed

+171
-94
lines changed

7 files changed

+171
-94
lines changed

samples/SampleApp/PooledHttpContextFactory.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using System.Text;
76
using Microsoft.AspNetCore.Http;
87
using Microsoft.AspNetCore.Http.Features;
98
using Microsoft.Extensions.ObjectPool;
@@ -12,7 +11,6 @@ namespace SampleApp
1211
{
1312
public class PooledHttpContextFactory : IHttpContextFactory
1413
{
15-
private readonly ObjectPool<StringBuilder> _builderPool;
1614
private readonly IHttpContextAccessor _httpContextAccessor;
1715
private readonly Stack<PooledHttpContext> _pool = new Stack<PooledHttpContext>();
1816

@@ -28,7 +26,6 @@ public PooledHttpContextFactory(ObjectPoolProvider poolProvider, IHttpContextAcc
2826
throw new ArgumentNullException(nameof(poolProvider));
2927
}
3028

31-
_builderPool = poolProvider.CreateStringBuilderPool();
3229
_httpContextAccessor = httpContextAccessor;
3330
}
3431

@@ -39,9 +36,6 @@ public HttpContext Create(IFeatureCollection featureCollection)
3936
throw new ArgumentNullException(nameof(featureCollection));
4037
}
4138

42-
var responseCookiesFeature = new ResponseCookiesFeature(featureCollection, _builderPool);
43-
featureCollection.Set<IResponseCookiesFeature>(responseCookiesFeature);
44-
4539
PooledHttpContext httpContext = null;
4640
lock (_pool)
4741
{

src/Microsoft.AspNetCore.Http/Features/ResponseCookiesFeature.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ namespace Microsoft.AspNetCore.Http.Features
1313
/// </summary>
1414
public class ResponseCookiesFeature : IResponseCookiesFeature
1515
{
16-
// Object pool will be null only in test scenarios e.g. if code news up a DefaultHttpContext.
17-
private readonly ObjectPool<StringBuilder> _builderPool;
18-
1916
private FeatureReferences<IHttpResponseFeature> _features;
2017
private IResponseCookies _cookiesCollection;
2118

@@ -47,7 +44,6 @@ public ResponseCookiesFeature(IFeatureCollection features, ObjectPool<StringBuil
4744
}
4845

4946
_features = new FeatureReferences<IHttpResponseFeature>(features);
50-
_builderPool = builderPool;
5147
}
5248

5349
private IHttpResponseFeature HttpResponseFeature => _features.Fetch(ref _features.Cache, f => null);
@@ -60,7 +56,7 @@ public IResponseCookies Cookies
6056
if (_cookiesCollection == null)
6157
{
6258
var headers = HttpResponseFeature.Headers;
63-
_cookiesCollection = new ResponseCookies(headers, _builderPool);
59+
_cookiesCollection = new ResponseCookies(headers, null);
6460
}
6561

6662
return _cookiesCollection;

src/Microsoft.AspNetCore.Http/HttpContextFactory.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Text;
65
using Microsoft.AspNetCore.Http.Features;
76
using Microsoft.Extensions.ObjectPool;
87
using Microsoft.Extensions.Options;
@@ -11,7 +10,6 @@ namespace Microsoft.AspNetCore.Http
1110
{
1211
public class HttpContextFactory : IHttpContextFactory
1312
{
14-
private readonly ObjectPool<StringBuilder> _builderPool;
1513
private readonly IHttpContextAccessor _httpContextAccessor;
1614
private readonly FormOptions _formOptions;
1715

@@ -31,7 +29,6 @@ public HttpContextFactory(ObjectPoolProvider poolProvider, IOptions<FormOptions>
3129
throw new ArgumentNullException(nameof(formOptions));
3230
}
3331

34-
_builderPool = poolProvider.CreateStringBuilderPool();
3532
_formOptions = formOptions.Value;
3633
_httpContextAccessor = httpContextAccessor;
3734
}
@@ -43,9 +40,6 @@ public HttpContext Create(IFeatureCollection featureCollection)
4340
throw new ArgumentNullException(nameof(featureCollection));
4441
}
4542

46-
var responseCookiesFeature = new ResponseCookiesFeature(featureCollection, _builderPool);
47-
featureCollection.Set<IResponseCookiesFeature>(responseCookiesFeature);
48-
4943
var httpContext = new DefaultHttpContext(featureCollection);
5044
if (_httpContextAccessor != null)
5145
{

src/Microsoft.AspNetCore.Http/Internal/ResponseCookies.cs

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ namespace Microsoft.AspNetCore.Http.Internal
1515
/// </summary>
1616
public class ResponseCookies : IResponseCookies
1717
{
18-
private readonly ObjectPool<StringBuilder> _builderPool;
19-
2018
/// <summary>
2119
/// Create a new wrapper.
2220
/// </summary>
@@ -30,7 +28,6 @@ public ResponseCookies(IHeaderDictionary headers, ObjectPool<StringBuilder> buil
3028
}
3129

3230
Headers = headers;
33-
_builderPool = builderPool;
3431
}
3532

3633
private IHeaderDictionary Headers { get; set; }
@@ -45,24 +42,7 @@ public void Append(string key, string value)
4542
Path = "/"
4643
};
4744

48-
string cookieValue;
49-
if (_builderPool == null)
50-
{
51-
cookieValue = setCookieHeaderValue.ToString();
52-
}
53-
else
54-
{
55-
var stringBuilder = _builderPool.Get();
56-
try
57-
{
58-
setCookieHeaderValue.AppendToStringBuilder(stringBuilder);
59-
cookieValue = stringBuilder.ToString();
60-
}
61-
finally
62-
{
63-
_builderPool.Return(stringBuilder);
64-
}
65-
}
45+
var cookieValue = setCookieHeaderValue.ToString();
6646

6747
Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], cookieValue);
6848
}
@@ -86,24 +66,7 @@ public void Append(string key, string value, CookieOptions options)
8666
HttpOnly = options.HttpOnly,
8767
};
8868

89-
string cookieValue;
90-
if (_builderPool == null)
91-
{
92-
cookieValue = setCookieHeaderValue.ToString();
93-
}
94-
else
95-
{
96-
var stringBuilder = _builderPool.Get();
97-
try
98-
{
99-
setCookieHeaderValue.AppendToStringBuilder(stringBuilder);
100-
cookieValue = stringBuilder.ToString();
101-
}
102-
finally
103-
{
104-
_builderPool.Return(stringBuilder);
105-
}
106-
}
69+
var cookieValue = setCookieHeaderValue.ToString();
10770

10871
Headers[HeaderNames.SetCookie] = StringValues.Concat(Headers[HeaderNames.SetCookie], cookieValue);
10972
}

src/Microsoft.Net.Http.Headers/SetCookieHeaderValue.cs

Lines changed: 150 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics.Contracts;
7+
using System.Runtime.CompilerServices;
78
using System.Text;
89

910
namespace Microsoft.Net.Http.Headers
@@ -87,12 +88,157 @@ public string Value
8788
public bool HttpOnly { get; set; }
8889

8990
// name="val ue"; expires=Sun, 06 Nov 1994 08:49:37 GMT; max-age=86400; domain=domain1; path=path1; secure; httponly
90-
public override string ToString()
91+
public override unsafe string ToString()
9192
{
92-
StringBuilder header = new StringBuilder();
93-
AppendToStringBuilder(header);
93+
const string equals = "=";
94+
const string separator = "; ";
9495

95-
return header.ToString();
96+
var length = _name.Length + equals.Length + _value.Length;
97+
98+
string expires = null;
99+
string maxAge = null;
100+
101+
if (Expires.HasValue)
102+
{
103+
expires = HeaderUtilities.FormatDate(Expires.Value);
104+
length += separator.Length + ExpiresToken.Length + equals.Length + expires.Length;
105+
}
106+
107+
if (MaxAge.HasValue)
108+
{
109+
maxAge = HeaderUtilities.FormatInt64((long)MaxAge.Value.TotalSeconds);
110+
length += separator.Length + MaxAgeToken.Length + equals.Length + maxAge.Length;
111+
}
112+
113+
if (Domain != null)
114+
{
115+
length += separator.Length + DomainToken.Length + equals.Length + Domain.Length;
116+
}
117+
118+
if (Path != null)
119+
{
120+
length += separator.Length + PathToken.Length + equals.Length + Path.Length;
121+
}
122+
123+
if (Secure)
124+
{
125+
length += separator.Length + SecureToken.Length;
126+
}
127+
128+
if (HttpOnly)
129+
{
130+
length += separator.Length + HttpOnlyToken.Length;
131+
}
132+
133+
var header = new string('\0', length);
134+
135+
fixed (char* pSeparator = separator)
136+
fixed (char* pEquals = equals)
137+
fixed (char* pHeader = header)
138+
{
139+
var cHeader = pHeader;
140+
fixed (char* pName = _name)
141+
{
142+
Unsafe.CopyBlock(cHeader, pName, (uint)_name.Length * 2);
143+
cHeader += _name.Length;
144+
}
145+
146+
Unsafe.CopyBlock(cHeader, pEquals, (uint)equals.Length * 2);
147+
cHeader += equals.Length;
148+
149+
fixed (char* pValue = _value)
150+
{
151+
Unsafe.CopyBlock(cHeader, pValue, (uint)_value.Length * 2);
152+
cHeader += _value.Length;
153+
}
154+
155+
if (expires != null)
156+
{
157+
fixed (char* pExpires = expires)
158+
fixed (char* pExpiresToken = ExpiresToken)
159+
{
160+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
161+
cHeader += separator.Length;
162+
Unsafe.CopyBlock(cHeader, pExpiresToken, (uint)ExpiresToken.Length * 2);
163+
cHeader += ExpiresToken.Length;
164+
Unsafe.CopyBlock(cHeader, pEquals, (uint)equals.Length * 2);
165+
cHeader += equals.Length;
166+
Unsafe.CopyBlock(cHeader, pExpires, (uint)expires.Length * 2);
167+
cHeader += expires.Length;
168+
}
169+
}
170+
171+
if (maxAge != null)
172+
{
173+
fixed (char* pMaxAge = maxAge)
174+
fixed (char* pMaxAgeToken = MaxAgeToken)
175+
{
176+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
177+
cHeader += separator.Length;
178+
Unsafe.CopyBlock(cHeader, pMaxAgeToken, (uint)MaxAgeToken.Length * 2);
179+
cHeader += MaxAgeToken.Length;
180+
Unsafe.CopyBlock(cHeader, pEquals, (uint)equals.Length * 2);
181+
cHeader += equals.Length;
182+
Unsafe.CopyBlock(cHeader, pMaxAge, (uint)maxAge.Length * 2);
183+
cHeader += maxAge.Length;
184+
}
185+
}
186+
187+
if (Domain != null)
188+
{
189+
fixed (char* pDomain = Domain)
190+
fixed (char* pDomainToken = DomainToken)
191+
{
192+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
193+
cHeader += separator.Length;
194+
Unsafe.CopyBlock(cHeader, pDomainToken, (uint)DomainToken.Length * 2);
195+
cHeader += DomainToken.Length;
196+
Unsafe.CopyBlock(cHeader, pEquals, (uint)equals.Length * 2);
197+
cHeader += equals.Length;
198+
Unsafe.CopyBlock(cHeader, pDomain, (uint)Domain.Length * 2);
199+
cHeader += Domain.Length;
200+
}
201+
}
202+
203+
if (Path != null)
204+
{
205+
fixed (char* pPath = Path)
206+
fixed (char* pPathToken = PathToken)
207+
{
208+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
209+
cHeader += separator.Length;
210+
Unsafe.CopyBlock(cHeader, pPathToken, (uint)PathToken.Length * 2);
211+
cHeader += PathToken.Length;
212+
Unsafe.CopyBlock(cHeader, pEquals, (uint)equals.Length * 2);
213+
cHeader += equals.Length;
214+
Unsafe.CopyBlock(cHeader, pPath, (uint)Path.Length * 2);
215+
cHeader += Path.Length;
216+
}
217+
}
218+
219+
if (Secure)
220+
{
221+
fixed (char* pSecureToken = SecureToken)
222+
{
223+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
224+
cHeader += separator.Length;
225+
Unsafe.CopyBlock(cHeader, pSecureToken, (uint)SecureToken.Length * 2);
226+
cHeader += SecureToken.Length;
227+
}
228+
}
229+
230+
if (HttpOnly)
231+
{
232+
fixed (char* pHttpOnlyToken = HttpOnlyToken)
233+
{
234+
Unsafe.CopyBlock(cHeader, pSeparator, (uint)separator.Length * 2);
235+
cHeader += separator.Length;
236+
Unsafe.CopyBlock(cHeader, pHttpOnlyToken, (uint)HttpOnlyToken.Length * 2);
237+
}
238+
}
239+
}
240+
241+
return header;
96242
}
97243

98244
/// <summary>

src/Microsoft.Net.Http.Headers/project.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"buildOptions": {
1414
"warningsAsErrors": true,
15+
"allowUnsafe": true,
1516
"keyFile": "../../tools/Key.snk",
1617
"nowarn": [
1718
"CS1591"
@@ -29,7 +30,8 @@
2930
"System.Resources.ResourceManager": "4.0.1-*",
3031
"System.Runtime.Extensions": "4.1.0-*",
3132
"System.Text.Encoding": "4.0.11-*",
32-
"System.Buffers": "4.0.0-*"
33+
"System.Buffers": "4.0.0-*",
34+
"System.Runtime.CompilerServices.Unsafe": "4.0.0"
3335
}
3436
}
3537
}

0 commit comments

Comments
 (0)