UNPKG

verdaccio

Version:

A lightweight private npm proxy registry

1 lines 32 kB
{"version":3,"file":"up-storage.mjs","names":[],"sources":["../../src/lib/up-storage.ts"],"sourcesContent":["import request from '@cypress/request';\nimport JSONStream from 'JSONStream';\nimport buildDebug from 'debug';\nimport _ from 'lodash';\nimport Stream from 'stream';\nimport zlib from 'zlib';\n\nimport { ReadTarball } from '@verdaccio/streams';\nimport { Callback, Config, Headers, Logger, Package, UpLinkConf } from '@verdaccio/types';\nimport { buildToken } from '@verdaccio/utils';\n\nimport {\n API_ERROR,\n CHARACTER_ENCODING,\n ERROR_CODE,\n HEADERS,\n HEADER_TYPE,\n HTTP_STATUS,\n TOKEN_BASIC,\n TOKEN_BEARER,\n} from './constants';\nimport { logger } from './logger';\nimport { ErrorCode, isObject, isObjectOrArray, parseInterval } from './utils';\n\nconst debug = buildDebug('verdaccio:proxy');\n\nconst encode = function (thing): string {\n return encodeURIComponent(thing).replace(/^%40/, '@');\n};\n\nconst jsonContentType = HEADERS.JSON;\nconst contentTypeAccept = `${jsonContentType};`;\n\n/**\n * Just a helper (`config[key] || default` doesn't work because of zeroes)\n */\nconst setConfig = (config, key, def): string => {\n return _.isNil(config[key]) === false ? config[key] : def;\n};\n\n/**\n * Implements Storage interface\n * (same for storage.js, local-storage.js, up-storage.js)\n */\nclass ProxyStorage {\n public config: UpLinkConf;\n public failed_requests: number;\n public userAgent: string;\n public ca: string | void;\n public logger: Logger;\n public server_id: string;\n public url: any;\n public maxage: number;\n public timeout: number;\n public max_fails: number;\n public fail_timeout: number;\n public agent_options: any;\n // FIXME: upname is assigned to each instance\n // @ts-ignore\n public upname: string;\n // FIXME: proxy can be boolean or object, something smells here\n // @ts-ignore\n public proxy: string | void;\n // @ts-ignore\n public last_request_time: number | null;\n public strict_ssl: boolean;\n\n /**\n * Constructor\n * @param {*} config\n * @param {*} mainConfig\n */\n public constructor(config: UpLinkConf, mainConfig: Config) {\n this.config = config;\n this.failed_requests = 0;\n // @ts-ignore\n this.userAgent = mainConfig.user_agent ?? 'hidden';\n this.ca = config.ca;\n this.logger = logger;\n this.server_id = mainConfig.server_id;\n\n try {\n this.url = new URL(this.config.url);\n } catch (err) {\n // WHATWG URL throws on malformed input; surface a clear, credential-redacted\n // message so operators can identify the offending uplink config at startup.\n const redactedUrl = String(this.config.url).replace(/\\/\\/[^/@]+@/, '//***@');\n throw new Error(`invalid uplink url: ${redactedUrl}`, { cause: err });\n }\n\n this._setupProxy(this.url.hostname, config, mainConfig, this.url.protocol === 'https:');\n\n this.config.url = this.config.url.replace(/\\/$/, '');\n\n if (this.config.timeout && Number(this.config.timeout) >= 1000) {\n this.logger.warn(\n [\n 'Too big timeout value: ' + this.config.timeout,\n 'We changed time format to nginx-like one',\n '(see http://nginx.org/en/docs/syntax.html)',\n 'so please update your config accordingly',\n ].join('\\n')\n );\n }\n\n // a bunch of different configurable timers\n this.maxage = parseInterval(setConfig(this.config, 'maxage', '2m'));\n this.timeout = parseInterval(setConfig(this.config, 'timeout', '30s'));\n this.max_fails = Number(setConfig(this.config, 'max_fails', 2));\n this.fail_timeout = parseInterval(setConfig(this.config, 'fail_timeout', '5m'));\n this.strict_ssl = Boolean(setConfig(this.config, 'strict_ssl', true));\n this.agent_options = setConfig(this.config, 'agent_options', {\n keepAlive: true,\n maxSockets: 40,\n maxFreeSockets: 10,\n });\n }\n\n /**\n * Fetch an asset.\n * @param {*} options\n * @param {*} cb\n * @return {Request}\n */\n private request(options: any, cb?: Callback): Stream.Readable {\n let json;\n\n if (this._statusCheck() === false) {\n const streamRead = new Stream.Readable();\n\n process.nextTick(function (): void {\n if (cb) {\n cb(ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE));\n }\n streamRead.emit('error', ErrorCode.getInternalError(API_ERROR.UPLINK_OFFLINE));\n });\n\n streamRead._read = function (): void {};\n // preventing 'Uncaught, unspecified \"error\" event'\n streamRead.on('error', function (): void {});\n return streamRead;\n }\n\n const self = this;\n const headers: Headers = this._setHeaders(options);\n\n this._addProxyHeaders(options.req, headers);\n this._overrideWithUpLinkConfigHeaders(headers);\n\n const method = options.method || 'GET';\n const uri = options.uri_full || this.config.url + options.uri;\n\n self.logger.info(\n {\n method: method,\n uri: uri,\n },\n \"making request: '@{method} @{uri}'\"\n );\n\n if (isObject(options.json)) {\n json = JSON.stringify(options.json);\n headers['Content-Type'] = headers['Content-Type'] || HEADERS.JSON;\n }\n\n const requestCallback = cb\n ? function (err, res, body): void {\n let error;\n const responseLength = err ? 0 : body.length;\n\n processBody();\n logActivity();\n\n cb(err, res, body);\n\n /**\n * Perform a decode.\n */\n function processBody(): void {\n if (err) {\n error = err.message;\n return;\n }\n\n if (options.json && res.statusCode < 300) {\n try {\n body = JSON.parse(body.toString(CHARACTER_ENCODING.UTF8));\n } catch (_err) {\n body = {};\n err = _err;\n error = err.message;\n }\n }\n\n if (!err && isObject(body)) {\n if (_.isString(body.error)) {\n error = body.error;\n }\n }\n }\n /**\n * Perform a log.\n */\n function logActivity(): void {\n let message = \"@{!status}, req: '@{request.method} @{request.url}'\";\n message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}';\n self.logger.http(\n {\n err: err || undefined, // if error is null/false change this to undefined so it wont log\n request: { method: method, url: uri },\n status: res != null ? res.statusCode : 'ERR',\n error: error,\n bytes: {\n in: json ? json.length : 0,\n out: responseLength || 0,\n },\n },\n message\n );\n }\n }\n : undefined;\n let requestOptions: request.OptionsWithUrl = {\n url: uri,\n method: method,\n headers: headers,\n body: json,\n proxy: this.proxy,\n encoding: null,\n gzip: true,\n timeout: this.timeout,\n strictSSL: this.strict_ssl,\n agentOptions: this.agent_options,\n };\n\n if (this.ca) {\n requestOptions = Object.assign({}, requestOptions, {\n ca: this.ca,\n });\n }\n\n const req = request(requestOptions, requestCallback);\n\n let statusCalled = false;\n req.on('response', function (res): void {\n // FIXME: _verdaccio_aborted seems not used\n // @ts-ignore\n if (!req._verdaccio_aborted && !statusCalled) {\n statusCalled = true;\n self._statusCheck(true);\n }\n\n if (_.isNil(requestCallback) === false) {\n (function do_log(): void {\n const message = \"@{!status}, req: '@{request.method} @{request.url}' (streaming)\";\n self.logger.http(\n {\n request: {\n method: method,\n url: uri,\n },\n status: _.isNull(res) === false ? res.statusCode : 'ERR',\n },\n message\n );\n })();\n }\n });\n req.on('error', function (err): void {\n // FIXME: _verdaccio_aborted seems not used\n debug('_verdaccio_aborted: %o', err);\n if (!req._verdaccio_aborted && !statusCalled) {\n statusCalled = true;\n self._statusCheck(false);\n }\n });\n\n return req;\n }\n\n /**\n * Set default headers.\n * @param {Object} options\n * @return {Object}\n * @private\n */\n private _setHeaders(options: any): Headers {\n const headers = options.headers || {};\n const accept = HEADERS.ACCEPT;\n const acceptEncoding = HEADERS.ACCEPT_ENCODING;\n const userAgent = HEADERS.USER_AGENT;\n\n headers[accept] = headers[accept] || contentTypeAccept;\n headers[acceptEncoding] = headers[acceptEncoding] || 'gzip';\n // registry.npmjs.org will only return search result if user-agent include string 'npm'\n headers[userAgent] = this.userAgent\n ? `npm (${this.userAgent})`\n : options.req?.get('user-agent');\n\n return this._setAuth(headers);\n }\n\n /**\n * Validate configuration auth and assign Header authorization\n * @param {Object} headers\n * @return {Object}\n * @private\n */\n private _setAuth(headers: any): Headers {\n const { auth } = this.config;\n\n if (_.isNil(auth) || headers[HEADERS.AUTHORIZATION]) {\n return headers;\n }\n\n if (_.isObject(auth) === false && _.isObject((auth as any).token) === false) {\n this._throwErrorAuth('Auth invalid');\n }\n\n // get NPM_TOKEN http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules\n // or get other variable export in env\n // https://github.com/verdaccio/verdaccio/releases/tag/v2.5.0\n let token: any;\n const tokenConf: any = auth;\n\n if (_.isNil(tokenConf.token) === false && _.isString(tokenConf.token)) {\n token = tokenConf.token;\n } else if (_.isNil(tokenConf.token_env) === false) {\n if (_.isString(tokenConf.token_env)) {\n token = process.env[tokenConf.token_env];\n } else if (_.isBoolean(tokenConf.token_env) && tokenConf.token_env) {\n token = process.env.NPM_TOKEN;\n } else {\n this.logger.error(ERROR_CODE.token_required);\n this._throwErrorAuth(ERROR_CODE.token_required);\n }\n } else {\n token = process.env.NPM_TOKEN;\n }\n\n if (_.isNil(token)) {\n this._throwErrorAuth(ERROR_CODE.token_required);\n }\n\n // define type Auth allow basic and bearer\n const type = tokenConf.type || TOKEN_BASIC;\n this._setHeaderAuthorization(headers, type, token);\n\n return headers;\n }\n\n /**\n * @param {string} message\n * @throws {Error}\n * @private\n */\n private _throwErrorAuth(message: string): Error {\n this.logger.error(message);\n throw new Error(message);\n }\n\n /**\n * Assign Header authorization with type authentication\n * @param {Object} headers\n * @param {string} type\n * @param {string} token\n * @private\n */\n private _setHeaderAuthorization(headers: any, type: string, token: any): void {\n const _type: string = type.toLowerCase();\n\n if ([TOKEN_BEARER.toLowerCase(), TOKEN_BASIC.toLowerCase()].includes(_type) === false) {\n this._throwErrorAuth(`Auth type '${_type}' not allowed`);\n }\n\n type = _.upperFirst(type);\n headers[HEADERS.AUTHORIZATION] = buildToken(type, token);\n }\n\n /**\n * It will add or override specified headers from config file.\n *\n * Eg:\n *\n * uplinks:\n npmjs:\n url: https://registry.npmjs.org/\n headers:\n Accept: \"application/vnd.npm.install-v2+json; q=1.0\"\n verdaccio-staging:\n url: https://mycompany.com/npm\n headers:\n Accept: \"application/json\"\n authorization: \"Basic YourBase64EncodedCredentials==\"\n\n * @param {Object} headers\n * @private\n */\n private _overrideWithUpLinkConfigHeaders(headers: Headers): any {\n if (!this.config.headers) {\n return headers;\n }\n\n // add/override headers specified in the config\n for (const key in this.config.headers) {\n headers[key] = this.config.headers[key];\n }\n }\n\n /**\n * Determine whether can fetch from the provided URL\n * @param {*} url\n * @return {Boolean}\n */\n public isUplinkValid(url: string): boolean {\n const urlParsed = URL.parse(url);\n if (urlParsed === null) {\n return false;\n }\n // WHATWG URL normalizes the default HTTPS port (443) away, exposing it as ''\n const isHTTPS = (urlDomainParsed: URL): boolean =>\n urlDomainParsed.protocol === 'https:' &&\n (urlDomainParsed.port === '' || urlDomainParsed.port === '443');\n const getHost = (urlDomainParsed: URL): string =>\n isHTTPS(urlDomainParsed) ? urlDomainParsed.hostname : urlDomainParsed.host;\n // WHATWG URL has no `path`; reconstruct it from `pathname` + `search`\n const getPath = (urlDomainParsed: URL): string =>\n urlDomainParsed.pathname + urlDomainParsed.search;\n const isMatchProtocol: boolean = urlParsed.protocol === this.url.protocol;\n const isMatchHost: boolean = getHost(urlParsed) === getHost(this.url);\n const isMatchPath: boolean = getPath(urlParsed).indexOf(getPath(this.url)) === 0;\n\n return isMatchProtocol && isMatchHost && isMatchPath;\n }\n\n /**\n * Get a remote package metadata\n * @param {*} name package name\n * @param {*} options request options, eg: eTag.\n * @param {*} callback\n */\n public getRemoteMetadata(name: string, options: any, callback: Callback): void {\n const headers = {};\n if (_.isNil(options.etag) === false) {\n headers['If-None-Match'] = options.etag;\n headers[HEADERS.ACCEPT] = contentTypeAccept;\n }\n\n this.request(\n {\n uri: `/${encode(name)}`,\n json: true,\n headers: headers,\n req: options.req,\n },\n (err, res, body): void => {\n if (err) {\n return callback(err);\n }\n if (res.statusCode === HTTP_STATUS.NOT_FOUND) {\n return callback(ErrorCode.getNotFound(API_ERROR.NOT_PACKAGE_UPLINK));\n }\n if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {\n const error = ErrorCode.getInternalError(\n `${API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`\n );\n\n error.remoteStatus = res.statusCode;\n return callback(error);\n }\n callback(null, body, res.headers.etag);\n }\n );\n }\n\n /**\n * Fetch a tarball from the uplink.\n * @param {String} url\n * @return {Stream}\n */\n public fetchTarball(url: string) {\n const stream = new ReadTarball({});\n let current_length = 0;\n let expected_length;\n\n stream.abort = () => {};\n const readStream = this.request({\n uri_full: url,\n encoding: null,\n headers: {\n Accept: contentTypeAccept,\n },\n });\n\n readStream.on('response', function (res: any) {\n if (res.statusCode === HTTP_STATUS.NOT_FOUND) {\n return stream.emit('error', ErrorCode.getNotFound(API_ERROR.NOT_FILE_UPLINK));\n }\n if (res.statusCode === HTTP_STATUS.FORBIDDEN) {\n const chunks: Buffer[] = [];\n readStream.on('data', (chunk: Buffer) => chunks.push(chunk));\n readStream.once('end', () => {\n let body: any;\n const raw = Buffer.concat(chunks);\n if (raw.length > 0) {\n try {\n body = JSON.parse(raw.toString('utf8'));\n } catch {}\n }\n const message =\n body?.detail ||\n body?.title ||\n body?.message ||\n 'tarball not available: forbidden by uplink';\n stream.emit('error', ErrorCode.getForbidden(message));\n });\n return;\n }\n if (!(res.statusCode >= HTTP_STATUS.OK && res.statusCode < HTTP_STATUS.MULTIPLE_CHOICES)) {\n return stream.emit(\n 'error',\n ErrorCode.getInternalError(`bad uplink status code: ${res.statusCode}`)\n );\n }\n if (res.headers[HEADER_TYPE.CONTENT_LENGTH]) {\n expected_length = res.headers[HEADER_TYPE.CONTENT_LENGTH];\n stream.emit(HEADER_TYPE.CONTENT_LENGTH, res.headers[HEADER_TYPE.CONTENT_LENGTH]);\n }\n\n readStream.pipe(stream);\n });\n\n readStream.on('error', function (err) {\n stream.emit('error', err);\n });\n readStream.on('data', function (data) {\n current_length += data.length;\n });\n readStream.on('end', function (data) {\n if (data) {\n current_length += data.length;\n }\n if (expected_length && current_length != expected_length) {\n stream.emit('error', ErrorCode.getInternalError(API_ERROR.CONTENT_MISMATCH));\n }\n });\n return stream;\n }\n\n /**\n * Perform a stream search.\n * @param {*} options request options\n * @return {Stream}\n */\n public search(options: any) {\n const transformStream: any = new Stream.PassThrough({ objectMode: true });\n const requestStream: Stream.Readable = this.request({\n // prefer the sanitized url (clamped pagination) built by the search\n // endpoint over the raw request url\n uri: options.url || options.req.url,\n req: options.req,\n headers: {\n // query for search\n referer: options.req.get('referer'),\n },\n });\n\n const parsePackage = (pkg: Package): void => {\n if (isObjectOrArray(pkg)) {\n transformStream.emit('data', pkg);\n }\n };\n\n requestStream.on('response', (res): void => {\n if (!String(res.statusCode).match(/^2\\d\\d$/)) {\n return transformStream.emit(\n 'error',\n ErrorCode.getInternalError(`bad status code ${res.statusCode} from uplink`)\n );\n }\n\n // See https://github.com/request/request#requestoptions-callback\n // Request library will not decode gzip stream.\n let jsonStream;\n if (res.headers[HEADER_TYPE.CONTENT_ENCODING] === HEADERS.GZIP) {\n jsonStream = res.pipe(zlib.createUnzip());\n } else {\n jsonStream = res;\n }\n jsonStream.pipe(JSONStream.parse('*')).on('data', parsePackage);\n jsonStream.on('end', (): void => {\n transformStream.emit('end');\n });\n });\n\n requestStream.on('error', (err: Error): void => {\n transformStream.emit('error', err);\n });\n\n transformStream.abort = (): void => {\n // FIXME: this is clearly a potential issue\n // there is no abort method on Stream.Readable\n // @ts-ignore\n requestStream.abort();\n transformStream.emit('end');\n };\n\n return transformStream;\n }\n\n /**\n * Add proxy headers.\n * FIXME: object mutations, it should return an new object\n * @param {*} req the http request\n * @param {*} headers the request headers\n */\n private _addProxyHeaders(req: any, headers: any): void {\n if (req) {\n // Only submit X-Forwarded-For field if we don't have a proxy selected\n // in the config file.\n //\n // Otherwise misconfigured proxy could return 407:\n // https://github.com/rlidwka/sinopia/issues/254\n //\n // FIXME: proxy logic is odd, something is wrong here.\n // @ts-ignore\n if (!this.proxy) {\n headers['x-forwarded-for'] =\n (req.get('x-forwarded-for') ? req.get('x-forwarded-for') + ', ' : '') +\n req.connection.remoteAddress;\n }\n }\n\n // always attach Via header to avoid loops, even if we're not proxying\n headers['via'] = req && req.get('via') ? req.get('via') + ', ' : '';\n\n headers['via'] += '1.1 ' + this.server_id + ' (Verdaccio)';\n }\n\n /**\n * Check whether the remote host is available.\n * @param {*} alive\n * @return {Boolean}\n */\n private _statusCheck(alive?: boolean): boolean | void {\n if (arguments.length === 0) {\n return this._ifRequestFailure() === false;\n }\n if (alive) {\n if (this.failed_requests >= this.max_fails) {\n this.logger.warn(\n {\n host: this.url.host,\n },\n 'host @{host} is back online'\n );\n }\n this.failed_requests = 0;\n } else {\n this.failed_requests++;\n if (this.failed_requests === this.max_fails) {\n this.logger.warn(\n {\n host: this.url.host,\n },\n 'host @{host} is now offline'\n );\n }\n }\n\n this.last_request_time = Date.now();\n }\n\n /**\n * If the request failure.\n * @return {boolean}\n * @private\n */\n private _ifRequestFailure(): boolean {\n return (\n this.failed_requests >= this.max_fails &&\n Math.abs(Date.now() - (this.last_request_time as number)) < this.fail_timeout\n );\n }\n\n /**\n * Set up a proxy.\n * @param {*} hostname\n * @param {*} config\n * @param {*} mainconfig\n * @param {*} isHTTPS\n */\n private _setupProxy(\n hostname: string,\n config: UpLinkConf,\n mainconfig: Config,\n isHTTPS: boolean\n ): void {\n let noProxyList;\n const proxy_key: string = isHTTPS ? 'https_proxy' : 'http_proxy';\n\n // get http_proxy and no_proxy configs\n if (proxy_key in config) {\n this.proxy = config[proxy_key];\n } else if (proxy_key in mainconfig) {\n this.proxy = mainconfig[proxy_key];\n }\n if ('no_proxy' in config) {\n noProxyList = config.no_proxy;\n } else if ('no_proxy' in mainconfig) {\n noProxyList = mainconfig.no_proxy;\n }\n\n // use wget-like algorithm to determine if proxy shouldn't be used\n if (hostname[0] !== '.') {\n hostname = '.' + hostname;\n }\n\n if (_.isString(noProxyList) && noProxyList.length) {\n noProxyList = noProxyList.split(',');\n }\n\n if (_.isArray(noProxyList)) {\n for (let i = 0; i < noProxyList.length; i++) {\n let noProxyItem = noProxyList[i];\n if (noProxyItem[0] !== '.') {\n noProxyItem = '.' + noProxyItem;\n }\n if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) {\n if (this.proxy) {\n debug('not using proxy for %o, excluded by %o rule', this.url.href, noProxyItem);\n // @ts-ignore\n this.proxy = false;\n }\n break;\n }\n }\n }\n\n // if it's non-string (i.e. \"false\"), don't use it\n if (_.isString(this.proxy) === false) {\n // @ts-ignore\n delete this.proxy;\n } else {\n debug('using proxy %o for %o', this.url.href, this.proxy);\n }\n }\n}\n\nexport default ProxyStorage;\n"],"mappings":"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"}