index.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. var url = require('url');
  2. var http = require('http');
  3. var https = require('https');
  4. var through = require('through2');
  5. var duplexer = require('duplexer2');
  6. module.exports = hyperquest;
  7. function bind (obj, fn) {
  8. var args = Array.prototype.slice.call(arguments, 2);
  9. return function () {
  10. var argv = args.concat(Array.prototype.slice.call(arguments));
  11. return fn.apply(obj, argv);
  12. }
  13. }
  14. function hyperquest (uri, opts, cb, extra) {
  15. if (typeof uri === 'object') {
  16. cb = opts;
  17. opts = uri;
  18. uri = undefined;
  19. }
  20. if (typeof opts === 'function') {
  21. cb = opts;
  22. opts = undefined;
  23. }
  24. if (!opts) opts = {};
  25. if (uri !== undefined) opts.uri = uri;
  26. if (extra) opts.method = extra.method;
  27. var req = new Req(opts);
  28. var ws = req.duplex && through();
  29. var rs = through();
  30. var dup = req.duplex ? duplexer(ws, rs) : rs;
  31. if (!req.duplex) {
  32. rs.writable = false;
  33. }
  34. dup.request = req;
  35. dup.setHeader = bind(req, req.setHeader);
  36. dup.setLocation = bind(req, req.setLocation);
  37. var closed = false;
  38. dup.on('close', function () { closed = true });
  39. process.nextTick(function () {
  40. if (closed) return;
  41. dup.on('close', function () { r.destroy() });
  42. var r = req._send();
  43. r.on('error', bind(dup, dup.emit, 'error'));
  44. dup.emit('request', r);
  45. r.on('response', function (res) {
  46. dup.response = res;
  47. dup.emit('response', res);
  48. if (req.duplex) res.pipe(rs)
  49. else {
  50. res.on('data', function (buf) { rs.push(buf) });
  51. res.on('end', function () { rs.push(null) });
  52. }
  53. });
  54. if (req.duplex) {
  55. ws.pipe(r);
  56. }
  57. else r.end();
  58. });
  59. if (cb) {
  60. dup.on('error', cb);
  61. dup.on('response', bind(dup, cb, null));
  62. }
  63. return dup;
  64. }
  65. hyperquest.get = hyperquest;
  66. hyperquest.post = function (uri, opts, cb) {
  67. return hyperquest(uri, opts, cb, { method: 'POST' });
  68. };
  69. hyperquest.put = function (uri, opts, cb) {
  70. return hyperquest(uri, opts, cb, { method: 'PUT' });
  71. };
  72. hyperquest['delete'] = function (uri, opts, cb) {
  73. return hyperquest(uri, opts, cb, { method: 'DELETE' });
  74. };
  75. function Req (opts) {
  76. this.headers = opts.headers || {};
  77. var method = (opts.method || 'GET').toUpperCase();
  78. this.method = method;
  79. this.duplex = !(method === 'GET' || method === 'DELETE'
  80. || method === 'HEAD');
  81. this.auth = opts.auth;
  82. this.options = opts;
  83. if (opts.uri) this.setLocation(opts.uri);
  84. }
  85. Req.prototype._send = function () {
  86. this._sent = true;
  87. var headers = this.headers || {};
  88. var u = url.parse(this.uri);
  89. var au = u.auth || this.auth;
  90. if (au) {
  91. headers.authorization = 'Basic ' + Buffer(au).toString('base64');
  92. }
  93. var protocol = u.protocol || '';
  94. var iface = protocol === 'https:' ? https : http;
  95. var opts = {
  96. scheme: protocol.replace(/:$/, ''),
  97. method: this.method,
  98. host: u.hostname,
  99. port: Number(u.port) || (protocol === 'https:' ? 443 : 80),
  100. path: u.path,
  101. agent: this.options.agent || false,
  102. headers: headers,
  103. withCredentials: this.options.withCredentials
  104. };
  105. if (protocol === 'https:') {
  106. opts.pfx = this.options.pfx;
  107. opts.key = this.options.key;
  108. opts.cert = this.options.cert;
  109. opts.ca = this.options.ca;
  110. opts.ciphers = this.options.ciphers;
  111. opts.rejectUnauthorized = this.options.rejectUnauthorized;
  112. opts.secureProtocol = this.options.secureProtocol;
  113. }
  114. var req = iface.request(opts);
  115. var timeout = this.options.timeout || Math.pow(2, 32) * 1000;
  116. if (req.setTimeout) req.setTimeout(timeout);
  117. return req;
  118. };
  119. Req.prototype.setHeader = function (key, value) {
  120. if (this._sent) throw new Error('request already sent');
  121. this.headers[key] = value;
  122. return this;
  123. };
  124. Req.prototype.setLocation = function (uri) {
  125. this.uri = uri;
  126. return this;
  127. };