urllib.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. 'use strict';
  2. var debug = require('debug')('urllib');
  3. var path = require('path');
  4. var dns = require('dns');
  5. var http = require('http');
  6. var https = require('https');
  7. var urlutil = require('url');
  8. var URL = urlutil.URL;
  9. var util = require('util');
  10. var qs = require('qs');
  11. var ip = require('ip');
  12. var querystring = require('querystring');
  13. var zlib = require('zlib');
  14. var ua = require('default-user-agent');
  15. var digestAuthHeader = require('digest-header');
  16. var ms = require('humanize-ms');
  17. var statuses = require('statuses');
  18. var contentTypeParser = require('content-type');
  19. var first = require('ee-first');
  20. var pump = require('pump');
  21. var utility = require('utility');
  22. var FormStream = require('formstream');
  23. var detectProxyAgent = require('./detect_proxy_agent');
  24. var _Promise;
  25. var _iconv;
  26. var pkg = require('../package.json');
  27. var USER_AGENT = exports.USER_AGENT = ua('node-urllib', pkg.version);
  28. var NODE_MAJOR_VERSION = parseInt(process.versions.node.split('.')[0]);
  29. // change Agent.maxSockets to 1000
  30. exports.agent = new http.Agent();
  31. exports.agent.maxSockets = 1000;
  32. exports.httpsAgent = new https.Agent();
  33. exports.httpsAgent.maxSockets = 1000;
  34. var LONG_STACK_DELIMITER = '\n --------------------\n';
  35. /**
  36. * The default request timeout(in milliseconds).
  37. * @type {Number}
  38. * @const
  39. */
  40. exports.TIMEOUT = ms('5s');
  41. exports.TIMEOUTS = [ms('5s'), ms('5s')];
  42. var REQUEST_ID = 0;
  43. var MAX_VALUE = Math.pow(2, 31) - 10;
  44. var isNode010 = /^v0\.10\.\d+$/.test(process.version);
  45. var isNode012 = /^v0\.12\.\d+$/.test(process.version);
  46. /**
  47. * support data types
  48. * will auto decode response body
  49. * @type {Array}
  50. */
  51. var TEXT_DATA_TYPES = [
  52. 'json',
  53. 'text'
  54. ];
  55. var PROTO_RE = /^https?:\/\//i;
  56. // Keep-Alive: timeout=5, max=100
  57. var KEEP_ALIVE_RE = /^timeout=(\d+)/i;
  58. var SOCKET_REQUEST_COUNT = '_URLLIB_SOCKET_REQUEST_COUNT';
  59. var SOCKET_RESPONSE_COUNT = '_URLLIB_SOCKET_RESPONSE_COUNT';
  60. /**
  61. * Handle all http request, both http and https support well.
  62. *
  63. * @example
  64. *
  65. * ```js
  66. * // GET https://nodejs.org
  67. * urllib.request('https://nodejs.org', function(err, data, res) {});
  68. * // POST https://nodejs.org
  69. * var args = { type: 'post', data: { foo: 'bar' } };
  70. * urllib.request('https://nodejs.org', args, function(err, data, res) {});
  71. * ```
  72. *
  73. * @param {String|Object} url: the request full URL.
  74. * @param {Object} [args]: optional
  75. * - {Object} [data]: request data, will auto be query stringify.
  76. * - {Boolean} [dataAsQueryString]: force convert `data` to query string.
  77. * - {String|Buffer} [content]: optional, if set content, `data` will ignore.
  78. * - {ReadStream} [stream]: read stream to sent.
  79. * - {WriteStream} [writeStream]: writable stream to save response data.
  80. * If you use this, callback's data should be null.
  81. * We will just `pipe(ws, {end: true})`.
  82. * - {consumeWriteStream} [true]: consume the writeStream, invoke the callback after writeStream close.
  83. * - {Array<ReadStream|Buffer|String>|Object|ReadStream|Buffer|String} [files]: optional,
  84. * The files will send with `multipart/form-data` format, base on `formstream`.
  85. * If `method` not set, will use `POST` method by default.
  86. * - {String} [method]: optional, could be GET | POST | DELETE | PUT, default is GET
  87. * - {String} [contentType]: optional, request data type, could be `json`, default is undefined
  88. * - {String} [dataType]: optional, response data type, could be `text` or `json`, default is buffer
  89. * - {Boolean|Function} [fixJSONCtlChars]: optional, fix the control characters (U+0000 through U+001F)
  90. * before JSON parse response. Default is `false`.
  91. * `fixJSONCtlChars` can be a function, will pass data to the first argument. e.g.: `data = fixJSONCtlChars(data)`
  92. * - {Object} [headers]: optional, request headers
  93. * - {Boolean} [keepHeaderCase]: optional, by default will convert header keys to lowercase
  94. * - {Number|Array} [timeout]: request timeout(in milliseconds), default is `exports.TIMEOUTS containing connect timeout and response timeout`
  95. * - {Agent} [agent]: optional, http agent. Set `false` if you does not use agent.
  96. * - {Agent} [httpsAgent]: optional, https agent. Set `false` if you does not use agent.
  97. * - {String} [auth]: Basic authentication i.e. 'user:password' to compute an Authorization header.
  98. * - {String} [digestAuth]: Digest authentication i.e. 'user:password' to compute an Authorization header.
  99. * - {String|Buffer|Array} [ca]: An array of strings or Buffers of trusted certificates.
  100. * If this is omitted several well known "root" CAs will be used, like VeriSign.
  101. * These are used to authorize connections.
  102. * Notes: This is necessary only if the server uses the self-signed certificate
  103. * - {Boolean} [rejectUnauthorized]: If true, the server certificate is verified against the list of supplied CAs.
  104. * An 'error' event is emitted if verification fails. Default: true.
  105. * - {String|Buffer} [pfx]: A string or Buffer containing the private key,
  106. * certificate and CA certs of the server in PFX or PKCS12 format.
  107. * - {String|Buffer} [key]: A string or Buffer containing the private key of the client in PEM format.
  108. * Notes: This is necessary only if using the client certificate authentication
  109. * - {String|Buffer} [cert]: A string or Buffer containing the certificate key of the client in PEM format.
  110. * Notes: This is necessary only if using the client certificate authentication
  111. * - {String} [passphrase]: A string of passphrase for the private key or pfx.
  112. * - {String} [ciphers]: A string describing the ciphers to use or exclude.
  113. * - {String} [secureProtocol]: The SSL method to use, e.g. SSLv3_method to force SSL version 3.
  114. * The possible values depend on your installation of OpenSSL and are defined in the constant SSL_METHODS.
  115. * - {Boolean} [followRedirect]: Follow HTTP 3xx responses as redirects. defaults to false.
  116. * - {Number} [maxRedirects]: The maximum number of redirects to follow, defaults to 10.
  117. * - {Function(from, to)} [formatRedirectUrl]: Format the redirect url by your self. Default is `url.resolve(from, to)`
  118. * - {Function(options)} [beforeRequest]: Before request hook, you can change every thing here.
  119. * - {Boolean} [streaming]: let you get the res object when request connected, default is `false`. alias `customResponse`
  120. * - {Boolean} [gzip]: Accept gzip response content and auto decode it, default is `false`.
  121. * - {Boolean} [timing]: Enable timing or not, default is `false`.
  122. * - {Function} [lookup]: Custom DNS lookup function, default is `dns.lookup`.
  123. * Require node >= 4.0.0 and only work on `http` protocol.
  124. * - {Boolean} [enableProxy]: optional, enable proxy request. Default is `false`.
  125. * - {String|Object} [proxy]: optional proxy agent uri or options. Default is `null`.
  126. * - {String} [socketPath]: optional, unix domain socket file path.
  127. * - {Function} checkAddress: optional, check request address to protect from SSRF and similar attacks.
  128. * @param {Function} [callback]: callback(error, data, res). If missing callback, will return a promise object.
  129. * @return {HttpRequest} req object.
  130. * @api public
  131. */
  132. exports.request = function request(url, args, callback) {
  133. // request(url, callback)
  134. if (arguments.length === 2 && typeof args === 'function') {
  135. callback = args;
  136. args = null;
  137. }
  138. if (typeof callback === 'function') {
  139. return exports.requestWithCallback(url, args, callback);
  140. }
  141. // Promise
  142. if (!_Promise) {
  143. _Promise = require('any-promise');
  144. }
  145. return new _Promise(function (resolve, reject) {
  146. exports.requestWithCallback(url, args, makeCallback(resolve, reject));
  147. });
  148. };
  149. // alias to curl
  150. exports.curl = exports.request;
  151. function makeCallback(resolve, reject) {
  152. return function (err, data, res) {
  153. if (err) {
  154. return reject(err);
  155. }
  156. resolve({
  157. data: data,
  158. status: res.statusCode,
  159. headers: res.headers,
  160. res: res
  161. });
  162. };
  163. }
  164. // yield urllib.requestThunk(url, args)
  165. exports.requestThunk = function requestThunk(url, args) {
  166. return function (callback) {
  167. exports.requestWithCallback(url, args, function (err, data, res) {
  168. if (err) {
  169. return callback(err);
  170. }
  171. callback(null, {
  172. data: data,
  173. status: res.statusCode,
  174. headers: res.headers,
  175. res: res
  176. });
  177. });
  178. };
  179. };
  180. function requestWithCallback(url, args, callback) {
  181. var req;
  182. // requestWithCallback(url, callback)
  183. if (!url || (typeof url !== 'string' && typeof url !== 'object')) {
  184. var msg = util.format('expect request url to be a string or a http request options, but got %j', url);
  185. throw new Error(msg);
  186. }
  187. if (arguments.length === 2 && typeof args === 'function') {
  188. callback = args;
  189. args = null;
  190. }
  191. args = args || {};
  192. if (REQUEST_ID >= MAX_VALUE) {
  193. REQUEST_ID = 0;
  194. }
  195. var reqId = ++REQUEST_ID;
  196. args.requestUrls = args.requestUrls || [];
  197. args.timeout = args.timeout || exports.TIMEOUTS;
  198. args.maxRedirects = args.maxRedirects || 10;
  199. args.streaming = args.streaming || args.customResponse;
  200. var requestStartTime = Date.now();
  201. var parsedUrl;
  202. if (typeof url === 'string') {
  203. if (!PROTO_RE.test(url)) {
  204. // Support `request('www.server.com')`
  205. url = 'http://' + url;
  206. }
  207. if (URL) {
  208. parsedUrl = urlutil.parse(new URL(url).href);
  209. } else {
  210. parsedUrl = urlutil.parse(url);
  211. }
  212. } else {
  213. parsedUrl = url;
  214. }
  215. var reqMeta = {
  216. requestId: reqId,
  217. url: parsedUrl.href,
  218. args: args,
  219. ctx: args.ctx,
  220. };
  221. if (args.emitter) {
  222. args.emitter.emit('request', reqMeta);
  223. }
  224. var method = (args.type || args.method || parsedUrl.method || 'GET').toUpperCase();
  225. var port = parsedUrl.port || 80;
  226. var httplib = http;
  227. var agent = getAgent(args.agent, exports.agent);
  228. var fixJSONCtlChars = args.fixJSONCtlChars;
  229. if (parsedUrl.protocol === 'https:') {
  230. httplib = https;
  231. agent = getAgent(args.httpsAgent, exports.httpsAgent);
  232. if (!parsedUrl.port) {
  233. port = 443;
  234. }
  235. }
  236. // request through proxy tunnel
  237. var proxyTunnelAgent = detectProxyAgent(parsedUrl, args);
  238. if (proxyTunnelAgent) {
  239. agent = proxyTunnelAgent;
  240. }
  241. var lookup = args.lookup;
  242. // check address to protect from SSRF and similar attacks
  243. if (args.checkAddress) {
  244. var _lookup = lookup || dns.lookup;
  245. lookup = function(host, dnsopts, callback) {
  246. _lookup(host, dnsopts, function emitLookup(err, ip, family) {
  247. // add check address logic in custom dns lookup
  248. if (!err && !args.checkAddress(ip, family)) {
  249. err = new Error('illegal address');
  250. err.name = 'IllegalAddressError';
  251. err.hostname = host;
  252. err.ip = ip;
  253. err.family = family;
  254. }
  255. callback(err, ip, family);
  256. });
  257. };
  258. }
  259. var requestSize = 0;
  260. var options = {
  261. host: parsedUrl.hostname || parsedUrl.host || 'localhost',
  262. path: parsedUrl.path || '/',
  263. method: method,
  264. port: port,
  265. agent: agent,
  266. headers: {},
  267. // default is dns.lookup
  268. // https://github.com/nodejs/node/blob/master/lib/net.js#L986
  269. // custom dnslookup require node >= 4.0.0 (for http), node >=8 (for https)
  270. // https://github.com/nodejs/node/blob/archived-io.js-v0.12/lib/net.js#L952
  271. lookup: lookup,
  272. };
  273. var originHeaderKeys = {};
  274. if (args.headers) {
  275. // only allow enumerable and ownProperty value of args.headers
  276. var names = utility.getOwnEnumerables(args.headers, true);
  277. for (var i = 0; i < names.length; i++) {
  278. var name = names[i];
  279. var key = name.toLowerCase();
  280. if (key !== name) {
  281. originHeaderKeys[key] = name;
  282. }
  283. options.headers[key] = args.headers[name];
  284. }
  285. }
  286. if (args.socketPath) {
  287. options.socketPath = args.socketPath;
  288. }
  289. var sslNames = [
  290. 'pfx',
  291. 'key',
  292. 'passphrase',
  293. 'cert',
  294. 'ca',
  295. 'ciphers',
  296. 'rejectUnauthorized',
  297. 'secureProtocol',
  298. 'secureOptions',
  299. ];
  300. for (var i = 0; i < sslNames.length; i++) {
  301. var name = sslNames[i];
  302. if (args.hasOwnProperty(name)) {
  303. options[name] = args[name];
  304. }
  305. }
  306. // fix rejectUnauthorized when major version < 12
  307. if (NODE_MAJOR_VERSION < 12) {
  308. if (options.rejectUnauthorized === false && !options.hasOwnProperty('secureOptions')) {
  309. options.secureOptions = require('constants').SSL_OP_NO_TLSv1_2;
  310. }
  311. }
  312. var auth = args.auth || parsedUrl.auth;
  313. if (auth) {
  314. options.auth = auth;
  315. }
  316. var body = null;
  317. var dataAsQueryString = false;
  318. if (args.files) {
  319. if (!options.method || options.method === 'GET' || options.method === 'HEAD') {
  320. options.method = 'POST';
  321. }
  322. var files = args.files;
  323. var uploadFiles = [];
  324. if (Array.isArray(files)) {
  325. for (var i = 0; i < files.length; i++) {
  326. var field = 'file' + (i === 0 ? '' : i);
  327. uploadFiles.push([ field, files[i] ]);
  328. }
  329. } else {
  330. if (Buffer.isBuffer(files) || typeof files.pipe === 'function' || typeof files === 'string') {
  331. uploadFiles.push([ 'file', files ]);
  332. } else if (typeof files === 'object') {
  333. for (var field in files) {
  334. uploadFiles.push([ field, files[field] ]);
  335. }
  336. }
  337. }
  338. var form = new FormStream();
  339. // set normal fields first
  340. if (args.data) {
  341. for (var fieldName in args.data) {
  342. form.field(fieldName, args.data[fieldName]);
  343. }
  344. }
  345. for (var i = 0; i < uploadFiles.length; i++) {
  346. var item = uploadFiles[i];
  347. if (Buffer.isBuffer(item[1])) {
  348. form.buffer(item[0], item[1], 'bufferfile' + i);
  349. } else if (typeof item[1].pipe === 'function') {
  350. var filename = item[1].path || ('streamfile' + i);
  351. filename = path.basename(filename);
  352. form.stream(item[0], item[1], filename);
  353. } else {
  354. form.file(item[0], item[1]);
  355. }
  356. }
  357. var formHeaders = form.headers();
  358. var formHeaderNames = utility.getOwnEnumerables(formHeaders, true);
  359. for (var i = 0; i < formHeaderNames.length; i++) {
  360. var name = formHeaderNames[i];
  361. options.headers[name.toLowerCase()] = formHeaders[name];
  362. }
  363. debug('set multipart headers: %j, method: %s', formHeaders, options.method);
  364. args.stream = form;
  365. } else {
  366. body = args.content || args.data;
  367. dataAsQueryString = method === 'GET' || method === 'HEAD' || args.dataAsQueryString;
  368. if (!args.content) {
  369. if (body && !(typeof body === 'string' || Buffer.isBuffer(body))) {
  370. if (dataAsQueryString) {
  371. // read: GET, HEAD, use query string
  372. body = args.nestedQuerystring ? qs.stringify(body) : querystring.stringify(body);
  373. } else {
  374. var contentType = options.headers['content-type'];
  375. // auto add application/x-www-form-urlencoded when using urlencode form request
  376. if (!contentType) {
  377. if (args.contentType === 'json') {
  378. contentType = 'application/json';
  379. } else {
  380. contentType = 'application/x-www-form-urlencoded';
  381. }
  382. options.headers['content-type'] = contentType;
  383. }
  384. if (parseContentType(contentType).type === 'application/json') {
  385. body = JSON.stringify(body);
  386. } else {
  387. // 'application/x-www-form-urlencoded'
  388. body = args.nestedQuerystring ? qs.stringify(body) : querystring.stringify(body);
  389. }
  390. }
  391. }
  392. }
  393. }
  394. if (body) {
  395. // if it's a GET or HEAD request, data should be sent as query string
  396. if (dataAsQueryString) {
  397. options.path += (parsedUrl.query ? '&' : '?') + body;
  398. body = null;
  399. }
  400. if (body) {
  401. var length = body.length;
  402. if (!Buffer.isBuffer(body)) {
  403. length = Buffer.byteLength(body);
  404. }
  405. requestSize = length;
  406. options.headers['content-length'] = length.toString();
  407. }
  408. }
  409. if (args.dataType === 'json') {
  410. if (!options.headers.accept) {
  411. options.headers.accept = 'application/json';
  412. }
  413. }
  414. if (typeof args.beforeRequest === 'function') {
  415. // you can use this hook to change every thing.
  416. args.beforeRequest(options);
  417. }
  418. var connectTimer = null;
  419. var responseTimer = null;
  420. var __err = null;
  421. var connected = false; // socket connected or not
  422. var keepAliveSocket = false; // request with keepalive socket
  423. var socketHandledRequests = 0; // socket already handled request count
  424. var socketHandledResponses = 0; // socket already handled response count
  425. var responseSize = 0;
  426. var statusCode = -1;
  427. var statusMessage = null;
  428. var responseAborted = false;
  429. var remoteAddress = '';
  430. var remotePort = '';
  431. var timing = null;
  432. if (args.timing) {
  433. timing = {
  434. // socket assigned
  435. queuing: 0,
  436. // dns lookup time
  437. dnslookup: 0,
  438. // socket connected
  439. connected: 0,
  440. // request sent
  441. requestSent: 0,
  442. // Time to first byte (TTFB)
  443. waiting: 0,
  444. contentDownload: 0,
  445. };
  446. }
  447. function cancelConnectTimer() {
  448. if (connectTimer) {
  449. clearTimeout(connectTimer);
  450. connectTimer = null;
  451. debug('Request#%d connect timer canceled', reqId);
  452. }
  453. }
  454. function cancelResponseTimer() {
  455. if (responseTimer) {
  456. clearTimeout(responseTimer);
  457. responseTimer = null;
  458. debug('Request#%d response timer canceled', reqId);
  459. }
  460. }
  461. function done(err, data, res) {
  462. cancelConnectTimer();
  463. cancelResponseTimer();
  464. if (!callback) {
  465. console.warn('[urllib:warn] [%s] [%s] [worker:%s] %s %s callback twice!!!',
  466. Date(), reqId, process.pid, options.method, url);
  467. // https://github.com/node-modules/urllib/pull/30
  468. if (err) {
  469. console.warn('[urllib:warn] [%s] [%s] [worker:%s] %s: %s\nstack: %s',
  470. Date(), reqId, process.pid, err.name, err.message, err.stack);
  471. }
  472. return;
  473. }
  474. var cb = callback;
  475. callback = null;
  476. var headers = {};
  477. if (res) {
  478. statusCode = res.statusCode;
  479. statusMessage = res.statusMessage;
  480. headers = res.headers;
  481. }
  482. if (handleDigestAuth(res, cb)) {
  483. return;
  484. }
  485. var response = createCallbackResponse(data, res);
  486. debug('[%sms] done, %s bytes HTTP %s %s %s %s, keepAliveSocket: %s, timing: %j, socketHandledRequests: %s, socketHandledResponses: %s',
  487. response.requestUseTime, responseSize, statusCode, options.method, options.host, options.path,
  488. keepAliveSocket, timing, socketHandledRequests, socketHandledResponses);
  489. if (err) {
  490. var agentStatus = '';
  491. if (agent && typeof agent.getCurrentStatus === 'function') {
  492. // add current agent status to error message for logging and debug
  493. agentStatus = ', agent status: ' + JSON.stringify(agent.getCurrentStatus());
  494. }
  495. err.message += ', ' + options.method + ' ' + url + ' ' + statusCode
  496. + ' (connected: ' + connected + ', keepalive socket: ' + keepAliveSocket + agentStatus
  497. + ', socketHandledRequests: ' + socketHandledRequests
  498. + ', socketHandledResponses: ' + socketHandledResponses + ')'
  499. + '\nheaders: ' + JSON.stringify(headers);
  500. err.data = data;
  501. err.path = options.path;
  502. err.status = statusCode;
  503. err.headers = headers;
  504. err.res = response;
  505. addLongStackTrace(err, req);
  506. }
  507. // only support agentkeepalive module for now
  508. // agentkeepalive@4: agent.options.freeSocketTimeout
  509. // agentkeepalive@3: agent.freeSocketKeepAliveTimeout
  510. var freeSocketTimeout = agent && (agent.options && agent.options.freeSocketTimeout || agent.freeSocketKeepAliveTimeout);
  511. if (agent && agent.keepAlive && freeSocketTimeout > 0 &&
  512. statusCode >= 200 && headers.connection === 'keep-alive' && headers['keep-alive']) {
  513. // adjust freeSocketTimeout on the socket
  514. var m = KEEP_ALIVE_RE.exec(headers['keep-alive']);
  515. if (m) {
  516. var seconds = parseInt(m[1]);
  517. if (seconds > 0) {
  518. // network delay 500ms
  519. var serverSocketTimeout = seconds * 1000 - 500;
  520. if (serverSocketTimeout < freeSocketTimeout) {
  521. // https://github.com/node-modules/agentkeepalive/blob/master/lib/agent.js#L127
  522. // agentkeepalive@4
  523. var socket = res.socket || (req && req.socket);
  524. if (agent.options && agent.options.freeSocketTimeout) {
  525. socket.freeSocketTimeout = serverSocketTimeout;
  526. } else {
  527. socket.freeSocketKeepAliveTimeout = serverSocketTimeout;
  528. }
  529. }
  530. }
  531. }
  532. }
  533. cb(err, data, args.streaming ? res : response);
  534. emitResponseEvent(err, response);
  535. }
  536. function createAndEmitResponseEvent(data, res) {
  537. var response = createCallbackResponse(data, res);
  538. emitResponseEvent(null, response);
  539. }
  540. function createCallbackResponse(data, res) {
  541. var requestUseTime = Date.now() - requestStartTime;
  542. if (timing) {
  543. timing.contentDownload = requestUseTime;
  544. }
  545. var headers = {};
  546. if (res && res.headers) {
  547. headers = res.headers;
  548. }
  549. return {
  550. status: statusCode,
  551. statusCode: statusCode,
  552. statusMessage: statusMessage,
  553. headers: headers,
  554. size: responseSize,
  555. aborted: responseAborted,
  556. rt: requestUseTime,
  557. keepAliveSocket: keepAliveSocket,
  558. data: data,
  559. requestUrls: args.requestUrls,
  560. timing: timing,
  561. remoteAddress: remoteAddress,
  562. remotePort: remotePort,
  563. socketHandledRequests: socketHandledRequests,
  564. socketHandledResponses: socketHandledResponses,
  565. };
  566. }
  567. function emitResponseEvent(err, response) {
  568. if (args.emitter) {
  569. // keep to use the same reqMeta object on request event before
  570. reqMeta.url = parsedUrl.href;
  571. reqMeta.socket = req && req.connection;
  572. reqMeta.options = options;
  573. reqMeta.size = requestSize;
  574. args.emitter.emit('response', {
  575. requestId: reqId,
  576. error: err,
  577. ctx: args.ctx,
  578. req: reqMeta,
  579. res: response,
  580. });
  581. }
  582. }
  583. function handleDigestAuth(res, cb) {
  584. var headers = {};
  585. if (res && res.headers) {
  586. headers = res.headers;
  587. }
  588. // handle digest auth
  589. if (statusCode === 401 && headers['www-authenticate']
  590. && !options.headers.authorization && args.digestAuth) {
  591. var authenticate = headers['www-authenticate'];
  592. if (authenticate.indexOf('Digest ') >= 0) {
  593. debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', reqId, url, authenticate);
  594. options.headers.authorization = digestAuthHeader(options.method, options.path, authenticate, args.digestAuth);
  595. debug('Request#%d %s: auth with digest header: %s', reqId, url, options.headers.authorization);
  596. if (res.headers['set-cookie']) {
  597. options.headers.cookie = res.headers['set-cookie'].join(';');
  598. }
  599. args.headers = options.headers;
  600. exports.requestWithCallback(url, args, cb);
  601. return true;
  602. }
  603. }
  604. return false;
  605. }
  606. function handleRedirect(res) {
  607. var err = null;
  608. if (args.followRedirect && statuses.redirect[res.statusCode]) { // handle redirect
  609. args._followRedirectCount = (args._followRedirectCount || 0) + 1;
  610. var location = res.headers.location;
  611. if (!location) {
  612. err = new Error('Got statusCode ' + res.statusCode + ' but cannot resolve next location from headers');
  613. err.name = 'FollowRedirectError';
  614. } else if (args._followRedirectCount > args.maxRedirects) {
  615. err = new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + url);
  616. err.name = 'MaxRedirectError';
  617. } else {
  618. var newUrl = args.formatRedirectUrl ? args.formatRedirectUrl(url, location) : urlutil.resolve(url, location);
  619. debug('Request#%d %s: `redirected` from %s to %s', reqId, options.path, url, newUrl);
  620. // make sure timer stop
  621. cancelResponseTimer();
  622. // should clean up headers.host on `location: http://other-domain/url`
  623. if (options.headers.host && PROTO_RE.test(location)) {
  624. options.headers.host = null;
  625. args.headers = options.headers;
  626. }
  627. // avoid done will be execute in the future change.
  628. var cb = callback;
  629. callback = null;
  630. exports.requestWithCallback(newUrl, args, cb);
  631. return {
  632. redirect: true,
  633. error: null
  634. };
  635. }
  636. }
  637. return {
  638. redirect: false,
  639. error: err
  640. };
  641. }
  642. // don't set user-agent
  643. if (args.headers && (args.headers['User-Agent'] === null || args.headers['user-agent'] === null)) {
  644. if (options.headers['user-agent']) {
  645. delete options.headers['user-agent'];
  646. }
  647. } else {
  648. // need to set user-agent
  649. var hasAgentHeader = options.headers['user-agent'];
  650. if (!hasAgentHeader) {
  651. options.headers['user-agent'] = USER_AGENT;
  652. }
  653. }
  654. if (args.gzip) {
  655. var isAcceptEncodingNull = (args.headers && (args.headers['Accept-Encoding'] === null || args.headers['accept-encoding'] === null));
  656. if (!isAcceptEncodingNull) {
  657. var hasAcceptEncodingHeader = options.headers['accept-encoding'];
  658. if (!hasAcceptEncodingHeader) {
  659. options.headers['accept-encoding'] = 'gzip, deflate';
  660. }
  661. }
  662. }
  663. function decodeContent(res, body, cb) {
  664. var encoding = res.headers['content-encoding'];
  665. if (body.length === 0 || !encoding) {
  666. return cb(null, body, encoding);
  667. }
  668. encoding = encoding.toLowerCase();
  669. switch (encoding) {
  670. case 'gzip':
  671. case 'deflate':
  672. debug('unzip %d length body', body.length);
  673. zlib.unzip(body, function(err, data) {
  674. if (err && err.name === 'Error') {
  675. err.name = 'UnzipError';
  676. }
  677. cb(err, data);
  678. });
  679. break;
  680. default:
  681. cb(null, body, encoding);
  682. }
  683. }
  684. var writeStream = args.writeStream;
  685. var isWriteStreamClose = false;
  686. debug('Request#%d %s %s with headers %j, options.path: %s',
  687. reqId, method, url, options.headers, options.path);
  688. args.requestUrls.push(parsedUrl.href);
  689. function onResponse(res) {
  690. socketHandledResponses = res.socket[SOCKET_RESPONSE_COUNT] = (res.socket[SOCKET_RESPONSE_COUNT] || 0) + 1;
  691. if (timing) {
  692. timing.waiting = Date.now() - requestStartTime;
  693. }
  694. debug('Request#%d %s `req response` event emit: status %d, headers: %j',
  695. reqId, url, res.statusCode, res.headers);
  696. if (args.streaming) {
  697. var result = handleRedirect(res);
  698. if (result.redirect) {
  699. res.resume();
  700. createAndEmitResponseEvent(null, res);
  701. return;
  702. }
  703. if (result.error) {
  704. res.resume();
  705. return done(result.error, null, res);
  706. }
  707. return done(null, null, res);
  708. }
  709. res.on('error', function () {
  710. debug('Request#%d %s: `res error` event emit, total size %d, socket handled %s requests and %s responses',
  711. reqId, url, responseSize, socketHandledRequests, socketHandledResponses);
  712. });
  713. res.on('aborted', function () {
  714. responseAborted = true;
  715. debug('Request#%d %s: `res aborted` event emit, total size %d',
  716. reqId, url, responseSize);
  717. });
  718. if (writeStream) {
  719. // If there's a writable stream to recieve the response data, just pipe the
  720. // response stream to that writable stream and call the callback when it has
  721. // finished writing.
  722. //
  723. // NOTE that when the response stream `res` emits an 'end' event it just
  724. // means that it has finished piping data to another stream. In the
  725. // meanwhile that writable stream may still writing data to the disk until
  726. // it emits a 'close' event.
  727. //
  728. // That means that we should not apply callback until the 'close' of the
  729. // writable stream is emited.
  730. //
  731. // See also:
  732. // - https://github.com/TBEDP/urllib/commit/959ac3365821e0e028c231a5e8efca6af410eabb
  733. // - http://nodejs.org/api/stream.html#stream_event_end
  734. // - http://nodejs.org/api/stream.html#stream_event_close_1
  735. var result = handleRedirect(res);
  736. if (result.redirect) {
  737. res.resume();
  738. createAndEmitResponseEvent(null, res);
  739. return;
  740. }
  741. if (result.error) {
  742. res.resume();
  743. // end ths stream first
  744. writeStream.end();
  745. done(result.error, null, res);
  746. return;
  747. }
  748. // you can set consumeWriteStream false that only wait response end
  749. if (args.consumeWriteStream === false) {
  750. res.on('end', done.bind(null, null, null, res));
  751. pump(res, writeStream, function(err) {
  752. if (isWriteStreamClose) {
  753. return;
  754. }
  755. isWriteStreamClose = true;
  756. debug('Request#%d %s: writeStream close, error: %s', reqId, url, err);
  757. });
  758. return;
  759. }
  760. // node 0.10, 0.12: only emit res aborted, writeStream close not fired
  761. if (isNode010 || isNode012) {
  762. first([
  763. [ writeStream, 'close' ],
  764. [ res, 'aborted' ],
  765. ], function(_, stream, event) {
  766. debug('Request#%d %s: writeStream or res %s event emitted', reqId, url, event);
  767. done(__err || null, null, res);
  768. });
  769. res.pipe(writeStream);
  770. return;
  771. }
  772. debug('Request#%d %s: pump res to writeStream', reqId, url);
  773. pump(res, writeStream, function(err) {
  774. debug('Request#%d %s: writeStream close event emitted, error: %s, isWriteStreamClose: %s',
  775. reqId, url, err, isWriteStreamClose);
  776. if (isWriteStreamClose) {
  777. return;
  778. }
  779. isWriteStreamClose = true;
  780. done(__err || err, null, res);
  781. });
  782. return;
  783. }
  784. // Otherwise, just concat those buffers.
  785. //
  786. // NOTE that the `chunk` is not a String but a Buffer. It means that if
  787. // you simply concat two chunk with `+` you're actually converting both
  788. // Buffers into Strings before concating them. It'll cause problems when
  789. // dealing with multi-byte characters.
  790. //
  791. // The solution is to store each chunk in an array and concat them with
  792. // 'buffer-concat' when all chunks is recieved.
  793. //
  794. // See also:
  795. // http://cnodejs.org/topic/4faf65852e8fb5bc65113403
  796. var chunks = [];
  797. res.on('data', function (chunk) {
  798. debug('Request#%d %s: `res data` event emit, size %d', reqId, url, chunk.length);
  799. responseSize += chunk.length;
  800. chunks.push(chunk);
  801. });
  802. var isEmitted = false;
  803. function handleResponseCloseAndEnd(event) {
  804. debug('Request#%d %s: `res %s` event emit, total size %d, socket handled %s requests and %s responses',
  805. reqId, url, event, responseSize, socketHandledRequests, socketHandledResponses);
  806. if (isEmitted) {
  807. return;
  808. }
  809. isEmitted = true;
  810. var body = Buffer.concat(chunks, responseSize);
  811. debug('Request#%d %s: _dumped: %s',
  812. reqId, url, res._dumped);
  813. if (__err) {
  814. // req.abort() after `res data` event emit.
  815. return done(__err, body, res);
  816. }
  817. var result = handleRedirect(res);
  818. if (result.error) {
  819. return done(result.error, body, res);
  820. }
  821. if (result.redirect) {
  822. createAndEmitResponseEvent(null, res);
  823. return;
  824. }
  825. decodeContent(res, body, function (err, data, encoding) {
  826. if (err) {
  827. return done(err, body, res);
  828. }
  829. // if body not decode, dont touch it
  830. if (!encoding && TEXT_DATA_TYPES.indexOf(args.dataType) >= 0) {
  831. // try to decode charset
  832. try {
  833. data = decodeBodyByCharset(data, res);
  834. } catch (e) {
  835. debug('decodeBodyByCharset error: %s', e);
  836. // if error, dont touch it
  837. return done(null, data, res);
  838. }
  839. if (args.dataType === 'json') {
  840. if (responseSize === 0) {
  841. data = null;
  842. } else {
  843. var r = parseJSON(data, fixJSONCtlChars);
  844. if (r.error) {
  845. err = r.error;
  846. } else {
  847. data = r.data;
  848. }
  849. }
  850. }
  851. }
  852. if (responseAborted) {
  853. // err = new Error('Remote socket was terminated before `response.end()` was called');
  854. // err.name = 'RemoteSocketClosedError';
  855. debug('Request#%d %s: Remote socket was terminated before `response.end()` was called', reqId, url);
  856. }
  857. done(err, data, res);
  858. });
  859. }
  860. // node >= 14 only emit close if req abort
  861. res.on('close', function () {
  862. handleResponseCloseAndEnd('close');
  863. });
  864. res.on('end', function () {
  865. handleResponseCloseAndEnd('end');
  866. });
  867. }
  868. var connectTimeout, responseTimeout;
  869. if (Array.isArray(args.timeout)) {
  870. connectTimeout = ms(args.timeout[0]);
  871. responseTimeout = ms(args.timeout[1]);
  872. } else { // set both timeout equal
  873. connectTimeout = responseTimeout = ms(args.timeout);
  874. }
  875. debug('ConnectTimeout: %d, ResponseTimeout: %d', connectTimeout, responseTimeout);
  876. function startConnectTimer() {
  877. debug('Connect timer ticking, timeout: %d', connectTimeout);
  878. connectTimer = setTimeout(function () {
  879. connectTimer = null;
  880. if (statusCode === -1) {
  881. statusCode = -2;
  882. }
  883. var msg = 'Connect timeout for ' + connectTimeout + 'ms';
  884. var errorName = 'ConnectionTimeoutError';
  885. if (!req.socket) {
  886. errorName = 'SocketAssignTimeoutError';
  887. msg += ', working sockets is full';
  888. }
  889. __err = new Error(msg);
  890. __err.name = errorName;
  891. __err.requestId = reqId;
  892. debug('ConnectTimeout: Request#%d %s %s: %s, connected: %s', reqId, url, __err.name, msg, connected);
  893. abortRequest();
  894. }, connectTimeout);
  895. }
  896. function startResposneTimer() {
  897. debug('Response timer ticking, timeout: %d', responseTimeout);
  898. responseTimer = setTimeout(function () {
  899. responseTimer = null;
  900. var msg = 'Response timeout for ' + responseTimeout + 'ms';
  901. var errorName = 'ResponseTimeoutError';
  902. __err = new Error(msg);
  903. __err.name = errorName;
  904. __err.requestId = reqId;
  905. debug('ResponseTimeout: Request#%d %s %s: %s, connected: %s', reqId, url, __err.name, msg, connected);
  906. abortRequest();
  907. }, responseTimeout);
  908. }
  909. if (args.checkAddress) {
  910. var hostname = parsedUrl.hostname;
  911. // if request hostname is ip, custom lookup wont excute
  912. var family = null;
  913. if (ip.isV4Format(hostname)) {
  914. family = 4;
  915. } else if (ip.isV6Format(hostname)) {
  916. family = 6;
  917. }
  918. if (family) {
  919. if (!args.checkAddress(hostname, family)) {
  920. var err = new Error('illegal address');
  921. err.name = 'IllegalAddressError';
  922. err.hostname = hostname;
  923. err.ip = hostname;
  924. err.family = family;
  925. return done(err);
  926. }
  927. }
  928. }
  929. // request headers checker will throw error
  930. try {
  931. var finalOptions = options;
  932. // restore origin header key
  933. if (args.keepHeaderCase) {
  934. var originKeys = Object.keys(originHeaderKeys);
  935. if (originKeys.length) {
  936. var finalHeaders = {};
  937. var names = utility.getOwnEnumerables(options.headers, true);
  938. for (var i = 0; i < names.length; i++) {
  939. var name = names[i];
  940. finalHeaders[originHeaderKeys[name] || name] = options.headers[name];
  941. }
  942. finalOptions = Object.assign({}, options);
  943. finalOptions.headers = finalHeaders;
  944. }
  945. }
  946. req = httplib.request(finalOptions, onResponse);
  947. if (args.trace) {
  948. req._callSite = {};
  949. Error.captureStackTrace(req._callSite, requestWithCallback);
  950. }
  951. } catch (err) {
  952. return done(err);
  953. }
  954. // environment detection: browser or nodejs
  955. if (typeof(window) === 'undefined') {
  956. // start connect timer just after `request` return, and just in nodejs environment
  957. startConnectTimer();
  958. }
  959. var isRequestAborted = false;
  960. function abortRequest() {
  961. if (isRequestAborted) {
  962. return;
  963. }
  964. isRequestAborted = true;
  965. debug('Request#%d %s abort, connected: %s', reqId, url, connected);
  966. // it wont case error event when req haven't been assigned a socket yet.
  967. if (!req.socket) {
  968. __err.noSocket = true;
  969. done(__err);
  970. }
  971. req.abort();
  972. }
  973. if (timing) {
  974. // request sent
  975. req.on('finish', function() {
  976. timing.requestSent = Date.now() - requestStartTime;
  977. });
  978. }
  979. req.once('socket', function (socket) {
  980. if (timing) {
  981. // socket queuing time
  982. timing.queuing = Date.now() - requestStartTime;
  983. }
  984. // https://github.com/nodejs/node/blob/master/lib/net.js#L377
  985. // https://github.com/nodejs/node/blob/v0.10.40-release/lib/net.js#L352
  986. // should use socket.socket on 0.10.x
  987. if (isNode010 && socket.socket) {
  988. socket = socket.socket;
  989. }
  990. var orginalSocketTimeout = getSocketTimeout(socket);
  991. if (orginalSocketTimeout && orginalSocketTimeout < responseTimeout) {
  992. // make sure socket live longer than the response timer
  993. var socketTimeout = responseTimeout + 500;
  994. debug('Request#%d socket.timeout(%s) < responseTimeout(%s), reset socket timeout to %s',
  995. reqId, orginalSocketTimeout, responseTimeout, socketTimeout);
  996. socket.setTimeout(socketTimeout);
  997. }
  998. socketHandledRequests = socket[SOCKET_REQUEST_COUNT] = (socket[SOCKET_REQUEST_COUNT] || 0) + 1;
  999. if (socket[SOCKET_RESPONSE_COUNT]) {
  1000. socketHandledResponses = socket[SOCKET_RESPONSE_COUNT];
  1001. }
  1002. var readyState = socket.readyState;
  1003. if (readyState === 'opening') {
  1004. socket.once('lookup', function(err, ip, addressType) {
  1005. debug('Request#%d %s lookup: %s, %s, %s', reqId, url, err, ip, addressType);
  1006. if (timing) {
  1007. timing.dnslookup = Date.now() - requestStartTime;
  1008. }
  1009. if (ip) {
  1010. remoteAddress = ip;
  1011. }
  1012. });
  1013. socket.once('connect', function() {
  1014. if (timing) {
  1015. // socket connected
  1016. timing.connected = Date.now() - requestStartTime;
  1017. }
  1018. // cancel socket timer at first and start tick for TTFB
  1019. cancelConnectTimer();
  1020. startResposneTimer();
  1021. debug('Request#%d %s new socket connected', reqId, url);
  1022. connected = true;
  1023. if (!remoteAddress) {
  1024. remoteAddress = socket.remoteAddress;
  1025. }
  1026. remotePort = socket.remotePort;
  1027. });
  1028. return;
  1029. }
  1030. debug('Request#%d %s reuse socket connected, readyState: %s', reqId, url, readyState);
  1031. connected = true;
  1032. keepAliveSocket = true;
  1033. if (!remoteAddress) {
  1034. remoteAddress = socket.remoteAddress;
  1035. }
  1036. remotePort = socket.remotePort;
  1037. // reuse socket, timer should be canceled.
  1038. cancelConnectTimer();
  1039. startResposneTimer();
  1040. });
  1041. if (writeStream) {
  1042. writeStream.once('error', function(err) {
  1043. err.message += ' (writeStream "error")';
  1044. __err = err;
  1045. debug('Request#%d %s `writeStream error` event emit, %s: %s', reqId, url, err.name, err.message);
  1046. abortRequest();
  1047. });
  1048. }
  1049. var isRequestError = false;
  1050. function handleRequestError(err) {
  1051. if (isRequestError || !err) {
  1052. return;
  1053. }
  1054. isRequestError = true;
  1055. if (err.name === 'Error') {
  1056. err.name = connected ? 'ResponseError' : 'RequestError';
  1057. }
  1058. debug('Request#%d %s `req error` event emit, %s: %s', reqId, url, err.name, err.message);
  1059. done(__err || err);
  1060. }
  1061. if (args.stream) {
  1062. debug('Request#%d pump args.stream to req', reqId);
  1063. pump(args.stream, req, handleRequestError);
  1064. } else {
  1065. req.end(body);
  1066. }
  1067. // when stream already consumed, req's `finish` event is emitted and pump will ignore error after pipe finished
  1068. // but if server response timeout later, we will abort the request and emit an error in req
  1069. // so we must always manually listen to req's `error` event here to ensure this error is handled
  1070. req.on('error', handleRequestError);
  1071. req.requestId = reqId;
  1072. return req;
  1073. }
  1074. exports.requestWithCallback = requestWithCallback;
  1075. var JSONCtlCharsMap = {
  1076. '"': '\\"', // \u0022
  1077. '\\': '\\\\', // \u005c
  1078. '\b': '\\b', // \u0008
  1079. '\f': '\\f', // \u000c
  1080. '\n': '\\n', // \u000a
  1081. '\r': '\\r', // \u000d
  1082. '\t': '\\t' // \u0009
  1083. };
  1084. var JSONCtlCharsRE = /[\u0000-\u001F\u005C]/g;
  1085. function _replaceOneChar(c) {
  1086. return JSONCtlCharsMap[c] || '\\u' + (c.charCodeAt(0) + 0x10000).toString(16).substr(1);
  1087. }
  1088. function replaceJSONCtlChars(str) {
  1089. return str.replace(JSONCtlCharsRE, _replaceOneChar);
  1090. }
  1091. function parseJSON(data, fixJSONCtlChars) {
  1092. var result = {
  1093. error: null,
  1094. data: null
  1095. };
  1096. if (fixJSONCtlChars) {
  1097. if (typeof fixJSONCtlChars === 'function') {
  1098. data = fixJSONCtlChars(data);
  1099. } else {
  1100. // https://github.com/node-modules/urllib/pull/77
  1101. // remote the control characters (U+0000 through U+001F)
  1102. data = replaceJSONCtlChars(data);
  1103. }
  1104. }
  1105. try {
  1106. result.data = JSON.parse(data);
  1107. } catch (err) {
  1108. if (err.name === 'SyntaxError') {
  1109. err.name = 'JSONResponseFormatError';
  1110. }
  1111. if (data.length > 1024) {
  1112. // show 0~512 ... -512~end data
  1113. err.message += ' (data json format: ' +
  1114. JSON.stringify(data.slice(0, 512)) + ' ...skip... ' + JSON.stringify(data.slice(data.length - 512)) + ')';
  1115. } else {
  1116. err.message += ' (data json format: ' + JSON.stringify(data) + ')';
  1117. }
  1118. result.error = err;
  1119. }
  1120. return result;
  1121. }
  1122. /**
  1123. * decode response body by parse `content-type`'s charset
  1124. * @param {Buffer} data
  1125. * @param {Http(s)Response} res
  1126. * @return {String}
  1127. */
  1128. function decodeBodyByCharset(data, res) {
  1129. var type = res.headers['content-type'];
  1130. if (!type) {
  1131. return data.toString();
  1132. }
  1133. var type = parseContentType(type);
  1134. var charset = type.parameters.charset || 'utf-8';
  1135. if (!Buffer.isEncoding(charset)) {
  1136. if (!_iconv) {
  1137. _iconv = require('iconv-lite');
  1138. }
  1139. return _iconv.decode(data, charset);
  1140. }
  1141. return data.toString(charset);
  1142. }
  1143. function getAgent(agent, defaultAgent) {
  1144. return agent === undefined ? defaultAgent : agent;
  1145. }
  1146. function parseContentType(str) {
  1147. try {
  1148. return contentTypeParser.parse(str);
  1149. } catch (err) {
  1150. // ignore content-type error, tread as default
  1151. return { parameters: {} };
  1152. }
  1153. }
  1154. function addLongStackTrace(err, req) {
  1155. if (!req) {
  1156. return;
  1157. }
  1158. var callSiteStack = req._callSite && req._callSite.stack;
  1159. if (!callSiteStack || typeof callSiteStack !== 'string') {
  1160. return;
  1161. }
  1162. if (err._longStack) {
  1163. return;
  1164. }
  1165. var index = callSiteStack.indexOf('\n');
  1166. if (index !== -1) {
  1167. err._longStack = true;
  1168. err.stack += LONG_STACK_DELIMITER + callSiteStack.substr(index + 1);
  1169. }
  1170. }
  1171. // node 8 don't has timeout attribute on socket
  1172. // https://github.com/nodejs/node/pull/21204/files#diff-e6ef024c3775d787c38487a6309e491dR408
  1173. function getSocketTimeout(socket) {
  1174. return socket.timeout || socket._idleTimeout;
  1175. }