WebsocketServer.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. "use strict";
  2. const WebSocket = require("ws");
  3. const BaseServer = require("./BaseServer");
  4. /** @typedef {import("../Server").WebSocketServerConfiguration} WebSocketServerConfiguration */
  5. /** @typedef {import("../Server").ClientConnection} ClientConnection */
  6. module.exports = class WebsocketServer extends BaseServer {
  7. static heartbeatInterval = 1000;
  8. /**
  9. * @param {import("../Server")} server server
  10. */
  11. constructor(server) {
  12. super(server);
  13. /** @type {import("ws").ServerOptions} */
  14. const options = {
  15. .../** @type {WebSocketServerConfiguration} */
  16. (this.server.options.webSocketServer).options,
  17. clientTracking: false,
  18. };
  19. const isNoServerMode =
  20. typeof options.port === "undefined" &&
  21. typeof options.server === "undefined";
  22. if (isNoServerMode) {
  23. options.noServer = true;
  24. }
  25. this.implementation = new WebSocket.Server(options);
  26. /** @type {import("http").Server} */
  27. (this.server.server).on(
  28. "upgrade",
  29. /**
  30. * @param {import("http").IncomingMessage} req request
  31. * @param {import("stream").Duplex} sock socket
  32. * @param {Buffer} head head
  33. */
  34. (req, sock, head) => {
  35. if (!this.implementation.shouldHandle(req)) {
  36. return;
  37. }
  38. this.implementation.handleUpgrade(req, sock, head, (connection) => {
  39. this.implementation.emit("connection", connection, req);
  40. });
  41. },
  42. );
  43. this.implementation.on(
  44. "error",
  45. /**
  46. * @param {Error} err error
  47. */
  48. (err) => {
  49. this.server.logger.error(err.message);
  50. },
  51. );
  52. const interval = setInterval(() => {
  53. for (const client of this.clients) {
  54. if (client.isAlive === false) {
  55. client.terminate();
  56. continue;
  57. }
  58. client.isAlive = false;
  59. client.ping(() => {});
  60. }
  61. }, WebsocketServer.heartbeatInterval);
  62. this.implementation.on(
  63. "connection",
  64. /**
  65. * @param {ClientConnection} client client
  66. */
  67. (client) => {
  68. this.clients.push(client);
  69. client.isAlive = true;
  70. client.on("pong", () => {
  71. client.isAlive = true;
  72. });
  73. client.on("close", () => {
  74. this.clients.splice(this.clients.indexOf(client), 1);
  75. });
  76. // TODO: add a test case for this - https://github.com/webpack/webpack-dev-server/issues/5018
  77. client.on(
  78. "error",
  79. /**
  80. * @param {Error} err err
  81. */
  82. (err) => {
  83. this.server.logger.error(err.message);
  84. },
  85. );
  86. },
  87. );
  88. this.implementation.on("close", () => {
  89. clearInterval(interval);
  90. });
  91. }
  92. };