You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.1 KiB
153 lines
4.1 KiB
var url = require('url'); |
|
var http = require('http'); |
|
var https = require('https'); |
|
var through = require('through2'); |
|
var duplexer = require('duplexer2'); |
|
var bufferFrom = require('buffer-from'); |
|
|
|
module.exports = hyperquest; |
|
|
|
function bind (obj, fn) { |
|
var args = Array.prototype.slice.call(arguments, 2); |
|
return function () { |
|
var argv = args.concat(Array.prototype.slice.call(arguments)); |
|
return fn.apply(obj, argv); |
|
} |
|
} |
|
|
|
function hyperquest (uri, opts, cb, extra) { |
|
if (typeof uri === 'object') { |
|
cb = opts; |
|
opts = uri; |
|
uri = undefined; |
|
} |
|
if (typeof opts === 'function') { |
|
cb = opts; |
|
opts = undefined; |
|
} |
|
if (!opts) opts = {}; |
|
if (uri !== undefined) opts.uri = uri; |
|
if (extra) opts.method = extra.method; |
|
|
|
var req = new Req(opts); |
|
var ws = req.duplex && through(); |
|
var rs = through(); |
|
|
|
var dup = req.duplex ? duplexer(ws, rs) : rs; |
|
if (!req.duplex) { |
|
rs.writable = false; |
|
} |
|
dup.request = req; |
|
dup.setHeader = bind(req, req.setHeader); |
|
dup.setLocation = bind(req, req.setLocation); |
|
|
|
var closed = false; |
|
dup.on('close', function () { closed = true }); |
|
|
|
process.nextTick(function () { |
|
if (closed) return; |
|
dup.on('close', function () { r.destroy() }); |
|
|
|
var r = req._send(); |
|
r.on('error', bind(dup, dup.emit, 'error')); |
|
dup.emit('request', r); |
|
|
|
r.on('response', function (res) { |
|
dup.response = res; |
|
dup.emit('response', res); |
|
if (req.duplex) res.pipe(rs) |
|
else { |
|
res.on('data', function (buf) { rs.push(buf) }); |
|
res.on('end', function () { rs.push(null) }); |
|
} |
|
}); |
|
|
|
if (req.duplex) { |
|
ws.pipe(r); |
|
} |
|
else r.end(); |
|
}); |
|
|
|
if (cb) { |
|
dup.on('error', cb); |
|
dup.on('response', bind(dup, cb, null)); |
|
} |
|
return dup; |
|
} |
|
|
|
hyperquest.get = hyperquest; |
|
|
|
hyperquest.post = function (uri, opts, cb) { |
|
return hyperquest(uri, opts, cb, { method: 'POST' }); |
|
}; |
|
|
|
hyperquest.put = function (uri, opts, cb) { |
|
return hyperquest(uri, opts, cb, { method: 'PUT' }); |
|
}; |
|
|
|
hyperquest['delete'] = function (uri, opts, cb) { |
|
return hyperquest(uri, opts, cb, { method: 'DELETE' }); |
|
}; |
|
|
|
function Req (opts) { |
|
this.headers = opts.headers || {}; |
|
|
|
var method = (opts.method || 'GET').toUpperCase(); |
|
this.method = method; |
|
this.duplex = !(method === 'GET' || method === 'HEAD'); |
|
this.auth = opts.auth; |
|
|
|
this.options = opts; |
|
|
|
if (opts.uri) this.setLocation(opts.uri); |
|
} |
|
|
|
Req.prototype._send = function () { |
|
this._sent = true; |
|
|
|
var headers = this.headers || {}; |
|
var u = url.parse(this.uri); |
|
var au = u.auth || this.auth; |
|
if (au) { |
|
headers.authorization = 'Basic ' + bufferFrom(au).toString('base64'); |
|
} |
|
|
|
var protocol = u.protocol || ''; |
|
var iface = protocol === 'https:' ? https : http; |
|
var opts = { |
|
scheme: protocol.replace(/:$/, ''), |
|
method: this.method, |
|
host: u.hostname, |
|
port: Number(u.port) || (protocol === 'https:' ? 443 : 80), |
|
path: u.path, |
|
agent: this.options.agent || false, |
|
headers: headers, |
|
withCredentials: this.options.withCredentials, |
|
localAddress: this.options.localAddress |
|
}; |
|
if (protocol === 'https:') { |
|
opts.pfx = this.options.pfx; |
|
opts.key = this.options.key; |
|
opts.cert = this.options.cert; |
|
opts.ca = this.options.ca; |
|
opts.ciphers = this.options.ciphers; |
|
opts.rejectUnauthorized = this.options.rejectUnauthorized; |
|
opts.secureProtocol = this.options.secureProtocol; |
|
} |
|
var req = iface.request(opts); |
|
|
|
var timeout = this.options.timeout || 2147483647; |
|
if (req.setTimeout) req.setTimeout(timeout); |
|
return req; |
|
}; |
|
|
|
Req.prototype.setHeader = function (key, value) { |
|
if (this._sent) throw new Error('request already sent'); |
|
this.headers[key] = value; |
|
return this; |
|
}; |
|
|
|
Req.prototype.setLocation = function (uri) { |
|
this.uri = uri; |
|
return this; |
|
};
|
|
|