Skip to content

Commit b6be6be

Browse files
authored
Use a single request object (#28347)
- Merged NativeRequestContext, RequestContext and FeatureContext into a single object.
1 parent 7065c07 commit b6be6be

File tree

6 files changed

+148
-155
lines changed

6 files changed

+148
-155
lines changed

src/Servers/HttpSys/HttpSysServer.slnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"src\\Servers\\HttpSys\\src\\Microsoft.AspNetCore.Server.HttpSys.csproj",
2424
"src\\Servers\\HttpSys\\test\\FunctionalTests\\Microsoft.AspNetCore.Server.HttpSys.FunctionalTests.csproj",
2525
"src\\Servers\\HttpSys\\test\\Tests\\Microsoft.AspNetCore.Server.HttpSys.Tests.csproj",
26-
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj"
26+
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj",
27+
"src\\Testing\\src\\Microsoft.AspNetCore.Testing.csproj"
2728
]
2829
}
2930
}

src/Servers/HttpSys/src/AsyncAcceptContext.cs

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal unsafe class AsyncAcceptContext : IValueTaskSource<RequestContext>, IDi
2323
RunContinuationsAsynchronously = false
2424
};
2525

26-
private NativeRequestContext _nativeRequestContext;
26+
private RequestContext _requestContext;
2727

2828
internal AsyncAcceptContext(HttpSysListener server)
2929
{
@@ -53,14 +53,18 @@ internal ValueTask<RequestContext> AcceptAsync()
5353

5454
private static void IOCompleted(AsyncAcceptContext asyncContext, uint errorCode, uint numBytes)
5555
{
56-
bool complete = false;
56+
var complete = false;
57+
// This is important to stash a ref to as it's a mutable struct
58+
ref var mrvts = ref asyncContext._mrvts;
59+
var requestContext = asyncContext._requestContext;
60+
var requestId = requestContext.RequestId;
5761

5862
try
5963
{
6064
if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS &&
6165
errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
6266
{
63-
asyncContext._mrvts.SetException(new HttpSysException((int)errorCode));
67+
mrvts.SetException(new HttpSysException((int)errorCode));
6468
return;
6569
}
6670

@@ -71,37 +75,39 @@ private static void IOCompleted(AsyncAcceptContext asyncContext, uint errorCode,
7175
// points to it we need to hook up our authentication handling code here.
7276
try
7377
{
74-
var nativeContext = asyncContext._nativeRequestContext;
75-
76-
if (server.ValidateRequest(nativeContext) && server.ValidateAuth(nativeContext))
78+
if (server.ValidateRequest(requestContext) && server.ValidateAuth(requestContext))
7779
{
78-
// It's important that we clear the native request context before we set the result
79-
// we want to reuse this object for future accepts.
80-
asyncContext._nativeRequestContext = null;
80+
// It's important that we clear the request context before we set the result
81+
// we want to reuse the acceptContext object for future accepts.
82+
asyncContext._requestContext = null;
83+
84+
// Initialize features here once we're successfully validated the request
85+
// TODO: In the future defer this work to the thread pool so we can get off the IO thread
86+
// as quickly as possible
87+
requestContext.InitializeFeatures();
8188

82-
var requestContext = new RequestContext(server, nativeContext);
83-
asyncContext._mrvts.SetResult(requestContext);
89+
mrvts.SetResult(requestContext);
8490

8591
complete = true;
8692
}
8793
}
8894
catch (Exception ex)
8995
{
90-
server.SendError(asyncContext._nativeRequestContext.RequestId, StatusCodes.Status400BadRequest);
91-
asyncContext._mrvts.SetException(ex);
96+
server.SendError(requestId, StatusCodes.Status400BadRequest);
97+
mrvts.SetException(ex);
9298
}
9399
finally
94100
{
95101
if (!complete)
96102
{
97-
asyncContext.AllocateNativeRequest(size: asyncContext._nativeRequestContext.Size);
103+
asyncContext.AllocateNativeRequest(size: requestContext.Size);
98104
}
99105
}
100106
}
101107
else
102108
{
103109
// (uint)backingBuffer.Length - AlignmentPadding
104-
asyncContext.AllocateNativeRequest(numBytes, asyncContext._nativeRequestContext.RequestId);
110+
asyncContext.AllocateNativeRequest(numBytes, requestId);
105111
}
106112

107113
// We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
@@ -114,20 +120,20 @@ private static void IOCompleted(AsyncAcceptContext asyncContext, uint errorCode,
114120
{
115121
// someother bad error, possible(?) return values are:
116122
// ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
117-
asyncContext._mrvts.SetException(new HttpSysException((int)statusCode));
123+
mrvts.SetException(new HttpSysException((int)statusCode));
118124
}
119125
}
120126
}
121127
catch (Exception exception)
122128
{
123-
asyncContext._mrvts.SetException(exception);
129+
mrvts.SetException(exception);
124130
}
125131
}
126132

127133
private static unsafe void IOWaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
128134
{
129-
var asyncResult = (AsyncAcceptContext)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped);
130-
IOCompleted(asyncResult, errorCode, numBytes);
135+
var acceptContext = (AsyncAcceptContext)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped);
136+
IOCompleted(acceptContext, errorCode, numBytes);
131137
}
132138

