@@ -13,19 +13,19 @@ import {
13
13
Tag ,
14
14
UploadPartCommand ,
15
15
} from "@aws-sdk/client-s3" ;
16
- import { AbortController } from "@smithy/abort-controller" ;
17
16
import {
18
17
EndpointParameterInstructionsSupplier ,
19
18
getEndpointFromInstructions ,
20
19
toEndpointV1 ,
21
20
} from "@smithy/middleware-endpoint" ;
22
21
import { HttpRequest } from "@smithy/protocol-http" ;
23
22
import { extendedEncodeURIComponent } from "@smithy/smithy-client" ;
24
- import type { AbortController as IAbortController , AbortSignal as IAbortSignal , Endpoint } from "@smithy/types" ;
23
+ import type { Endpoint } from "@smithy/types" ;
25
24
import { EventEmitter } from "events" ;
26
25
27
26
import { byteLength } from "./bytelength" ;
28
27
import { getChunk } from "./chunker" ;
28
+ import { wireSignal } from "./signal" ;
29
29
import { BodyDataTypes , Options , Progress } from "./types" ;
30
30
31
31
export interface RawDataPart {
@@ -59,8 +59,7 @@ export class Upload extends EventEmitter {
59
59
private bytesUploadedSoFar : number ;
60
60
61
61
// used in the upload.
62
- private abortController : IAbortController ;
63
- private concurrentUploaders : Promise < void > [ ] = [ ] ;
62
+ private abortController = new AbortController ( ) ;
64
63
private createMultiPartPromise ?: Promise < CreateMultipartUploadCommandOutput > ;
65
64
private abortMultipartUploadCommand : AbortMultipartUploadCommand | null = null ;
66
65
@@ -93,7 +92,9 @@ export class Upload extends EventEmitter {
93
92
// set progress defaults
94
93
this . totalBytes = byteLength ( this . params . Body ) ;
95
94
this . bytesUploadedSoFar = 0 ;
96
- this . abortController = options . abortController ?? new AbortController ( ) ;
95
+
96
+ wireSignal ( this . abortController , options . abortSignal ) ;
97
+ wireSignal ( this . abortController , options . abortController ?. signal ) ;
97
98
}
98
99
99
100
async abort ( ) : Promise < void > {
@@ -111,7 +112,12 @@ export class Upload extends EventEmitter {
111
112
) ;
112
113
}
113
114
this . sent = true ;
114
- return await Promise . race ( [ this . __doMultipartUpload ( ) , this . __abortTimeout ( this . abortController . signal ) ] ) ;
115
+
116
+ try {
117
+ return await this . __doMultipartUpload ( ) ;
118
+ } finally {
119
+ this . abortController . abort ( ) ;
120
+ }
115
121
}
116
122
117
123
public on ( event : "httpUploadProgress" , listener : ( progress : Progress ) => void ) : this {
@@ -143,7 +149,12 @@ export class Upload extends EventEmitter {
143
149
eventEmitter . on ( "xhr.upload.progress" , uploadEventListener ) ;
144
150
}
145
151
146
- const resolved = await Promise . all ( [ this . client . send ( new PutObjectCommand ( params ) ) , clientConfig ?. endpoint ?.( ) ] ) ;
152
+ const resolved = await Promise . all ( [
153
+ this . client . send ( new PutObjectCommand ( params ) , {
154
+ abortSignal : this . abortController . signal ,
155
+ } ) ,
156
+ clientConfig ?. endpoint ?.( ) ,
157
+ ] ) ;
147
158
const putResult = resolved [ 0 ] ;
148
159
let endpoint : Endpoint | undefined = resolved [ 1 ] ;
149
160
@@ -291,7 +302,10 @@ export class Upload extends EventEmitter {
291
302
UploadId : this . uploadId ,
292
303
Body : dataPart . data ,
293
304
PartNumber : dataPart . partNumber ,
294
- } )
305
+ } ) ,
306
+ {
307
+ abortSignal : this . abortController . signal ,
308
+ }
295
309
) ;
296
310
297
311
if ( eventEmitter !== null ) {
@@ -333,28 +347,27 @@ export class Upload extends EventEmitter {
333
347
334
348
private async __doMultipartUpload ( ) : Promise < CompleteMultipartUploadCommandOutput > {
335
349
const dataFeeder = getChunk ( this . params . Body , this . partSize ) ;
336
- const concurrentUploaderFailures : Error [ ] = [ ] ;
350
+ const concurrentUploads : Promise < void > [ ] = [ ] ;
337
351
338
352
for ( let index = 0 ; index < this . queueSize ; index ++ ) {
339
- const currentUpload = this . __doConcurrentUpload ( dataFeeder ) . catch ( ( err ) => {
340
- concurrentUploaderFailures . push ( err ) ;
341
- } ) ;
342
- this . concurrentUploaders . push ( currentUpload ) ;
353
+ const currentUpload = this . __doConcurrentUpload ( dataFeeder ) ;
354
+ concurrentUploads . push ( currentUpload ) ;
343
355
}
344
356
345
- await Promise . all ( this . concurrentUploaders ) ;
346
- if ( concurrentUploaderFailures . length >= 1 ) {
357
+ /**
358
+ * Previously, each promise in concurrentUploads could potentially throw
359
+ * and immediately return control to user code. However, we want to wait for
360
+ * all uploaders to finish before calling AbortMultipartUpload to avoid
361
+ * stranding uploaded parts.
362
+ *
363
+ * We throw only the first error to be consistent with prior behavior,
364
+ * but may consider combining the errors into a report in the future.
365
+ */
366
+ const results = await Promise . allSettled ( concurrentUploads ) ;
367
+ const firstFailure = results . find ( ( result ) => result . status === "rejected" ) ;
368
+ if ( firstFailure ) {
347
369
await this . markUploadAsAborted ( ) ;
348
- /**
349
- * Previously, each promise in concurrentUploaders could potentially throw
350
- * and immediately return control to user code. However, we want to wait for
351
- * all uploaders to finish before calling AbortMultipartUpload to avoid
352
- * stranding uploaded parts.
353
- *
354
- * We throw only the first error to be consistent with prior behavior,
355
- * but may consider combining the errors into a report in the future.
356
- */
357
- throw concurrentUploaderFailures [ 0 ] ;
370
+ throw firstFailure . reason ;
358
371
}
359
372
360
373
if ( this . abortController . signal . aborted ) {
@@ -417,16 +430,6 @@ export class Upload extends EventEmitter {
417
430
}
418
431
}
419
432
420
- private async __abortTimeout ( abortSignal : IAbortSignal ) : Promise < never > {
421
- return new Promise ( ( resolve , reject ) => {
422
- abortSignal . onabort = ( ) => {
423
- const abortError = new Error ( "Upload aborted." ) ;
424
- abortError . name = "AbortError" ;
425
- reject ( abortError ) ;
426
- } ;
427
- } ) ;
428
- }
429
-
430
433
private __validateInput ( ) : void {
431
434
if ( ! this . params ) {
432
435
throw new Error ( `InputError: Upload requires params to be passed to upload.` ) ;
0 commit comments