redirect_spec.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. var helpers = require('./helpers'),
  2. should = require('should'),
  3. sinon = require('sinon'),
  4. needle = require('./../');
  5. var ports = {
  6. http : 8888,
  7. https : 9999
  8. }
  9. var protocols = {
  10. http : require('http'),
  11. https : require('https')
  12. }
  13. var code = 301;
  14. var location; // var to set the response location
  15. function response_code() {
  16. return code;
  17. }
  18. function response_headers() {
  19. return { 'Content-Type': 'text/plain', 'Location': location }
  20. }
  21. describe('redirects', function() {
  22. var spies = {},
  23. servers = {};
  24. var current_protocol;
  25. var hostname = require('os').hostname();
  26. // open two servers, one that responds to a redirect
  27. before(function(done) {
  28. var conf = {
  29. port : ports.http,
  30. code : response_code,
  31. headers : response_headers
  32. }
  33. servers.http = helpers.server(conf, function() {
  34. conf.port = ports.https;
  35. conf.protocol = 'https';
  36. servers.https = helpers.server(conf, done);
  37. });
  38. })
  39. after(function(done) {
  40. servers.http.close(function() {
  41. servers.https.close(done);
  42. });
  43. })
  44. var prots = {'http': 'https'};
  45. Object.keys(prots).forEach(function(protocol) {
  46. current_protocol = protocol;
  47. var other_protocol = protocol == 'http' ? 'https' : 'http';
  48. var opts, // each test will modify this
  49. host = '127.0.0.1',
  50. url = protocol + '://' + host + ':' + ports[protocol] + '/hello';
  51. function send_request(opts, cb) {
  52. if (protocol == 'https') opts.rejectUnauthorized = false;
  53. // console.log(' -- sending request ' + url + ' -- redirect to ' + location);
  54. needle.post(url, { foo: 'bar' }, opts, cb);
  55. }
  56. function not_followed(done) {
  57. send_request(opts, function(err, resp) {
  58. resp.statusCode.should.eql(301);
  59. if (current_protocol == 'http') {
  60. spies.http.callCount.should.eql(1); // only original request
  61. spies.https.callCount.should.eql(0);
  62. } else {
  63. spies.http.callCount.should.eql(0);
  64. spies.https.callCount.should.eql(1); // only original request
  65. }
  66. done();
  67. })
  68. }
  69. function followed_same_protocol(done) {
  70. send_request(opts, function(err, resp) {
  71. // the original request plus the redirect one
  72. spies[current_protocol].callCount.should.eql(2);
  73. done();
  74. })
  75. }
  76. function followed_other_protocol(done) {
  77. send_request(opts, function(err, resp) {
  78. // on new-ish node versions, https.request calls http.request internally,
  79. // so we need to amount for that additional call.
  80. // update: this doesn't happen on node > 10.x
  81. var node_major_ver = process.version.split('.')[0].replace('v', '');
  82. var http_calls = protocols.http.Agent.defaultMaxSockets == Infinity && parseInt(node_major_ver) < 10 ? 2 : 1;
  83. spies.http.callCount.should.eql(http_calls); // the one(s) from http.request
  84. spies.https.callCount.should.eql(1); // the one from https.request (redirect)
  85. done();
  86. })
  87. }
  88. // set a spy on [protocol].request
  89. // so we can see how many times a request was made
  90. before(function() {
  91. spies.http = sinon.spy(protocols.http, 'request');
  92. spies.https = sinon.spy(protocols.https, 'request');
  93. })
  94. // and make sure it is restored after each test
  95. afterEach(function() {
  96. spies.http.reset();
  97. spies.https.reset();
  98. })
  99. after(function() {
  100. spies.http.restore();
  101. spies.https.restore();
  102. })
  103. describe('when overriding defaults', function() {
  104. before(function() {
  105. needle.defaults({ follow_max: 10 });
  106. opts = {};
  107. })
  108. after(function() {
  109. // reset values to previous
  110. needle.defaults({ follow_max: 0 });
  111. })
  112. describe('and redirected to the same path on same host and protocol', function() {
  113. before(function() {
  114. location = url;
  115. })
  116. it('does not follow redirect', not_followed);
  117. })
  118. describe('and redirected to the same path on same host and different protocol', function() {
  119. before(function() {
  120. location = url.replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
  121. })
  122. it('follows redirect', followed_other_protocol);
  123. })
  124. describe('and redirected to a different path on same host, same protocol', function() {
  125. before(function() {
  126. location = url.replace('/hello', '/goodbye');
  127. })
  128. it('follows redirect', followed_same_protocol);
  129. })
  130. describe('and redirected to a different path on same host, different protocol', function() {
  131. before(function() {
  132. location = url.replace('/hello', '/goodbye').replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
  133. })
  134. it('follows redirect', followed_other_protocol);
  135. })
  136. describe('and redirected to same path on another host, same protocol', function() {
  137. before(function() {
  138. location = url.replace(host, hostname);
  139. })
  140. it('follows redirect', followed_same_protocol);
  141. })
  142. describe('and redirected to same path on another host, different protocol', function() {
  143. before(function() {
  144. location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
  145. })
  146. it('follows redirect', followed_other_protocol);
  147. })
  148. })
  149. // false and null have the same result
  150. var values = [false, null];
  151. values.forEach(function(value) {
  152. describe('when follow is ' + value, function() {
  153. before(function() {
  154. opts = { follow: value };
  155. })
  156. describe('and redirected to the same path on same host and protocol', function() {
  157. before(function() {
  158. location = url;
  159. })
  160. it('throws an error', function() {
  161. (function() {
  162. send_request(opts, function() { });
  163. }).should.throw;
  164. })
  165. })
  166. })
  167. })
  168. describe('when follow is true', function() {
  169. before(function() {
  170. opts = { follow: true };
  171. })
  172. describe('and redirected to the same path on same host and protocol', function() {
  173. before(function() { location = url })
  174. it('throws an error', function() {
  175. (function() {
  176. send_request(opts, function() { });
  177. }).should.throw;
  178. })
  179. })
  180. })
  181. describe('when follow is > 0', function() {
  182. before(function() {
  183. needle.defaults({ follow: 10 });
  184. })
  185. after(function() {
  186. needle.defaults({ follow: 0 });
  187. })
  188. describe('when keep_method is false', function() {
  189. before(function() {
  190. opts = { follow_keep_method: false };
  191. })
  192. // defaults to follow host and protocol
  193. describe('and redirected to the same path on same host and different protocol', function() {
  194. before(function() {
  195. location = url.replace(protocol, other_protocol);
  196. })
  197. it('follows redirect', followed_other_protocol);
  198. it('sends a GET request with no data', function(done) {
  199. send_request(opts, function(err, resp) {
  200. // spy.args[0][3].should.eql(null);
  201. spies.http.args[0][0].method.should.eql('GET');
  202. done();
  203. })
  204. })
  205. it('does not resend cookies if follow_set_cookies is false', function(done) {
  206. opts.cookies = {foo: 'bar'};
  207. opts.follow_set_cookies = false;
  208. send_request(opts, function(err, resp) {
  209. should.not.exist(spies.http.args[0][0].headers['cookie']);
  210. done();
  211. })
  212. })
  213. it('resends cookies if follow_set_cookies is true', function(done) {
  214. opts.cookies = {foo: 'bar'};
  215. opts.follow_set_cookies = true;
  216. send_request(opts, function(err, resp) {
  217. spies.http.args[0][0].headers['cookie'].should.eql('foo=bar')
  218. done();
  219. })
  220. })
  221. })
  222. })
  223. describe('and set_referer is true', function() {
  224. before(function() {
  225. opts = { follow_set_referer: true };
  226. })
  227. // defaults to follow host and protocol
  228. describe('and redirected to the same path on same host and different protocol', function() {
  229. before(function() {
  230. location = url.replace(protocol, other_protocol);
  231. })
  232. it('follows redirect', followed_other_protocol);
  233. it('sets Referer header when following redirect', function(done) {
  234. send_request(opts, function(err, resp) {
  235. // spies.http.args[0][3].should.eql({ foo: 'bar'});
  236. spies.http.args[0][0].headers['referer'].should.eql("http://" + host + ":8888/hello");
  237. done();
  238. })
  239. })
  240. it('does not resend cookies if follow_set_cookies is false', function(done) {
  241. opts.cookies = {foo: 'bar'};
  242. opts.follow_set_cookies = false;
  243. send_request(opts, function(err, resp) {
  244. should.not.exist(spies.http.args[0][0].headers['cookie']);
  245. done();
  246. })
  247. })
  248. it('resends cookies if follow_set_cookies is true', function(done) {
  249. opts.cookies = {foo: 'bar'};
  250. opts.follow_set_cookies = true;
  251. send_request(opts, function(err, resp) {
  252. spies.http.args[0][0].headers['cookie'].should.eql('foo=bar')
  253. done();
  254. })
  255. })
  256. })
  257. })
  258. describe('and keep_method is true', function() {
  259. before(function() {
  260. opts = { follow_keep_method: true };
  261. })
  262. // defaults to follow host and protocol
  263. describe('and redirected to the same path on same host and different protocol', function() {
  264. before(function() {
  265. location = url.replace(protocol, other_protocol);
  266. })
  267. it('follows redirect', followed_other_protocol);
  268. it('sends a POST request with the original data', function(done) {
  269. send_request(opts, function(err, resp) {
  270. spies.http.args[0][0].method.should.eql('post');
  271. // spies.http.args[0][3].should.eql({ foo: 'bar'});
  272. done();
  273. })
  274. })
  275. it('does not resend cookies if follow_set_cookies is false', function(done) {
  276. opts.cookies = {foo: 'bar'};
  277. opts.follow_set_cookies = false;
  278. send_request(opts, function(err, resp) {
  279. should.not.exist(spies.http.args[0][0].headers['cookie']);
  280. done();
  281. })
  282. })
  283. it('resends cookies if follow_set_cookies is true', function(done) {
  284. opts.cookies = {foo: 'bar'};
  285. opts.follow_set_cookies = true;
  286. send_request(opts, function(err, resp) {
  287. spies.http.args[0][0].headers['cookie'].should.eql('foo=bar')
  288. done();
  289. })
  290. })
  291. })
  292. })
  293. describe('and if_same_host is false', function() {
  294. before(function() {
  295. opts = { follow_if_same_host: false };
  296. })
  297. // by default it will follow other protocols
  298. describe('and redirected to same path on another domain, same protocol', function() {
  299. before(function() {
  300. location = url.replace(host, hostname);
  301. })
  302. it('follows redirect', followed_same_protocol);
  303. it('does not resend cookies even if follow_set_cookies is true', function(done) {
  304. opts.cookies = {foo: 'bar'};
  305. opts.follow_set_cookies = true;
  306. send_request(opts, function(err, resp) {
  307. should.not.exist(spies.http.args[0][0].headers['cookie']);
  308. done();
  309. })
  310. })
  311. })
  312. })
  313. describe('and if_same_host is true', function() {
  314. before(function() {
  315. opts = { follow_if_same_host: true };
  316. })
  317. // by default it will follow other protocols
  318. describe('and redirected to same path on another domain, same protocol', function() {
  319. before(function() {
  320. location = url.replace(host, hostname);
  321. })
  322. it('does not follow redirect', not_followed);
  323. })
  324. })
  325. describe('and if_same_protocol is false', function() {
  326. before(function() {
  327. opts = { follow_if_same_protocol: false };
  328. })
  329. // by default it will follow other hosts
  330. describe('and redirected to same path on another domain, different protocol', function() {
  331. before(function() {
  332. location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
  333. })
  334. it('follows redirect', followed_other_protocol);
  335. it('does not resend cookies even if follow_set_cookies is true', function(done) {
  336. opts.cookies = {foo: 'bar'};
  337. opts.follow_set_cookies = true;
  338. send_request(opts, function(err, resp) {
  339. should.not.exist(spies.http.args[0][0].headers['cookie']);
  340. done();
  341. })
  342. })
  343. })
  344. })
  345. describe('and if_same_protocol is true', function() {
  346. before(function() {
  347. opts = { follow_if_same_protocol: true };
  348. })
  349. // by default it will follow other hosts
  350. describe('and redirected to same path on another domain, different protocol', function() {
  351. before(function() {
  352. location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
  353. })
  354. it('does not follow redirect', not_followed);
  355. })
  356. })
  357. })
  358. })
  359. });