@@ -44,6 +44,7 @@ public partial class Frame : FrameContext, IFrameControl
4444 private volatile bool _requestProcessingStopping ; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx
4545 private volatile bool _requestAborted ;
4646 private CancellationTokenSource _abortedCts ;
47+ private CancellationToken ? _manuallySetRequestAbortToken ;
4748
4849 private FrameRequestStream _requestBody ;
4950 private FrameResponseStream _responseBody ;
@@ -92,8 +93,47 @@ public Frame(ConnectionContext context,
9293
9394 public Stream DuplexStream { get ; set ; }
9495
95- public CancellationToken RequestAborted { get ; set ; }
96+ public CancellationToken RequestAborted
97+ {
98+ get
99+ {
100+ // If a request abort token was previously explicitly set, return it.
101+ if ( _manuallySetRequestAbortToken . HasValue )
102+ return _manuallySetRequestAbortToken . Value ;
103+
104+ // Otherwise, get the abort CTS. If we have one, which would mean that someone previously
105+ // asked for the RequestAborted token, simply return its token. If we don't,
106+ // check to see whether we've already aborted, in which case just return an
107+ // already canceled token. Finally, force a source into existence if we still
108+ // don't have one, and return its token.
109+ var cts = _abortedCts ;
110+ return
111+ cts != null ? cts . Token :
112+ _requestAborted ? new CancellationToken ( true ) :
113+ RequestAbortedSource . Token ;
114+ }
115+ set
116+ {
117+ // Set an abort token, overriding one we create internally. This setter and associated
118+ // field exist purely to support IHttpRequestLifetimeFeature.set_RequestAborted.
119+ _manuallySetRequestAbortToken = value ;
120+ }
121+ }
96122
123+ private CancellationTokenSource RequestAbortedSource
124+ {
125+ get
126+ {
127+ // Get the abort token, lazily-initializing it if necessary.
128+ // Make sure it's canceled if an abort request already came in.
129+ var cts = LazyInitializer . EnsureInitialized ( ref _abortedCts , ( ) => new CancellationTokenSource ( ) ) ;
130+ if ( _requestAborted )
131+ {
132+ cts . Cancel ( ) ;
133+ }
134+ return cts ;
135+ }
136+ }
97137 public bool HasResponseStarted
98138 {
99139 get { return _responseStarted ; }
@@ -145,7 +185,7 @@ public void Reset()
145185
146186 _prepareRequest ? . Invoke ( this ) ;
147187
148- _abortedCts ? . Dispose ( ) ;
188+ _manuallySetRequestAbortToken = null ;
149189 _abortedCts = null ;
150190 }
151191
@@ -198,24 +238,14 @@ public void Abort()
198238 {
199239 ConnectionControl . End ( ProduceEndType . SocketDisconnect ) ;
200240 SocketInput . AbortAwaiting ( ) ;
201-
202- try
203- {
204- _abortedCts ? . Cancel ( ) ;
205- }
206- catch ( ObjectDisposedException )
207- {
208- // Don't log ODEs thrown from _abortedCts.Cancel()
209- // If _abortedCts is disposed, the app has already completed.
210- }
241+ RequestAbortedSource . Cancel ( ) ;
211242 }
212243 catch ( Exception ex )
213244 {
214245 Log . LogError ( "Abort" , ex ) ;
215246 }
216247 finally
217248 {
218- _abortedCts ? . Dispose ( ) ;
219249 _abortedCts = null ;
220250 }
221251 }
@@ -261,8 +291,8 @@ public async Task RequestProcessingAsync()
261291 ResponseBody = _responseBody ;
262292 DuplexStream = new FrameDuplexStream ( RequestBody , ResponseBody ) ;
263293
264- _abortedCts = new CancellationTokenSource ( ) ;
265- RequestAborted = _abortedCts . Token ;
294+ _abortedCts = null ;
295+ _manuallySetRequestAbortToken = null ;
266296
267297 var httpContext = HttpContextFactory . Create ( this ) ;
268298 try
@@ -315,7 +345,6 @@ public async Task RequestProcessingAsync()
315345 {
316346 try
317347 {
318- _abortedCts ? . Dispose ( ) ;
319348 _abortedCts = null ;
320349
321350 // If _requestAborted is set, the connection has already been closed.
0 commit comments