@@ -135,110 +135,120 @@ export class SendStore {
135135 const requestInput = sendRequest . request ;
136136 const pendingRequestDeferred = getObservableDeferred ( ) ;
137137 const abortController = new AbortController ( ) ;
138- runInAction ( ( ) => {
139- sendRequest . sentExchange = undefined ;
140138
141- sendRequest . pendingSend = {
142- promise : pendingRequestDeferred . promise ,
143- abort : ( ) => abortController . abort ( )
144- } ;
145-
146- const clearPending = action ( ( ) => { sendRequest . pendingSend = undefined ; } ) ;
147- sendRequest . pendingSend . promise . then ( clearPending , clearPending ) ;
148- } ) ;
139+ try {
140+ runInAction ( ( ) => {
141+ sendRequest . sentExchange = undefined ;
149142
150- const exchangeId = uuid ( ) ;
143+ sendRequest . pendingSend = {
144+ promise : pendingRequestDeferred . promise ,
145+ abort : ( ) => abortController . abort ( )
146+ } ;
151147
152- const passthroughOptions = this . rulesStore . activePassthroughOptions ;
153-
154- const url = new URL ( requestInput . url ) ;
155- const effectivePort = getEffectivePort ( url ) ;
156- const hostWithPort = `${ url . hostname } :${ effectivePort } ` ;
157- const clientCertificate = passthroughOptions . clientCertificateHostMap ?. [ hostWithPort ] ||
158- passthroughOptions . clientCertificateHostMap ?. [ url . hostname ! ] ||
159- undefined ;
160-
161- const requestOptions = {
162- ignoreHostHttpsErrors : passthroughOptions . ignoreHostHttpsErrors ,
163- trustAdditionalCAs : this . rulesStore . additionalCaCertificates . map ( ( cert ) =>
164- ( { cert : cert . rawPEM } )
165- ) ,
166- clientCertificate,
167- proxyConfig : getProxyConfig ( this . rulesStore . proxyConfig ) ,
168- lookupOptions : passthroughOptions . lookupOptions
169- } ;
148+ const clearPending = action ( ( ) => { sendRequest . pendingSend = undefined ; } ) ;
149+ sendRequest . pendingSend . promise . then ( clearPending , clearPending ) ;
150+ } ) ;
170151
171- const encodedBody = await requestInput . rawBody . encodingBestEffortPromise ;
152+ const exchangeId = uuid ( ) ;
153+
154+ const passthroughOptions = this . rulesStore . activePassthroughOptions ;
155+
156+ const url = new URL ( requestInput . url ) ;
157+ const effectivePort = getEffectivePort ( url ) ;
158+ const hostWithPort = `${ url . hostname } :${ effectivePort } ` ;
159+ const clientCertificate = passthroughOptions . clientCertificateHostMap ?. [ hostWithPort ] ||
160+ passthroughOptions . clientCertificateHostMap ?. [ url . hostname ! ] ||
161+ undefined ;
162+
163+ const requestOptions = {
164+ ignoreHostHttpsErrors : passthroughOptions . ignoreHostHttpsErrors ,
165+ trustAdditionalCAs : this . rulesStore . additionalCaCertificates . map ( ( cert ) =>
166+ ( { cert : cert . rawPEM } )
167+ ) ,
168+ clientCertificate,
169+ proxyConfig : getProxyConfig ( this . rulesStore . proxyConfig ) ,
170+ lookupOptions : passthroughOptions . lookupOptions
171+ } ;
172172
173- const responseStream = await ServerApi . sendRequest (
174- {
175- url : requestInput . url ,
173+ const encodedBody = await requestInput . rawBody . encodingBestEffortPromise ;
174+
175+ const responseStream = await ServerApi . sendRequest (
176+ {
177+ url : requestInput . url ,
178+ method : requestInput . method ,
179+ headers : requestInput . headers ,
180+ rawBody : encodedBody
181+ } ,
182+ requestOptions ,
183+ abortController . signal
184+ ) ;
185+
186+ const exchange = this . eventStore . recordSentRequest ( {
187+ id : exchangeId ,
188+ httpVersion : '1.1' ,
189+ matchedRuleId : false ,
176190 method : requestInput . method ,
177- headers : requestInput . headers ,
178- rawBody : encodedBody
179- } ,
180- requestOptions ,
181- abortController . signal
182- ) ;
183-
184- const exchange = this . eventStore . recordSentRequest ( {
185- id : exchangeId ,
186- httpVersion : '1.1' ,
187- matchedRuleId : false ,
188- method : requestInput . method ,
189- url : requestInput . url ,
190- protocol : url . protocol . slice ( 0 , - 1 ) ,
191- path : url . pathname ,
192- hostname : url . hostname ,
193- headers : rawHeadersToHeaders ( requestInput . headers ) ,
194- rawHeaders : _ . cloneDeep ( requestInput . headers ) ,
195- body : { buffer : encodedBody } ,
196- timingEvents : {
197- startTime : Date . now ( )
198- } as TimingEvents ,
199- tags : [ 'httptoolkit:manually-sent-request' ]
200- } ) ;
191+ url : requestInput . url ,
192+ protocol : url . protocol . slice ( 0 , - 1 ) ,
193+ path : url . pathname ,
194+ hostname : url . hostname ,
195+ headers : rawHeadersToHeaders ( requestInput . headers ) ,
196+ rawHeaders : _ . cloneDeep ( requestInput . headers ) ,
197+ body : { buffer : encodedBody } ,
198+ timingEvents : {
199+ startTime : Date . now ( )
200+ } as TimingEvents ,
201+ tags : [ 'httptoolkit:manually-sent-request' ]
202+ } ) ;
201203
202- // Keep the exchange up to date as response data arrives:
203- trackResponseEvents ( responseStream , exchange )
204- . catch ( action ( ( error : ErrorLike & { timingEvents ?: TimingEvents } ) => {
205- if ( error . name === 'AbortError' && abortController . signal . aborted ) {
206- const startTime = exchange . timingEvents . startTime ! ; // Always set in Send case (just above)
207- // Make a guess at an aborted timestamp, since this error won't give us one automatically:
208- const durationBeforeAbort = Date . now ( ) - startTime ;
209- const startTimestamp = exchange . timingEvents . startTimestamp ?? startTime ;
210- const abortedTimestamp = startTimestamp + durationBeforeAbort ;
211-
212- exchange . markAborted ( {
213- id : exchange . id ,
214- error : {
215- message : 'Request cancelled'
216- } ,
217- timingEvents : {
218- startTimestamp,
219- abortedTimestamp,
220- ...exchange . timingEvents ,
221- ...error . timingEvents
222- } as TimingEvents ,
223- tags : [ 'client-error:ECONNABORTED' ]
224- } ) ;
225- } else {
226- exchange . markAborted ( {
227- id : exchange . id ,
228- error : error ,
229- timingEvents : {
230- ...exchange . timingEvents as TimingEvents ,
231- ...error . timingEvents
232- } ,
233- tags : error . code ? [ `passthrough-error:${ error . code } ` ] : [ ]
234- } ) ;
235- }
236- } ) )
237- . then ( ( ) => pendingRequestDeferred . resolve ( ) ) ;
204+ // Keep the exchange up to date as response data arrives:
205+ trackResponseEvents ( responseStream , exchange )
206+ . catch ( action ( ( error : ErrorLike & { timingEvents ?: TimingEvents } ) => {
207+ if ( error . name === 'AbortError' && abortController . signal . aborted ) {
208+ const startTime = exchange . timingEvents . startTime ! ; // Always set in Send case (just above)
209+ // Make a guess at an aborted timestamp, since this error won't give us one automatically:
210+ const durationBeforeAbort = Date . now ( ) - startTime ;
211+ const startTimestamp = exchange . timingEvents . startTimestamp ?? startTime ;
212+ const abortedTimestamp = startTimestamp + durationBeforeAbort ;
213+
214+ exchange . markAborted ( {
215+ id : exchange . id ,
216+ error : {
217+ message : 'Request cancelled'
218+ } ,
219+ timingEvents : {
220+ startTimestamp,
221+ abortedTimestamp,
222+ ...exchange . timingEvents ,
223+ ...error . timingEvents
224+ } as TimingEvents ,
225+ tags : [ 'client-error:ECONNABORTED' ]
226+ } ) ;
227+ } else {
228+ exchange . markAborted ( {
229+ id : exchange . id ,
230+ error : error ,
231+ timingEvents : {
232+ ...exchange . timingEvents as TimingEvents ,
233+ ...error . timingEvents
234+ } ,
235+ tags : error . code ? [ `passthrough-error:${ error . code } ` ] : [ ]
236+ } ) ;
237+ }
238+ } ) )
239+ . then ( ( ) => pendingRequestDeferred . resolve ( ) ) ;
238240
239- runInAction ( ( ) => {
240- sendRequest . sentExchange = exchange ;
241- } ) ;
241+ runInAction ( ( ) => {
242+ sendRequest . sentExchange = exchange ;
243+ } ) ;
244+ } catch ( e : any ) {
245+ pendingRequestDeferred . reject ( e ) ;
246+ runInAction ( ( ) => {
247+ sendRequest . pendingSend = undefined ;
248+ sendRequest . sentExchange = undefined ;
249+ } ) ;
250+ throw e ;
251+ }
242252 }
243253
244254}
0 commit comments