@@ -10,7 +10,7 @@ import MongoBinaryDownloadUrl from './MongoBinaryDownloadUrl';
10
10
import { HttpsProxyAgent } from 'https-proxy-agent' ;
11
11
import resolveConfig , { envToBool , ResolveConfigVariables } from './resolveConfig' ;
12
12
import debug from 'debug' ;
13
- import { assertion , mkdir , pathExists , md5FromFile } from './utils' ;
13
+ import { assertion , mkdir , pathExists , md5FromFile , statPath } from './utils' ;
14
14
import { DryMongoBinary } from './DryMongoBinary' ;
15
15
import { MongoBinaryOpts } from './MongoBinary' ;
16
16
import { clearLine } from 'readline' ;
@@ -19,7 +19,7 @@ import { RequestOptions } from 'https';
19
19
20
20
const log = debug ( 'MongoMS:MongoBinaryDownload' ) ;
21
21
22
- const retryableStatusCodes = [ 429 , 500 , 503 ] ;
22
+ const retryableStatusCodes = [ 416 , 429 , 500 , 503 ] ;
23
23
24
24
const retryableErrorCodes = [
25
25
'ECONNRESET' ,
@@ -460,17 +460,31 @@ export class MongoBinaryDownload {
460
460
*/
461
461
private async attemptDownload (
462
462
url : URL ,
463
- useHttpsOptions : Parameters < typeof https . get > [ 1 ] ,
463
+ useHttpsOptions : RequestOptions ,
464
464
downloadLocation : string ,
465
465
tempDownloadLocation : string ,
466
466
downloadUrl : string ,
467
467
httpOptions : RequestOptions
468
468
) : Promise < string > {
469
+ /** Offset to resume from; for now a non-0 value indicates to use file "append" mode */
470
+ let offset = 0 ;
471
+
472
+ if ( envToBool ( resolveConfig ( ResolveConfigVariables . EXP_RESUME_DOWNLOAD ) ) ) {
473
+ const stat = await statPath ( tempDownloadLocation ) ;
474
+
475
+ if ( stat && stat . size != 0 ) {
476
+ useHttpsOptions . headers = useHttpsOptions . headers ?? { } ;
477
+ useHttpsOptions . headers [ 'Range' ] = `bytes=${ stat . size } -` ;
478
+ offset = stat . size ;
479
+ log ( `httpDownload: resuming download at ${ offset } ` ) ;
480
+ }
481
+ }
482
+
469
483
return new Promise ( ( resolve , reject ) => {
470
484
log ( `httpDownload: trying to download "${ downloadUrl } "` ) ;
471
485
472
- const request = https . get ( url , useHttpsOptions , ( response ) => {
473
- if ( response . statusCode != 200 ) {
486
+ const request = https . get ( url , useHttpsOptions , async ( response ) => {
487
+ if ( response . statusCode != 200 && response . statusCode != 206 ) {
474
488
if ( response . statusCode === 403 ) {
475
489
reject (
476
490
new DownloadError (
@@ -487,6 +501,11 @@ export class MongoBinaryDownload {
487
501
return ;
488
502
}
489
503
504
+ // delete temporary file on "416: Range Not Satisfiable" and let it re-try
505
+ if ( response . statusCode === 416 ) {
506
+ await fspromises . rm ( tempDownloadLocation , { force : true } ) ;
507
+ }
508
+
490
509
reject (
491
510
new DownloadError (
492
511
downloadUrl ,
@@ -499,6 +518,7 @@ export class MongoBinaryDownload {
499
518
}
500
519
501
520
// content-length, otherwise 0
521
+ // note that content-length will not be the full size when resuming a download via "Range"
502
522
let contentLength : number ;
503
523
504
524
if ( typeof response . headers [ 'content-length' ] != 'string' ) {
@@ -533,7 +553,12 @@ export class MongoBinaryDownload {
533
553
this . dlProgress . length = contentLength ;
534
554
this . dlProgress . totalMb = Math . round ( ( this . dlProgress . length / 1048576 ) * 10 ) / 10 ;
535
555
536
- const fileStream = createWriteStream ( tempDownloadLocation ) ;
556
+ // "w" opens for write, creates the file if it does not exist, or truncates if it does
557
+ // "a" opens for append, creates the file if it does not exist
558
+ const flags = offset === 0 ? 'w' : 'a' ;
559
+
560
+ // not using option "start" as we already open in "append" mode
561
+ const fileStream = createWriteStream ( tempDownloadLocation , { /* start: offset, */ flags } ) ;
537
562
538
563
response . pipe ( fileStream ) ;
539
564
0 commit comments