1
1
import async from "async" ;
2
2
3
- import { HTTPClient } from "../../lib/http.js" ;
4
3
import {
5
4
AfterErrorContext ,
6
5
AfterErrorHook ,
@@ -25,10 +24,12 @@ import {
25
24
stringToBoolean ,
26
25
} from "./utils/index.js" ;
27
26
import {
27
+ HTTPClientExtension ,
28
28
MIN_PAGES_PER_THREAD ,
29
29
PARTITION_FORM_FILES_KEY ,
30
30
PARTITION_FORM_SPLIT_PDF_PAGE_KEY ,
31
31
} from "./common.js" ;
32
+ import { retry , RetryConfig } from "../../lib/retries" ;
32
33
33
34
/**
34
35
* Represents a hook for splitting and sending PDF files as per page requests.
@@ -39,8 +40,7 @@ export class SplitPdfHook
39
40
/**
40
41
* The HTTP client used for making requests.
41
42
*/
42
- client : HTTPClient | undefined ;
43
-
43
+ client : HTTPClientExtension | undefined ;
44
44
45
45
/**
46
46
* Keeps the strict-mode setting for splitPdfPage feature.
@@ -68,9 +68,16 @@ export class SplitPdfHook
68
68
* @returns The initialized SDK options.
69
69
*/
70
70
sdkInit ( opts : SDKInitOptions ) : SDKInitOptions {
71
- const { baseURL, client } = opts ;
72
- this . client = client ;
73
- return { baseURL : baseURL , client : client } ;
71
+ const { baseURL } = opts ;
72
+ this . client = new HTTPClientExtension ( ) ;
73
+
74
+ this . client . addHook ( "response" , ( res ) => {
75
+ if ( res . status != 200 ) {
76
+ console . error ( "Request failed with status code" , `${ res . status } ` ) ;
77
+ }
78
+ } ) ;
79
+
80
+ return { baseURL : baseURL , client : this . client } ;
74
81
}
75
82
76
83
/**
@@ -178,23 +185,48 @@ export class SplitPdfHook
178
185
file . name ,
179
186
firstPageNumber
180
187
) ;
188
+ const timeoutInMs = 60 * 10 * 1000 ;
181
189
const req = new Request ( requestClone , {
182
190
headers,
183
191
body,
192
+ signal : AbortSignal . timeout ( timeoutInMs )
184
193
} ) ;
185
194
requests . push ( req ) ;
186
195
setIndex += 1 ;
187
196
}
188
197
189
198
this . partitionSuccessfulResponses [ operationID ] = new Array ( requests . length ) ;
199
+ this . partitionFailedResponses [ operationID ] = new Array ( requests . length ) ;
190
200
191
201
const allowFailed = this . allowFailed ;
192
202
203
+ // These are the retry values from our api spec
204
+ // We need to hardcode them here until we're able to reuse the SDK
205
+ // from within this hook
206
+ const oneSecond = 1000 ;
207
+ const oneMinute = 1000 * 60 ;
208
+ const retryConfig = {
209
+ strategy : "backoff" ,
210
+ backoff : {
211
+ initialInterval : oneSecond * 3 ,
212
+ maxInterval : oneMinute * 12 ,
213
+ exponent : 1.88 ,
214
+ maxElapsedTime : oneMinute * 30 ,
215
+ } ,
216
+ } as RetryConfig ;
217
+
218
+ const retryCodes = [ "502" , "503" , "504" ] ;
219
+
193
220
this . partitionRequests [ operationID ] = async . parallelLimit (
194
- requests . slice ( 0 , - 1 ) . map ( ( req , pageIndex ) => async ( ) => {
221
+ requests . map ( ( req , pageIndex ) => async ( ) => {
195
222
const pageNumber = pageIndex + startingPageNumber ;
196
223
try {
197
- const response = await this . client ! . request ( req ) ;
224
+ const response = await retry (
225
+ async ( ) => {
226
+ return await this . client ! . request ( req . clone ( ) ) ;
227
+ } ,
228
+ { config : retryConfig , statusCodes : retryCodes }
229
+ ) ;
198
230
if ( response . status === 200 ) {
199
231
( this . partitionSuccessfulResponses [ operationID ] as Response [ ] ) [ pageIndex ] =
200
232
response . clone ( ) ;
@@ -206,7 +238,7 @@ export class SplitPdfHook
206
238
}
207
239
}
208
240
} catch ( e ) {
209
- console . error ( `Failed to send request for page ${ pageNumber } .` ) ;
241
+ console . error ( `Failed to send request for page ${ pageNumber } .` , e ) ;
210
242
if ( ! allowFailed ) {
211
243
throw e ;
212
244
}
@@ -215,7 +247,8 @@ export class SplitPdfHook
215
247
concurrencyLevel
216
248
) ;
217
249
218
- return requests . at ( - 1 ) as Request ;
250
+ const dummyRequest = new Request ( "https://no-op/" ) ;
251
+ return dummyRequest ;
219
252
}
220
253
221
254
/**
@@ -230,28 +263,39 @@ export class SplitPdfHook
230
263
successfulResponses : Response [ ] ,
231
264
failedResponses : Response [ ]
232
265
) : Promise < Response > {
266
+ let realResponse = response . clone ( ) ;
267
+ const firstSuccessfulResponse = successfulResponses . at ( 0 ) ;
268
+ const isFakeResponse = response . headers . has ( "fake-response" ) ;
269
+ if ( firstSuccessfulResponse !== undefined && isFakeResponse ) {
270
+ realResponse = firstSuccessfulResponse . clone ( ) ;
271
+ }
272
+
233
273
let responseBody , responseStatus , responseStatusText ;
234
274
const numFailedResponses = failedResponses ?. length ?? 0 ;
235
- const headers = prepareResponseHeaders ( response ) ;
275
+ const headers = prepareResponseHeaders ( realResponse ) ;
236
276
237
277
if ( ! this . allowFailed && failedResponses && failedResponses . length > 0 ) {
238
278
const failedResponse = failedResponses [ 0 ] ?. clone ( ) ;
239
279
if ( failedResponse ) {
240
280
responseBody = await failedResponse . text ( ) ;
241
- responseStatus = failedResponse . status ;
242
281
responseStatusText = failedResponse . statusText ;
243
282
} else {
244
283
responseBody = JSON . stringify ( { "details:" : "Unknown error" } ) ;
245
- responseStatus = 503
246
284
responseStatusText = "Unknown error"
247
285
}
286
+ // if the response status is unknown or was 502, 503, 504, set back to 500 to ensure we don't cause more retries
287
+ responseStatus = 500 ;
248
288
console . warn (
249
289
`${ numFailedResponses } requests failed. The partition operation is cancelled.`
250
290
) ;
251
291
} else {
252
- responseBody = await prepareResponseBody ( [ ...successfulResponses , response ] ) ;
253
- responseStatus = response . status
254
- responseStatusText = response . statusText
292
+ if ( isFakeResponse ) {
293
+ responseBody = await prepareResponseBody ( [ ...successfulResponses ] ) ;
294
+ } else {
295
+ responseBody = await prepareResponseBody ( [ ...successfulResponses , response ] ) ;
296
+ }
297
+ responseStatus = realResponse . status
298
+ responseStatusText = realResponse . statusText
255
299
if ( numFailedResponses > 0 ) {
256
300
console . warn (
257
301
`${ numFailedResponses } requests failed. The results might miss some pages.`
0 commit comments