http.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 createError = require('../core/createError');
  13. var enhanceError = require('../core/enhanceError');
  14. /*eslint consistent-return:0*/
  15. module.exports = function httpAdapter(config) {
  16. return new Promise(function dispatchHttpRequest(resolve, reject) {
  17. var data = config.data;
  18. var headers = config.headers;
  19. var timer;
  20. var aborted = false;
  21. // Set User-Agent (required by some servers)
  22. // Only set header if it hasn't been set in config
  23. // See https://github.com/mzabriskie/axios/issues/69
  24. if (!headers['User-Agent'] && !headers['user-agent']) {
  25. headers['User-Agent'] = 'axios/' + pkg.version;
  26. }
  27. if (data && !utils.isStream(data)) {
  28. if (Buffer.isBuffer(data)) {
  29. // Nothing to do...
  30. } else if (utils.isArrayBuffer(data)) {
  31. data = new Buffer(new Uint8Array(data));
  32. } else if (utils.isString(data)) {
  33. data = new Buffer(data, 'utf-8');
  34. } else {
  35. return reject(createError(
  36. 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream',
  37. config
  38. ));
  39. }
  40. // Add Content-Length header if data exists
  41. headers['Content-Length'] = data.length;
  42. }
  43. // HTTP basic authentication
  44. var auth = undefined;
  45. if (config.auth) {
  46. var username = config.auth.username || '';
  47. var password = config.auth.password || '';
  48. auth = username + ':' + password;
  49. }
  50. // Parse url
  51. var parsed = url.parse(config.url);
  52. var protocol = parsed.protocol || 'http:';
  53. if (!auth && parsed.auth) {
  54. var urlAuth = parsed.auth.split(':');
  55. var urlUsername = urlAuth[0] || '';
  56. var urlPassword = urlAuth[1] || '';
  57. auth = urlUsername + ':' + urlPassword;
  58. }
  59. if (auth) {
  60. delete headers.Authorization;
  61. }
  62. var isHttps = protocol === 'https:';
  63. var agent = isHttps ? config.httpsAgent : config.httpAgent;
  64. var options = {
  65. hostname: parsed.hostname,
  66. port: parsed.port,
  67. path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
  68. method: config.method,
  69. headers: headers,
  70. agent: agent,
  71. auth: auth
  72. };
  73. var proxy = config.proxy;
  74. if (!proxy) {
  75. var proxyEnv = protocol.slice(0, -1) + '_proxy';
  76. var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()];
  77. if (proxyUrl) {
  78. var parsedProxyUrl = url.parse(proxyUrl);
  79. proxy = {
  80. host: parsedProxyUrl.hostname,
  81. port: parsedProxyUrl.port
  82. };
  83. if (parsedProxyUrl.auth) {
  84. var proxyUrlAuth = parsedProxyUrl.auth.split(':');
  85. proxy.auth = {
  86. username: proxyUrlAuth[0],
  87. password: proxyUrlAuth[1]
  88. };
  89. }
  90. }
  91. }
  92. if (proxy) {
  93. options.hostname = proxy.host;
  94. options.host = proxy.host;
  95. options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : '');
  96. options.port = proxy.port;
  97. options.path = protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path;
  98. // Basic proxy authorization
  99. if (proxy.auth) {
  100. var base64 = new Buffer(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64');
  101. options.headers['Proxy-Authorization'] = 'Basic ' + base64;
  102. }
  103. }
  104. var transport;
  105. if (config.maxRedirects === 0) {
  106. transport = isHttps ? https : http;
  107. } else {
  108. if (config.maxRedirects) {
  109. options.maxRedirects = config.maxRedirects;
  110. }
  111. transport = isHttps ? httpsFollow : httpFollow;
  112. }
  113. // Create the request
  114. var req = transport.request(options, function handleResponse(res) {
  115. if (aborted) return;
  116. // Response has been received so kill timer that handles request timeout
  117. clearTimeout(timer);
  118. timer = null;
  119. // uncompress the response body transparently if required
  120. var stream = res;
  121. switch (res.headers['content-encoding']) {
  122. /*eslint default-case:0*/
  123. case 'gzip':
  124. case 'compress':
  125. case 'deflate':
  126. // add the unzipper to the body stream processing pipeline
  127. stream = stream.pipe(zlib.createUnzip());
  128. // remove the content-encoding in order to not confuse downstream operations
  129. delete res.headers['content-encoding'];
  130. break;
  131. }
  132. // return the last request in case of redirects
  133. var lastRequest = res.req || req;
  134. var response = {
  135. status: res.statusCode,
  136. statusText: res.statusMessage,
  137. headers: res.headers,
  138. config: config,
  139. request: lastRequest
  140. };
  141. if (config.responseType === 'stream') {
  142. response.data = stream;
  143. settle(resolve, reject, response);
  144. } else {
  145. var responseBuffer = [];
  146. stream.on('data', function handleStreamData(chunk) {
  147. responseBuffer.push(chunk);
  148. // make sure the content length is not over the maxContentLength if specified
  149. if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
  150. reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
  151. config, null, lastRequest));
  152. }
  153. });
  154. stream.on('error', function handleStreamError(err) {
  155. if (aborted) return;
  156. reject(enhanceError(err, config, null, lastRequest));
  157. });
  158. stream.on('end', function handleStreamEnd() {
  159. var responseData = Buffer.concat(responseBuffer);
  160. if (config.responseType !== 'arraybuffer') {
  161. responseData = responseData.toString('utf8');
  162. }
  163. response.data = responseData;
  164. settle(resolve, reject, response);
  165. });
  166. }
  167. });
  168. // Handle errors
  169. req.on('error', function handleRequestError(err) {
  170. if (aborted) return;
  171. reject(enhanceError(err, config, null, req));
  172. });
  173. // Handle request timeout
  174. if (config.timeout && !timer) {
  175. timer = setTimeout(function handleRequestTimeout() {
  176. req.abort();
  177. reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
  178. aborted = true;
  179. }, config.timeout);
  180. }
  181. if (config.cancelToken) {
  182. // Handle cancellation
  183. config.cancelToken.promise.then(function onCanceled(cancel) {
  184. if (aborted) {
  185. return;
  186. }
  187. req.abort();
  188. reject(cancel);
  189. aborted = true;
  190. });
  191. }
  192. // Send the request
  193. if (utils.isStream(data)) {
  194. data.pipe(req);
  195. } else {
  196. req.end(data);
  197. }
  198. });
  199. };