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.

1103 lines
35 KiB

  1. var http = require('http')
  2. , https = require('https')
  3. , WebSocket = require('../')
  4. , WebSocketServer = WebSocket.Server
  5. , fs = require('fs')
  6. , should = require('should');
  7. var port = 8000;
  8. function getArrayBuffer(buf) {
  9. var l = buf.length;
  10. var arrayBuf = new ArrayBuffer(l);
  11. for (var i = 0; i < l; ++i) {
  12. arrayBuf[i] = buf[i];
  13. }
  14. return arrayBuf;
  15. }
  16. function areArraysEqual(x, y) {
  17. if (x.length != y.length) return false;
  18. for (var i = 0, l = x.length; i < l; ++i) {
  19. if (x[i] !== y[i]) return false;
  20. }
  21. return true;
  22. }
  23. describe('WebSocketServer', function() {
  24. describe('#ctor', function() {
  25. it('throws an error if no option object is passed', function() {
  26. var gotException = false;
  27. try {
  28. var wss = new WebSocketServer();
  29. }
  30. catch (e) {
  31. gotException = true;
  32. }
  33. gotException.should.be.ok;
  34. });
  35. it('throws an error if no port or server is specified', function() {
  36. var gotException = false;
  37. try {
  38. var wss = new WebSocketServer({});
  39. }
  40. catch (e) {
  41. gotException = true;
  42. }
  43. gotException.should.be.ok;
  44. });
  45. it('does not throw an error if no port or server is specified, when the noServer option is true', function() {
  46. var gotException = false;
  47. try {
  48. var wss = new WebSocketServer({noServer: true});
  49. }
  50. catch (e) {
  51. gotException = true;
  52. }
  53. gotException.should.eql(false);
  54. });
  55. it('emits an error if http server bind fails', function(done) {
  56. var wss = new WebSocketServer({port: 1});
  57. wss.on('error', function() { done(); });
  58. });
  59. it('starts a server on a given port', function(done) {
  60. var wss = new WebSocketServer({port: ++port}, function() {
  61. var ws = new WebSocket('ws://localhost:' + port);
  62. });
  63. wss.on('connection', function(client) {
  64. wss.close();
  65. done();
  66. });
  67. });
  68. it('uses a precreated http server', function (done) {
  69. var srv = http.createServer();
  70. srv.listen(++port, function () {
  71. var wss = new WebSocketServer({server: srv});
  72. var ws = new WebSocket('ws://localhost:' + port);
  73. wss.on('connection', function(client) {
  74. wss.close();
  75. srv.close();
  76. done();
  77. });
  78. });
  79. });
  80. it('uses a precreated http server listening on unix socket', function (done) {
  81. var srv = http.createServer();
  82. var sockPath = '/tmp/ws_socket_'+new Date().getTime()+'.'+Math.floor(Math.random() * 1000);
  83. srv.listen(sockPath, function () {
  84. var wss = new WebSocketServer({server: srv});
  85. var ws = new WebSocket('ws+unix://'+sockPath);
  86. wss.on('connection', function(client) {
  87. wss.close();
  88. srv.close();
  89. done();
  90. });
  91. });
  92. });
  93. it('emits path specific connection event', function (done) {
  94. var srv = http.createServer();
  95. srv.listen(++port, function () {
  96. var wss = new WebSocketServer({server: srv});
  97. var ws = new WebSocket('ws://localhost:' + port+'/endpointName');
  98. wss.on('connection/endpointName', function(client) {
  99. wss.close();
  100. srv.close();
  101. done();
  102. });
  103. });
  104. });
  105. it('can have two different instances listening on the same http server with two different paths', function(done) {
  106. var srv = http.createServer();
  107. srv.listen(++port, function () {
  108. var wss1 = new WebSocketServer({server: srv, path: '/wss1'})
  109. , wss2 = new WebSocketServer({server: srv, path: '/wss2'});
  110. var doneCount = 0;
  111. wss1.on('connection', function(client) {
  112. wss1.close();
  113. if (++doneCount == 2) {
  114. srv.close();
  115. done();
  116. }
  117. });
  118. wss2.on('connection', function(client) {
  119. wss2.close();
  120. if (++doneCount == 2) {
  121. srv.close();
  122. done();
  123. }
  124. });
  125. var ws1 = new WebSocket('ws://localhost:' + port + '/wss1');
  126. var ws2 = new WebSocket('ws://localhost:' + port + '/wss2?foo=1');
  127. });
  128. });
  129. it('cannot have two different instances listening on the same http server with the same path', function(done) {
  130. var srv = http.createServer();
  131. srv.listen(++port, function () {
  132. var wss1 = new WebSocketServer({server: srv, path: '/wss1'});
  133. try {
  134. var wss2 = new WebSocketServer({server: srv, path: '/wss1'});
  135. }
  136. catch (e) {
  137. wss1.close();
  138. srv.close();
  139. done();
  140. }
  141. });
  142. });
  143. });
  144. describe('#close', function() {
  145. it('will close all clients', function(done) {
  146. var wss = new WebSocketServer({port: ++port}, function() {
  147. var ws = new WebSocket('ws://localhost:' + port);
  148. ws.on('close', function() {
  149. if (++closes == 2) done();
  150. });
  151. });
  152. var closes = 0;
  153. wss.on('connection', function(client) {
  154. client.on('close', function() {
  155. if (++closes == 2) done();
  156. });
  157. wss.close();
  158. });
  159. });
  160. it('does not close a precreated server', function(done) {
  161. var srv = http.createServer();
  162. var realClose = srv.close;
  163. srv.close = function() {
  164. should.fail('must not close pre-created server');
  165. }
  166. srv.listen(++port, function () {
  167. var wss = new WebSocketServer({server: srv});
  168. var ws = new WebSocket('ws://localhost:' + port);
  169. wss.on('connection', function(client) {
  170. wss.close();
  171. srv.close = realClose;
  172. srv.close();
  173. done();
  174. });
  175. });
  176. });
  177. it('cleans up websocket data on a precreated server', function(done) {
  178. var srv = http.createServer();
  179. srv.listen(++port, function () {
  180. var wss1 = new WebSocketServer({server: srv, path: '/wss1'})
  181. , wss2 = new WebSocketServer({server: srv, path: '/wss2'});
  182. (typeof srv._webSocketPaths).should.eql('object');
  183. Object.keys(srv._webSocketPaths).length.should.eql(2);
  184. wss1.close();
  185. Object.keys(srv._webSocketPaths).length.should.eql(1);
  186. wss2.close();
  187. (typeof srv._webSocketPaths).should.eql('undefined');
  188. srv.close();
  189. done();
  190. });
  191. });
  192. });
  193. describe('#clients', function() {
  194. it('returns a list of connected clients', function(done) {
  195. var wss = new WebSocketServer({port: ++port}, function() {
  196. wss.clients.length.should.eql(0);
  197. var ws = new WebSocket('ws://localhost:' + port);
  198. });
  199. wss.on('connection', function(client) {
  200. wss.clients.length.should.eql(1);
  201. wss.close();
  202. done();
  203. });
  204. });
  205. it('can be disabled', function(done) {
  206. var wss = new WebSocketServer({port: ++port, clientTracking: false}, function() {
  207. wss.clients.length.should.eql(0);
  208. var ws = new WebSocket('ws://localhost:' + port);
  209. });
  210. wss.on('connection', function(client) {
  211. wss.clients.length.should.eql(0);
  212. wss.close();
  213. done();
  214. });
  215. });
  216. it('is updated when client terminates the connection', function(done) {
  217. var ws;
  218. var wss = new WebSocketServer({port: ++port}, function() {
  219. ws = new WebSocket('ws://localhost:' + port);
  220. });
  221. wss.on('connection', function(client) {
  222. client.on('close', function() {
  223. wss.clients.length.should.eql(0);
  224. wss.close();
  225. done();
  226. });
  227. ws.terminate();
  228. });
  229. });
  230. it('is updated when client closes the connection', function(done) {
  231. var ws;
  232. var wss = new WebSocketServer({port: ++port}, function() {
  233. ws = new WebSocket('ws://localhost:' + port);
  234. });
  235. wss.on('connection', function(client) {
  236. client.on('close', function() {
  237. wss.clients.length.should.eql(0);
  238. wss.close();
  239. done();
  240. });
  241. ws.close();
  242. });
  243. });
  244. });
  245. describe('#options', function() {
  246. it('exposes options passed to constructor', function(done) {
  247. var wss = new WebSocketServer({port: ++port}, function() {
  248. wss.options.port.should.eql(port);
  249. wss.close();
  250. done();
  251. });
  252. });
  253. });
  254. describe('#handleUpgrade', function() {
  255. it('can be used for a pre-existing server', function (done) {
  256. var srv = http.createServer();
  257. srv.listen(++port, function () {
  258. var wss = new WebSocketServer({noServer: true});
  259. srv.on('upgrade', function(req, socket, upgradeHead) {
  260. wss.handleUpgrade(req, socket, upgradeHead, function(client) {
  261. client.send('hello');
  262. });
  263. });
  264. var ws = new WebSocket('ws://localhost:' + port);
  265. ws.on('message', function(message) {
  266. message.should.eql('hello');
  267. wss.close();
  268. srv.close();
  269. done();
  270. });
  271. });
  272. });
  273. });
  274. describe('hybi mode', function() {
  275. describe('connection establishing', function() {
  276. it('does not accept connections with no sec-websocket-key', function(done) {
  277. var wss = new WebSocketServer({port: ++port}, function() {
  278. var options = {
  279. port: port,
  280. host: '127.0.0.1',
  281. headers: {
  282. 'Connection': 'Upgrade',
  283. 'Upgrade': 'websocket'
  284. }
  285. };
  286. var req = http.request(options);
  287. req.end();
  288. req.on('response', function(res) {
  289. res.statusCode.should.eql(400);
  290. wss.close();
  291. done();
  292. });
  293. });
  294. wss.on('connection', function(ws) {
  295. done(new Error('connection must not be established'));
  296. });
  297. wss.on('error', function() {});
  298. });
  299. it('does not accept connections with no sec-websocket-version', function(done) {
  300. var wss = new WebSocketServer({port: ++port}, function() {
  301. var options = {
  302. port: port,
  303. host: '127.0.0.1',
  304. headers: {
  305. 'Connection': 'Upgrade',
  306. 'Upgrade': 'websocket',
  307. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ=='
  308. }
  309. };
  310. var req = http.request(options);
  311. req.end();
  312. req.on('response', function(res) {
  313. res.statusCode.should.eql(400);
  314. wss.close();
  315. done();
  316. });
  317. });
  318. wss.on('connection', function(ws) {
  319. done(new Error('connection must not be established'));
  320. });
  321. wss.on('error', function() {});
  322. });
  323. it('does not accept connections with invalid sec-websocket-version', function(done) {
  324. var wss = new WebSocketServer({port: ++port}, function() {
  325. var options = {
  326. port: port,
  327. host: '127.0.0.1',
  328. headers: {
  329. 'Connection': 'Upgrade',
  330. 'Upgrade': 'websocket',
  331. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  332. 'Sec-WebSocket-Version': 12
  333. }
  334. };
  335. var req = http.request(options);
  336. req.end();
  337. req.on('response', function(res) {
  338. res.statusCode.should.eql(400);
  339. wss.close();
  340. done();
  341. });
  342. });
  343. wss.on('connection', function(ws) {
  344. done(new Error('connection must not be established'));
  345. });
  346. wss.on('error', function() {});
  347. });
  348. it('client can be denied', function(done) {
  349. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  350. return false;
  351. }}, function() {
  352. var options = {
  353. port: port,
  354. host: '127.0.0.1',
  355. headers: {
  356. 'Connection': 'Upgrade',
  357. 'Upgrade': 'websocket',
  358. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  359. 'Sec-WebSocket-Version': 8,
  360. 'Sec-WebSocket-Origin': 'http://foobar.com'
  361. }
  362. };
  363. var req = http.request(options);
  364. req.end();
  365. req.on('response', function(res) {
  366. res.statusCode.should.eql(401);
  367. process.nextTick(function() {
  368. wss.close();
  369. done();
  370. });
  371. });
  372. });
  373. wss.on('connection', function(ws) {
  374. done(new Error('connection must not be established'));
  375. });
  376. wss.on('error', function() {});
  377. });
  378. it('client can be accepted', function(done) {
  379. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  380. return true;
  381. }}, function() {
  382. var options = {
  383. port: port,
  384. host: '127.0.0.1',
  385. headers: {
  386. 'Connection': 'Upgrade',
  387. 'Upgrade': 'websocket',
  388. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  389. 'Sec-WebSocket-Version': 13,
  390. 'Origin': 'http://foobar.com'
  391. }
  392. };
  393. var req = http.request(options);
  394. req.end();
  395. });
  396. wss.on('connection', function(ws) {
  397. ws.terminate();
  398. wss.close();
  399. done();
  400. });
  401. wss.on('error', function() {});
  402. });
  403. it('verifyClient gets client origin', function(done) {
  404. var verifyClientCalled = false;
  405. var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
  406. info.origin.should.eql('http://foobarbaz.com');
  407. verifyClientCalled = true;
  408. return false;
  409. }}, function() {
  410. var options = {
  411. port: port,
  412. host: '127.0.0.1',
  413. headers: {
  414. 'Connection': 'Upgrade',
  415. 'Upgrade': 'websocket',
  416. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  417. 'Sec-WebSocket-Version': 13,
  418. 'Origin': 'http://foobarbaz.com'
  419. }
  420. };
  421. var req = http.request(options);
  422. req.end();
  423. req.on('response', function(res) {
  424. verifyClientCalled.should.be.ok;
  425. wss.close();
  426. done();
  427. });
  428. });
  429. wss.on('error', function() {});
  430. });
  431. it('verifyClient gets original request', function(done) {
  432. var verifyClientCalled = false;
  433. var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
  434. info.req.headers['sec-websocket-key'].should.eql('dGhlIHNhbXBsZSBub25jZQ==');
  435. verifyClientCalled = true;
  436. return false;
  437. }}, function() {
  438. var options = {
  439. port: port,
  440. host: '127.0.0.1',
  441. headers: {
  442. 'Connection': 'Upgrade',
  443. 'Upgrade': 'websocket',
  444. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  445. 'Sec-WebSocket-Version': 13,
  446. 'Origin': 'http://foobarbaz.com'
  447. }
  448. };
  449. var req = http.request(options);
  450. req.end();
  451. req.on('response', function(res) {
  452. verifyClientCalled.should.be.ok;
  453. wss.close();
  454. done();
  455. });
  456. });
  457. wss.on('error', function() {});
  458. });
  459. it('verifyClient has secure:true for ssl connections', function(done) {
  460. var options = {
  461. key: fs.readFileSync('test/fixtures/key.pem'),
  462. cert: fs.readFileSync('test/fixtures/certificate.pem')
  463. };
  464. var app = https.createServer(options, function (req, res) {
  465. res.writeHead(200);
  466. res.end();
  467. });
  468. var success = false;
  469. var wss = new WebSocketServer({
  470. server: app,
  471. verifyClient: function(info) {
  472. success = info.secure === true;
  473. return true;
  474. }
  475. });
  476. app.listen(++port, function() {
  477. var ws = new WebSocket('wss://localhost:' + port);
  478. });
  479. wss.on('connection', function(ws) {
  480. app.close();
  481. ws.terminate();
  482. wss.close();
  483. success.should.be.ok;
  484. done();
  485. });
  486. });
  487. it('verifyClient has secure:false for non-ssl connections', function(done) {
  488. var app = http.createServer(function (req, res) {
  489. res.writeHead(200);
  490. res.end();
  491. });
  492. var success = false;
  493. var wss = new WebSocketServer({
  494. server: app,
  495. verifyClient: function(info) {
  496. success = info.secure === false;
  497. return true;
  498. }
  499. });
  500. app.listen(++port, function() {
  501. var ws = new WebSocket('ws://localhost:' + port);
  502. });
  503. wss.on('connection', function(ws) {
  504. app.close();
  505. ws.terminate();
  506. wss.close();
  507. success.should.be.ok;
  508. done();
  509. });
  510. });
  511. it('client can be denied asynchronously', function(done) {
  512. var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
  513. process.nextTick(function() {
  514. cb(false);
  515. });
  516. }}, function() {
  517. var options = {
  518. port: port,
  519. host: '127.0.0.1',
  520. headers: {
  521. 'Connection': 'Upgrade',
  522. 'Upgrade': 'websocket',
  523. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  524. 'Sec-WebSocket-Version': 8,
  525. 'Sec-WebSocket-Origin': 'http://foobar.com'
  526. }
  527. };
  528. var req = http.request(options);
  529. req.end();
  530. req.on('response', function(res) {
  531. res.statusCode.should.eql(401);
  532. process.nextTick(function() {
  533. wss.close();
  534. done();
  535. });
  536. });
  537. });
  538. wss.on('connection', function(ws) {
  539. done(new Error('connection must not be established'));
  540. });
  541. wss.on('error', function() {});
  542. });
  543. it('client can be accepted asynchronously', function(done) {
  544. var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
  545. process.nextTick(function() {
  546. cb(true);
  547. });
  548. }}, function() {
  549. var options = {
  550. port: port,
  551. host: '127.0.0.1',
  552. headers: {
  553. 'Connection': 'Upgrade',
  554. 'Upgrade': 'websocket',
  555. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  556. 'Sec-WebSocket-Version': 13,
  557. 'Origin': 'http://foobar.com'
  558. }
  559. };
  560. var req = http.request(options);
  561. req.end();
  562. });
  563. wss.on('connection', function(ws) {
  564. ws.terminate();
  565. wss.close();
  566. done();
  567. });
  568. wss.on('error', function() {});
  569. });
  570. it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
  571. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  572. return true;
  573. }}, function() {
  574. var options = {
  575. port: port,
  576. host: '127.0.0.1',
  577. headers: {
  578. 'Connection': 'Upgrade',
  579. 'Upgrade': 'websocket',
  580. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  581. 'Sec-WebSocket-Version': 13,
  582. 'Origin': 'http://foobar.com'
  583. }
  584. };
  585. var req = http.request(options);
  586. req.write(new Buffer([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], 'binary'));
  587. req.end();
  588. });
  589. wss.on('connection', function(ws) {
  590. ws.on('message', function(data) {
  591. data.should.eql('Hello');
  592. ws.terminate();
  593. wss.close();
  594. done();
  595. });
  596. });
  597. wss.on('error', function() {});
  598. });
  599. it('selects the first protocol by default', function(done) {
  600. var wss = new WebSocketServer({port: ++port}, function() {
  601. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
  602. ws.on('open', function(client) {
  603. ws.protocol.should.eql('prot1');
  604. wss.close();
  605. done();
  606. });
  607. });
  608. });
  609. it('selects the last protocol via protocol handler', function(done) {
  610. var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
  611. cb(true, ps[ps.length-1]); }}, function() {
  612. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
  613. ws.on('open', function(client) {
  614. ws.protocol.should.eql('prot2');
  615. wss.close();
  616. done();
  617. });
  618. });
  619. });
  620. it('client detects invalid server protocol', function(done) {
  621. var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
  622. cb(true, 'prot3'); }}, function() {
  623. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
  624. ws.on('open', function(client) {
  625. done(new Error('connection must not be established'));
  626. });
  627. ws.on('error', function() {
  628. done();
  629. });
  630. });
  631. });
  632. it('client detects no server protocol', function(done) {
  633. var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
  634. cb(true); }}, function() {
  635. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
  636. ws.on('open', function(client) {
  637. done(new Error('connection must not be established'));
  638. });
  639. ws.on('error', function() {
  640. done();
  641. });
  642. });
  643. });
  644. it('client refuses server protocols', function(done) {
  645. var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
  646. cb(false); }}, function() {
  647. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'});
  648. ws.on('open', function(client) {
  649. done(new Error('connection must not be established'));
  650. });
  651. ws.on('error', function() {
  652. done();
  653. });
  654. });
  655. });
  656. it('server detects invalid protocol handler', function(done) {
  657. var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) {
  658. // not calling callback is an error and shouldn't timeout
  659. }}, function() {
  660. var options = {
  661. port: port,
  662. host: '127.0.0.1',
  663. headers: {
  664. 'Connection': 'Upgrade',
  665. 'Upgrade': 'websocket',
  666. 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
  667. 'Sec-WebSocket-Version': 13,
  668. 'Sec-WebSocket-Origin': 'http://foobar.com'
  669. }
  670. };
  671. options.port = port;
  672. var req = http.request(options);
  673. req.end();
  674. req.on('response', function(res) {
  675. res.statusCode.should.eql(501);
  676. wss.close();
  677. done();
  678. });
  679. });
  680. wss.on('connection', function(ws) {
  681. done(new Error('connection must not be established'));
  682. });
  683. wss.on('error', function() {});
  684. });
  685. });
  686. describe('messaging', function() {
  687. it('can send and receive data', function(done) {
  688. var data = new Array(65*1024);
  689. for (var i = 0; i < data.length; ++i) {
  690. data[i] = String.fromCharCode(65 + ~~(25 * Math.random()));
  691. }
  692. data = data.join('');
  693. var wss = new WebSocketServer({port: ++port}, function() {
  694. var ws = new WebSocket('ws://localhost:' + port);
  695. ws.on('message', function(message, flags) {
  696. ws.send(message);
  697. });
  698. });
  699. wss.on('connection', function(client) {
  700. client.on('message', function(message) {
  701. message.should.eql(data);
  702. wss.close();
  703. done();
  704. });
  705. client.send(data);
  706. });
  707. });
  708. });
  709. });
  710. describe('hixie mode', function() {
  711. it('can be disabled', function(done) {
  712. var wss = new WebSocketServer({port: ++port, disableHixie: true}, function() {
  713. var options = {
  714. port: port,
  715. host: '127.0.0.1',
  716. headers: {
  717. 'Connection': 'Upgrade',
  718. 'Upgrade': 'WebSocket',
  719. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  720. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  721. }
  722. };
  723. var req = http.request(options);
  724. req.write('WjN}|M(6');
  725. req.end();
  726. req.on('response', function(res) {
  727. res.statusCode.should.eql(401);
  728. process.nextTick(function() {
  729. wss.close();
  730. done();
  731. });
  732. });
  733. });
  734. wss.on('connection', function(ws) {
  735. done(new Error('connection must not be established'));
  736. });
  737. wss.on('error', function() {});
  738. });
  739. describe('connection establishing', function() {
  740. it('does not accept connections with no sec-websocket-key1', function(done) {
  741. var wss = new WebSocketServer({port: ++port}, function() {
  742. var options = {
  743. port: port,
  744. host: '127.0.0.1',
  745. headers: {
  746. 'Connection': 'Upgrade',
  747. 'Upgrade': 'WebSocket',
  748. 'Sec-WebSocket-Key1': '3e6b263 4 17 80'
  749. }
  750. };
  751. var req = http.request(options);
  752. req.end();
  753. req.on('response', function(res) {
  754. res.statusCode.should.eql(400);
  755. wss.close();
  756. done();
  757. });
  758. });
  759. wss.on('connection', function(ws) {
  760. done(new Error('connection must not be established'));
  761. });
  762. wss.on('error', function() {});
  763. });
  764. it('does not accept connections with no sec-websocket-key2', function(done) {
  765. var wss = new WebSocketServer({port: ++port}, function() {
  766. var options = {
  767. port: port,
  768. host: '127.0.0.1',
  769. headers: {
  770. 'Connection': 'Upgrade',
  771. 'Upgrade': 'WebSocket',
  772. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  773. }
  774. };
  775. var req = http.request(options);
  776. req.end();
  777. req.on('response', function(res) {
  778. res.statusCode.should.eql(400);
  779. wss.close();
  780. done();
  781. });
  782. });
  783. wss.on('connection', function(ws) {
  784. done(new Error('connection must not be established'));
  785. });
  786. wss.on('error', function() {});
  787. });
  788. it('accepts connections with valid handshake', function(done) {
  789. var wss = new WebSocketServer({port: ++port}, function() {
  790. var options = {
  791. port: port,
  792. host: '127.0.0.1',
  793. headers: {
  794. 'Connection': 'Upgrade',
  795. 'Upgrade': 'WebSocket',
  796. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  797. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  798. }
  799. };
  800. var req = http.request(options);
  801. req.write('WjN}|M(6');
  802. req.end();
  803. });
  804. wss.on('connection', function(ws) {
  805. ws.terminate();
  806. wss.close();
  807. done();
  808. });
  809. wss.on('error', function() {});
  810. });
  811. it('client can be denied', function(done) {
  812. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  813. return false;
  814. }}, function() {
  815. var options = {
  816. port: port,
  817. host: '127.0.0.1',
  818. headers: {
  819. 'Connection': 'Upgrade',
  820. 'Upgrade': 'WebSocket',
  821. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  822. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  823. }
  824. };
  825. var req = http.request(options);
  826. req.write('WjN}|M(6');
  827. req.end();
  828. req.on('response', function(res) {
  829. res.statusCode.should.eql(401);
  830. process.nextTick(function() {
  831. wss.close();
  832. done();
  833. });
  834. });
  835. });
  836. wss.on('connection', function(ws) {
  837. done(new Error('connection must not be established'));
  838. });
  839. wss.on('error', function() {});
  840. });
  841. it('client can be accepted', function(done) {
  842. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  843. return true;
  844. }}, function() {
  845. var options = {
  846. port: port,
  847. host: '127.0.0.1',
  848. headers: {
  849. 'Connection': 'Upgrade',
  850. 'Upgrade': 'WebSocket',
  851. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  852. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  853. }
  854. };
  855. var req = http.request(options);
  856. req.write('WjN}|M(6');
  857. req.end();
  858. });
  859. wss.on('connection', function(ws) {
  860. ws.terminate();
  861. wss.close();
  862. done();
  863. });
  864. wss.on('error', function() {});
  865. });
  866. it('verifyClient gets client origin', function(done) {
  867. var verifyClientCalled = false;
  868. var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
  869. info.origin.should.eql('http://foobarbaz.com');
  870. verifyClientCalled = true;
  871. return false;
  872. }}, function() {
  873. var options = {
  874. port: port,
  875. host: '127.0.0.1',
  876. headers: {
  877. 'Connection': 'Upgrade',
  878. 'Upgrade': 'WebSocket',
  879. 'Origin': 'http://foobarbaz.com',
  880. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  881. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  882. }
  883. };
  884. var req = http.request(options);
  885. req.write('WjN}|M(6');
  886. req.end();
  887. req.on('response', function(res) {
  888. verifyClientCalled.should.be.ok;
  889. wss.close();
  890. done();
  891. });
  892. });
  893. wss.on('error', function() {});
  894. });
  895. it('verifyClient gets original request', function(done) {
  896. var verifyClientCalled = false;
  897. var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
  898. info.req.headers['sec-websocket-key1'].should.eql('3e6b263 4 17 80');
  899. verifyClientCalled = true;
  900. return false;
  901. }}, function() {
  902. var options = {
  903. port: port,
  904. host: '127.0.0.1',
  905. headers: {
  906. 'Connection': 'Upgrade',
  907. 'Upgrade': 'WebSocket',
  908. 'Origin': 'http://foobarbaz.com',
  909. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  910. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  911. }
  912. };
  913. var req = http.request(options);
  914. req.write('WjN}|M(6');
  915. req.end();
  916. req.on('response', function(res) {
  917. verifyClientCalled.should.be.ok;
  918. wss.close();
  919. done();
  920. });
  921. });
  922. wss.on('error', function() {});
  923. });
  924. it('client can be denied asynchronously', function(done) {
  925. var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
  926. cb(false);
  927. }}, function() {
  928. var options = {
  929. port: port,
  930. host: '127.0.0.1',
  931. headers: {
  932. 'Connection': 'Upgrade',
  933. 'Upgrade': 'WebSocket',
  934. 'Origin': 'http://foobarbaz.com',
  935. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  936. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  937. }
  938. };
  939. var req = http.request(options);
  940. req.write('WjN}|M(6');
  941. req.end();
  942. req.on('response', function(res) {
  943. res.statusCode.should.eql(401);
  944. process.nextTick(function() {
  945. wss.close();
  946. done();
  947. });
  948. });
  949. });
  950. wss.on('connection', function(ws) {
  951. done(new Error('connection must not be established'));
  952. });
  953. wss.on('error', function() {});
  954. });
  955. it('client can be accepted asynchronously', function(done) {
  956. var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
  957. cb(true);
  958. }}, function() {
  959. var options = {
  960. port: port,
  961. host: '127.0.0.1',
  962. headers: {
  963. 'Connection': 'Upgrade',
  964. 'Upgrade': 'WebSocket',
  965. 'Origin': 'http://foobarbaz.com',
  966. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  967. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
  968. }
  969. };
  970. var req = http.request(options);
  971. req.write('WjN}|M(6');
  972. req.end();
  973. });
  974. wss.on('connection', function(ws) {
  975. wss.close();
  976. done();
  977. });
  978. wss.on('error', function() {});
  979. });
  980. it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
  981. var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
  982. return true;
  983. }}, function() {
  984. var options = {
  985. port: port,
  986. host: '127.0.0.1',
  987. headers: {
  988. 'Connection': 'Upgrade',
  989. 'Upgrade': 'WebSocket',
  990. 'Sec-WebSocket-Key1': '3e6b263 4 17 80',
  991. 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90',
  992. 'Origin': 'http://foobar.com'
  993. }
  994. };
  995. var req = http.request(options);
  996. req.write('WjN}|M(6');
  997. req.write(new Buffer([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], 'binary'));
  998. req.end();
  999. });
  1000. wss.on('connection', function(ws) {
  1001. ws.on('message', function(data) {
  1002. data.should.eql('Hello');
  1003. ws.terminate();
  1004. wss.close();
  1005. done();
  1006. });
  1007. });
  1008. wss.on('error', function() {});
  1009. });
  1010. });
  1011. });
  1012. describe('client properties', function() {
  1013. it('protocol is exposed', function(done) {
  1014. var wss = new WebSocketServer({port: ++port}, function() {
  1015. var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'});
  1016. });
  1017. wss.on('connection', function(client) {
  1018. client.protocol.should.eql('hi');
  1019. wss.close();
  1020. done();
  1021. });
  1022. });
  1023. it('protocolVersion is exposed', function(done) {
  1024. var wss = new WebSocketServer({port: ++port}, function() {
  1025. var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8});
  1026. });
  1027. wss.on('connection', function(client) {
  1028. client.protocolVersion.should.eql(8);
  1029. wss.close();
  1030. done();
  1031. });
  1032. });
  1033. it('upgradeReq is the original request object', function(done) {
  1034. var wss = new WebSocketServer({port: ++port}, function() {
  1035. var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8});
  1036. });
  1037. wss.on('connection', function(client) {
  1038. client.upgradeReq.httpVersion.should.eql('1.1');
  1039. wss.close();
  1040. done();
  1041. });
  1042. });
  1043. });
  1044. });