@@ -27,7 +27,7 @@ import { BrowserContext, verifyClientCertificates } from './browserContext';
27
27
import { Cookie , CookieStore , domainMatches , parseRawCookie } from './cookieStore' ;
28
28
import { MultipartFormData } from './formData' ;
29
29
import { SdkObject } from './instrumentation' ;
30
- import { ProgressController } from './progress' ;
30
+ import { isAbortError , ProgressController } from './progress' ;
31
31
import { getMatchingTLSOptionsForOrigin , rewriteOpenSSLErrorIfNeeded } from './socksClientCertificatesInterceptor' ;
32
32
import { httpHappyEyeballsAgent , httpsHappyEyeballsAgent , timingForSocket } from './utils/happyEyeballs' ;
33
33
import { Tracing } from './trace/recorder/tracing' ;
@@ -84,11 +84,12 @@ export type APIRequestFinishedEvent = {
84
84
85
85
type SendRequestOptions = https . RequestOptions & {
86
86
maxRedirects : number ,
87
- deadline : number ,
88
87
headers : HeadersObject ,
89
88
__testHookLookup ?: ( hostname : string ) => LookupAddress [ ]
90
89
} ;
91
90
91
+ type SendRequestResult = Omit < channels . APIResponse , 'fetchUid' > & { body : Buffer } ;
92
+
92
93
export abstract class APIRequestContext extends SdkObject {
93
94
static Events = {
94
95
Dispose : 'dispose' ,
@@ -185,16 +186,11 @@ export abstract class APIRequestContext extends SdkObject {
185
186
let maxRedirects = params . maxRedirects ?? ( defaults . maxRedirects ?? 20 ) ;
186
187
maxRedirects = maxRedirects === 0 ? - 1 : maxRedirects ;
187
188
188
- const timeout = params . timeout ;
189
- const deadline = timeout && ( monotonicTime ( ) + timeout ) ;
190
-
191
189
const options : SendRequestOptions = {
192
190
method,
193
191
headers,
194
192
agent,
195
193
maxRedirects,
196
- timeout,
197
- deadline,
198
194
...getMatchingTLSOptionsForOrigin ( this . _defaultOptions ( ) . clientCertificates , requestUrl . origin ) ,
199
195
__testHookLookup : ( params as any ) . __testHookLookup ,
200
196
} ;
@@ -205,10 +201,10 @@ export abstract class APIRequestContext extends SdkObject {
205
201
const postData = serializePostData ( params , headers ) ;
206
202
if ( postData )
207
203
setHeader ( headers , 'content-length' , String ( postData . byteLength ) ) ;
208
- const controller = new ProgressController ( metadata , this ) ;
204
+ const controller = new ProgressController ( metadata , this , 'strict' ) ;
209
205
const fetchResponse = await controller . run ( progress => {
210
206
return this . _sendRequestWithRetries ( progress , requestUrl , options , postData , params . maxRetries ) ;
211
- } , timeout ) ;
207
+ } , params . timeout ) ;
212
208
const fetchUid = this . _storeResponseBody ( fetchResponse . body ) ;
213
209
this . fetchLog . set ( fetchUid , controller . metadata . log ) ;
214
210
const failOnStatusCode = params . failOnStatusCode !== undefined ? params . failOnStatusCode : ! ! defaults . failOnStatusCode ;
@@ -252,10 +248,10 @@ export abstract class APIRequestContext extends SdkObject {
252
248
return cookies ;
253
249
}
254
250
255
- private async _updateRequestCookieHeader ( url : URL , headers : HeadersObject ) {
251
+ private async _updateRequestCookieHeader ( progress : Progress , url : URL , headers : HeadersObject ) {
256
252
if ( getHeader ( headers , 'cookie' ) !== undefined )
257
253
return ;
258
- const contextCookies = await this . _cookies ( url ) ;
254
+ const contextCookies = await progress . race ( this . _cookies ( url ) ) ;
259
255
// Browser context returns cookies with domain matching both .example.com and
260
256
// example.com. Those without leading dot are only sent when domain is strictly
261
257
// matching example.com, but not for sub.example.com.
@@ -266,31 +262,33 @@ export abstract class APIRequestContext extends SdkObject {
266
262
}
267
263
}
268
264
269
- private async _sendRequestWithRetries ( progress : Progress , url : URL , options : SendRequestOptions , postData ?: Buffer , maxRetries ?: number ) : Promise < Omit < channels . APIResponse , 'fetchUid' > & { body : Buffer } > {
265
+ private async _sendRequestWithRetries ( progress : Progress , url : URL , options : SendRequestOptions , postData ?: Buffer , maxRetries ?: number ) : Promise < SendRequestResult > {
270
266
maxRetries ??= 0 ;
271
267
let backoff = 250 ;
272
268
for ( let i = 0 ; i <= maxRetries ; i ++ ) {
273
269
try {
274
270
return await this . _sendRequest ( progress , url , options , postData ) ;
275
271
} catch ( e ) {
272
+ if ( isAbortError ( e ) )
273
+ throw e ;
276
274
e = rewriteOpenSSLErrorIfNeeded ( e ) ;
277
275
if ( maxRetries === 0 )
278
276
throw e ;
279
- if ( i === maxRetries || ( options . deadline && monotonicTime ( ) + backoff > options . deadline ) )
277
+ if ( i === maxRetries )
280
278
throw new Error ( `Failed after ${ i + 1 } attempt(s): ${ e } ` ) ;
281
279
// Retry on connection reset only.
282
280
if ( e . code !== 'ECONNRESET' )
283
281
throw e ;
284
282
progress . log ( ` Received ECONNRESET, will retry after ${ backoff } ms.` ) ;
285
- await new Promise ( f => setTimeout ( f , backoff ) ) ;
283
+ await progress . wait ( backoff ) ;
286
284
backoff *= 2 ;
287
285
}
288
286
}
289
287
throw new Error ( 'Unreachable' ) ;
290
288
}
291
289
292
- private async _sendRequest ( progress : Progress , url : URL , options : SendRequestOptions , postData ?: Buffer ) : Promise < Omit < channels . APIResponse , 'fetchUid' > & { body : Buffer } > {
293
- await this . _updateRequestCookieHeader ( url , options . headers ) ;
290
+ private async _sendRequest ( progress : Progress , url : URL , options : SendRequestOptions , postData ?: Buffer ) : Promise < SendRequestResult > {
291
+ await this . _updateRequestCookieHeader ( progress , url , options . headers ) ;
294
292
295
293
const requestCookies = getHeader ( options . headers , 'cookie' ) ?. split ( ';' ) . map ( p => {
296
294
const [ name , value ] = p . split ( '=' ) . map ( v => v . trim ( ) ) ;
@@ -305,7 +303,7 @@ export abstract class APIRequestContext extends SdkObject {
305
303
} ;
306
304
this . emit ( APIRequestContext . Events . Request , requestEvent ) ;
307
305
308
- return new Promise ( ( fulfill , reject ) => {
306
+ const resultPromise = new Promise < SendRequestResult > ( ( fulfill , reject ) => {
309
307
const requestConstructor : ( ( url : URL , options : http . RequestOptions , callback ?: ( res : http . IncomingMessage ) => void ) => http . ClientRequest )
310
308
= ( url . protocol === 'https:' ? https : http ) . request ;
311
309
// If we have a proxy agent already, do not override it.
@@ -402,8 +400,6 @@ export abstract class APIRequestContext extends SdkObject {
402
400
headers,
403
401
agent : options . agent ,
404
402
maxRedirects : options . maxRedirects - 1 ,
405
- timeout : options . timeout ,
406
- deadline : options . deadline ,
407
403
...getMatchingTLSOptionsForOrigin ( this . _defaultOptions ( ) . clientCertificates , url . origin ) ,
408
404
__testHookLookup : options . __testHookLookup ,
409
405
} ;
@@ -492,6 +488,7 @@ export abstract class APIRequestContext extends SdkObject {
492
488
body . on ( 'end' , notifyBodyFinished ) ;
493
489
} ) ;
494
490
request . on ( 'error' , reject ) ;
491
+ progress . cleanupWhenAborted ( ( ) => request . destroy ( ) ) ;
495
492
496
493
listeners . push (
497
494
eventsHelper . addEventListener ( this , APIRequestContext . Events . Dispose , ( ) => {
@@ -543,23 +540,11 @@ export abstract class APIRequestContext extends SdkObject {
543
540
progress . log ( ` ${ name } : ${ value } ` ) ;
544
541
}
545
542
546
- if ( options . deadline ) {
547
- const rejectOnTimeout = ( ) => {
548
- reject ( new Error ( `Request timed out after ${ options . timeout } ms` ) ) ;
549
- request . destroy ( ) ;
550
- } ;
551
- const remaining = options . deadline - monotonicTime ( ) ;
552
- if ( remaining <= 0 ) {
553
- rejectOnTimeout ( ) ;
554
- return ;
555
- }
556
- request . setTimeout ( remaining , rejectOnTimeout ) ;
557
- }
558
-
559
543
if ( postData )
560
544
request . write ( postData ) ;
561
545
request . end ( ) ;
562
546
} ) ;
547
+ return progress . race ( resultPromise ) ;
563
548
}
564
549
565
550
private _getHttpCredentials ( url : URL ) {
0 commit comments