@@ -132,13 +132,18 @@ export class SendStore {
132132
133133 const requestInput = sendRequest . request ;
134134 const pendingRequestDeferred = getObservableDeferred ( ) ;
135+ const abortController = new AbortController ( ) ;
135136 runInAction ( ( ) => {
136137 sendRequest . sentExchange = undefined ;
137138
138- sendRequest . pendingSendPromise = pendingRequestDeferred . promise ;
139- sendRequest . pendingSendPromise . then ( ( ) => { sendRequest . pendingSendPromise = undefined ; } ) ;
140- } ) ;
139+ sendRequest . pendingSend = {
140+ promise : pendingRequestDeferred . promise ,
141+ abort : ( ) => abortController . abort ( )
142+ } ;
141143
144+ const clearPending = action ( ( ) => { sendRequest . pendingSend = undefined ; } ) ;
145+ sendRequest . pendingSend . promise . then ( clearPending , clearPending ) ;
146+ } ) ;
142147
143148 const exchangeId = uuid ( ) ;
144149
@@ -163,12 +168,16 @@ export class SendStore {
163168
164169 const encodedBody = await requestInput . rawBody . encodingBestEffortPromise ;
165170
166- const responseStream = await ServerApi . sendRequest ( {
167- url : requestInput . url ,
168- method : requestInput . method ,
169- headers : requestInput . headers ,
170- rawBody : encodedBody
171- } , requestOptions ) ;
171+ const responseStream = await ServerApi . sendRequest (
172+ {
173+ url : requestInput . url ,
174+ method : requestInput . method ,
175+ headers : requestInput . headers ,
176+ rawBody : encodedBody
177+ } ,
178+ requestOptions ,
179+ abortController . signal
180+ ) ;
172181
173182 const exchange = this . eventStore . recordSentRequest ( {
174183 id : exchangeId ,
@@ -190,23 +199,43 @@ export class SendStore {
190199 // Keep the exchange up to date as response data arrives:
191200 trackResponseEvents ( responseStream , exchange )
192201 . catch ( action ( ( error : ErrorLike & { timingEvents ?: TimingEvents } ) => {
193- exchange . markAborted ( {
194- id : exchange . id ,
195- error : error ,
196- timingEvents : {
197- ...exchange . timingEvents as TimingEvents ,
198- ...error . timingEvents
199- } ,
200- tags : error . code ? [ `passthrough-error:${ error . code } ` ] : [ ]
201- } ) ;
202+ if ( error . name === 'AbortError' && abortController . signal . aborted ) {
203+ const startTime = exchange . timingEvents . startTime ! ; // Always set in Send case (just above)
204+ // Make a guess at an aborted timestamp, since this error won't give us one automatically:
205+ const durationBeforeAbort = Date . now ( ) - startTime ;
206+ const startTimestamp = exchange . timingEvents . startTimestamp ?? startTime ;
207+ const abortedTimestamp = startTimestamp + durationBeforeAbort ;
208+
209+ exchange . markAborted ( {
210+ id : exchange . id ,
211+ error : {
212+ message : 'Request cancelled'
213+ } ,
214+ timingEvents : {
215+ startTimestamp,
216+ abortedTimestamp,
217+ ...exchange . timingEvents ,
218+ ...error . timingEvents
219+ } as TimingEvents ,
220+ tags : [ 'client-error:ECONNABORTED' ]
221+ } ) ;
222+ } else {
223+ exchange . markAborted ( {
224+ id : exchange . id ,
225+ error : error ,
226+ timingEvents : {
227+ ...exchange . timingEvents as TimingEvents ,
228+ ...error . timingEvents
229+ } ,
230+ tags : error . code ? [ `passthrough-error:${ error . code } ` ] : [ ]
231+ } ) ;
232+ }
202233 } ) )
203234 . then ( ( ) => pendingRequestDeferred . resolve ( ) ) ;
204235
205236 runInAction ( ( ) => {
206237 sendRequest . sentExchange = exchange ;
207238 } ) ;
208-
209- return sendRequest . pendingSendPromise ;
210239 }
211240
212241}
0 commit comments