index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  10. Object.defineProperty(o, "default", { enumerable: true, value: v });
  11. }) : function(o, v) {
  12. o["default"] = v;
  13. });
  14. var __importStar = (this && this.__importStar) || function (mod) {
  15. if (mod && mod.__esModule) return mod;
  16. var result = {};
  17. if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  18. __setModuleDefault(result, mod);
  19. return result;
  20. };
  21. var __importDefault = (this && this.__importDefault) || function (mod) {
  22. return (mod && mod.__esModule) ? mod : { "default": mod };
  23. };
  24. Object.defineProperty(exports, "__esModule", { value: true });
  25. exports.Namespace = exports.Socket = exports.Server = void 0;
  26. const http = require("http");
  27. const fs_1 = require("fs");
  28. const zlib_1 = require("zlib");
  29. const accepts = require("accepts");
  30. const stream_1 = require("stream");
  31. const path = require("path");
  32. const engine = require("engine.io");
  33. const client_1 = require("./client");
  34. const events_1 = require("events");
  35. const namespace_1 = require("./namespace");
  36. Object.defineProperty(exports, "Namespace", { enumerable: true, get: function () { return namespace_1.Namespace; } });
  37. const parent_namespace_1 = require("./parent-namespace");
  38. const socket_io_adapter_1 = require("socket.io-adapter");
  39. const parser = __importStar(require("socket.io-parser"));
  40. const debug_1 = __importDefault(require("debug"));
  41. const socket_1 = require("./socket");
  42. Object.defineProperty(exports, "Socket", { enumerable: true, get: function () { return socket_1.Socket; } });
  43. const debug = debug_1.default("socket.io:server");
  44. const clientVersion = require("../package.json").version;
  45. const dotMapRegex = /\.map/;
  46. class Server extends events_1.EventEmitter {
  47. constructor(srv, opts = {}) {
  48. super();
  49. /**
  50. * @private
  51. */
  52. this._nsps = new Map();
  53. this.parentNsps = new Map();
  54. if ("object" == typeof srv && srv instanceof Object && !srv.listen) {
  55. opts = srv;
  56. srv = null;
  57. }
  58. this.path(opts.path || "/socket.io");
  59. this.connectTimeout(opts.connectTimeout || 45000);
  60. this.serveClient(false !== opts.serveClient);
  61. this._parser = opts.parser || parser;
  62. this.encoder = new this._parser.Encoder();
  63. this.adapter(opts.adapter || socket_io_adapter_1.Adapter);
  64. this.sockets = this.of("/");
  65. this.opts = opts;
  66. if (srv)
  67. this.attach(srv);
  68. }
  69. serveClient(v) {
  70. if (!arguments.length)
  71. return this._serveClient;
  72. this._serveClient = v;
  73. return this;
  74. }
  75. /**
  76. * Executes the middleware for an incoming namespace not already created on the server.
  77. *
  78. * @param {String} name - name of incoming namespace
  79. * @param {Object} auth - the auth parameters
  80. * @param {Function} fn - callback
  81. *
  82. * @private
  83. */
  84. _checkNamespace(name, auth, fn) {
  85. if (this.parentNsps.size === 0)
  86. return fn(false);
  87. const keysIterator = this.parentNsps.keys();
  88. const run = () => {
  89. let nextFn = keysIterator.next();
  90. if (nextFn.done) {
  91. return fn(false);
  92. }
  93. nextFn.value(name, auth, (err, allow) => {
  94. if (err || !allow) {
  95. run();
  96. }
  97. else {
  98. fn(this.parentNsps.get(nextFn.value).createChild(name));
  99. }
  100. });
  101. };
  102. run();
  103. }
  104. path(v) {
  105. if (!arguments.length)
  106. return this._path;
  107. this._path = v.replace(/\/$/, "");
  108. const escapedPath = this._path.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
  109. this.clientPathRegex = new RegExp("^" +
  110. escapedPath +
  111. "/socket\\.io(\\.min|\\.msgpack\\.min)?\\.js(\\.map)?$");
  112. return this;
  113. }
  114. connectTimeout(v) {
  115. if (v === undefined)
  116. return this._connectTimeout;
  117. this._connectTimeout = v;
  118. return this;
  119. }
  120. adapter(v) {
  121. if (!arguments.length)
  122. return this._adapter;
  123. this._adapter = v;
  124. for (const nsp of this._nsps.values()) {
  125. nsp._initAdapter();
  126. }
  127. return this;
  128. }
  129. listen(srv, opts = {}) {
  130. return this.attach(srv, opts);
  131. }
  132. attach(srv, opts = {}) {
  133. if ("function" == typeof srv) {
  134. const msg = "You are trying to attach socket.io to an express " +
  135. "request handler function. Please pass a http.Server instance.";
  136. throw new Error(msg);
  137. }
  138. // handle a port as a string
  139. if (Number(srv) == srv) {
  140. srv = Number(srv);
  141. }
  142. if ("number" == typeof srv) {
  143. debug("creating http server and binding to %d", srv);
  144. const port = srv;
  145. srv = http.createServer((req, res) => {
  146. res.writeHead(404);
  147. res.end();
  148. });
  149. srv.listen(port);
  150. }
  151. // merge the options passed to the Socket.IO server
  152. Object.assign(opts, this.opts);
  153. // set engine.io path to `/socket.io`
  154. opts.path = opts.path || this._path;
  155. this.initEngine(srv, opts);
  156. return this;
  157. }
  158. /**
  159. * Initialize engine
  160. *
  161. * @param srv - the server to attach to
  162. * @param opts - options passed to engine.io
  163. * @private
  164. */
  165. initEngine(srv, opts) {
  166. // initialize engine
  167. debug("creating engine.io instance with opts %j", opts);
  168. this.eio = engine.attach(srv, opts);
  169. // attach static file serving
  170. if (this._serveClient)
  171. this.attachServe(srv);
  172. // Export http server
  173. this.httpServer = srv;
  174. // bind to engine events
  175. this.bind(this.eio);
  176. }
  177. /**
  178. * Attaches the static file serving.
  179. *
  180. * @param {Function|http.Server} srv http server
  181. * @private
  182. */
  183. attachServe(srv) {
  184. debug("attaching client serving req handler");
  185. const evs = srv.listeners("request").slice(0);
  186. srv.removeAllListeners("request");
  187. srv.on("request", (req, res) => {
  188. if (this.clientPathRegex.test(req.url)) {
  189. this.serve(req, res);
  190. }
  191. else {
  192. for (let i = 0; i < evs.length; i++) {
  193. evs[i].call(srv, req, res);
  194. }
  195. }
  196. });
  197. }
  198. /**
  199. * Handles a request serving of client source and map
  200. *
  201. * @param {http.IncomingMessage} req
  202. * @param {http.ServerResponse} res
  203. * @private
  204. */
  205. serve(req, res) {
  206. const filename = req.url.replace(this._path, "");
  207. const isMap = dotMapRegex.test(filename);
  208. const type = isMap ? "map" : "source";
  209. // Per the standard, ETags must be quoted:
  210. // https://tools.ietf.org/html/rfc7232#section-2.3
  211. const expectedEtag = '"' + clientVersion + '"';
  212. const etag = req.headers["if-none-match"];
  213. if (etag) {
  214. if (expectedEtag == etag) {
  215. debug("serve client %s 304", type);
  216. res.writeHead(304);
  217. res.end();
  218. return;
  219. }
  220. }
  221. debug("serve client %s", type);
  222. res.setHeader("Cache-Control", "public, max-age=0");
  223. res.setHeader("Content-Type", "application/" + (isMap ? "json" : "javascript"));
  224. res.setHeader("ETag", expectedEtag);
  225. if (!isMap) {
  226. res.setHeader("X-SourceMap", filename.substring(1) + ".map");
  227. }
  228. Server.sendFile(filename, req, res);
  229. }
  230. /**
  231. * @param filename
  232. * @param req
  233. * @param res
  234. * @private
  235. */
  236. static sendFile(filename, req, res) {
  237. const readStream = fs_1.createReadStream(path.join(__dirname, "../client-dist/", filename));
  238. const encoding = accepts(req).encodings(["br", "gzip", "deflate"]);
  239. const onError = err => {
  240. if (err) {
  241. res.end();
  242. }
  243. };
  244. switch (encoding) {
  245. case "br":
  246. res.writeHead(200, { "content-encoding": "br" });
  247. readStream.pipe(zlib_1.createBrotliCompress()).pipe(res);
  248. stream_1.pipeline(readStream, zlib_1.createBrotliCompress(), res, onError);
  249. break;
  250. case "gzip":
  251. res.writeHead(200, { "content-encoding": "gzip" });
  252. stream_1.pipeline(readStream, zlib_1.createGzip(), res, onError);
  253. break;
  254. case "deflate":
  255. res.writeHead(200, { "content-encoding": "deflate" });
  256. stream_1.pipeline(readStream, zlib_1.createDeflate(), res, onError);
  257. break;
  258. default:
  259. res.writeHead(200);
  260. stream_1.pipeline(readStream, res, onError);
  261. }
  262. }
  263. /**
  264. * Binds socket.io to an engine.io instance.
  265. *
  266. * @param {engine.Server} engine engine.io (or compatible) server
  267. * @return {Server} self
  268. * @public
  269. */
  270. bind(engine) {
  271. this.engine = engine;
  272. this.engine.on("connection", this.onconnection.bind(this));
  273. return this;
  274. }
  275. /**
  276. * Called with each incoming transport connection.
  277. *
  278. * @param {engine.Socket} conn
  279. * @return {Server} self
  280. * @private
  281. */
  282. onconnection(conn) {
  283. debug("incoming connection with id %s", conn.id);
  284. new client_1.Client(this, conn);
  285. return this;
  286. }
  287. /**
  288. * Looks up a namespace.
  289. *
  290. * @param {String|RegExp|Function} name nsp name
  291. * @param {Function} [fn] optional, nsp `connection` ev handler
  292. * @public
  293. */
  294. of(name, fn) {
  295. if (typeof name === "function" || name instanceof RegExp) {
  296. const parentNsp = new parent_namespace_1.ParentNamespace(this);
  297. debug("initializing parent namespace %s", parentNsp.name);
  298. if (typeof name === "function") {
  299. this.parentNsps.set(name, parentNsp);
  300. }
  301. else {
  302. this.parentNsps.set((nsp, conn, next) => next(null, name.test(nsp)), parentNsp);
  303. }
  304. if (fn) {
  305. // @ts-ignore
  306. parentNsp.on("connect", fn);
  307. }
  308. return parentNsp;
  309. }
  310. if (String(name)[0] !== "/")
  311. name = "/" + name;
  312. let nsp = this._nsps.get(name);
  313. if (!nsp) {
  314. debug("initializing namespace %s", name);
  315. nsp = new namespace_1.Namespace(this, name);
  316. this._nsps.set(name, nsp);
  317. }
  318. if (fn)
  319. nsp.on("connect", fn);
  320. return nsp;
  321. }
  322. /**
  323. * Closes server connection
  324. *
  325. * @param {Function} [fn] optional, called as `fn([err])` on error OR all conns closed
  326. * @public
  327. */
  328. close(fn) {
  329. for (const socket of this.sockets.sockets.values()) {
  330. socket._onclose("server shutting down");
  331. }
  332. this.engine.close();
  333. if (this.httpServer) {
  334. this.httpServer.close(fn);
  335. }
  336. else {
  337. fn && fn();
  338. }
  339. }
  340. /**
  341. * Sets up namespace middleware.
  342. *
  343. * @return {Server} self
  344. * @public
  345. */
  346. use(fn) {
  347. this.sockets.use(fn);
  348. return this;
  349. }
  350. /**
  351. * Targets a room when emitting.
  352. *
  353. * @param {String} name
  354. * @return {Server} self
  355. * @public
  356. */
  357. to(name) {
  358. this.sockets.to(name);
  359. return this;
  360. }
  361. /**
  362. * Targets a room when emitting.
  363. *
  364. * @param {String} name
  365. * @return {Server} self
  366. * @public
  367. */
  368. in(name) {
  369. this.sockets.in(name);
  370. return this;
  371. }
  372. /**
  373. * Sends a `message` event to all clients.
  374. *
  375. * @return {Server} self
  376. * @public
  377. */
  378. send(...args) {
  379. args.unshift("message");
  380. this.sockets.emit.apply(this.sockets, args);
  381. return this;
  382. }
  383. /**
  384. * Sends a `message` event to all clients.
  385. *
  386. * @return {Server} self
  387. * @public
  388. */
  389. write(...args) {
  390. args.unshift("message");
  391. this.sockets.emit.apply(this.sockets, args);
  392. return this;
  393. }
  394. /**
  395. * Gets a list of socket ids.
  396. *
  397. * @public
  398. */
  399. allSockets() {
  400. return this.sockets.allSockets();
  401. }
  402. /**
  403. * Sets the compress flag.
  404. *
  405. * @param {Boolean} compress - if `true`, compresses the sending data
  406. * @return {Server} self
  407. * @public
  408. */
  409. compress(compress) {
  410. this.sockets.compress(compress);
  411. return this;
  412. }
  413. /**
  414. * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to
  415. * receive messages (because of network slowness or other issues, or because they’re connected through long polling
  416. * and is in the middle of a request-response cycle).
  417. *
  418. * @return {Server} self
  419. * @public
  420. */
  421. get volatile() {
  422. this.sockets.volatile;
  423. return this;
  424. }
  425. /**
  426. * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
  427. *
  428. * @return {Server} self
  429. * @public
  430. */
  431. get local() {
  432. this.sockets.local;
  433. return this;
  434. }
  435. }
  436. exports.Server = Server;
  437. /**
  438. * Expose main namespace (/).
  439. */
  440. const emitterMethods = Object.keys(events_1.EventEmitter.prototype).filter(function (key) {
  441. return typeof events_1.EventEmitter.prototype[key] === "function";
  442. });
  443. emitterMethods.forEach(function (fn) {
  444. Server.prototype[fn] = function () {
  445. return this.sockets[fn].apply(this.sockets, arguments);
  446. };
  447. });
  448. module.exports = (srv, opts) => new Server(srv, opts);
  449. module.exports.Server = Server;