0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-01-06 22:40:26 -05:00

chore(flow): update flow definitions for proxy

This commit is contained in:
Juan Picado @jotadeveloper 2018-02-04 02:24:38 +01:00
parent 7e06ccc8ac
commit f7414e9bb2
No known key found for this signature in database
GPG key ID: 18AC54485952D158
6 changed files with 82 additions and 40 deletions

View file

@ -51,7 +51,7 @@
"@commitlint/cli": "^6.1.0", "@commitlint/cli": "^6.1.0",
"@commitlint/config-conventional": "^6.1.0", "@commitlint/config-conventional": "^6.1.0",
"@commitlint/travis-cli": "^6.1.0", "@commitlint/travis-cli": "^6.1.0",
"@verdaccio/types": "0.3.0", "@verdaccio/types": "0.3.1",
"axios": "0.17.1", "axios": "0.17.1",
"babel-cli": "6.26.0", "babel-cli": "6.26.0",
"babel-core": "6.26.0", "babel-core": "6.26.0",

View file

@ -570,7 +570,8 @@ class Storage implements IStorageHandler {
} }
if (err || !upLinkResponse) { if (err || !upLinkResponse) {
return cb(null, [err || Error(500, 'no data')]); // $FlowFixMe
return cb(null, [err || Error('no data')]);
} }
try { try {
@ -597,6 +598,7 @@ class Storage implements IStorageHandler {
try { try {
Storage._mergeVersions(packageInfo, upLinkResponse, self.config); Storage._mergeVersions(packageInfo, upLinkResponse, self.config);
} catch(err) { } catch(err) {
self.logger.error({ self.logger.error({
sub: 'out', sub: 'out',

View file

@ -1,3 +1,5 @@
// @flow
import zlib from 'zlib'; import zlib from 'zlib';
import JSONStream from 'JSONStream'; import JSONStream from 'JSONStream';
import createError from 'http-errors'; import createError from 'http-errors';
@ -8,7 +10,17 @@ import URL from 'url';
import {parseInterval, is_object, ErrorCode} from './utils'; import {parseInterval, is_object, ErrorCode} from './utils';
import {ReadTarball} from '@verdaccio/streams'; import {ReadTarball} from '@verdaccio/streams';
const Logger = require('./logger'); import type {
IProxy,
Config,
Callback,
Logger,
} from '@verdaccio/types';
import type {IUploadTarball} from '@verdaccio/streams';
const LoggerApi = require('./logger');
const encode = function(thing) { const encode = function(thing) {
return encodeURIComponent(thing).replace(/^%40/, '@'); return encodeURIComponent(thing).replace(/^%40/, '@');
@ -33,23 +45,37 @@ const setConfig = (config, key, def) => {
* Implements Storage interface * Implements Storage interface
* (same for storage.js, local-storage.js, up-storage.js) * (same for storage.js, local-storage.js, up-storage.js)
*/ */
class ProxyStorage { class ProxyStorage implements IProxy {
config: Config;
failed_requests: number;
userAgent: string;
ca: string | void;
logger: Logger;
server_id: string;
url: any;
maxage: string;
timeout: string;
max_fails: number;
fail_timeout: number;
upname: string;
proxy: string;
last_request_time: number;
/** /**
* Constructor * Constructor
* @param {*} config * @param {*} config
* @param {*} mainConfig * @param {*} mainConfig
*/ */
constructor(config, mainConfig) { constructor(config: UpLinkConf, mainConfig: Config) {
this.config = config; this.config = config;
this.failed_requests = 0; this.failed_requests = 0;
this.userAgent = mainConfig.user_agent; this.userAgent = mainConfig.user_agent;
this.ca = config.ca; this.ca = config.ca;
this.logger = Logger.logger.child({sub: 'out'}); this.logger = LoggerApi.logger.child({sub: 'out'});
this.server_id = mainConfig.server_id; this.server_id = mainConfig.server_id;
this.url = URL.parse(this.config.url); this.url = URL.parse(this.config.url);
// $FlowFixMe
this._setupProxy(this.url.hostname, config, mainConfig, this.url.protocol === 'https:'); this._setupProxy(this.url.hostname, config, mainConfig, this.url.protocol === 'https:');
this.config.url = this.config.url.replace(/\/$/, ''); this.config.url = this.config.url.replace(/\/$/, '');
@ -74,7 +100,7 @@ class ProxyStorage {
* @param {*} cb * @param {*} cb
* @return {Request} * @return {Request}
*/ */
request(options, cb) { request(options: any, cb: Callback) {
let json; let json;
if (this._statusCheck() === false) { if (this._statusCheck() === false) {
@ -84,9 +110,10 @@ class ProxyStorage {
if (_.isFunction(cb)) { if (_.isFunction(cb)) {
cb(ErrorCode.get500('uplink is offline')); cb(ErrorCode.get500('uplink is offline'));
} }
// $FlowFixMe
streamRead.emit('error', createError('uplink is offline')); streamRead.emit('error', createError('uplink is offline'));
}); });
// $FlowFixMe
streamRead._read = function() {}; streamRead._read = function() {};
// preventing 'Uncaught, unspecified "error" event' // preventing 'Uncaught, unspecified "error" event'
streamRead.on('error', function() {}); streamRead.on('error', function() {});
@ -116,7 +143,7 @@ class ProxyStorage {
let requestCallback = cb ? (function(err, res, body) { let requestCallback = cb ? (function(err, res, body) {
let error; let error;
const responseLength = err ? 0 : body.length; const responseLength = err ? 0 : body.length;
// $FlowFixMe
processBody(err, body); processBody(err, body);
logActivity(); logActivity();
cb(err, res, body); cb(err, res, body);
@ -132,6 +159,7 @@ class ProxyStorage {
if (options.json && res.statusCode < 300) { if (options.json && res.statusCode < 300) {
try { try {
// $FlowFixMe
body = JSON.parse(body.toString('utf8')); body = JSON.parse(body.toString('utf8'));
} catch(_err) { } catch(_err) {
body = {}; body = {};
@ -216,7 +244,7 @@ class ProxyStorage {
* @return {Object} * @return {Object}
* @private * @private
*/ */
_setHeaders(options) { _setHeaders(options: any) {
const headers = options.headers || {}; const headers = options.headers || {};
const accept = 'Accept'; const accept = 'Accept';
const acceptEncoding = 'Accept-Encoding'; const acceptEncoding = 'Accept-Encoding';
@ -236,7 +264,7 @@ class ProxyStorage {
* @return {Object} * @return {Object}
* @private * @private
*/ */
_setAuth(headers) { _setAuth(headers: any) {
if (_.isNil(this.config.auth) || headers['authorization']) { if (_.isNil(this.config.auth) || headers['authorization']) {
return headers; return headers;
@ -248,7 +276,7 @@ class ProxyStorage {
// get NPM_TOKEN http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules // get NPM_TOKEN http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules
// or get other variable export in env // or get other variable export in env
let token = process.env.NPM_TOKEN; let token: any = process.env.NPM_TOKEN;
if (this.config.auth.token) { if (this.config.auth.token) {
token = this.config.auth.token; token = this.config.auth.token;
} else if (this.config.auth.token_env) { } else if (this.config.auth.token_env) {
@ -270,7 +298,7 @@ class ProxyStorage {
* @throws {Error} * @throws {Error}
* @private * @private
*/ */
_throwErrorAuth(message) { _throwErrorAuth(message: string) {
this.logger.error(message); this.logger.error(message);
throw new Error(message); throw new Error(message);
} }
@ -282,7 +310,7 @@ class ProxyStorage {
* @param {string} token * @param {string} token
* @private * @private
*/ */
_setHeaderAuthorization(headers, type, token) { _setHeaderAuthorization(headers: any, type: string, token: string) {
if (type !== 'bearer' && type !== 'basic') { if (type !== 'bearer' && type !== 'basic') {
this._throwErrorAuth(`Auth type '${type}' not allowed`); this._throwErrorAuth(`Auth type '${type}' not allowed`);
} }
@ -310,7 +338,7 @@ class ProxyStorage {
* @param {Object} headers * @param {Object} headers
* @private * @private
*/ */
_overrideWithUplinkConfigHeaders(headers) { _overrideWithUplinkConfigHeaders(headers: any) {
// add/override headers specified in the config // add/override headers specified in the config
for (let key in this.config.headers) { for (let key in this.config.headers) {
if (Object.prototype.hasOwnProperty.call(this.config.headers, key)) { if (Object.prototype.hasOwnProperty.call(this.config.headers, key)) {
@ -320,13 +348,15 @@ class ProxyStorage {
} }
/** /**
* Determine whether can fetch from the provided URL. * Determine whether can fetch from the provided URL
* @param {*} url * @param {*} url
* @return {Boolean} * @return {Boolean}
*/ */
isUplinkValid(url) { isUplinkValid(url: string) {
url = URL.parse(url); // $FlowFixMe
return url.protocol === this.url.protocol && url.host === this.url.host && url.path.indexOf(this.url.path) === 0; url = URL.parse(url);
// $FlowFixMe
return url.protocol === this.url.protocol && url.host === this.url.host && url.path.indexOf(this.url.path) === 0;
} }
/** /**
@ -335,7 +365,7 @@ class ProxyStorage {
* @param {*} options request options, eg: eTag. * @param {*} options request options, eg: eTag.
* @param {*} callback * @param {*} callback
*/ */
getRemoteMetadata(name, options, callback) { getRemoteMetadata(name: string, options: any, callback: Callback) {
const headers = {}; const headers = {};
if (_.isNil(options.etag) === false) { if (_.isNil(options.etag) === false) {
headers['If-None-Match'] = options.etag; headers['If-None-Match'] = options.etag;
@ -355,7 +385,9 @@ class ProxyStorage {
return callback( ErrorCode.get404('package doesn\'t exist on uplink')); return callback( ErrorCode.get404('package doesn\'t exist on uplink'));
} }
if (!(res.statusCode >= 200 && res.statusCode < 300)) { if (!(res.statusCode >= 200 && res.statusCode < 300)) {
// $FlowFixMe
const error = createError(`bad status code: ${res.statusCode}`); const error = createError(`bad status code: ${res.statusCode}`);
// $FlowFixMe
error.remoteStatus = res.statusCode; error.remoteStatus = res.statusCode;
return callback(error); return callback(error);
} }
@ -368,7 +400,7 @@ class ProxyStorage {
* @param {String} url * @param {String} url
* @return {Stream} * @return {Stream}
*/ */
fetchTarball(url) { fetchTarball(url: string) {
const stream = new ReadTarball({}); const stream = new ReadTarball({});
let current_length = 0; let current_length = 0;
let expected_length; let expected_length;
@ -382,11 +414,12 @@ class ProxyStorage {
}, },
}); });
readStream.on('response', function(res) { readStream.on('response', function(res: any) {
if (res.statusCode === 404) { if (res.statusCode === 404) {
return stream.emit('error', ErrorCode.get404('file doesn\'t exist on uplink')); return stream.emit('error', ErrorCode.get404('file doesn\'t exist on uplink'));
} }
if (!(res.statusCode >= 200 && res.statusCode < 300)) { if (!(res.statusCode >= 200 && res.statusCode < 300)) {
// $FlowFixMe
return stream.emit('error', createError('bad uplink status code: ' + res.statusCode)); return stream.emit('error', createError('bad uplink status code: ' + res.statusCode));
} }
if (res.headers['content-length']) { if (res.headers['content-length']) {
@ -408,6 +441,7 @@ class ProxyStorage {
current_length += data.length; current_length += data.length;
} }
if (expected_length && current_length != expected_length) { if (expected_length && current_length != expected_length) {
// $FlowFixMe
stream.emit('error', createError('content length mismatch')); stream.emit('error', createError('content length mismatch'));
} }
}); });
@ -419,9 +453,9 @@ class ProxyStorage {
* @param {*} options request options * @param {*} options request options
* @return {Stream} * @return {Stream}
*/ */
search(options) { search(options: any) {
const transformStream = new Stream.PassThrough({objectMode: true}); const transformStream: IUploadTarball = new Stream.PassThrough({objectMode: true});
const requestStream = this.request({ const requestStream: IUploadTarball = this.request({
uri: options.req.url, uri: options.req.url,
req: options.req, req: options.req,
headers: { headers: {
@ -437,6 +471,7 @@ class ProxyStorage {
requestStream.on('response', (res) => { requestStream.on('response', (res) => {
if (!String(res.statusCode).match(/^2\d\d$/)) { if (!String(res.statusCode).match(/^2\d\d$/)) {
// $FlowFixMe
return transformStream.emit('error', createError(`bad status code ${res.statusCode} from uplink`)); return transformStream.emit('error', createError(`bad status code ${res.statusCode} from uplink`));
} }
@ -471,7 +506,7 @@ class ProxyStorage {
* @param {*} req the http request * @param {*} req the http request
* @param {*} headers the request headers * @param {*} headers the request headers
*/ */
_addProxyHeaders(req, headers) { _addProxyHeaders(req: any, headers: any) {
if (req) { if (req) {
// Only submit X-Forwarded-For field if we don't have a proxy selected // Only submit X-Forwarded-For field if we don't have a proxy selected
// in the config file. // in the config file.
@ -502,7 +537,7 @@ class ProxyStorage {
* @param {*} alive * @param {*} alive
* @return {Boolean} * @return {Boolean}
*/ */
_statusCheck(alive) { _statusCheck(alive?: boolean) {
if (arguments.length === 0) { if (arguments.length === 0) {
return this._ifRequestFailure() === false; return this._ifRequestFailure() === false;
} else { } else {
@ -541,9 +576,9 @@ class ProxyStorage {
* @param {*} mainconfig * @param {*} mainconfig
* @param {*} isHTTPS * @param {*} isHTTPS
*/ */
_setupProxy(hostname, config, mainconfig, isHTTPS) { _setupProxy(hostname: string, config: UpLinkConf, mainconfig: Config, isHTTPS: boolean) {
let noProxyList; let noProxyList;
let proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy'; let proxy_key: string = isHTTPS ? 'https_proxy' : 'http_proxy';
// get http_proxy and no_proxy configs // get http_proxy and no_proxy configs
if (proxy_key in config) { if (proxy_key in config) {
@ -552,6 +587,7 @@ class ProxyStorage {
this.proxy = mainconfig[proxy_key]; this.proxy = mainconfig[proxy_key];
} }
if ('no_proxy' in config) { if ('no_proxy' in config) {
// $FlowFixMe
noProxyList = config.no_proxy; noProxyList = config.no_proxy;
} else if ('no_proxy' in mainconfig) { } else if ('no_proxy' in mainconfig) {
noProxyList = mainconfig.no_proxy; noProxyList = mainconfig.no_proxy;
@ -561,17 +597,22 @@ class ProxyStorage {
if (hostname[0] !== '.') { if (hostname[0] !== '.') {
hostname = '.' + hostname; hostname = '.' + hostname;
} }
// $FlowFixMe
if (_.isString(noProxyList) && noProxyList.length) { if (_.isString(noProxyList) && noProxyList.length) {
// $FlowFixMe
noProxyList = noProxyList.split(','); noProxyList = noProxyList.split(',');
} }
if (_.isArray(noProxyList)) { if (_.isArray(noProxyList)) {
// $FlowFixMe
for (let i = 0; i < noProxyList.length; i++) { for (let i = 0; i < noProxyList.length; i++) {
// $FlowFixMe
let noProxyItem = noProxyList[i]; let noProxyItem = noProxyList[i];
if (noProxyItem[0] !== '.') noProxyItem = '.' + noProxyItem; if (noProxyItem[0] !== '.') noProxyItem = '.' + noProxyItem;
if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) { if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) {
if (this.proxy) { if (this.proxy) {
this.logger.debug({url: this.url.href, rule: noProxyItem}, this.logger.debug({url: this.url.href, rule: noProxyItem},
'not using proxy for @{url}, excluded by @{rule} rule'); 'not using proxy for @{url}, excluded by @{rule} rule');
// $FlowFixMe
this.proxy = false; this.proxy = false;
} }
break; break;

View file

@ -1,9 +1,7 @@
import assert from 'assert';
export default function(server, express) { export default function(server, express) {
describe('test for unexpected client hangs', () => { describe('test for unexpected client hangs', () => {
let on_tarball; let handleResponseTarball;
beforeAll(function() { beforeAll(function() {
express.get('/testexp-racycrash', function(request, response) { express.get('/testexp-racycrash', function(request, response) {
@ -23,16 +21,17 @@ export default function(server, express) {
}); });
express.get('/testexp-racycrash/-/test.tar.gz', function(request, response) { express.get('/testexp-racycrash/-/test.tar.gz', function(request, response) {
on_tarball(response); handleResponseTarball(response);
}); });
}); });
test('should not crash on error if client disconnects', callback => { test('should not crash on error if client disconnects', callback => {
on_tarball = function(res) { handleResponseTarball = function(res) {
res.header('content-length', 1e6); res.header('content-length', 1e6);
res.write('test test test\n'); res.write('test test test');
setTimeout(function() { setTimeout(function() {
res.write('test test test\n'); res.write('-');
// destroy the connection
res.socket.destroy(); res.socket.destroy();
cb(); cb();
}, 200); }, 200);
@ -40,7 +39,7 @@ export default function(server, express) {
server.request({uri: '/testexp-racycrash/-/test.tar.gz'}) server.request({uri: '/testexp-racycrash/-/test.tar.gz'})
.then(function(body) { .then(function(body) {
assert.equal(body, 'test test test\n'); expect(body).toEqual('test test test');
}); });
function cb() { function cb() {
@ -54,7 +53,7 @@ export default function(server, express) {
}); });
test('should not store tarball', () => { test('should not store tarball', () => {
on_tarball = function(res) { handleResponseTarball = function(res) {
res.socket.destroy(); res.socket.destroy();
}; };

View file

@ -13,7 +13,7 @@ describe('UpStorge', () => {
const uplinkDefault = { const uplinkDefault = {
url: 'https://registry.npmjs.org/' url: 'https://registry.npmjs.org/'
}; };
let generateProxy = (config = uplinkDefault) => { let generateProxy = (config: UpLinkConf = uplinkDefault) => {
const appConfig: Config = new AppConfig(configExample); const appConfig: Config = new AppConfig(configExample);
return new ProxyStorage(config, appConfig); return new ProxyStorage(config, appConfig);

BIN
yarn.lock

Binary file not shown.