index.js 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. 'use strict';
  2. /**
  3. * Module dependencies.
  4. */
  5. const debug = require('debug')('superagent');
  6. const formidable = require('formidable');
  7. const FormData = require('form-data');
  8. const Response = require('./response');
  9. const parse = require('url').parse;
  10. const format = require('url').format;
  11. const resolve = require('url').resolve;
  12. let methods = require('methods');
  13. const Stream = require('stream');
  14. const utils = require('../utils');
  15. const unzip = require('./unzip').unzip;
  16. const extend = require('extend');
  17. const mime = require('mime');
  18. const https = require('https');
  19. const http = require('http');
  20. const fs = require('fs');
  21. const qs = require('qs');
  22. const zlib = require('zlib');
  23. const util = require('util');
  24. const pkg = require('../../package.json');
  25. const RequestBase = require('../request-base');
  26. const CookieJar = require('cookiejar');
  27. function request(method, url) {
  28. // callback
  29. if ('function' == typeof url) {
  30. return new exports.Request('GET', method).end(url);
  31. }
  32. // url first
  33. if (1 == arguments.length) {
  34. return new exports.Request('GET', method);
  35. }
  36. return new exports.Request(method, url);
  37. }
  38. exports = module.exports = request;
  39. /**
  40. * Expose `Request`.
  41. */
  42. exports.Request = Request;
  43. /**
  44. * Expose the agent function
  45. */
  46. exports.agent = require('./agent');
  47. /**
  48. * Noop.
  49. */
  50. function noop(){};
  51. /**
  52. * Expose `Response`.
  53. */
  54. exports.Response = Response;
  55. /**
  56. * Define "form" mime type.
  57. */
  58. mime.define({
  59. 'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data']
  60. }, true);
  61. /**
  62. * Protocol map.
  63. */
  64. exports.protocols = {
  65. 'http:': http,
  66. 'https:': https,
  67. };
  68. /**
  69. * Default serialization map.
  70. *
  71. * superagent.serialize['application/xml'] = function(obj){
  72. * return 'generated xml here';
  73. * };
  74. *
  75. */
  76. exports.serialize = {
  77. 'application/x-www-form-urlencoded': qs.stringify,
  78. 'application/json': JSON.stringify,
  79. };
  80. /**
  81. * Default parsers.
  82. *
  83. * superagent.parse['application/xml'] = function(res, fn){
  84. * fn(null, res);
  85. * };
  86. *
  87. */
  88. exports.parse = require('./parsers');
  89. /**
  90. * Initialize internal header tracking properties on a request instance.
  91. *
  92. * @param {Object} req the instance
  93. * @api private
  94. */
  95. function _initHeaders(req) {
  96. const ua = `node-superagent/${pkg.version}`;
  97. req._header = { // coerces header names to lowercase
  98. 'user-agent': ua
  99. };
  100. req.header = { // preserves header name case
  101. 'User-Agent': ua
  102. };
  103. }
  104. /**
  105. * Initialize a new `Request` with the given `method` and `url`.
  106. *
  107. * @param {String} method
  108. * @param {String|Object} url
  109. * @api public
  110. */
  111. function Request(method, url) {
  112. Stream.call(this);
  113. if ('string' != typeof url) url = format(url);
  114. this._agent = false;
  115. this._formData = null;
  116. this.method = method;
  117. this.url = url;
  118. _initHeaders(this);
  119. this.writable = true;
  120. this._redirects = 0;
  121. this.redirects(method === 'HEAD' ? 0 : 5);
  122. this.cookies = '';
  123. this.qs = {};
  124. this._query = [];
  125. this.qsRaw = this._query; // Unused, for backwards compatibility only
  126. this._redirectList = [];
  127. this._streamRequest = false;
  128. this.once('end', this.clearTimeout.bind(this));
  129. }
  130. /**
  131. * Inherit from `Stream` (which inherits from `EventEmitter`).
  132. * Mixin `RequestBase`.
  133. */
  134. util.inherits(Request, Stream);
  135. RequestBase(Request.prototype);
  136. /**
  137. * Queue the given `file` as an attachment to the specified `field`,
  138. * with optional `options` (or filename).
  139. *
  140. * ``` js
  141. * request.post('http://localhost/upload')
  142. * .attach('field', Buffer.from('<b>Hello world</b>'), 'hello.html')
  143. * .end(callback);
  144. * ```
  145. *
  146. * A filename may also be used:
  147. *
  148. * ``` js
  149. * request.post('http://localhost/upload')
  150. * .attach('files', 'image.jpg')
  151. * .end(callback);
  152. * ```
  153. *
  154. * @param {String} field
  155. * @param {String|fs.ReadStream|Buffer} file
  156. * @param {String|Object} options
  157. * @return {Request} for chaining
  158. * @api public
  159. */
  160. Request.prototype.attach = function(field, file, options){
  161. if (file) {
  162. if (this._data) {
  163. throw Error("superagent can't mix .send() and .attach()");
  164. }
  165. let o = options || {};
  166. if ('string' == typeof options) {
  167. o = { filename: options };
  168. }
  169. if ('string' == typeof file) {
  170. if (!o.filename) o.filename = file;
  171. debug('creating `fs.ReadStream` instance for file: %s', file);
  172. file = fs.createReadStream(file);
  173. } else if (!o.filename && file.path) {
  174. o.filename = file.path;
  175. }
  176. this._getFormData().append(field, file, o);
  177. }
  178. return this;
  179. };
  180. Request.prototype._getFormData = function() {
  181. if (!this._formData) {
  182. this._formData = new FormData();
  183. this._formData.on('error', err => {
  184. this.emit('error', err);
  185. this.abort();
  186. });
  187. }
  188. return this._formData;
  189. };
  190. /**
  191. * Gets/sets the `Agent` to use for this HTTP request. The default (if this
  192. * function is not called) is to opt out of connection pooling (`agent: false`).
  193. *
  194. * @param {http.Agent} agent
  195. * @return {http.Agent}
  196. * @api public
  197. */
  198. Request.prototype.agent = function(agent){
  199. if (!arguments.length) return this._agent;
  200. this._agent = agent;
  201. return this;
  202. };
  203. /**
  204. * Set _Content-Type_ response header passed through `mime.lookup()`.
  205. *
  206. * Examples:
  207. *
  208. * request.post('/')
  209. * .type('xml')
  210. * .send(xmlstring)
  211. * .end(callback);
  212. *
  213. * request.post('/')
  214. * .type('json')
  215. * .send(jsonstring)
  216. * .end(callback);
  217. *
  218. * request.post('/')
  219. * .type('application/json')
  220. * .send(jsonstring)
  221. * .end(callback);
  222. *
  223. * @param {String} type
  224. * @return {Request} for chaining
  225. * @api public
  226. */
  227. Request.prototype.type = function(type) {
  228. return this.set(
  229. 'Content-Type',
  230. ~type.indexOf('/') ? type : mime.lookup(type)
  231. );
  232. };
  233. /**
  234. * Set _Accept_ response header passed through `mime.lookup()`.
  235. *
  236. * Examples:
  237. *
  238. * superagent.types.json = 'application/json';
  239. *
  240. * request.get('/agent')
  241. * .accept('json')
  242. * .end(callback);
  243. *
  244. * request.get('/agent')
  245. * .accept('application/json')
  246. * .end(callback);
  247. *
  248. * @param {String} accept
  249. * @return {Request} for chaining
  250. * @api public
  251. */
  252. Request.prototype.accept = function(type){
  253. return this.set('Accept', ~type.indexOf('/')
  254. ? type
  255. : mime.lookup(type));
  256. };
  257. /**
  258. * Add query-string `val`.
  259. *
  260. * Examples:
  261. *
  262. * request.get('/shoes')
  263. * .query('size=10')
  264. * .query({ color: 'blue' })
  265. *
  266. * @param {Object|String} val
  267. * @return {Request} for chaining
  268. * @api public
  269. */
  270. Request.prototype.query = function(val){
  271. if ('string' == typeof val) {
  272. this._query.push(val);
  273. } else {
  274. extend(this.qs, val);
  275. }
  276. return this;
  277. };
  278. /**
  279. * Write raw `data` / `encoding` to the socket.
  280. *
  281. * @param {Buffer|String} data
  282. * @param {String} encoding
  283. * @return {Boolean}
  284. * @api public
  285. */
  286. Request.prototype.write = function(data, encoding){
  287. const req = this.request();
  288. if (!this._streamRequest) {
  289. this._streamRequest = true;
  290. }
  291. return req.write(data, encoding);
  292. };
  293. /**
  294. * Pipe the request body to `stream`.
  295. *
  296. * @param {Stream} stream
  297. * @param {Object} options
  298. * @return {Stream}
  299. * @api public
  300. */
  301. Request.prototype.pipe = function(stream, options){
  302. this.piped = true; // HACK...
  303. this.buffer(false);
  304. this.end();
  305. return this._pipeContinue(stream, options);
  306. };
  307. Request.prototype._pipeContinue = function(stream, options){
  308. this.req.once('response', res => {
  309. // redirect
  310. const redirect = isRedirect(res.statusCode);
  311. if (redirect && this._redirects++ != this._maxRedirects) {
  312. return this._redirect(res)._pipeContinue(stream, options);
  313. }
  314. this.res = res;
  315. this._emitResponse();
  316. if (this._aborted) return;
  317. if (this._shouldUnzip(res)) {
  318. const unzipObj = zlib.createUnzip();
  319. unzipObj.on('error', err => {
  320. if (err && err.code === 'Z_BUF_ERROR') { // unexpected end of file is ignored by browsers and curl
  321. stream.emit('end');
  322. return;
  323. }
  324. stream.emit('error', err);
  325. });
  326. res.pipe(unzipObj).pipe(stream, options);
  327. } else {
  328. res.pipe(stream, options);
  329. }
  330. res.once('end', () => {
  331. this.emit('end');
  332. });
  333. });
  334. return stream;
  335. };
  336. /**
  337. * Enable / disable buffering.
  338. *
  339. * @return {Boolean} [val]
  340. * @return {Request} for chaining
  341. * @api public
  342. */
  343. Request.prototype.buffer = function(val){
  344. this._buffer = (false !== val);
  345. return this;
  346. };
  347. /**
  348. * Redirect to `url
  349. *
  350. * @param {IncomingMessage} res
  351. * @return {Request} for chaining
  352. * @api private
  353. */
  354. Request.prototype._redirect = function(res){
  355. let url = res.headers.location;
  356. if (!url) {
  357. return this.callback(new Error('No location header for redirect'), res);
  358. }
  359. debug('redirect %s -> %s', this.url, url);
  360. // location
  361. url = resolve(this.url, url);
  362. // ensure the response is being consumed
  363. // this is required for Node v0.10+
  364. res.resume();
  365. let headers = this.req._headers;
  366. const changesOrigin = parse(url).host !== parse(this.url).host;
  367. // implementation of 302 following defacto standard
  368. if (res.statusCode == 301 || res.statusCode == 302){
  369. // strip Content-* related fields
  370. // in case of POST etc
  371. headers = utils.cleanHeader(this.req._headers, changesOrigin);
  372. // force GET
  373. this.method = 'HEAD' == this.method
  374. ? 'HEAD'
  375. : 'GET';
  376. // clear data
  377. this._data = null;
  378. }
  379. // 303 is always GET
  380. if (res.statusCode == 303) {
  381. // strip Content-* related fields
  382. // in case of POST etc
  383. headers = utils.cleanHeader(this.req._headers, changesOrigin);
  384. // force method
  385. this.method = 'GET';
  386. // clear data
  387. this._data = null;
  388. }
  389. // 307 preserves method
  390. // 308 preserves method
  391. delete headers.host;
  392. delete this.req;
  393. delete this._formData;
  394. // remove all add header except User-Agent
  395. _initHeaders(this);
  396. // redirect
  397. this._endCalled = false;
  398. this.url = url;
  399. this.qs = {};
  400. this._query.length = 0;
  401. this.set(headers);
  402. this.emit('redirect', res);
  403. this._redirectList.push(this.url);
  404. this.end(this._callback);
  405. return this;
  406. };
  407. /**
  408. * Set Authorization field value with `user` and `pass`.
  409. *
  410. * Examples:
  411. *
  412. * .auth('tobi', 'learnboost')
  413. * .auth('tobi:learnboost')
  414. * .auth('tobi')
  415. * .auth(accessToken, { type: 'bearer' })
  416. *
  417. * @param {String} user
  418. * @param {String} [pass]
  419. * @param {Object} [options] options with authorization type 'basic' or 'bearer' ('basic' is default)
  420. * @return {Request} for chaining
  421. * @api public
  422. */
  423. Request.prototype.auth = function(user, pass, options){
  424. if (1 === arguments.length) pass = '';
  425. if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options
  426. options = pass;
  427. pass = '';
  428. }
  429. if (!options) {
  430. options = { type: 'basic' };
  431. }
  432. var encoder = function(string) {
  433. return new Buffer(string).toString('base64');
  434. };
  435. return this._auth(user, pass, options, encoder);
  436. };
  437. /**
  438. * Set the certificate authority option for https request.
  439. *
  440. * @param {Buffer | Array} cert
  441. * @return {Request} for chaining
  442. * @api public
  443. */
  444. Request.prototype.ca = function(cert){
  445. this._ca = cert;
  446. return this;
  447. };
  448. /**
  449. * Set the client certificate key option for https request.
  450. *
  451. * @param {Buffer | String} cert
  452. * @return {Request} for chaining
  453. * @api public
  454. */
  455. Request.prototype.key = function(cert){
  456. this._key = cert;
  457. return this;
  458. };
  459. /**
  460. * Set the key, certificate, and CA certs of the client in PFX or PKCS12 format.
  461. *
  462. * @param {Buffer | String} cert
  463. * @return {Request} for chaining
  464. * @api public
  465. */
  466. Request.prototype.pfx = function(cert) {
  467. if (typeof cert === 'object' && !Buffer.isBuffer(cert)) {
  468. this._pfx = cert.pfx;
  469. this._passphrase = cert.passphrase;
  470. } else {
  471. this._pfx = cert;
  472. }
  473. return this;
  474. };
  475. /**
  476. * Set the client certificate option for https request.
  477. *
  478. * @param {Buffer | String} cert
  479. * @return {Request} for chaining
  480. * @api public
  481. */
  482. Request.prototype.cert = function(cert){
  483. this._cert = cert;
  484. return this;
  485. };
  486. /**
  487. * Return an http[s] request.
  488. *
  489. * @return {OutgoingMessage}
  490. * @api private
  491. */
  492. Request.prototype.request = function(){
  493. if (this.req) return this.req;
  494. const options = {};
  495. try {
  496. const query = qs.stringify(this.qs, {
  497. indices: false,
  498. strictNullHandling: true,
  499. });
  500. if (query) {
  501. this.qs = {};
  502. this._query.push(query);
  503. }
  504. this._finalizeQueryString();
  505. } catch (e) {
  506. return this.emit('error', e);
  507. }
  508. let url = this.url;
  509. const retries = this._retries;
  510. // default to http://
  511. if (0 != url.indexOf('http')) url = `http://${url}`;
  512. url = parse(url);
  513. // support unix sockets
  514. if (/^https?\+unix:/.test(url.protocol) === true) {
  515. // get the protocol
  516. url.protocol = `${url.protocol.split('+')[0]}:`;
  517. // get the socket, path
  518. const unixParts = url.path.match(/^([^/]+)(.+)$/);
  519. options.socketPath = unixParts[1].replace(/%2F/g, '/');
  520. url.path = unixParts[2];
  521. }
  522. // options
  523. options.method = this.method;
  524. options.port = url.port;
  525. options.path = url.path;
  526. options.host = url.hostname;
  527. options.ca = this._ca;
  528. options.key = this._key;
  529. options.pfx = this._pfx;
  530. options.cert = this._cert;
  531. options.passphrase = this._passphrase;
  532. options.agent = this._agent;
  533. // initiate request
  534. const mod = exports.protocols[url.protocol];
  535. // request
  536. const req = (this.req = mod.request(options));
  537. // set tcp no delay
  538. req.setNoDelay(true);
  539. if ('HEAD' != options.method) {
  540. req.setHeader('Accept-Encoding', 'gzip, deflate');
  541. }
  542. this.protocol = url.protocol;
  543. this.host = url.host;
  544. // expose events
  545. req.once('drain', () => { this.emit('drain'); });
  546. req.once('error', err => {
  547. // flag abortion here for out timeouts
  548. // because node will emit a faux-error "socket hang up"
  549. // when request is aborted before a connection is made
  550. if (this._aborted) return;
  551. // if not the same, we are in the **old** (cancelled) request,
  552. // so need to continue (same as for above)
  553. if (this._retries !== retries) return;
  554. // if we've received a response then we don't want to let
  555. // an error in the request blow up the response
  556. if (this.response) return;
  557. this.callback(err);
  558. });
  559. // auth
  560. if (url.auth) {
  561. const auth = url.auth.split(':');
  562. this.auth(auth[0], auth[1]);
  563. }
  564. if (this.username && this.password) {
  565. this.auth(this.username, this.password);
  566. }
  567. for (const key in this.header) {
  568. if (this.header.hasOwnProperty(key))
  569. req.setHeader(key, this.header[key]);
  570. }
  571. // add cookies
  572. if (this.cookies) {
  573. if(this.header.hasOwnProperty('cookie')) {
  574. // merge
  575. const tmpJar = new CookieJar.CookieJar();
  576. tmpJar.setCookies(this.header.cookie.split(';'));
  577. tmpJar.setCookies(this.cookies.split(';'));
  578. req.setHeader('Cookie',tmpJar.getCookies(CookieJar.CookieAccessInfo.All).toValueString());
  579. } else {
  580. req.setHeader('Cookie', this.cookies);
  581. }
  582. }
  583. return req;
  584. };
  585. /**
  586. * Invoke the callback with `err` and `res`
  587. * and handle arity check.
  588. *
  589. * @param {Error} err
  590. * @param {Response} res
  591. * @api private
  592. */
  593. Request.prototype.callback = function(err, res){
  594. if (this._shouldRetry(err, res)) {
  595. return this._retry();
  596. }
  597. // Avoid the error which is emitted from 'socket hang up' to cause the fn undefined error on JS runtime.
  598. const fn = this._callback || noop;
  599. this.clearTimeout();
  600. if (this.called) return console.warn('superagent: double callback bug');
  601. this.called = true;
  602. if (!err) {
  603. try {
  604. if (!this._isResponseOK(res)) {
  605. let msg = 'Unsuccessful HTTP response';
  606. if (res) {
  607. msg = http.STATUS_CODES[res.status] || msg;
  608. }
  609. err = new Error(msg);
  610. err.status = res ? res.status : undefined;
  611. }
  612. } catch (new_err) {
  613. err = new_err;
  614. }
  615. }
  616. // It's important that the callback is called outside try/catch
  617. // to avoid double callback
  618. if (!err) {
  619. return fn(null, res);
  620. }
  621. err.response = res;
  622. if (this._maxRetries) err.retries = this._retries - 1;
  623. // only emit error event if there is a listener
  624. // otherwise we assume the callback to `.end()` will get the error
  625. if (err && this.listeners('error').length > 0) {
  626. this.emit('error', err);
  627. }
  628. fn(err, res);
  629. };
  630. /**
  631. * Check if `obj` is a host object,
  632. *
  633. * @param {Object} obj
  634. * @return {Boolean}
  635. * @api private
  636. */
  637. Request.prototype._isHost = function _isHost(obj) {
  638. return Buffer.isBuffer(obj) || obj instanceof Stream || obj instanceof FormData;
  639. }
  640. /**
  641. * Initiate request, invoking callback `fn(err, res)`
  642. * with an instanceof `Response`.
  643. *
  644. * @param {Function} fn
  645. * @return {Request} for chaining
  646. * @api public
  647. */
  648. Request.prototype._emitResponse = function(body, files) {
  649. const response = new Response(this);
  650. this.response = response;
  651. response.redirects = this._redirectList;
  652. if (undefined !== body) {
  653. response.body = body;
  654. }
  655. response.files = files;
  656. this.emit('response', response);
  657. return response;
  658. };
  659. Request.prototype.end = function(fn) {
  660. this.request();
  661. debug('%s %s', this.method, this.url);
  662. if (this._endCalled) {
  663. console.warn(
  664. 'Warning: .end() was called twice. This is not supported in superagent'
  665. );
  666. }
  667. this._endCalled = true;
  668. // store callback
  669. this._callback = fn || noop;
  670. return this._end();
  671. };
  672. Request.prototype._end = function() {
  673. let data = this._data;
  674. const req = this.req;
  675. let buffer = this._buffer;
  676. const method = this.method;
  677. this._setTimeouts();
  678. // body
  679. if ('HEAD' != method && !req._headerSent) {
  680. // serialize stuff
  681. if ('string' != typeof data) {
  682. let contentType = req.getHeader('Content-Type');
  683. // Parse out just the content type from the header (ignore the charset)
  684. if (contentType) contentType = contentType.split(';')[0];
  685. let serialize = exports.serialize[contentType];
  686. if (!serialize && isJSON(contentType)) {
  687. serialize = exports.serialize['application/json'];
  688. }
  689. if (serialize) data = serialize(data);
  690. }
  691. // content-length
  692. if (data && !req.getHeader('Content-Length')) {
  693. req.setHeader('Content-Length', Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data));
  694. }
  695. }
  696. // response
  697. req.once('response', res => {
  698. debug('%s %s -> %s', this.method, this.url, res.statusCode);
  699. if (this._responseTimeoutTimer) {
  700. clearTimeout(this._responseTimeoutTimer);
  701. }
  702. if (this.piped) {
  703. return;
  704. }
  705. const max = this._maxRedirects;
  706. const mime = utils.type(res.headers['content-type'] || '') || 'text/plain';
  707. const type = mime.split('/')[0];
  708. const multipart = 'multipart' == type;
  709. const redirect = isRedirect(res.statusCode);
  710. let parser = this._parser;
  711. const responseType = this._responseType;
  712. this.res = res;
  713. // redirect
  714. if (redirect && this._redirects++ != max) {
  715. return this._redirect(res);
  716. }
  717. if ('HEAD' == this.method) {
  718. this.emit('end');
  719. this.callback(null, this._emitResponse());
  720. return;
  721. }
  722. // zlib support
  723. if (this._shouldUnzip(res)) {
  724. unzip(req, res);
  725. }
  726. if (!parser) {
  727. if (responseType) {
  728. parser = exports.parse.image; // It's actually a generic Buffer
  729. buffer = true;
  730. } else if (multipart) {
  731. const form = new formidable.IncomingForm();
  732. parser = form.parse.bind(form);
  733. buffer = true;
  734. } else if (isImageOrVideo(mime)) {
  735. parser = exports.parse.image;
  736. buffer = true; // For backwards-compatibility buffering default is ad-hoc MIME-dependent
  737. } else if (exports.parse[mime]) {
  738. parser = exports.parse[mime];
  739. } else if ('text' == type) {
  740. parser = exports.parse.text;
  741. buffer = (buffer !== false);
  742. // everyone wants their own white-labeled json
  743. } else if (isJSON(mime)) {
  744. parser = exports.parse['application/json'];
  745. buffer = (buffer !== false);
  746. } else if (buffer) {
  747. parser = exports.parse.text;
  748. }
  749. }
  750. // by default only buffer text/*, json and messed up thing from hell
  751. if ((undefined === buffer && isText(mime)) || isJSON(mime)) {
  752. buffer = true;
  753. }
  754. let parserHandlesEnd = false;
  755. if (buffer) {
  756. // Protectiona against zip bombs and other nuisance
  757. let responseBytesLeft = this._maxResponseSize || 200000000;
  758. res.on('data', buf => {
  759. responseBytesLeft -= buf.byteLength || buf.length;
  760. if (responseBytesLeft < 0) {
  761. // This will propagate through error event
  762. const err = Error("Maximum response size reached");
  763. err.code = "ETOOLARGE";
  764. // Parsers aren't required to observe error event,
  765. // so would incorrectly report success
  766. parserHandlesEnd = false;
  767. // Will emit error event
  768. res.destroy(err);
  769. }
  770. });
  771. }
  772. if (parser) {
  773. try {
  774. // Unbuffered parsers are supposed to emit response early,
  775. // which is weird BTW, because response.body won't be there.
  776. parserHandlesEnd = buffer;
  777. parser(res, (err, obj, files) => {
  778. if (this.timedout) {
  779. // Timeout has already handled all callbacks
  780. return;
  781. }
  782. // Intentional (non-timeout) abort is supposed to preserve partial response,
  783. // even if it doesn't parse.
  784. if (err && !this._aborted) {
  785. return this.callback(err);
  786. }
  787. if (parserHandlesEnd) {
  788. this.emit('end');
  789. this.callback(null, this._emitResponse(obj, files));
  790. }
  791. });
  792. } catch (err) {
  793. this.callback(err);
  794. return;
  795. }
  796. }
  797. this.res = res;
  798. // unbuffered
  799. if (!buffer) {
  800. debug('unbuffered %s %s', this.method, this.url);
  801. this.callback(null, this._emitResponse());
  802. if (multipart) return; // allow multipart to handle end event
  803. res.once('end', () => {
  804. debug('end %s %s', this.method, this.url);
  805. this.emit('end');
  806. });
  807. return;
  808. }
  809. // terminating events
  810. res.once('error', err => {
  811. parserHandlesEnd = false;
  812. this.callback(err, null);
  813. });
  814. if (!parserHandlesEnd)
  815. res.once('end', () => {
  816. debug('end %s %s', this.method, this.url);
  817. // TODO: unless buffering emit earlier to stream
  818. this.emit('end');
  819. this.callback(null, this._emitResponse());
  820. });
  821. });
  822. this.emit('request', this);
  823. const getProgressMonitor = () => {
  824. const lengthComputable = true;
  825. const total = req.getHeader('Content-Length');
  826. let loaded = 0;
  827. const progress = new Stream.Transform();
  828. progress._transform = (chunk, encoding, cb) => {
  829. loaded += chunk.length;
  830. this.emit('progress', {
  831. direction: 'upload',
  832. lengthComputable,
  833. loaded,
  834. total,
  835. });
  836. cb(null, chunk);
  837. };
  838. return progress;
  839. };
  840. const bufferToChunks = (buffer) => {
  841. const chunkSize = 16 * 1024; // default highWaterMark value
  842. const chunking = new Stream.Readable();
  843. const totalLength = buffer.length;
  844. const remainder = totalLength % chunkSize;
  845. const cutoff = totalLength - remainder;
  846. for (let i = 0; i < cutoff; i += chunkSize) {
  847. const chunk = buffer.slice(i, i + chunkSize);
  848. chunking.push(chunk);
  849. }
  850. if (remainder > 0) {
  851. const remainderBuffer = buffer.slice(-remainder);
  852. chunking.push(remainderBuffer);
  853. }
  854. chunking.push(null); // no more data
  855. return chunking;
  856. }
  857. // if a FormData instance got created, then we send that as the request body
  858. const formData = this._formData;
  859. if (formData) {
  860. // set headers
  861. const headers = formData.getHeaders();
  862. for (const i in headers) {
  863. debug('setting FormData header: "%s: %s"', i, headers[i]);
  864. req.setHeader(i, headers[i]);
  865. }
  866. // attempt to get "Content-Length" header
  867. formData.getLength((err, length) => {
  868. // TODO: Add chunked encoding when no length (if err)
  869. debug('got FormData Content-Length: %s', length);
  870. if ('number' == typeof length) {
  871. req.setHeader('Content-Length', length);
  872. }
  873. formData.pipe(getProgressMonitor()).pipe(req);
  874. });
  875. } else if (Buffer.isBuffer(data)) {
  876. bufferToChunks(data).pipe(getProgressMonitor()).pipe(req);
  877. } else {
  878. req.end(data);
  879. }
  880. return this;
  881. };
  882. /**
  883. * Check whether response has a non-0-sized gzip-encoded body
  884. */
  885. Request.prototype._shouldUnzip = res => {
  886. if (res.statusCode === 204 || res.statusCode === 304) {
  887. // These aren't supposed to have any body
  888. return false;
  889. }
  890. // header content is a string, and distinction between 0 and no information is crucial
  891. if ('0' === res.headers['content-length']) {
  892. // We know that the body is empty (unfortunately, this check does not cover chunked encoding)
  893. return false;
  894. }
  895. // console.log(res);
  896. return /^\s*(?:deflate|gzip)\s*$/.test(res.headers['content-encoding']);
  897. };
  898. // generate HTTP verb methods
  899. if (methods.indexOf('del') == -1) {
  900. // create a copy so we don't cause conflicts with
  901. // other packages using the methods package and
  902. // npm 3.x
  903. methods = methods.slice(0);
  904. methods.push('del');
  905. }
  906. methods.forEach(method => {
  907. const name = method;
  908. method = 'del' == method ? 'delete' : method;
  909. method = method.toUpperCase();
  910. request[name] = (url, data, fn) => {
  911. const req = request(method, url);
  912. if ('function' == typeof data) (fn = data), (data = null);
  913. if (data) {
  914. if (method === 'GET' || method === 'HEAD') {
  915. req.query(data);
  916. } else {
  917. req.send(data);
  918. }
  919. }
  920. fn && req.end(fn);
  921. return req;
  922. };
  923. });
  924. /**
  925. * Check if `mime` is text and should be buffered.
  926. *
  927. * @param {String} mime
  928. * @return {Boolean}
  929. * @api public
  930. */
  931. function isText(mime) {
  932. const parts = mime.split('/');
  933. const type = parts[0];
  934. const subtype = parts[1];
  935. return 'text' == type || 'x-www-form-urlencoded' == subtype;
  936. }
  937. function isImageOrVideo(mime) {
  938. const type = mime.split('/')[0];
  939. return 'image' == type || 'video' == type;
  940. }
  941. /**
  942. * Check if `mime` is json or has +json structured syntax suffix.
  943. *
  944. * @param {String} mime
  945. * @return {Boolean}
  946. * @api private
  947. */
  948. function isJSON(mime) {
  949. // should match /json or +json
  950. // but not /json-seq
  951. return /[\/+]json($|[^-\w])/.test(mime);
  952. }
  953. /**
  954. * Check if we should follow the redirect `code`.
  955. *
  956. * @param {Number} code
  957. * @return {Boolean}
  958. * @api private
  959. */
  960. function isRedirect(code) {
  961. return ~[301, 302, 303, 305, 307, 308].indexOf(code);
  962. }