133139
private uint QueueBeginGetContext()
@@ -140,21 +146,21 @@ private uint QueueBeginGetContext()
140146
uint bytesTransferred = 0;
141147
statusCode = HttpApi.HttpReceiveHttpRequest(
142148
Server.RequestQueue.Handle,
143-
_nativeRequestContext.RequestId,
149+
_requestContext.RequestId,
144150
// Small perf impact by not using HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY
145151
// if the request sends header+body in a single TCP packet
146152
(uint)HttpApiTypes.HTTP_FLAGS.NONE,
147-
_nativeRequestContext.NativeRequest,
148-
_nativeRequestContext.Size,
153+
_requestContext.NativeRequest,
154+
_requestContext.Size,
149155
&bytesTransferred,
150156
_overlapped);
151157

152-
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER && _nativeRequestContext.RequestId != 0)
158+
if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER && _requestContext.RequestId != 0)
153159
{
154160
// we might get this if somebody stole our RequestId,
155161
// set RequestId to 0 and start all over again with the buffer we just allocated
156162
// BUGBUG: how can someone steal our request ID? seems really bad and in need of fix.
157-
_nativeRequestContext.RequestId = 0;
163+
_requestContext.RequestId = 0;
158164
retry = true;
159165
}
160166
else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
@@ -178,17 +184,16 @@ private uint QueueBeginGetContext()
178184

179185
private void AllocateNativeRequest(uint? size = null, ulong requestId = 0)
180186
{
181-
_nativeRequestContext?.ReleasePins();
182-
_nativeRequestContext?.Dispose();
187+
_requestContext?.ReleasePins();
188+
_requestContext?.Dispose();
183189

184190
var boundHandle = Server.RequestQueue.BoundHandle;
185-
186191
if (_overlapped != null)
187192
{
188193
boundHandle.FreeNativeOverlapped(_overlapped);
189194
}
190195

191-
_nativeRequestContext = new NativeRequestContext(Server.MemoryPool, size, requestId);
196+
_requestContext = new RequestContext(Server, size, requestId);
192197
_overlapped = boundHandle.AllocateNativeOverlapped(_preallocatedOverlapped);
193198
}
194199

