httpism
Version:
HTTP client with middleware and good defaults
250 lines (210 loc) • 6.46 kB
JavaScript
var deepExtend = require('./deepExtend')
var resolveUrl = require('./resolveUrl')
function client (url, options, middleware) {
var args = parseClientArguments(url, options, middleware)
return new Httpism(args.url, args.options || {}, args.middleware)
}
function Httpism (url, options, middleware) {
this.url = url
this._options = options
this.middleware = middleware
}
Httpism.prototype.send = function (method, url, body, _options) {
console.warn('httpism.send() is deprecated please use httpism.request()')
return this.request.apply(this, arguments)
}
Httpism.prototype.request = function (method, url, body, _options) {
var request
if (method instanceof Object) {
request = method
} else {
var options = mergeClientOptions(this._options, _options)
request = {
method: method,
url: resolveUrl(this.url, url),
headers: lowerCaseHeaders(options.headers || {}),
body: body,
options: options
}
}
var self = this
function sendToMiddleware (index, req) {
if (index < self.middleware.length) {
var middleware = self.middleware[index]
return middleware(req, function (nextRequest) { return sendToMiddleware(index + 1, nextRequest || req) }, self)
}
}
return sendToMiddleware(0, request).then(function (response) {
if (request.options.response === true) {
return response
} else {
responseCompatibility(response)
return response.body
}
}, function (e) {
if (e.isRedirectResponse) {
return e.redirectResponse
} else {
throw e
}
})
}
function responseCompatibility (response) {
function responseWarning () {
console.warn('httpism >= 3.0.0 returns the response body by default, please pass the {response: true} option if you want the whole response')
}
if (response.body instanceof Object && !Object.isFrozen(response.body)) {
if (!Object.prototype.hasOwnProperty.call(response.body, 'body')) {
Object.defineProperty(response.body, 'body', {
get: function () {
responseWarning()
return this
}
})
}
if (!Object.prototype.hasOwnProperty.call(response.body, 'url')) {
Object.defineProperty(response.body, 'url', {
get: function () {
responseWarning()
return response.url
}
})
}
if (!Object.prototype.hasOwnProperty.call(response.body, 'statusCode')) {
Object.defineProperty(response.body, 'statusCode', {
get: function () {
responseWarning()
return response.statusCode
}
})
}
if (!Object.prototype.hasOwnProperty.call(response.body, 'headers')) {
Object.defineProperty(response.body, 'headers', {
get: function () {
responseWarning()
return response.headers
}
})
}
}
}
function lowerCaseHeaders (headers) {
Object.keys(headers).forEach(function (key) {
var lower = key.toLowerCase()
if (key.toLowerCase() !== key) {
headers[lower] = headers[key]
delete headers[key]
}
})
return headers
}
function findMiddlewareIndexes (names, middleware) {
return names.map(function (name) {
for (var n = 0; n < middleware.length; n++) {
var m = middleware[n]
if (m.httpismMiddleware && m.httpismMiddleware.name === name) {
return n
}
}
return -1
}).filter(function (i) {
return i >= 0
})
}
function insertMiddlewareIntoIndex (middleware, m, index) {
middleware.splice(index, 0, m)
}
Httpism.prototype.client = function (url, options, middleware) {
var args = parseClientArguments(url, options, middleware)
var client = new Httpism(
resolveUrl(this.url, args.url),
mergeClientOptions(this._options, args.options),
this.middleware.slice()
)
if (args.middleware) {
args.middleware.forEach(function (m) {
client.use(m)
})
}
return client
}
Httpism.prototype.api = function (url, options, middleware) {
console.warn('httpism >= 3.0.0 renamed httpism.api() to httpism.client(), please update your usage')
return this.client(url, options, middleware)
}
Httpism.prototype.insertMiddleware = function (m) {
console.warn('httpism >= 3.0.0 renamed httpism.insertMiddleware() to httpism.use(), please update your usage')
return this.use(m)
}
Httpism.prototype.use = function (m) {
var meta = m.httpismMiddleware
if (meta && (meta.before || meta.after)) {
var position = meta.before || meta.after
var names = typeof position === 'string' ? [position] : position
var indexes = findMiddlewareIndexes(names, this.middleware)
if (indexes.length) {
var index = meta.before ? Math.min.apply(Math, indexes) : Math.max.apply(Math, indexes) + 1
if (index >= 0) {
insertMiddlewareIntoIndex(this.middleware, m, index)
return
}
}
throw new Error('no such middleware: ' + (meta.before || meta.after))
} else {
this.middleware.unshift(m)
}
}
Httpism.prototype.removeMiddleware = function (name) {
console.warn('httpism.removeMiddleware() is deprecated please use httpism.remove()')
this.remove(name)
}
Httpism.prototype.remove = function (name) {
var indexes = findMiddlewareIndexes([name], this.middleware)
for (var i = indexes.length - 1; i >= 0; i--) {
this.middleware.splice(indexes[i], 1)
}
}
function addMethod (method) {
Httpism.prototype[method] = function (url, options) {
return this.request(method, url, undefined, options)
}
}
function addMethodWithBody (method) {
Httpism.prototype[method] = function (url, body, options) {
return this.request(method, url, body, options)
}
}
addMethod('get')
addMethod('delete')
addMethod('head')
addMethodWithBody('post')
addMethodWithBody('put')
addMethodWithBody('patch')
addMethodWithBody('options')
function parseClientArguments () {
var url, options, middleware
for (var n = 0; n < arguments.length; n++) {
var arg = arguments[n]
if (typeof arg === 'string') {
url = arg
} else if (typeof arg === 'function') {
middleware = [arg]
} else if (arg instanceof Array) {
middleware = arg
} else if (arg instanceof Object) {
options = arg
}
}
return {
url: url,
options: options,
middleware: middleware
}
}
function mergeClientOptions (x, y) {
var r = {}
deepExtend(r, x || {})
deepExtend(r, y || {})
return r
}
module.exports = client