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

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