function relax()

in lib/nano.js [257:461]


  function relax (opts, callback) {
    if (typeof opts === 'function') {
      callback = opts
      opts = { path: '' }
    }

    if (typeof opts === 'string') {
      opts = { path: opts }
    }

    if (!opts) {
      opts = { path: '' }
      callback = null
    }

    const qs = Object.assign({}, opts.qs)

    const headers = {
      'content-type': 'application/json',
      accept: 'application/json',
      'user-agent': `${pkg.name}/${pkg.version} (Node.js ${process.version})`,
      'Accept-Encoding': 'deflate, gzip'
    }

    const req = Object.assign({
      method: (opts.method || 'GET'),
      headers,
      uri: cfg.url
    }, {
      ...cfg.requestDefaults,
      headers: Object.assign(headers, cfg.requestDefaults && cfg.requestDefaults.headers ? cfg.requestDefaults.headers : {})
    })

    // https://github.com/mikeal/request#requestjar
    const isJar = opts.jar || cfg.jar || (cfg.requestDefaults && cfg.requestDefaults.jar)

    if (opts.signal) {
      req.signal = opts.signal
    }

    if (isJar) {
      req.withCredentials = true
    }

    // http://wiki.apache.org/couchdb/HTTP_database_API#Naming_and_Addressing
    if (opts.db) {
      req.uri = urlResolveFix(req.uri, encodeURIComponent(opts.db))
    }

    if (opts.multipart) {
      // generate the multipart/related body, header and boundary to
      // upload multiple binary attachments in one request
      const mp = new MultiPartFactory(opts.multipart)
      opts.contentType = mp.header
      req.body = mp.data
    }

    req.headers = Object.assign(req.headers, opts.headers, cfg.defaultHeaders)

    if (opts.path) {
      req.uri += '/' + opts.path
    } else if (opts.doc) {
      if (!/^_design|_local/.test(opts.doc)) {
        // http://wiki.apache.org/couchdb/HTTP_Document_API#Naming.2FAddressing
        req.uri += '/' + encodeURIComponent(opts.doc)
      } else {
        // http://wiki.apache.org/couchdb/HTTP_Document_API#Document_IDs
        req.uri += '/' + opts.doc
      }

      // http://wiki.apache.org/couchdb/HTTP_Document_API#Attachments
      if (opts.att) {
        req.uri += '/' + opts.att
      }
    }

    // prevent bugs where people set encoding when piping
    if (opts.encoding !== undefined) {
      req.encoding = opts.encoding
      delete req.headers['content-type']
      delete req.headers.accept
    }

    if (opts.contentType) {
      req.headers['content-type'] = opts.contentType
      delete req.headers.accept
    }

    if (opts.accept) {
      req.headers.accept = opts.accept
    }

    // http://guide.couchdb.org/draft/security.html#cookies
    if (cfg.cookie) {
      req.headers['X-CouchDB-WWW-Authenticate'] = 'Cookie'
      req.headers.cookie = cfg.cookie
    }

    // http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
    if (typeof opts.qs === 'object' && !isEmpty(opts.qs)) {
      ['startkey', 'endkey', 'key', 'keys', 'start_key', 'end_key'].forEach(function (key) {
        if (key in opts.qs) {
          qs[key] = JSON.stringify(opts.qs[key])
        }
      })
      req.qs = qs
    }

    // add any cookies for this domain
    const cookie = cfg.cookieJar.getCookieString(req.uri)
    if (cookie) {
      req.headers.cookie = cookie
    }

    if (opts.body) {
      if (Buffer.isBuffer(opts.body) || opts.dontStringify) {
        req.body = opts.body
      } else {
        req.body = JSON.stringify(opts.body, function (key, value) {
          // don't encode functions
          if (typeof (value) === 'function') {
            return value.toString()
          } else {
            return value
          }
        })
      }
    }

    if (opts.form) {
      req.headers['content-type'] =
        'application/x-www-form-urlencoded; charset=utf-8'
      req.body = querystring.stringify(opts.form).toString('utf8')
    }

    // ask request to render query string arrays as repeated values e.g.
    // ?drilldown=["author","Dickens"]&drilldown=["publisher","Penguin"]
    req.qsStringifyOptions = { arrayFormat: 'repeat' }

    // This where the HTTP request is made.
    // Nano used to use the now-deprecated "request" library but now we're going to
    // use axios, so let's modify the "req" object to suit axios
    req.url = req.uri
    delete req.uri
    req.method = req.method.toLowerCase()
    req.params = req.qs
    delete req.qs
    req.paramsSerializer = {
      serialize: (params) => querystring.stringify(params, { arrayFormat: 'brackets' })
    }
    req.data = req.body
    delete req.body
    req.maxRedirects = 0
    if (opts.stream) {
      req.responseType = 'stream'
    } else if (opts.dontParse) {
      req.responseType = 'arraybuffer'
    }

    // scrub and log
    const scrubbedReq = {
      method: req.method,
      headers: JSON.parse(JSON.stringify(req.headers)),
      url: req.url
    }
    scrubRequest(scrubbedReq, true)
    log(scrubbedReq)

    // add http agents
    req.httpAgent = cfg.requestDefaults.agent || defaultHttpAgent
    req.httpsAgent = cfg.requestDefaults.agent || defaultHttpsAgent
    const ax = axios.create({
      httpAgent: req.httpAgent,
      httpsAgent: req.httpsAgent
    })

    // actually do the HTTP request
    if (opts.stream) {
      // return the Request object for streaming
      const outStream = new stream.PassThrough()
      ax(req)
        .then((response) => {
          response.data.pipe(outStream)
        }).catch(e => {
          streamResponseHandler(e, req, outStream)
        })
      return outStream
    } else {
      if (typeof callback === 'function') {
        ax(req).then((response) => {
          responseHandler(response, req, opts, null, null, callback)
        }).catch((e) => {
          responseHandler(e, req, opts, null, null, callback)
        })
      } else {
        return new Promise((resolve, reject) => {
          ax(req).then((response) => {
            responseHandler(response, req, opts, resolve, reject)
          }).catch((e) => {
            responseHandler(e, req, opts, resolve, reject)
          })
        })
      }
    }
  }