@@ -53,8 +53,8 @@ public partial class Frame : FrameContext, IFrameControl
53
53
private Task _requestProcessingTask ;
54
54
private volatile bool _requestProcessingStopping ; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
55
55
private volatile bool _requestAborted ;
56
- private CancellationTokenSource _disconnectCts = new CancellationTokenSource ( ) ;
57
- private CancellationTokenSource _requestAbortCts ;
56
+ private CancellationTokenSource _abortedCts ;
57
+ private CancellationToken ? _manuallySetRequestAbortToken ;
58
58
59
59
private FrameRequestStream _requestBody ;
60
60
private FrameResponseStream _responseBody ;
@@ -135,8 +135,47 @@ public string HttpVersion
135
135
136
136
public Stream DuplexStream { get ; set ; }
137
137
138
- public CancellationToken RequestAborted { get ; set ; }
138
+ public CancellationToken RequestAborted
139
+ {
140
+ get
141
+ {
142
+ // If a request abort token was previously explicitly set, return it.
143
+ if ( _manuallySetRequestAbortToken . HasValue )
144
+ return _manuallySetRequestAbortToken . Value ;
145
+
146
+ // Otherwise, get the abort CTS. If we have one, which would mean that someone previously
147
+ // asked for the RequestAborted token, simply return its token. If we don't,
148
+ // check to see whether we've already aborted, in which case just return an
149
+ // already canceled token. Finally, force a source into existence if we still
150
+ // don't have one, and return its token.
151
+ var cts = _abortedCts ;
152
+ return
153
+ cts != null ? cts . Token :
154
+ _requestAborted ? new CancellationToken ( true ) :
155
+ RequestAbortedSource . Token ;
156
+ }
157
+ set
158
+ {
159
+ // Set an abort token, overriding one we create internally. This setter and associated
160
+ // field exist purely to support IHttpRequestLifetimeFeature.set_RequestAborted.
161
+ _manuallySetRequestAbortToken = value ;
162
+ }
163
+ }
139
164
165
+ private CancellationTokenSource RequestAbortedSource
166
+ {
167
+ get
168
+ {
169
+ // Get the abort token, lazily-initializing it if necessary.
170
+ // Make sure it's canceled if an abort request already came in.
171
+ var cts = LazyInitializer . EnsureInitialized ( ref _abortedCts , ( ) => new CancellationTokenSource ( ) ) ;
172
+ if ( _requestAborted )
173
+ {
174
+ cts . Cancel ( ) ;
175
+ }
176
+ return cts ;
177
+ }
178
+ }
140
179
public bool HasResponseStarted
141
180
{
142
181
get { return _responseStarted ; }
@@ -188,7 +227,8 @@ public void Reset()
188
227
189
228
_prepareRequest ? . Invoke ( this ) ;
190
229
191
- _requestAbortCts ? . Dispose ( ) ;
230
+ _manuallySetRequestAbortToken = null ;
231
+ _abortedCts = null ;
192
232
}
193
233
194
234
public void ResetResponseHeaders ( )
@@ -244,13 +284,16 @@ public void Abort()
244
284
{
245
285
ConnectionControl . End ( ProduceEndType . SocketDisconnect ) ;
246
286
SocketInput . AbortAwaiting ( ) ;
247
-
248
- _disconnectCts . Cancel ( ) ;
287
+ RequestAbortedSource . Cancel ( ) ;
249
288
}
250
289
catch ( Exception ex )
251
290
{
252
291
Log . LogError ( "Abort" , ex ) ;
253
292
}
293
+ finally
294
+ {
295
+ _abortedCts = null ;
296
+ }
254
297
}
255
298
256
299
/// <summary>
@@ -294,8 +337,8 @@ public async Task RequestProcessingAsync()
294
337
ResponseBody = _responseBody ;
295
338
DuplexStream = new FrameDuplexStream ( RequestBody , ResponseBody ) ;
296
339
297
- _requestAbortCts = CancellationTokenSource . CreateLinkedTokenSource ( _disconnectCts . Token ) ;
298
- RequestAborted = _requestAbortCts . Token ;
340
+ _abortedCts = null ;
341
+ _manuallySetRequestAbortToken = null ;
299
342
300
343
var httpContext = HttpContextFactory . Create ( this ) ;
301
344
try
@@ -351,7 +394,7 @@ public async Task RequestProcessingAsync()
351
394
{
352
395
try
353
396
{
354
- _disconnectCts . Dispose ( ) ;
397
+ _abortedCts = null ;
355
398
356
399
// If _requestAborted is set, the connection has already been closed.
357
400
if ( ! _requestAborted )
0 commit comments