WebsocketServer.js 2.7 KB

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