9
9
* @flow
10
10
*/
11
11
12
- import { SecurityError } from "../errors.js" ;
12
+ import { SecurityError , MessageError } from "../errors.js" ;
13
13
import * as crypto from "../util/crypto.js" ;
14
14
import BaseFetcher from "./_base.js" ;
15
+ import * as fsUtil from "../util/fs.js" ;
15
16
16
17
let zlib = require ( "zlib" ) ;
17
18
let tar = require ( "tar" ) ;
18
19
let url = require ( "url" ) ;
20
+ let through = require ( "through2" ) ;
21
+ let fs = require ( "fs" ) ;
22
+ let path = require ( "path" ) ;
19
23
20
24
export default class TarballFetcher extends BaseFetcher {
25
+
21
26
async _fetch ( dest : string ) : Promise < string > {
22
- let { reference : ref , hash } = this ;
27
+ let { reference : ref , hash, config , saveForOffline , registry } = this ;
23
28
29
+ let parts = url . parse ( ref ) ;
24
30
if ( ! hash ) {
25
- let parts = url . parse ( ref ) ;
26
31
if ( parts . protocol === "http:" ) {
27
32
throw new SecurityError ( `${ ref } : Refusing to fetch tarball over plain HTTP without a hash` ) ;
28
33
}
29
34
}
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
+ }
30
67
31
68
return this . config . requestManager . request ( {
32
69
url : ref ,
@@ -50,6 +87,19 @@ export default class TarballFetcher extends BaseFetcher {
50
87
}
51
88
} ) ;
52
89
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
+
53
103
req
54
104
. on ( "redirect" , function ( ) {
55
105
if ( hash ) return ;
@@ -64,6 +114,7 @@ export default class TarballFetcher extends BaseFetcher {
64
114
}
65
115
} )
66
116
. pipe ( validateStream )
117
+ . pipe ( mirrorSaver )
67
118
. pipe ( zlib . createUnzip ( ) )
68
119
. on ( "error" , reject )
69
120
. pipe ( extractor ) ;
0 commit comments