Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. 'use strict';
  2. var assert = require('chai').assert;
  3. var sinon = require('sinon');
  4. var assign = require('lodash/assign');
  5. var pick = require('lodash/pick');
  6. // Deliberate: node and 3rd party modules before global-tunnel
  7. var EventEmitter = require('events').EventEmitter;
  8. var net = require('net');
  9. var tls = require('tls');
  10. var http = require('http');
  11. var globalHttpAgent = http.globalAgent;
  12. var https = require('https');
  13. var globalHttpsAgent = https.globalAgent;
  14. var request = require('request');
  15. // Deliberate: load after all 3rd party modules
  16. var globalTunnel = require('../index');
  17. function newFakeAgent() {
  18. var fakeAgent = {
  19. addRequest: sinon.stub()
  20. };
  21. return fakeAgent;
  22. }
  23. // This function replaces 'host' by 'hostname' in the options for http.request()
  24. // background: http.request() allows to use either 'host' or 'hostname' to be used,
  25. // both needs to be tested
  26. function replaceHostByHostname(useHostname, options) {
  27. if (useHostname) {
  28. options.hostname = options.host;
  29. delete options.host;
  30. }
  31. return options;
  32. }
  33. var origEnv;
  34. function saveEnv() {
  35. origEnv = process.env.http_proxy;
  36. delete process.env.http_proxy;
  37. }
  38. function restoreEnv() {
  39. if (origEnv !== undefined) {
  40. process.env.http_proxy = origEnv; // eslint-disable-line camelcase
  41. }
  42. }
  43. describe('global-proxy', function() {
  44. // Save and restore http_proxy environment variable (yes, it's lower-case by
  45. // convention).
  46. before(saveEnv);
  47. after(restoreEnv);
  48. // Sinon setup & teardown
  49. var sandbox;
  50. var origHttpCreateConnection;
  51. before(function() {
  52. sandbox = sinon.createSandbox();
  53. sandbox.stub(globalHttpAgent, 'addRequest');
  54. sandbox.stub(globalHttpsAgent, 'addRequest');
  55. assert.equal(http.Agent.prototype.addRequest, https.Agent.prototype.addRequest);
  56. sandbox.spy(http.Agent.prototype, 'addRequest');
  57. sandbox.stub(net, 'createConnection').callsFake(function() {
  58. return new EventEmitter();
  59. });
  60. sandbox.stub(tls, 'connect').callsFake(function() {
  61. return new EventEmitter();
  62. });
  63. // This is needed as at some point Node HTTP aggent implementation started
  64. // plucking the createConnection method from the `net` module
  65. // instead of doing `net.createConnection`
  66. origHttpCreateConnection = http.Agent.prototype.createConnection;
  67. http.Agent.prototype.createConnection = net.createConnection;
  68. });
  69. afterEach(function() {
  70. sandbox.resetHistory();
  71. });
  72. after(function() {
  73. sandbox.restore();
  74. http.Agent.prototype.createConnection = origHttpCreateConnection;
  75. });
  76. describe('invalid configs', function() {
  77. it('requires a host', function() {
  78. var conf = { host: null, port: 1234 };
  79. assert.throws(function() {
  80. globalTunnel.initialize(conf);
  81. }, 'upstream proxy host is required');
  82. globalTunnel.end();
  83. });
  84. it('requires a port', function() {
  85. var conf = { host: '10.2.3.4', port: 0 };
  86. assert.throws(function() {
  87. globalTunnel.initialize(conf);
  88. }, 'upstream proxy port is required');
  89. globalTunnel.end();
  90. });
  91. it('clamps tunnel types', function() {
  92. var conf = { host: '10.2.3.4', port: 1234, connect: 'INVALID' };
  93. assert.throws(function() {
  94. globalTunnel.initialize(conf);
  95. }, 'valid connect options are "neither", "https", or "both"');
  96. globalTunnel.end();
  97. });
  98. });
  99. describe('exposed config', function() {
  100. afterEach(function() {
  101. globalTunnel.end();
  102. });
  103. it('has the same params as the passed config', function() {
  104. var conf = {
  105. host: 'proxy.com',
  106. port: 1234,
  107. proxyAuth: 'user:pwd',
  108. protocol: 'https:'
  109. };
  110. globalTunnel.initialize(conf);
  111. assert.deepEqual(
  112. conf,
  113. pick(globalTunnel.proxyConfig, ['host', 'port', 'proxyAuth', 'protocol'])
  114. );
  115. });
  116. it('has the expected defaults', function() {
  117. var conf = { host: 'proxy.com', port: 1234, proxyAuth: 'user:pwd' };
  118. globalTunnel.initialize(conf);
  119. assert.equal(globalTunnel.proxyConfig.protocol, 'http:');
  120. });
  121. });
  122. describe('stringified config', function() {
  123. afterEach(function() {
  124. globalTunnel.end();
  125. });
  126. it('has the same params as the passed config', function() {
  127. var conf = {
  128. host: 'proxy.com',
  129. port: 1234,
  130. proxyAuth: 'user:pwd',
  131. protocol: 'https'
  132. };
  133. globalTunnel.initialize(conf);
  134. assert.equal(globalTunnel.proxyUrl, 'https://user:pwd@proxy.com:1234');
  135. });
  136. it('encodes url', function() {
  137. var conf = {
  138. host: 'proxy.com',
  139. port: 1234,
  140. proxyAuth: 'user:4P@S$W0_r-D',
  141. protocol: 'https'
  142. };
  143. globalTunnel.initialize(conf);
  144. assert.equal(globalTunnel.proxyUrl, 'https://user:4P%40S%24W0_r-D@proxy.com:1234');
  145. });
  146. });
  147. function proxyEnabledTests(testParams) {
  148. function connected(innerProto) {
  149. var innerSecure = innerProto === 'https:';
  150. var called;
  151. if (testParams.isHttpsProxy) {
  152. called = tls.connect;
  153. sinon.assert.notCalled(net.createConnection);
  154. } else {
  155. called = net.createConnection;
  156. sinon.assert.notCalled(tls.connect);
  157. }
  158. sinon.assert.calledOnce(called);
  159. if (typeof called.getCall(0).args[0] === 'object') {
  160. sinon.assert.calledWith(called, sinon.match.has('port', testParams.port));
  161. sinon.assert.calledWith(called, sinon.match.has('host', '10.2.3.4'));
  162. } else {
  163. sinon.assert.calledWith(called, testParams.port, '10.2.3.4');
  164. }
  165. var isCONNECT =
  166. testParams.connect === 'both' || (innerSecure && testParams.connect === 'https');
  167. if (isCONNECT) {
  168. var expectConnect = 'example.dev:' + (innerSecure ? 443 : 80);
  169. var whichAgent = innerSecure ? https.globalAgent : http.globalAgent;
  170. sinon.assert.calledOnce(whichAgent.request);
  171. sinon.assert.calledWith(whichAgent.request, sinon.match.has('method', 'CONNECT'));
  172. sinon.assert.calledWith(
  173. whichAgent.request,
  174. sinon.match.has('path', expectConnect)
  175. );
  176. } else {
  177. sinon.assert.calledOnce(http.Agent.prototype.addRequest);
  178. var req = http.Agent.prototype.addRequest.getCall(0).args[0];
  179. var method = req.method;
  180. assert.equal(method, 'GET');
  181. var path = req.path;
  182. if (innerSecure) {
  183. assert.match(path, new RegExp('^https://example\\.dev:443/'));
  184. } else {
  185. assert.match(path, new RegExp('^http://example\\.dev:80/'));
  186. }
  187. }
  188. }
  189. var localSandbox;
  190. beforeEach(function() {
  191. localSandbox = sinon.createSandbox();
  192. if (testParams.connect === 'both') {
  193. localSandbox.spy(http.globalAgent, 'request');
  194. }
  195. if (testParams.connect !== 'neither') {
  196. localSandbox.spy(https.globalAgent, 'request');
  197. }
  198. });
  199. afterEach(function() {
  200. localSandbox.restore();
  201. });
  202. it('(got proxying set up)', function() {
  203. assert.isTrue(globalTunnel.isProxying);
  204. });
  205. describe('with the request library', function() {
  206. it('will proxy http requests', function(done) {
  207. assert.isTrue(globalTunnel.isProxying);
  208. var dummyCb = sinon.stub();
  209. request.get('http://example.dev/', dummyCb);
  210. setImmediate(function() {
  211. connected('http:');
  212. sinon.assert.notCalled(globalHttpAgent.addRequest);
  213. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  214. done();
  215. });
  216. });
  217. it('will proxy https requests', function(done) {
  218. assert.isTrue(globalTunnel.isProxying);
  219. var dummyCb = sinon.stub();
  220. request.get('https://example.dev/', dummyCb);
  221. setImmediate(function() {
  222. connected('https:');
  223. sinon.assert.notCalled(globalHttpAgent.addRequest);
  224. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  225. done();
  226. });
  227. });
  228. });
  229. describe('using raw request interface', function() {
  230. function rawRequest(useHostname) {
  231. var req = http.request(
  232. replaceHostByHostname(useHostname, {
  233. method: 'GET',
  234. path: '/raw-http',
  235. host: 'example.dev'
  236. }),
  237. function() {}
  238. );
  239. req.end();
  240. connected('http:');
  241. sinon.assert.notCalled(globalHttpAgent.addRequest);
  242. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  243. }
  244. it('will proxy http requests (`host`)', function() {
  245. rawRequest(false);
  246. });
  247. it('will proxy http requests (`hostname`)', function() {
  248. rawRequest(true);
  249. });
  250. it('will proxy https requests', function() {
  251. var req = https.request(
  252. replaceHostByHostname(false, {
  253. method: 'GET',
  254. path: '/raw-https',
  255. host: 'example.dev'
  256. }),
  257. function() {}
  258. );
  259. req.end();
  260. connected('https:');
  261. sinon.assert.notCalled(globalHttpAgent.addRequest);
  262. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  263. });
  264. it('request respects explicit agent param', function() {
  265. var agent = newFakeAgent();
  266. var req = http.request(
  267. replaceHostByHostname(false, {
  268. method: 'GET',
  269. path: '/raw-http-w-agent',
  270. host: 'example.dev',
  271. agent: agent
  272. }),
  273. function() {}
  274. );
  275. req.end();
  276. sinon.assert.notCalled(globalHttpAgent.addRequest);
  277. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  278. sinon.assert.notCalled(net.createConnection);
  279. sinon.assert.notCalled(tls.connect);
  280. sinon.assert.calledOnce(agent.addRequest);
  281. });
  282. describe('request with `null` agent and defined `createConnection`', function() {
  283. before(function() {
  284. sinon.stub(http.ClientRequest.prototype, 'onSocket');
  285. });
  286. after(function() {
  287. http.ClientRequest.prototype.onSocket.restore();
  288. });
  289. function noAgent(useHostname) {
  290. var createConnection = sinon.stub();
  291. var req = http.request(
  292. replaceHostByHostname(useHostname, {
  293. method: 'GET',
  294. path: '/no-agent',
  295. host: 'example.dev',
  296. agent: null,
  297. createConnection: createConnection
  298. }),
  299. function() {} // eslint-disable-line max-nested-callbacks
  300. );
  301. req.end();
  302. sinon.assert.notCalled(globalHttpAgent.addRequest);
  303. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  304. sinon.assert.calledOnce(createConnection);
  305. }
  306. it('uses no agent (`host`)', function() {
  307. noAgent(false);
  308. });
  309. it('uses no agent (`hostname`)', function() {
  310. noAgent(true);
  311. });
  312. });
  313. });
  314. }
  315. function enabledBlock(conf, testParams) {
  316. before(function() {
  317. globalTunnel.initialize(conf);
  318. });
  319. after(function() {
  320. globalTunnel.end();
  321. });
  322. testParams = assign(
  323. {
  324. port: conf && conf.port,
  325. isHttpsProxy: conf && conf.protocol === 'https:',
  326. connect: (conf && conf.connect) || 'https'
  327. },
  328. testParams
  329. );
  330. proxyEnabledTests(testParams);
  331. }
  332. describe('with http proxy in intercept mode', function() {
  333. enabledBlock({
  334. connect: 'neither',
  335. protocol: 'http:',
  336. host: '10.2.3.4',
  337. port: 3333
  338. });
  339. });
  340. describe('with https proxy in intercept mode', function() {
  341. enabledBlock({
  342. connect: 'neither',
  343. protocol: 'https:',
  344. host: '10.2.3.4',
  345. port: 3334
  346. });
  347. });
  348. describe('with http proxy in CONNECT mode', function() {
  349. enabledBlock({
  350. connect: 'both',
  351. protocol: 'http:',
  352. host: '10.2.3.4',
  353. port: 3335
  354. });
  355. });
  356. describe('with https proxy in CONNECT mode', function() {
  357. enabledBlock({
  358. connect: 'both',
  359. protocol: 'https:',
  360. host: '10.2.3.4',
  361. port: 3336
  362. });
  363. });
  364. describe('with http proxy in mixed mode', function() {
  365. enabledBlock({
  366. protocol: 'http:',
  367. host: '10.2.3.4',
  368. port: 3337
  369. });
  370. });
  371. describe('with https proxy in mixed mode', function() {
  372. enabledBlock({
  373. protocol: 'https:',
  374. host: '10.2.3.4',
  375. port: 3338
  376. });
  377. });
  378. describe('using env var', function() {
  379. after(function() {
  380. delete process.env.http_proxy;
  381. assert.isUndefined(process.env.http_proxy);
  382. });
  383. describe('for http', function() {
  384. before(function() {
  385. process.env.http_proxy = 'http://10.2.3.4:1234'; // eslint-disable-line camelcase
  386. });
  387. enabledBlock(null, { isHttpsProxy: false, connect: 'https', port: 1234 });
  388. });
  389. describe('for https', function() {
  390. before(function() {
  391. process.env.http_proxy = 'https://10.2.3.4:1235'; // eslint-disable-line camelcase
  392. });
  393. enabledBlock(null, { isHttpsProxy: true, connect: 'https', port: 1235 });
  394. });
  395. });
  396. describe('using npm config', function() {
  397. var expectedProxy = { isHttpsProxy: false, connect: 'https', port: 1234 };
  398. var npmConfig = { get: function() {} };
  399. var npmConfigStub = sinon.stub(npmConfig, 'get');
  400. function configNpm(key, value) {
  401. return function() {
  402. global.__GLOBAL_TUNNEL_DEPENDENCY_NPMCONF__ = function() {
  403. return npmConfig;
  404. };
  405. npmConfigStub.withArgs(key).returns(value || 'http://10.2.3.4:1234');
  406. };
  407. }
  408. after(function() {
  409. global.__GLOBAL_TUNNEL_DEPENDENCY_NPMCONF__ = undefined;
  410. });
  411. describe('https-proxy', function() {
  412. before(configNpm('https-proxy'));
  413. enabledBlock(null, expectedProxy);
  414. });
  415. describe('http-proxy', function() {
  416. before(configNpm('http-proxy'));
  417. enabledBlock(null, expectedProxy);
  418. });
  419. describe('proxy', function() {
  420. before(configNpm('proxy'));
  421. enabledBlock(null, expectedProxy);
  422. });
  423. describe('order', function() {
  424. before(function() {
  425. configNpm('proxy')();
  426. configNpm('https-proxy', 'http://10.2.3.4:12345')();
  427. configNpm('http-proxy')();
  428. });
  429. enabledBlock(null, { isHttpsProxy: false, connect: 'https', port: 12345 });
  430. });
  431. describe('also using env var', function() {
  432. before(function() {
  433. configNpm('proxy')();
  434. process.env.http_proxy = 'http://10.2.3.4:1234'; // eslint-disable-line camelcase
  435. });
  436. after(function() {
  437. delete process.env.http_proxy;
  438. });
  439. enabledBlock(null, expectedProxy);
  440. });
  441. });
  442. // Deliberately after the block above
  443. describe('with proxy disabled', function() {
  444. it('claims to be disabled', function() {
  445. assert.isFalse(globalTunnel.isProxying);
  446. });
  447. it('will NOT proxy http requests', function(done) {
  448. var dummyCb = sinon.stub();
  449. request.get('http://example.dev/', dummyCb);
  450. setImmediate(function() {
  451. sinon.assert.calledOnce(globalHttpAgent.addRequest);
  452. sinon.assert.notCalled(globalHttpsAgent.addRequest);
  453. done();
  454. });
  455. });
  456. it('will NOT proxy https requests', function(done) {
  457. var dummyCb = sinon.stub();
  458. request.get('https://example.dev/', dummyCb);
  459. setImmediate(function() {
  460. sinon.assert.notCalled(globalHttpAgent.addRequest);
  461. sinon.assert.calledOnce(globalHttpsAgent.addRequest);
  462. done();
  463. });
  464. });
  465. });
  466. });