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)
})
})
}
}
}