@@ -201,11 +206,11 @@ protected virtual void Dispose(bool disposing)
201206
{
202207
if (disposing)
203208
{
204-
if (_nativeRequestContext != null)
209+
if (_requestContext != null)
205210
{
206-
_nativeRequestContext.ReleasePins();
207-
_nativeRequestContext.Dispose();
208-
_nativeRequestContext = null;
211+
_requestContext.ReleasePins();
212+
_requestContext.Dispose();
213+
_requestContext = null;
209214

210215
var boundHandle = Server.RequestQueue.BoundHandle;
211216

src/Servers/HttpSys/src/RequestProcessing/Request.cs

Lines changed: 26 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys
2020
{
2121
internal sealed class Request
2222
{
23-
private NativeRequestContext _nativeRequestContext;
24-
2523
private X509Certificate2 _clientCert;
2624
// TODO: https://github.com/aspnet/HttpSysServer/issues/231
2725
// private byte[] _providedTokenBindingId;
@@ -39,26 +37,25 @@ internal sealed class Request
3937

4038
private bool _isDisposed = false;
4139

42-
internal Request(RequestContext requestContext, NativeRequestContext nativeRequestContext)
40+
internal Request(RequestContext requestContext)
4341
{
4442
// TODO: Verbose log
4543
RequestContext = requestContext;
46-
_nativeRequestContext = nativeRequestContext;
4744
_contentBoundaryType = BoundaryType.None;
4845

49-
RequestId = nativeRequestContext.RequestId;
50-
UConnectionId = nativeRequestContext.ConnectionId;
51-
SslStatus = nativeRequestContext.SslStatus;
46+
RequestId = requestContext.RequestId;
47+
UConnectionId = requestContext.ConnectionId;
48+
SslStatus = requestContext.SslStatus;
5249

53-
KnownMethod = nativeRequestContext.VerbId;
54-
Method = _nativeRequestContext.GetVerb();
50+
KnownMethod = requestContext.VerbId;
51+
Method = requestContext.GetVerb();
5552

56-
RawUrl = nativeRequestContext.GetRawUrl();
53+
RawUrl = requestContext.GetRawUrl();
5754

58-
var cookedUrl = nativeRequestContext.GetCookedUrl();
55+
var cookedUrl = requestContext.GetCookedUrl();
5956
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
6057

61-
var rawUrlInBytes = _nativeRequestContext.GetRawUrlInBytes();
58+
var rawUrlInBytes = requestContext.GetRawUrlInBytes();
6259
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes);
6360

6461
PathBase = string.Empty;
@@ -72,7 +69,7 @@ internal Request(RequestContext requestContext, NativeRequestContext nativeReque
7269
}
7370
else
7471
{
75-
var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)nativeRequestContext.UrlContext);
72+
var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)requestContext.UrlContext);
7673
// Prefix may be null if the requested has been transfered to our queue
7774
if (!(prefix is null))
7875
{
@@ -97,11 +94,11 @@ internal Request(RequestContext requestContext, NativeRequestContext nativeReque
9794
}
9895
}
9996

100-
ProtocolVersion = _nativeRequestContext.GetVersion();
97+
ProtocolVersion = RequestContext.GetVersion();
10198

102-
Headers = new RequestHeaders(_nativeRequestContext);
99+
Headers = new RequestHeaders(RequestContext);
103100

104-
User = _nativeRequestContext.GetUser();
101+
User = RequestContext.GetUser();
105102

106103
if (IsHttps)
107104
{
@@ -111,7 +108,7 @@ internal Request(RequestContext requestContext, NativeRequestContext nativeReque
111108
// GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231
112109

113110
// Finished directly accessing the HTTP_REQUEST structure.
114-
_nativeRequestContext.ReleasePins();
111+
RequestContext.ReleasePins();
115112
// TODO: Verbose log parameters
116113
}
117114

@@ -222,7 +219,7 @@ private AspNetCore.HttpSys.Internal.SocketAddress RemoteEndPoint
222219
{
223220
if (_remoteEndPoint == null)
224221
{
225-
_remoteEndPoint = _nativeRequestContext.GetRemoteEndPoint();
222+
_remoteEndPoint = RequestContext.GetRemoteEndPoint();
226223
}
227224

228225
return _remoteEndPoint;
@@ -235,7 +232,7 @@ private AspNetCore.HttpSys.Internal.SocketAddress LocalEndPoint
235232
{
236233
if (_localEndPoint == null)
237234
{
238-
_localEndPoint = _nativeRequestContext.GetLocalEndPoint();
235+
_localEndPoint = RequestContext.GetLocalEndPoint();
239236
}
240237

241238
return _localEndPoint;
@@ -278,15 +275,15 @@ public IReadOnlyDictionary<int, ReadOnlyMemory<byte>> RequestInfo
278275
{
279276
if (_requestInfo == null)
280277
{
281-
_requestInfo = _nativeRequestContext.GetRequestInfo();
278+
_requestInfo = RequestContext.GetRequestInfo();
282279
}
283280
return _requestInfo;
284281
}
285282
}
286283

287284
private void GetTlsHandshakeResults()
288285
{
289-
var handshake = _nativeRequestContext.GetTlsHandshake();
286+
var handshake = RequestContext.GetTlsHandshake();
290287

291288
Protocol = handshake.Protocol;
292289
// The OS considers client and server TLS as different enum values. SslProtocols choose to combine those for some reason.
@@ -332,7 +329,7 @@ public X509Certificate2 ClientCertificate
332329
{
333330
try
334331
{
335-
_clientCert = _nativeRequestContext.GetClientCertificate();
332+
_clientCert = RequestContext.GetClientCertificate();
336333
}
337334
catch (CryptographicException ce)
338335
{
@@ -426,27 +423,19 @@ private unsafe void GetTlsTokenBindingInfo()
426423
*/
427424
internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size)
428425
{
429-
return _nativeRequestContext.GetChunks(ref dataChunkIndex, ref dataChunkOffset, buffer, offset, size);
426+
return RequestContext.GetChunks(ref dataChunkIndex, ref dataChunkOffset, buffer, offset, size);
430427
}
431428

432429
// should only be called from RequestContext
433430
internal void Dispose()
434431
{
435-
// TODO: Verbose log
436-
_isDisposed = true;
437-
_nativeRequestContext.Dispose();
438-
(User?.Identity as WindowsIdentity)?.Dispose();
439-
if (_nativeStream != null)
440-
{
441-
_nativeStream.Dispose();
442-
}
443-
}
444-
445-
private void CheckDisposed()
446-
{
447-
if (_isDisposed)
432+
if (!_isDisposed)
448433
{
449-
throw new ObjectDisposedException(this.GetType().FullName);
434+
// TODO: Verbose log
435+
_isDisposed = true;
436+
RequestContext.Dispose();
437+
(User?.Identity as WindowsIdentity)?.Dispose();
438+
_nativeStream?.Dispose();
450439
}
451440
}
452441

0 commit comments

Comments
 (0)