http.js 7.2 KB

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