{"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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"}