http.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. 'use strict';
  2. var utils = require('./../utils');
  3. var settle = require('./../core/settle');
  4. var buildURL = require('./../helpers/buildURL');
  5. var http = require('http');
  6. var https = require('https');
  7. var httpFollow = require('follow-redirects').http;
  8. var httpsFollow = require('follow-redirects').https;
  9. var url = require('url');
  10. var zlib = require('zlib');
  11. var pkg = require('./../../package.json');
  12. var Buffer = require('buffer').Buffer;
  13. var createError = require('../core/createError');
  14. var enhanceError = require('../core/enhanceError');
  15. /*eslint consistent-return:0*/
  16. module.exports = function httpAdapter(config) {
  17. return new Promise(function dispatchHttpRequest(resolve, reject) {
  18. var data = config.data;
  19. var headers = config.headers;
  20. var timer;
  21. var aborted = false;
  22. // Set User-Agent (required by some servers)
  23. // Only set header if it hasn't been set in config
  24. // See https://github.com/mzabriskie/axios/issues/69
  25. if (!headers['User-Agent'] && !headers['user-agent']) {
  26. headers['User-Agent'] = 'axios/' + pkg.version;
  27. }
  28. if (data && !utils.isStream(data)) {
  29. if (utils.isArrayBuffer(data)) {
  30. data = new Buffer(new Uint8Array(data));
  31. } else if (utils.isString(data)) {
  32. data = new Buffer(data, 'utf-8');
  33. } else {
  34. return reject(createError(
  35. 'Data after transformation must be a string, an ArrayBuffer, or a Stream',
  36. config
  37. ));
  38. }
  39. // Add Content-Length header if data exists
  40. headers['Content-Length'] = data.length;
  41. }
  42. // HTTP basic authentication
  43. var auth = undefined;
  44. if (config.auth) {
  45. var username = config.auth.username || '';
  46. var password = config.auth.password || '';
  47. auth = username + ':' + password;
  48. }
  49. // Parse url
  50. var parsed = url.parse(config.url);
  51. if (!auth && parsed.auth) {
  52. var urlAuth = parsed.auth.split(':');
  53. var urlUsername = urlAuth[0] || '';
  54. var urlPassword = urlAuth[1] || '';
  55. auth = urlUsername + ':' + urlPassword;
  56. }
  57. var options = {
  58. hostname: parsed.hostname,
  59. port: parsed.port,
  60. path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
  61. method: config.method,
  62. headers: headers,
  63. agent: config.agent,
  64. auth: auth
  65. };
  66. if (config.proxy) {
  67. options.host = config.proxy.host;
  68. options.port = config.proxy.port;
  69. options.path = parsed.protocol + '//' + parsed.hostname + options.path;
  70. }
  71. var transport;
  72. if (config.maxRedirects === 0) {
  73. transport = parsed.protocol === 'https:' ? https : http;
  74. } else {
  75. if (config.maxRedirects) {
  76. options.maxRedirects = config.maxRedirects;
  77. }
  78. transport = parsed.protocol === 'https:' ? httpsFollow : httpFollow;
  79. }
  80. // Create the request
  81. var req = transport.request(options, function handleResponse(res) {
  82. if (aborted) return;
  83. // Response has been received so kill timer that handles request timeout
  84. clearTimeout(timer);
  85. timer = null;
  86. // uncompress the response body transparently if required
  87. var stream = res;
  88. switch (res.headers['content-encoding']) {
  89. /*eslint default-case:0*/
  90. case 'gzip':
  91. case 'compress':
  92. case 'deflate':
  93. // add the unzipper to the body stream processing pipeline
  94. stream = stream.pipe(zlib.createUnzip());
  95. // remove the content-encoding in order to not confuse downstream operations
  96. delete res.headers['content-encoding'];
  97. break;
  98. }
  99. var response = {
  100. status: res.statusCode,
  101. statusText: res.statusMessage,
  102. headers: res.headers,
  103. config: config,
  104. request: req
  105. };
  106. if (config.responseType === 'stream') {
  107. response.data = stream;
  108. settle(resolve, reject, response);
  109. } else {
  110. var responseBuffer = [];
  111. stream.on('data', function handleStreamData(chunk) {
  112. responseBuffer.push(chunk);
  113. // make sure the content length is not over the maxContentLength if specified
  114. if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
  115. reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded', config));
  116. }
  117. });
  118. stream.on('error', function handleStreamError(err) {
  119. if (aborted) return;
  120. reject(enhanceError(err, config));
  121. });
  122. stream.on('end', function handleStreamEnd() {
  123. var responseData = Buffer.concat(responseBuffer);
  124. if (config.responseType !== 'arraybuffer') {
  125. responseData = responseData.toString('utf8');
  126. }
  127. response.data = responseData;
  128. settle(resolve, reject, response);
  129. });
  130. }
  131. });
  132. // Handle errors
  133. req.on('error', function handleRequestError(err) {
  134. if (aborted) return;
  135. reject(enhanceError(err, config));
  136. });
  137. // Handle request timeout
  138. if (config.timeout && !timer) {
  139. timer = setTimeout(function handleRequestTimeout() {
  140. req.abort();
  141. reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED'));
  142. aborted = true;
  143. }, config.timeout);
  144. }
  145. // Send the request
  146. if (utils.isStream(data)) {
  147. data.pipe(req);
  148. } else {
  149. req.end(data);
  150. }
  151. });
  152. };