2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
- using System . Diagnostics . CodeAnalysis ;
6
5
using System . Threading ;
7
6
using System . Threading . Tasks ;
8
- using Microsoft . AspNetCore . Http ;
7
+ using System . Threading . Tasks . Sources ;
9
8
using Microsoft . AspNetCore . HttpSys . Internal ;
10
9
11
10
namespace Microsoft . AspNetCore . Server . HttpSys
12
11
{
13
- internal unsafe class AsyncAcceptContext : TaskCompletionSource < RequestContext > , IDisposable
12
+ internal unsafe class AsyncAcceptContext : IValueTaskSource < RequestContext > , IDisposable
14
13
{
15
- internal static readonly IOCompletionCallback IOCallback = new IOCompletionCallback ( IOWaitCallback ) ;
14
+ private static readonly IOCompletionCallback IOCallback = IOWaitCallback ;
15
+ private readonly PreAllocatedOverlapped _preallocatedOverlapped ;
16
+
17
+ // mutable struct; do not make this readonly
18
+ private ManualResetValueTaskSourceCore < RequestContext > _tcs = new ( )
19
+ {
20
+ // We want to run continuations on the IO threads
21
+ RunContinuationsAsynchronously = false
22
+ } ;
16
23
17
24
private NativeRequestContext _nativeRequestContext ;
18
25
19
26
internal AsyncAcceptContext ( HttpSysListener server )
20
27
{
21
28
Server = server ;
22
- AllocateNativeRequest ( ) ;
29
+ _preallocatedOverlapped = new ( IOCallback , state : this , pinData : null ) ;
23
30
}
24
31
25
32
internal HttpSysListener Server { get ; }
26
33
27
- [ SuppressMessage ( "Microsoft.Design" , "CA1031:DoNotCatchGeneralExceptionTypes" , Justification = "Redirecting to callback" ) ]
28
- [ SuppressMessage ( "Microsoft.Reliability" , "CA2000:Dispose objects before losing scope" , Justification = "Disposed by callback" ) ]
34
+ internal ValueTask < RequestContext > AcceptAsync ( )
35
+ {
36
+ _tcs . Reset ( ) ;
37
+
38
+ AllocateNativeRequest ( ) ;
39
+
40
+ uint statusCode = QueueBeginGetContext ( ) ;
41
+ if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
42
+ statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
43
+ {
44
+ // some other bad error, possible(?) return values are:
45
+ // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
46
+ return ValueTask . FromException < RequestContext > ( new HttpSysException ( ( int ) statusCode ) ) ;
47
+ }
48
+
49
+ return new ValueTask < RequestContext > ( this , _tcs . Version ) ;
50
+ }
51
+
29
52
private static void IOCompleted ( AsyncAcceptContext asyncContext , uint errorCode , uint numBytes )
30
53
{
31
54
bool complete = false ;
55
+
32
56
try
33
57
{
34
58
if ( errorCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
35
59
errorCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_MORE_DATA )
36
60
{
37
- asyncContext . TrySetException ( new HttpSysException ( ( int ) errorCode ) ) ;
38
- complete = true ;
61
+ asyncContext . _tcs . SetException ( new HttpSysException ( ( int ) errorCode ) ) ;
62
+ return ;
39
63
}
40
- else
64
+
65
+ HttpSysListener server = asyncContext . Server ;
66
+ if ( errorCode == UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS )
41
67
{
42
- HttpSysListener server = asyncContext . Server ;
43
- if ( errorCode == UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS )
68
+ // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob
69
+ // points to it we need to hook up our authentication handling code here.
70
+ try
44
71
{
45
- // at this point we have received an unmanaged HTTP_REQUEST and memoryBlob
46
- // points to it we need to hook up our authentication handling code here.
47
- try
48
- {
49
- if ( server . ValidateRequest ( asyncContext . _nativeRequestContext ) && server . ValidateAuth ( asyncContext . _nativeRequestContext ) )
50
- {
51
- RequestContext requestContext = new RequestContext ( server , asyncContext . _nativeRequestContext ) ;
52
- asyncContext . TrySetResult ( requestContext ) ;
53
- complete = true ;
54
- }
55
- }
56
- catch ( Exception )
57
- {
58
- server . SendError ( asyncContext . _nativeRequestContext . RequestId , StatusCodes . Status400BadRequest ) ;
59
- throw ;
60
- }
61
- finally
72
+ var nativeContext = asyncContext . _nativeRequestContext ;
73
+
74
+ if ( server . ValidateRequest ( nativeContext ) && server . ValidateAuth ( nativeContext ) )
62
75
{
63
- // The request has been handed to the user, which means this code can't reuse the blob. Reset it here.
64
- if ( complete )
65
- {
66
- asyncContext . _nativeRequestContext = null ;
67
- }
68
- else
69
- {
70
- asyncContext . AllocateNativeRequest ( size : asyncContext . _nativeRequestContext . Size ) ;
71
- }
76
+ // It's important that we clear the native request context before we set the result
77
+ // we want to reuse this object for future accepts.
78
+ asyncContext . _nativeRequestContext = null ;
79
+
80
+ var requestContext = new RequestContext ( server , nativeContext ) ;
81
+ asyncContext . _tcs . SetResult ( requestContext ) ;
82
+
83
+ complete = true ;
72
84
}
73
85
}
74
- else
86
+ catch ( Exception ex )
75
87
{
76
- // (uint)backingBuffer.Length - AlignmentPadding
77
- asyncContext . AllocateNativeRequest ( numBytes , asyncContext . _nativeRequestContext . RequestId ) ;
88
+ asyncContext . _tcs . SetException ( ex ) ;
78
89
}
79
-
80
- // We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
81
- if ( ! complete )
90
+ finally
82
91
{
83
- uint statusCode = asyncContext . QueueBeginGetContext ( ) ;
84
- if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
85
- statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
92
+ if ( ! complete )
86
93
{
87
- // someother bad error, possible(?) return values are:
88
- // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
89
- asyncContext . TrySetException ( new HttpSysException ( ( int ) statusCode ) ) ;
90
- complete = true ;
94
+ asyncContext . AllocateNativeRequest ( size : asyncContext . _nativeRequestContext . Size ) ;
91
95
}
92
96
}
93
- if ( ! complete )
94
- {
95
- return ;
96
- }
97
+ }
98
+ else
99
+ {
100
+ // (uint)backingBuffer.Length - AlignmentPadding
101
+ asyncContext . AllocateNativeRequest ( numBytes , asyncContext . _nativeRequestContext . RequestId ) ;
97
102
}
98
103
99
- if ( complete )
104
+ // We need to issue a new request, either because auth failed, or because our buffer was too small the first time.
105
+ if ( ! complete )
100
106
{
101
- asyncContext . Dispose ( ) ;
107
+ uint statusCode = asyncContext . QueueBeginGetContext ( ) ;
108
+
109
+ if ( statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS &&
110
+ statusCode != UnsafeNclNativeMethods . ErrorCodes . ERROR_IO_PENDING )
111
+ {
112
+ // someother bad error, possible(?) return values are:
113
+ // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED
114
+ asyncContext . _tcs . SetException ( new HttpSysException ( ( int ) statusCode ) ) ;
115
+ }
102
116
}
103
117
}
104
118
catch ( Exception exception )
105
119
{
106
- // Logged by caller
107
- asyncContext . TrySetException ( exception ) ;
108
- asyncContext . Dispose ( ) ;
120
+ asyncContext . _tcs . SetException ( exception ) ;
109
121
}
110
122
}
111
123
112
124
private static unsafe void IOWaitCallback ( uint errorCode , uint numBytes , NativeOverlapped * nativeOverlapped )
113
125
{
114
- // take the ListenerAsyncResult object from the state
115
126
var asyncResult = ( AsyncAcceptContext ) ThreadPoolBoundHandle . GetNativeOverlappedState ( nativeOverlapped ) ;
116
127
IOCompleted ( asyncResult , errorCode , numBytes ) ;
117
128
}
118
129
119
- internal uint QueueBeginGetContext ( )
130
+ private uint QueueBeginGetContext ( )
120
131
{
121
132
uint statusCode = UnsafeNclNativeMethods . ErrorCodes . ERROR_SUCCESS ;
122
133
bool retry ;
@@ -162,17 +173,15 @@ internal uint QueueBeginGetContext()
162
173
return statusCode ;
163
174
}
164
175
165
- internal void AllocateNativeRequest ( uint ? size = null , ulong requestId = 0 )
176
+ private void AllocateNativeRequest ( uint ? size = null , ulong requestId = 0 )
166
177
{
167
178
_nativeRequestContext ? . ReleasePins ( ) ;
168
179
_nativeRequestContext ? . Dispose ( ) ;
169
180
170
- // We can't reuse overlapped objects
171
181
var boundHandle = Server . RequestQueue . BoundHandle ;
172
182
var nativeOverlapped = new SafeNativeOverlapped ( boundHandle ,
173
- boundHandle . AllocateNativeOverlapped ( IOCallback , this , pinData : null ) ) ;
183
+ boundHandle . AllocateNativeOverlapped ( _preallocatedOverlapped ) ) ;
174
184
175
- // nativeRequest
176
185
_nativeRequestContext = new NativeRequestContext ( nativeOverlapped , Server . MemoryPool , size , requestId ) ;
177
186
}
178
187
@@ -192,5 +201,20 @@ protected virtual void Dispose(bool disposing)
192
201
}
193
202
}
194
203
}
204
+
205
+ public RequestContext GetResult ( short token )
206
+ {
207
+ return _tcs . GetResult ( token ) ;
208
+ }
209
+
210
+ public ValueTaskSourceStatus GetStatus ( short token )
211
+ {
212
+ return _tcs . GetStatus ( token ) ;
213
+ }
214
+
215
+ public void OnCompleted ( Action < object > continuation , object state , short token , ValueTaskSourceOnCompletedFlags flags )
216
+ {
217
+ _tcs . OnCompleted ( continuation , state , token , flags ) ;
218
+ }
195
219
}
196
220
}
0 commit comments