@@ -10,7 +10,7 @@ import MongoBinaryDownloadUrl from './MongoBinaryDownloadUrl';
1010import { HttpsProxyAgent } from 'https-proxy-agent' ;
1111import resolveConfig , { envToBool , ResolveConfigVariables } from './resolveConfig' ;
1212import debug from 'debug' ;
13- import { assertion , mkdir , pathExists , md5FromFile } from './utils' ;
13+ import { assertion , mkdir , pathExists , md5FromFile , statPath } from './utils' ;
1414import { DryMongoBinary } from './DryMongoBinary' ;
1515import { MongoBinaryOpts } from './MongoBinary' ;
1616import { clearLine } from 'readline' ;
@@ -19,7 +19,7 @@ import { RequestOptions } from 'https';
1919
2020const log = debug ( 'MongoMS:MongoBinaryDownload' ) ;
2121
22- const retryableStatusCodes = [ 429 , 500 , 503 ] ;
22+ const retryableStatusCodes = [ 416 , 429 , 500 , 503 ] ;
2323
2424const retryableErrorCodes = [
2525 'ECONNRESET' ,
@@ -460,17 +460,31 @@ export class MongoBinaryDownload {
460460 */
461461 private async attemptDownload (
462462 url : URL ,
463- useHttpsOptions : Parameters < typeof https . get > [ 1 ] ,
463+ useHttpsOptions : RequestOptions ,
464464 downloadLocation : string ,
465465 tempDownloadLocation : string ,
466466 downloadUrl : string ,
467467 httpOptions : RequestOptions
468468 ) : 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+
469483 return new Promise ( ( resolve , reject ) => {
470484 log ( `httpDownload: trying to download "${ downloadUrl } "` ) ;
471485
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 ) {
474488 if ( response . statusCode === 403 ) {
475489 reject (
476490 new DownloadError (
@@ -487,6 +501,11 @@ export class MongoBinaryDownload {
487501 return ;
488502 }
489503
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+
490509 reject (
491510 new DownloadError (
492511 downloadUrl ,
@@ -499,6 +518,7 @@ export class MongoBinaryDownload {
499518 }
500519
501520 // content-length, otherwise 0
521+ // note that content-length will not be the full size when resuming a download via "Range"
502522 let contentLength : number ;
503523
504524 if ( typeof response . headers [ 'content-length' ] != 'string' ) {
@@ -533,7 +553,12 @@ export class MongoBinaryDownload {
533553 this . dlProgress . length = contentLength ;
534554 this . dlProgress . totalMb = Math . round ( ( this . dlProgress . length / 1048576 ) * 10 ) / 10 ;
535555
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 } ) ;
537562
538563 response . pipe ( fileStream ) ;
539564
0 commit comments