99 * @flow
1010 */
1111
12- import { SecurityError } from "../errors.js" ;
12+ import { SecurityError , MessageError } from "../errors.js" ;
1313import * as crypto from "../util/crypto.js" ;
1414import BaseFetcher from "./_base.js" ;
15+ import * as fsUtil from "../util/fs.js" ;
1516
1617let zlib = require ( "zlib" ) ;
1718let tar = require ( "tar" ) ;
1819let url = require ( "url" ) ;
20+ let through = require ( "through2" ) ;
21+ let fs = require ( "fs" ) ;
22+ let path = require ( "path" ) ;
1923
2024export default class TarballFetcher extends BaseFetcher {
25+
2126 async _fetch ( dest : string ) : Promise < string > {
22- let { reference : ref , hash } = this ;
27+ let { reference : ref , hash, config , saveForOffline , registry } = this ;
2328
29+ let parts = url . parse ( ref ) ;
2430 if ( ! hash ) {
25- let parts = url . parse ( ref ) ;
2631 if ( parts . protocol === "http:" ) {
2732 throw new SecurityError ( `${ ref } : Refusing to fetch tarball over plain HTTP without a hash` ) ;
2833 }
2934 }
35+ if ( parts . protocol === null ) {
36+ let localTarball = path . resolve (
37+ this . config . getOfflineMirrorPath ( registry , null ) ,
38+ ref ) ;
39+ if ( ! await fsUtil . exists ( localTarball ) ) {
40+ throw new MessageError ( `${ ref } : Tarball is not in network and can't be located in cache` ) ;
41+ }
42+ return new Promise ( ( resolve , reject ) => {
43+ let validateStream = crypto . hashStreamValidation ( ) ;
44+
45+ let extractor = tar . Extract ( { path : dest , strip : 1 } )
46+ . on ( "error" , reject )
47+ . on ( "end" , function ( ) {
48+ let expectHash = hash ;
49+ let actualHash = validateStream . getHash ( ) ;
50+ if ( ! expectHash || expectHash === actualHash ) {
51+ resolve ( actualHash ) ;
52+ } else {
53+ reject ( new SecurityError (
54+ `Bad hash. Expected ${ expectHash } but got ${ actualHash } `
55+ ) ) ;
56+ }
57+ } ) ;
58+ // flow gets confused with the pipe/on types chain
59+ let cachedStream : Object = fs . createReadStream ( localTarball ) ;
60+ cachedStream
61+ . pipe ( validateStream )
62+ . pipe ( zlib . createUnzip ( ) )
63+ . on ( "error" , reject )
64+ . pipe ( extractor ) ;
65+ } ) ;
66+ }
3067
3168 return this . config . requestManager . request ( {
3269 url : ref ,
@@ -50,6 +87,19 @@ export default class TarballFetcher extends BaseFetcher {
5087 }
5188 } ) ;
5289
90+ let mirrorPath = config . getOfflineMirrorPath ( registry , ref ) ;
91+ let mirrorTarballStream ;
92+ if ( mirrorPath && saveForOffline ) {
93+ mirrorTarballStream = fs . createWriteStream ( mirrorPath ) ;
94+ mirrorTarballStream . on ( "error" , reject ) ;
95+ }
96+ let mirrorSaver = through ( function ( chunk , enc , callback ) {
97+ if ( mirrorTarballStream ) {
98+ mirrorTarballStream . write ( chunk , enc ) ;
99+ }
100+ callback ( null , chunk ) ;
101+ } ) ;
102+
53103 req
54104 . on ( "redirect" , function ( ) {
55105 if ( hash ) return ;
@@ -64,6 +114,7 @@ export default class TarballFetcher extends BaseFetcher {
64114 }
65115 } )
66116 . pipe ( validateStream )
117+ . pipe ( mirrorSaver )
67118 . pipe ( zlib . createUnzip ( ) )
68119 . on ( "error" , reject )
69120 . pipe ( extractor ) ;
0 commit comments