Simple email application for Android. Original source code: https://framagit.org/dystopia-project/simple-email
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.

697 lines
18 KiB

  1. /**
  2. * Module dependencies.
  3. */
  4. var fs = require('fs');
  5. var url = require('url');
  6. var net = require('net');
  7. var tls = require('tls');
  8. var http = require('http');
  9. var https = require('https');
  10. var WebSocket = require('ws');
  11. var assert = require('assert');
  12. var events = require('events');
  13. var inherits = require('util').inherits;
  14. var Agent = require('../');
  15. var PassthroughAgent = Agent(function(req, opts) {
  16. return opts.secureEndpoint ? https.globalAgent : http.globalAgent;
  17. });
  18. describe('Agent', function() {
  19. describe('subclass', function() {
  20. it('should be subclassable', function(done) {
  21. function MyAgent() {
  22. Agent.call(this);
  23. }
  24. inherits(MyAgent, Agent);
  25. MyAgent.prototype.callback = function(req, opts, fn) {
  26. assert.equal(req.path, '/foo');
  27. assert.equal(req.getHeader('host'), '127.0.0.1:1234');
  28. assert.equal(opts.secureEndpoint, true);
  29. done();
  30. };
  31. var info = url.parse('https://127.0.0.1:1234/foo');
  32. info.agent = new MyAgent();
  33. https.get(info);
  34. });
  35. });
  36. describe('options', function() {
  37. it('should support an options Object as first argument', function() {
  38. var agent = new Agent({ timeout: 1000 });
  39. assert.equal(1000, agent.timeout);
  40. });
  41. it('should support an options Object as second argument', function() {
  42. var agent = new Agent(function() {}, { timeout: 1000 });
  43. assert.equal(1000, agent.timeout);
  44. });
  45. it('should be mixed in with HTTP request options', function(done) {
  46. var agent = new Agent({
  47. host: 'my-proxy.com',
  48. port: 3128,
  49. foo: 'bar'
  50. });
  51. agent.callback = function(req, opts, fn) {
  52. assert.equal('bar', opts.foo);
  53. assert.equal('a', opts.b);
  54. // `host` and `port` are special-cases, and should always be
  55. // overwritten in the request `opts` inside the agent-base callback
  56. assert.equal('localhost', opts.host);
  57. assert.equal(80, opts.port);
  58. done();
  59. };
  60. var opts = {
  61. b: 'a',
  62. agent: agent
  63. };
  64. http.get(opts);
  65. });
  66. });
  67. describe('`this` context', function() {
  68. it('should be the Agent instance', function(done) {
  69. var called = false;
  70. var agent = new Agent();
  71. agent.callback = function() {
  72. called = true;
  73. assert.equal(this, agent);
  74. };
  75. var info = url.parse('http://127.0.0.1/foo');
  76. info.agent = agent;
  77. var req = http.get(info);
  78. req.on('error', function(err) {
  79. assert(/no Duplex stream was returned/.test(err.message));
  80. done();
  81. });
  82. });
  83. it('should be the Agent instance with callback signature', function(done) {
  84. var called = false;
  85. var agent = new Agent();
  86. agent.callback = function(req, opts, fn) {
  87. called = true;
  88. assert.equal(this, agent);
  89. fn();
  90. };
  91. var info = url.parse('http://127.0.0.1/foo');
  92. info.agent = agent;
  93. var req = http.get(info);
  94. req.on('error', function(err) {
  95. assert(/no Duplex stream was returned/.test(err.message));
  96. done();
  97. });
  98. });
  99. });
  100. describe('"error" event', function() {
  101. it('should be invoked on `http.ClientRequest` instance if `callback()` has not been defined', function(
  102. done
  103. ) {
  104. var agent = new Agent();
  105. var info = url.parse('http://127.0.0.1/foo');
  106. info.agent = agent;
  107. var req = http.get(info);
  108. req.on('error', function(err) {
  109. assert.equal(
  110. '"agent-base" has no default implementation, you must subclass and override `callback()`',
  111. err.message
  112. );
  113. done();
  114. });
  115. });
  116. it('should be invoked on `http.ClientRequest` instance if Error passed to callback function on the first tick', function(
  117. done
  118. ) {
  119. var agent = new Agent(function(req, opts, fn) {
  120. fn(new Error('is this caught?'));
  121. });
  122. var info = url.parse('http://127.0.0.1/foo');
  123. info.agent = agent;
  124. var req = http.get(info);
  125. req.on('error', function(err) {
  126. assert.equal('is this caught?', err.message);
  127. done();
  128. });
  129. });
  130. it('should be invoked on `http.ClientRequest` instance if Error passed to callback function after the first tick', function(
  131. done
  132. ) {
  133. var agent = new Agent(function(req, opts, fn) {
  134. setTimeout(function() {
  135. fn(new Error('is this caught?'));
  136. }, 10);
  137. });
  138. var info = url.parse('http://127.0.0.1/foo');
  139. info.agent = agent;
  140. var req = http.get(info);
  141. req.on('error', function(err) {
  142. assert.equal('is this caught?', err.message);
  143. done();
  144. });
  145. });
  146. });
  147. describe('artificial "streams"', function() {
  148. it('should send a GET request', function(done) {
  149. var stream = new events.EventEmitter();
  150. // needed for the `http` module to call .write() on the stream
  151. stream.writable = true;
  152. stream.write = function(str) {
  153. assert(0 == str.indexOf('GET / HTTP/1.1'));
  154. done();
  155. };
  156. // needed for `http` module in Node.js 4
  157. stream.cork = function() {};
  158. var opts = {
  159. method: 'GET',
  160. host: '127.0.0.1',
  161. path: '/',
  162. port: 80,
  163. agent: new Agent(function(req, opts, fn) {
  164. fn(null, stream);
  165. })
  166. };
  167. var req = http.request(opts);
  168. req.end();
  169. });
  170. it('should receive a GET response', function(done) {
  171. var stream = new events.EventEmitter();
  172. var opts = {
  173. method: 'GET',
  174. host: '127.0.0.1',
  175. path: '/',
  176. port: 80,
  177. agent: new Agent(function(req, opts, fn) {
  178. fn(null, stream);
  179. })
  180. };
  181. var req = http.request(opts, function(res) {
  182. assert.equal('0.9', res.httpVersion);
  183. assert.equal(111, res.statusCode);
  184. assert.equal('bar', res.headers.foo);
  185. done();
  186. });
  187. // have to wait for the "socket" event since `http.ClientRequest`
  188. // doesn't *actually* attach the listeners to the "stream" until
  189. // this happens
  190. req.once('socket', function() {
  191. var buf = new Buffer(
  192. 'HTTP/0.9 111\r\n' +
  193. 'Foo: bar\r\n' +
  194. 'Set-Cookie: 1\r\n' +
  195. 'Set-Cookie: 2\r\n\r\n'
  196. );
  197. stream.emit('data', buf);
  198. });
  199. req.end();
  200. });
  201. });
  202. });
  203. describe('"http" module', function() {
  204. var server;
  205. var port;
  206. // setup test HTTP server
  207. before(function(done) {
  208. server = http.createServer();
  209. server.listen(0, function() {
  210. port = server.address().port;
  211. done();
  212. });
  213. });
  214. // shut down test HTTP server
  215. after(function(done) {
  216. server.once('close', function() {
  217. done();
  218. });
  219. server.close();
  220. });
  221. it('should work for basic HTTP requests', function(done) {
  222. var called = false;
  223. var agent = new Agent(function(req, opts, fn) {
  224. called = true;
  225. var socket = net.connect(opts);
  226. fn(null, socket);
  227. });
  228. // add HTTP server "request" listener
  229. var gotReq = false;
  230. server.once('request', function(req, res) {
  231. gotReq = true;
  232. res.setHeader('X-Foo', 'bar');
  233. res.setHeader('X-Url', req.url);
  234. res.end();
  235. });
  236. var info = url.parse('http://127.0.0.1:' + port + '/foo');
  237. info.agent = agent;
  238. http.get(info, function(res) {
  239. assert.equal('bar', res.headers['x-foo']);
  240. assert.equal('/foo', res.headers['x-url']);
  241. assert(gotReq);
  242. assert(called);
  243. done();
  244. });
  245. });
  246. it('should support direct return in `connect()`', function(done) {
  247. var called = false;
  248. var agent = new Agent(function(req, opts) {
  249. called = true;
  250. return net.connect(opts);
  251. });
  252. // add HTTP server "request" listener
  253. var gotReq = false;
  254. server.once('request', function(req, res) {
  255. gotReq = true;
  256. res.setHeader('X-Foo', 'bar');
  257. res.setHeader('X-Url', req.url);
  258. res.end();
  259. });
  260. var info = url.parse('http://127.0.0.1:' + port + '/foo');
  261. info.agent = agent;
  262. http.get(info, function(res) {
  263. assert.equal('bar', res.headers['x-foo']);
  264. assert.equal('/foo', res.headers['x-url']);
  265. assert(gotReq);
  266. assert(called);
  267. done();
  268. });
  269. });
  270. it('should support returning a Promise in `connect()`', function(done) {
  271. var called = false;
  272. var agent = new Agent(function(req, opts) {
  273. return new Promise(function(resolve, reject) {
  274. called = true;
  275. resolve(net.connect(opts));
  276. });
  277. });
  278. // add HTTP server "request" listener
  279. var gotReq = false;
  280. server.once('request', function(req, res) {
  281. gotReq = true;
  282. res.setHeader('X-Foo', 'bar');
  283. res.setHeader('X-Url', req.url);
  284. res.end();
  285. });
  286. var info = url.parse('http://127.0.0.1:' + port + '/foo');
  287. info.agent = agent;
  288. http.get(info, function(res) {
  289. assert.equal('bar', res.headers['x-foo']);
  290. assert.equal('/foo', res.headers['x-url']);
  291. assert(gotReq);
  292. assert(called);
  293. done();
  294. });
  295. });
  296. it('should set the `Connection: close` response header', function(done) {
  297. var called = false;
  298. var agent = new Agent(function(req, opts, fn) {
  299. called = true;
  300. var socket = net.connect(opts);
  301. fn(null, socket);
  302. });
  303. // add HTTP server "request" listener
  304. var gotReq = false;
  305. server.once('request', function(req, res) {
  306. gotReq = true;
  307. res.setHeader('X-Url', req.url);
  308. assert.equal('close', req.headers.connection);
  309. res.end();
  310. });
  311. var info = url.parse('http://127.0.0.1:' + port + '/bar');
  312. info.agent = agent;
  313. http.get(info, function(res) {
  314. assert.equal('/bar', res.headers['x-url']);
  315. assert.equal('close', res.headers.connection);
  316. assert(gotReq);
  317. assert(called);
  318. done();
  319. });
  320. });
  321. it('should pass through options from `http.request()`', function(done) {
  322. var agent = new Agent(function(req, opts, fn) {
  323. assert.equal('google.com', opts.host);
  324. assert.equal('bar', opts.foo);
  325. done();
  326. });
  327. http.get({
  328. host: 'google.com',
  329. foo: 'bar',
  330. agent: agent
  331. });
  332. });
  333. it('should default to port 80', function(done) {
  334. var agent = new Agent(function(req, opts, fn) {
  335. assert.equal(80, opts.port);
  336. done();
  337. });
  338. // (probably) not hitting a real HTTP server here,
  339. // so no need to add a httpServer request listener
  340. http.get({
  341. host: '127.0.0.1',
  342. path: '/foo',
  343. agent: agent
  344. });
  345. });
  346. it('should support the "timeout" option', function(done) {
  347. // ensure we timeout after the "error" event had a chance to trigger
  348. this.timeout(1000);
  349. this.slow(800);
  350. var agent = new Agent(
  351. function(req, opts, fn) {
  352. // this function will time out
  353. },
  354. { timeout: 100 }
  355. );
  356. var opts = url.parse('http://nodejs.org');
  357. opts.agent = agent;
  358. var req = http.get(opts);
  359. req.once('error', function(err) {
  360. assert.equal('ETIMEOUT', err.code);
  361. req.abort();
  362. done();
  363. });
  364. });
  365. it('should free sockets after use', function(done) {
  366. var agent = new Agent(function(req, opts, fn) {
  367. var socket = net.connect(opts);
  368. fn(null, socket);
  369. });
  370. // add HTTP server "request" listener
  371. var gotReq = false;
  372. server.once('request', function(req, res) {
  373. gotReq = true;
  374. res.end();
  375. });
  376. var info = url.parse('http://127.0.0.1:' + port + '/foo');
  377. info.agent = agent;
  378. http.get(info, function(res) {
  379. res.socket.emit('free');
  380. assert.equal(true, res.socket.destroyed);
  381. assert(gotReq);
  382. done();
  383. });
  384. });
  385. describe('PassthroughAgent', function() {
  386. it('should pass through to `http.globalAgent`', function(done) {
  387. // add HTTP server "request" listener
  388. var gotReq = false;
  389. server.once('request', function(req, res) {
  390. gotReq = true;
  391. res.setHeader('X-Foo', 'bar');
  392. res.setHeader('X-Url', req.url);
  393. res.end();
  394. });
  395. var info = url.parse('http://127.0.0.1:' + port + '/foo');
  396. info.agent = PassthroughAgent;
  397. http.get(info, function(res) {
  398. assert.equal('bar', res.headers['x-foo']);
  399. assert.equal('/foo', res.headers['x-url']);
  400. assert(gotReq);
  401. done();
  402. });
  403. });
  404. });
  405. });
  406. describe('"https" module', function() {
  407. var server;
  408. var port;
  409. // setup test HTTPS server
  410. before(function(done) {
  411. var options = {
  412. key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'),
  413. cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem')
  414. };
  415. server = https.createServer(options);
  416. server.listen(0, function() {
  417. port = server.address().port;
  418. done();
  419. });
  420. });
  421. // shut down test HTTP server
  422. after(function(done) {
  423. server.once('close', function() {
  424. done();
  425. });
  426. server.close();
  427. });
  428. it('should not modify the passed in Options object', function(done) {
  429. var called = false;
  430. var agent = new Agent(function(req, opts, fn) {
  431. called = true;
  432. assert.equal(true, opts.secureEndpoint);
  433. assert.equal(443, opts.port);
  434. assert.equal('localhost', opts.host);
  435. });
  436. var opts = { agent: agent };
  437. var req = https.request(opts);
  438. assert.equal(true, called);
  439. assert.equal(false, 'secureEndpoint' in opts);
  440. assert.equal(false, 'port' in opts);
  441. done();
  442. });
  443. it('should work with a String URL', function(done) {
  444. var endpoint = 'https://127.0.0.1:' + port;
  445. var req = https.get(endpoint);
  446. // it's gonna error out since `rejectUnauthorized` is not being passed in
  447. req.on('error', function(err) {
  448. assert.equal(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT');
  449. done();
  450. });
  451. });
  452. it('should work for basic HTTPS requests', function(done) {
  453. var called = false;
  454. var agent = new Agent(function(req, opts, fn) {
  455. called = true;
  456. assert(opts.secureEndpoint);
  457. var socket = tls.connect(opts);
  458. fn(null, socket);
  459. });
  460. // add HTTPS server "request" listener
  461. var gotReq = false;
  462. server.once('request', function(req, res) {
  463. gotReq = true;
  464. res.setHeader('X-Foo', 'bar');
  465. res.setHeader('X-Url', req.url);
  466. res.end();
  467. });
  468. var info = url.parse('https://127.0.0.1:' + port + '/foo');
  469. info.agent = agent;
  470. info.rejectUnauthorized = false;
  471. https.get(info, function(res) {
  472. assert.equal('bar', res.headers['x-foo']);
  473. assert.equal('/foo', res.headers['x-url']);
  474. assert(gotReq);
  475. assert(called);
  476. done();
  477. });
  478. });
  479. it('should pass through options from `https.request()`', function(done) {
  480. var agent = new Agent(function(req, opts, fn) {
  481. assert.equal('google.com', opts.host);
  482. assert.equal('bar', opts.foo);
  483. done();
  484. });
  485. https.get({
  486. host: 'google.com',
  487. foo: 'bar',
  488. agent: agent
  489. });
  490. });
  491. it('should default to port 443', function(done) {
  492. var agent = new Agent(function(req, opts, fn) {
  493. assert.equal(true, opts.secureEndpoint);
  494. assert.equal(false, opts.rejectUnauthorized);
  495. assert.equal(443, opts.port);
  496. done();
  497. });
  498. // (probably) not hitting a real HTTPS server here,
  499. // so no need to add a httpsServer request listener
  500. https.get({
  501. host: '127.0.0.1',
  502. path: '/foo',
  503. agent: agent,
  504. rejectUnauthorized: false
  505. });
  506. });
  507. describe('PassthroughAgent', function() {
  508. it('should pass through to `https.globalAgent`', function(done) {
  509. // add HTTP server "request" listener
  510. var gotReq = false;
  511. server.once('request', function(req, res) {
  512. gotReq = true;
  513. res.setHeader('X-Foo', 'bar');
  514. res.setHeader('X-Url', req.url);
  515. res.end();
  516. });
  517. var info = url.parse('https://127.0.0.1:' + port + '/foo');
  518. info.agent = PassthroughAgent;
  519. info.rejectUnauthorized = false;
  520. https.get(info, function(res) {
  521. assert.equal('bar', res.headers['x-foo']);
  522. assert.equal('/foo', res.headers['x-url']);
  523. assert(gotReq);
  524. done();
  525. });
  526. });
  527. });
  528. });
  529. describe('"ws" server', function() {
  530. var wss;
  531. var server;
  532. var port;
  533. // setup test HTTP server
  534. before(function(done) {
  535. server = http.createServer();
  536. wss = new WebSocket.Server({ server: server });
  537. server.listen(0, function() {
  538. port = server.address().port;
  539. done();
  540. });
  541. });
  542. // shut down test HTTP server
  543. after(function(done) {
  544. server.once('close', function() {
  545. done();
  546. });
  547. server.close();
  548. });
  549. it('should work for basic WebSocket connections', function(done) {
  550. function onconnection(ws) {
  551. ws.on('message', function(data) {
  552. assert.equal('ping', data);
  553. ws.send('pong');
  554. });
  555. }
  556. wss.on('connection', onconnection);
  557. var agent = new Agent(function(req, opts, fn) {
  558. var socket = net.connect(opts);
  559. fn(null, socket);
  560. });
  561. var client = new WebSocket('ws://127.0.0.1:' + port + '/', {
  562. agent: agent
  563. });
  564. client.on('open', function() {
  565. client.send('ping');
  566. });
  567. client.on('message', function(data) {
  568. assert.equal('pong', data);
  569. client.close();
  570. wss.removeListener('connection', onconnection);
  571. done();
  572. });
  573. });
  574. });
  575. describe('"wss" server', function() {
  576. var wss;
  577. var server;
  578. var port;
  579. // setup test HTTP server
  580. before(function(done) {
  581. var options = {
  582. key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'),
  583. cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem')
  584. };
  585. server = https.createServer(options);
  586. wss = new WebSocket.Server({ server: server });
  587. server.listen(0, function() {
  588. port = server.address().port;
  589. done();
  590. });
  591. });
  592. // shut down test HTTP server
  593. after(function(done) {
  594. server.once('close', function() {
  595. done();
  596. });
  597. server.close();
  598. });
  599. it('should work for secure WebSocket connections', function(done) {
  600. function onconnection(ws) {
  601. ws.on('message', function(data) {
  602. assert.equal('ping', data);
  603. ws.send('pong');
  604. });
  605. }
  606. wss.on('connection', onconnection);
  607. var agent = new Agent(function(req, opts, fn) {
  608. var socket = tls.connect(opts);
  609. fn(null, socket);
  610. });
  611. var client = new WebSocket('wss://127.0.0.1:' + port + '/', {
  612. agent: agent,
  613. rejectUnauthorized: false
  614. });
  615. client.on('open', function() {
  616. client.send('ping');
  617. });
  618. client.on('message', function(data) {
  619. assert.equal('pong', data);
  620. client.close();
  621. wss.removeListener('connection', onconnection);
  622. done();
  623. });
  624. });
  625. });