@@ -24,9 +24,60 @@ const versionIdUtils = versioning.VersionID;
24
24
const defaultHealthChecks = { allowFrom : [ '127.0.0.1/8' , '::1' ] } ;
25
25
26
26
const defaultLocalCache = { host : '127.0.0.1' , port : 6379 } ;
27
+ const defaultExternalBackendsConfig = {
28
+ // eslint-disable-next-line camelcase
29
+ aws_s3 : {
30
+ httpAgent : {
31
+ keepAlive : false ,
32
+ keepAliveMsecs : 1000 ,
33
+ maxFreeSockets : 256 ,
34
+ maxSockets : null ,
35
+ } ,
36
+ } ,
37
+ gcp : {
38
+ httpAgent : {
39
+ keepAlive : true ,
40
+ keepAliveMsecs : 1000 ,
41
+ maxFreeSockets : 256 ,
42
+ maxSockets : null ,
43
+ } ,
44
+ } ,
45
+ } ;
27
46
28
47
const gcpScope = 'https://www.googleapis.com/auth/cloud-platform' ;
29
48
49
+ function assertCertPaths ( key , cert , ca , basePath ) {
50
+ const certObj = { } ;
51
+ certObj . paths = { } ;
52
+ certObj . certs = { } ;
53
+ if ( key ) {
54
+ const keypath = key . startsWith ( '/' ) ? key : `${ basePath } /${ key } ` ;
55
+ assert . doesNotThrow ( ( ) =>
56
+ fs . accessSync ( keypath , fs . F_OK | fs . R_OK ) ,
57
+ `File not found or unreachable: ${ keypath } ` ) ;
58
+ certObj . paths . key = keypath ;
59
+ certObj . certs . key = fs . readFileSync ( keypath , 'ascii' ) ;
60
+ }
61
+ if ( cert ) {
62
+ const certpath = cert . startsWith ( '/' ) ? cert : `${ basePath } /${ cert } ` ;
63
+ assert . doesNotThrow ( ( ) =>
64
+ fs . accessSync ( certpath , fs . F_OK | fs . R_OK ) ,
65
+ `File not found or unreachable: ${ certpath } ` ) ;
66
+ certObj . paths . cert = certpath ;
67
+ certObj . certs . cert = fs . readFileSync ( certpath , 'ascii' ) ;
68
+ }
69
+
70
+ if ( ca ) {
71
+ const capath = ca . startsWith ( '/' ) ? ca : `${ basePath } /${ ca } ` ;
72
+ assert . doesNotThrow ( ( ) =>
73
+ fs . accessSync ( capath , fs . F_OK | fs . R_OK ) ,
74
+ `File not found or unreachable: ${ capath } ` ) ;
75
+ certObj . paths . ca = capath ;
76
+ certObj . certs . ca = fs . readFileSync ( capath , 'ascii' ) ;
77
+ }
78
+ return certObj ;
79
+ }
80
+
30
81
function parseSproxydConfig ( configSproxyd ) {
31
82
const joiSchema = joi . object ( {
32
83
bootstrap : joi . array ( ) . items ( joi . string ( ) ) . min ( 1 ) ,
@@ -1124,6 +1175,58 @@ class Config extends EventEmitter {
1124
1175
'certFilePaths.cert must be defined' ) ;
1125
1176
}
1126
1177
1178
+ this . outboundProxy = { } ;
1179
+ const envProxy = process . env . HTTP_PROXY || process . env . HTTPS_PROXY
1180
+ || process . env . http_proxy || process . env . https_proxy ;
1181
+ const p = config . outboundProxy ;
1182
+ const proxyUrl = envProxy || ( p ? p . url : '' ) ;
1183
+ if ( proxyUrl ) {
1184
+ assert ( typeof proxyUrl === 'string' ,
1185
+ 'bad proxy config: url must be a string' ) ;
1186
+ const { protocol, hostname, port, auth } = url . parse ( proxyUrl ) ;
1187
+ assert ( protocol === 'http:' || protocol === 'https:' ,
1188
+ 'bad proxy config: protocol must be http or https' ) ;
1189
+ assert ( typeof hostname === 'string' && hostname !== '' ,
1190
+ 'bad proxy config: hostname must be a non-empty string' ) ;
1191
+ if ( port ) {
1192
+ const portInt = Number . parseInt ( port , 10 ) ;
1193
+ assert ( ! Number . isNaN ( portInt ) && portInt > 0 ,
1194
+ 'bad proxy config: port must be a number greater than 0' ) ;
1195
+ }
1196
+ if ( auth ) {
1197
+ assert ( typeof auth === 'string' ,
1198
+ 'bad proxy config: auth must be string' ) ;
1199
+ const authArray = auth . split ( ':' ) ;
1200
+ assert ( authArray . length === 2 && authArray [ 0 ] . length > 0
1201
+ && authArray [ 1 ] . length > 0 , 'bad proxy config: ' +
1202
+ 'auth must be of format username:password' ) ;
1203
+ }
1204
+ this . outboundProxy . url = proxyUrl ;
1205
+ this . outboundProxy . certs = { } ;
1206
+ const envCert = process . env . HTTPS_PROXY_CERTIFICATE ;
1207
+ const key = p ? p . key : '' ;
1208
+ const cert = p ? p . cert : '' ;
1209
+ const caBundle = envCert || ( p ? p . caBundle : '' ) ;
1210
+ if ( p ) {
1211
+ assert ( typeof p === 'object' ,
1212
+ 'bad config: "proxy" should be an object' ) ;
1213
+ }
1214
+ if ( key ) {
1215
+ assert ( typeof key === 'string' ,
1216
+ 'bad config: proxy.key should be a string' ) ;
1217
+ }
1218
+ if ( cert ) {
1219
+ assert ( typeof cert === 'string' ,
1220
+ 'bad config: proxy.cert should be a string' ) ;
1221
+ }
1222
+ if ( caBundle ) {
1223
+ assert ( typeof caBundle === 'string' ,
1224
+ 'bad config: proxy.caBundle should be a string' ) ;
1225
+ }
1226
+ const certObj = assertCertPaths ( key , cert , caBundle , this . _basePath ) ;
1227
+ this . outboundProxy . certs = certObj . certs ;
1228
+ }
1229
+
1127
1230
// Ephemeral token to protect the reporting endpoint:
1128
1231
// try inherited from parent first, then hardcoded in conf file,
1129
1232
// then create a fresh one as last resort.
@@ -1132,6 +1235,40 @@ class Config extends EventEmitter {
1132
1235
config . reportToken ||
1133
1236
uuidv4 ( ) ;
1134
1237
1238
+ // External backends
1239
+ // Currently supports configuring httpAgent(s) for keepAlive
1240
+ this . externalBackends = defaultExternalBackendsConfig ;
1241
+ if ( config . externalBackends ) {
1242
+ const extBackendsConfig = Object . keys ( config . externalBackends ) ;
1243
+ extBackendsConfig . forEach ( b => {
1244
+ // assert that it's a valid backend
1245
+ assert ( externalBackends [ b ] !== undefined ,
1246
+ `bad config: ${ b } is not one of valid external backends: ` +
1247
+ `${ Object . keys ( externalBackends ) . join ( ', ' ) } ` ) ;
1248
+
1249
+ const { httpAgent } = config . externalBackends [ b ] ;
1250
+ assert ( typeof httpAgent === 'object' ,
1251
+ `bad config: ${ b } must have httpAgent object defined` ) ;
1252
+ const { keepAlive, keepAliveMsecs, maxFreeSockets, maxSockets }
1253
+ = httpAgent ;
1254
+ assert ( typeof keepAlive === 'boolean' ,
1255
+ `bad config: ${ b } .httpAgent.keepAlive must be a boolean` ) ;
1256
+ assert ( typeof keepAliveMsecs === 'number' &&
1257
+ httpAgent . keepAliveMsecs > 0 ,
1258
+ `bad config: ${ b } .httpAgent.keepAliveMsecs must be` +
1259
+ ' a number > 0' ) ;
1260
+ assert ( typeof maxFreeSockets === 'number' &&
1261
+ httpAgent . maxFreeSockets >= 0 ,
1262
+ `bad config: ${ b } .httpAgent.maxFreeSockets must be ` +
1263
+ 'a number >= 0' ) ;
1264
+ assert ( ( typeof maxSockets === 'number' && maxSockets >= 0 ) ||
1265
+ maxSockets === null ,
1266
+ `bad config: ${ b } .httpAgent.maxFreeSockets must be ` +
1267
+ 'null or a number >= 0' ) ;
1268
+ Object . assign ( this . externalBackends [ b ] . httpAgent , httpAgent ) ;
1269
+ } ) ;
1270
+ }
1271
+
1135
1272
// requests-proxy configuration
1136
1273
this . requests = {
1137
1274
viaProxy : false ,
0 commit comments