http.js 6.9 KB

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