@@ -1,24 +1,30 @@
|
||||
// import http = require("http");
|
||||
// import type { Server as HTTPSServer } from "https";
|
||||
// import type { Http2SecureServer } from "http2";
|
||||
// import { createReadStream } from "fs";
|
||||
// import { createDeflate, createGzip, createBrotliCompress } from "zlib";
|
||||
// import accepts = require("accepts");
|
||||
// import { pipeline } from "stream";
|
||||
// import path = require("path");
|
||||
import engine = require("../engine.io")
|
||||
import { Client } from './client'
|
||||
import { EventEmitter } from 'events'
|
||||
import {
|
||||
attach,
|
||||
Server as Engine,
|
||||
ServerOptions as EngineOptions,
|
||||
AttachOptions,
|
||||
// uServer,
|
||||
} from "../engine.io"
|
||||
import { Client } from "./client"
|
||||
import { EventEmitter } from "events"
|
||||
import { ExtendedError, Namespace, ServerReservedEventsMap } from "./namespace"
|
||||
import { ParentNamespace } from './parent-namespace'
|
||||
import { ParentNamespace } from "./parent-namespace"
|
||||
// import { Adapter, Room, SocketId } from "socket.io-adapter"
|
||||
import { Adapter, Room, SocketId } from "../socket.io-adapter"
|
||||
// import * as parser from "socket.io-parser";
|
||||
// import * as parser from "socket.io-parser"
|
||||
import * as parser from "../socket.io-parser"
|
||||
// import type { Encoder } from "socket.io-parser";
|
||||
// import type { Encoder } from "socket.io-parser"
|
||||
import type { Encoder } from "../socket.io-parser"
|
||||
// import debugModule from "debug";
|
||||
import { Socket } from './socket'
|
||||
// import type { CookieSerializeOptions } from "cookie";
|
||||
// import type { CorsOptions } from "cors";
|
||||
// import debugModule from "debug"
|
||||
import { Socket } from "./socket"
|
||||
import type { BroadcastOperator, RemoteSocket } from "./broadcast-operator"
|
||||
import {
|
||||
EventsMap,
|
||||
@@ -27,13 +33,14 @@ import {
|
||||
StrictEventEmitter,
|
||||
EventNames,
|
||||
} from "./typed-events"
|
||||
// import { patchAdapter, restoreAdapter, serveFile } from "./uws"
|
||||
|
||||
import type { Socket as EngineIOSocket } from '../engine.io/socket'
|
||||
// const debug = debugModule("socket.io:server")
|
||||
const debug = require('../debug')("socket.io:server")
|
||||
|
||||
// const clientVersion = require("../package.json").version
|
||||
// const dotMapRegex = /\.map/
|
||||
|
||||
// type Transport = "polling" | "websocket";
|
||||
type ParentNspNameMatchFn = (
|
||||
name: string,
|
||||
auth: { [key: string]: any },
|
||||
@@ -42,105 +49,7 @@ type ParentNspNameMatchFn = (
|
||||
|
||||
type AdapterConstructor = typeof Adapter | ((nsp: Namespace) => Adapter)
|
||||
|
||||
interface EngineOptions {
|
||||
/**
|
||||
* how many ms without a pong packet to consider the connection closed
|
||||
* @default 5000
|
||||
*/
|
||||
pingTimeout: number
|
||||
/**
|
||||
* how many ms before sending a new ping packet
|
||||
* @default 25000
|
||||
*/
|
||||
pingInterval: number
|
||||
/**
|
||||
* how many ms before an uncompleted transport upgrade is cancelled
|
||||
* @default 10000
|
||||
*/
|
||||
upgradeTimeout: number
|
||||
/**
|
||||
* how many bytes or characters a message can be, before closing the session (to avoid DoS).
|
||||
* @default 1e5 (100 KB)
|
||||
*/
|
||||
maxHttpBufferSize: number
|
||||
/**
|
||||
* A function that receives a given handshake or upgrade request as its first parameter,
|
||||
* and can decide whether to continue or not. The second argument is a function that needs
|
||||
* to be called with the decided information: fn(err, success), where success is a boolean
|
||||
* value where false means that the request is rejected, and err is an error code.
|
||||
*/
|
||||
// allowRequest: (
|
||||
// req: http.IncomingMessage,
|
||||
// fn: (err: string | null | undefined, success: boolean) => void
|
||||
// ) => void
|
||||
/**
|
||||
* the low-level transports that are enabled
|
||||
* @default ["polling", "websocket"]
|
||||
*/
|
||||
// transports: Transport[]
|
||||
/**
|
||||
* whether to allow transport upgrades
|
||||
* @default true
|
||||
*/
|
||||
allowUpgrades: boolean
|
||||
/**
|
||||
* parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
|
||||
* @default false
|
||||
*/
|
||||
perMessageDeflate: boolean | object
|
||||
/**
|
||||
* parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable.
|
||||
* @default true
|
||||
*/
|
||||
httpCompression: boolean | object
|
||||
/**
|
||||
* what WebSocket server implementation to use. Specified module must
|
||||
* conform to the ws interface (see ws module api docs). Default value is ws.
|
||||
* An alternative c++ addon is also available by installing uws module.
|
||||
*/
|
||||
wsEngine: string
|
||||
/**
|
||||
* an optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
|
||||
*/
|
||||
initialPacket: any
|
||||
/**
|
||||
* configuration of the cookie that contains the client sid to send as part of handshake response headers. This cookie
|
||||
* might be used for sticky-session. Defaults to not sending any cookie.
|
||||
* @default false
|
||||
*/
|
||||
// cookie: CookieSerializeOptions | boolean
|
||||
/**
|
||||
* the options that will be forwarded to the cors module
|
||||
*/
|
||||
// cors: CorsOptions
|
||||
/**
|
||||
* whether to enable compatibility with Socket.IO v2 clients
|
||||
* @default false
|
||||
*/
|
||||
allowEIO3: boolean
|
||||
}
|
||||
|
||||
interface AttachOptions {
|
||||
/**
|
||||
* name of the path to capture
|
||||
* @default "/engine.io"
|
||||
*/
|
||||
path: string
|
||||
/**
|
||||
* destroy unhandled upgrade requests
|
||||
* @default true
|
||||
*/
|
||||
destroyUpgrade: boolean
|
||||
/**
|
||||
* milliseconds after which unhandled requests are ended
|
||||
* @default 1000
|
||||
*/
|
||||
destroyUpgradeTimeout: number
|
||||
}
|
||||
|
||||
interface EngineAttachOptions extends EngineOptions, AttachOptions { }
|
||||
|
||||
interface ServerOptions extends EngineAttachOptions {
|
||||
interface ServerOptions extends EngineOptions, AttachOptions {
|
||||
/**
|
||||
* name of the path to capture
|
||||
* @default "/socket.io"
|
||||
@@ -155,6 +64,7 @@ interface ServerOptions extends EngineAttachOptions {
|
||||
* the adapter to use
|
||||
* @default the in-memory adapter (https://github.com/socketio/socket.io-adapter)
|
||||
*/
|
||||
// adapter: AdapterConstructor
|
||||
adapter: any
|
||||
/**
|
||||
* the parser to use
|
||||
@@ -168,31 +78,62 @@ interface ServerOptions extends EngineAttachOptions {
|
||||
connectTimeout: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a Socket.IO server.
|
||||
*
|
||||
* @example
|
||||
* import { Server } from "socket.io";
|
||||
*
|
||||
* const io = new Server();
|
||||
*
|
||||
* io.on("connection", (socket) => {
|
||||
* console.log(`socket ${socket.id} connected`);
|
||||
*
|
||||
* // send an event to the client
|
||||
* socket.emit("foo", "bar");
|
||||
*
|
||||
* socket.on("foobar", () => {
|
||||
* // an event was received from the client
|
||||
* });
|
||||
*
|
||||
* // upon disconnection
|
||||
* socket.on("disconnect", (reason) => {
|
||||
* console.log(`socket ${socket.id} disconnected due to ${reason}`);
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* io.listen(3000);
|
||||
*/
|
||||
export class Server<
|
||||
ListenEvents extends EventsMap = DefaultEventsMap,
|
||||
EmitEvents extends EventsMap = ListenEvents,
|
||||
ServerSideEvents extends EventsMap = DefaultEventsMap
|
||||
> extends StrictEventEmitter<
|
||||
ServerSideEvents extends EventsMap = DefaultEventsMap,
|
||||
SocketData = any
|
||||
> extends StrictEventEmitter<
|
||||
ServerSideEvents,
|
||||
EmitEvents,
|
||||
ServerReservedEventsMap<ListenEvents, EmitEvents, ServerSideEvents>
|
||||
> {
|
||||
ServerReservedEventsMap<
|
||||
ListenEvents,
|
||||
EmitEvents,
|
||||
ServerSideEvents,
|
||||
SocketData
|
||||
>
|
||||
> {
|
||||
public readonly sockets: Namespace<
|
||||
ListenEvents,
|
||||
EmitEvents,
|
||||
ServerSideEvents
|
||||
ServerSideEvents,
|
||||
SocketData
|
||||
>
|
||||
/**
|
||||
* A reference to the underlying Engine.IO server.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <code>
|
||||
* const clientsCount = io.engine.clientsCount;
|
||||
* </code>
|
||||
* @example
|
||||
* const clientsCount = io.engine.clientsCount;
|
||||
*
|
||||
*/
|
||||
public engine: any
|
||||
|
||||
/** @private */
|
||||
readonly _parser: typeof parser
|
||||
/** @private */
|
||||
@@ -201,28 +142,62 @@ export class Server<
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_nsps: Map<string, Namespace<ListenEvents, EmitEvents, ServerSideEvents>> =
|
||||
new Map();
|
||||
_nsps: Map<
|
||||
string,
|
||||
Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
|
||||
> = new Map();
|
||||
private parentNsps: Map<
|
||||
ParentNspNameMatchFn,
|
||||
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents>
|
||||
ParentNamespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
|
||||
> = new Map();
|
||||
private _adapter?: AdapterConstructor
|
||||
private _serveClient: boolean
|
||||
private opts: Partial<EngineOptions>
|
||||
private eio
|
||||
private eio: Engine
|
||||
private _path: string
|
||||
private clientPathRegex: RegExp
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_connectTimeout: number
|
||||
// private httpServer: http.Server | HTTPSServer | Http2SecureServer
|
||||
|
||||
// private httpServer: http.Server
|
||||
|
||||
constructor(srv: any, opts: Partial<ServerOptions> = {}) {
|
||||
/**
|
||||
* Server constructor.
|
||||
*
|
||||
* @param srv http server, port, or options
|
||||
* @param [opts]
|
||||
*/
|
||||
constructor(opts?: Partial<ServerOptions>)
|
||||
constructor(
|
||||
// srv?: http.Server | HTTPSServer | Http2SecureServer | number,
|
||||
srv?: any,
|
||||
opts?: Partial<ServerOptions>
|
||||
)
|
||||
constructor(
|
||||
srv:
|
||||
// | undefined
|
||||
// | Partial<ServerOptions>
|
||||
// | http.Server
|
||||
// | HTTPSServer
|
||||
// | Http2SecureServer
|
||||
// | number,
|
||||
any,
|
||||
opts?: Partial<ServerOptions>
|
||||
)
|
||||
constructor(
|
||||
srv:
|
||||
// | undefined
|
||||
// | Partial<ServerOptions>
|
||||
// | http.Server
|
||||
// | HTTPSServer
|
||||
// | Http2SecureServer
|
||||
// | number,
|
||||
any,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
) {
|
||||
super()
|
||||
if (!srv) { throw new Error('srv can\'t be undefiend!') }
|
||||
// if (
|
||||
// "object" === typeof srv &&
|
||||
// srv instanceof Object &&
|
||||
@@ -237,8 +212,12 @@ export class Server<
|
||||
this._parser = opts.parser || parser
|
||||
this.encoder = new this._parser.Encoder()
|
||||
this.adapter(opts.adapter || Adapter)
|
||||
this.sockets = this.of('/')
|
||||
// if (srv) this.attach(srv as http.Server);
|
||||
this.sockets = this.of("/")
|
||||
this.opts = opts
|
||||
// if (srv || typeof srv == "number")
|
||||
// this.attach(
|
||||
// srv as http.Server | HTTPSServer | Http2SecureServer | number
|
||||
// )
|
||||
this.attach(srv, this.opts)
|
||||
}
|
||||
|
||||
@@ -247,7 +226,6 @@ export class Server<
|
||||
*
|
||||
* @param v - whether to serve client code
|
||||
* @return self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public serveClient(v: boolean): this
|
||||
public serveClient(): boolean
|
||||
@@ -271,7 +249,9 @@ export class Server<
|
||||
name: string,
|
||||
auth: { [key: string]: any },
|
||||
fn: (
|
||||
nsp: Namespace<ListenEvents, EmitEvents, ServerSideEvents> | false
|
||||
nsp:
|
||||
| Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
|
||||
| false
|
||||
) => void
|
||||
): void {
|
||||
if (this.parentNsps.size === 0) return fn(false)
|
||||
@@ -285,15 +265,18 @@ export class Server<
|
||||
}
|
||||
nextFn.value(name, auth, (err, allow) => {
|
||||
if (err || !allow) {
|
||||
run()
|
||||
} else {
|
||||
const namespace = this.parentNsps
|
||||
.get(nextFn.value)!
|
||||
.createChild(name)
|
||||
// @ts-ignore
|
||||
this.sockets.emitReserved("new_namespace", namespace)
|
||||
fn(namespace)
|
||||
return run()
|
||||
}
|
||||
if (this._nsps.has(name)) {
|
||||
// the namespace was created in the meantime
|
||||
debug("dynamic namespace %s already exists", name)
|
||||
return fn(this._nsps.get(name) as Namespace)
|
||||
}
|
||||
const namespace = this.parentNsps.get(nextFn.value)!.createChild(name)
|
||||
debug("dynamic namespace %s was created", name)
|
||||
// @ts-ignore
|
||||
this.sockets.emitReserved("new_namespace", namespace)
|
||||
fn(namespace)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -305,7 +288,6 @@ export class Server<
|
||||
*
|
||||
* @param {String} v pathname
|
||||
* @return {Server|String} self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public path(v: string): this
|
||||
public path(): string
|
||||
@@ -319,7 +301,7 @@ export class Server<
|
||||
this.clientPathRegex = new RegExp(
|
||||
"^" +
|
||||
escapedPath +
|
||||
"/socket\\.io(\\.min|\\.msgpack\\.min)?\\.js(\\.map)?$"
|
||||
"/socket\\.io(\\.msgpack|\\.esm)?(\\.min)?\\.js(\\.map)?(?:\\?|$)"
|
||||
)
|
||||
return this
|
||||
}
|
||||
@@ -327,7 +309,6 @@ export class Server<
|
||||
/**
|
||||
* Set the delay after which a client without namespace is closed
|
||||
* @param v
|
||||
* @public
|
||||
*/
|
||||
public connectTimeout(v: number): this
|
||||
public connectTimeout(): number
|
||||
@@ -343,7 +324,6 @@ export class Server<
|
||||
*
|
||||
* @param v pathname
|
||||
* @return self when setting or value when getting
|
||||
* @public
|
||||
*/
|
||||
public adapter(): AdapterConstructor | undefined
|
||||
public adapter(v: AdapterConstructor): this
|
||||
@@ -364,14 +344,14 @@ export class Server<
|
||||
* @param srv - server or port
|
||||
* @param opts - options passed to engine.io
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public listen(
|
||||
srv: any,//http.Server | number,
|
||||
// srv: http.Server | HTTPSServer | Http2SecureServer | number,
|
||||
srv: any,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
): this {
|
||||
throw Error('Unsupport listen at MiaoScript Engine!')
|
||||
//return this.attach(srv, opts)
|
||||
// return this.attach(srv, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,10 +360,10 @@ export class Server<
|
||||
* @param srv - server or port
|
||||
* @param opts - options passed to engine.io
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public attach(
|
||||
srv: any,//http.Server | number,
|
||||
// srv: http.Server | HTTPSServer | Http2SecureServer | number,
|
||||
srv: any,
|
||||
opts: Partial<ServerOptions> = {}
|
||||
): this {
|
||||
// if ("function" == typeof srv) {
|
||||
@@ -418,6 +398,69 @@ export class Server<
|
||||
return this
|
||||
}
|
||||
|
||||
// public attachApp(app /*: TemplatedApp */, opts: Partial<ServerOptions> = {}) {
|
||||
// // merge the options passed to the Socket.IO server
|
||||
// Object.assign(opts, this.opts)
|
||||
// // set engine.io path to `/socket.io`
|
||||
// opts.path = opts.path || this._path
|
||||
|
||||
// // initialize engine
|
||||
// debug("creating uWebSockets.js-based engine with opts %j", opts)
|
||||
// const engine = new uServer(opts)
|
||||
|
||||
// engine.attach(app, opts)
|
||||
|
||||
// // bind to engine events
|
||||
// this.bind(engine)
|
||||
|
||||
// if (this._serveClient) {
|
||||
// // attach static file serving
|
||||
// app.get(`${this._path}/*`, (res, req) => {
|
||||
// if (!this.clientPathRegex.test(req.getUrl())) {
|
||||
// req.setYield(true)
|
||||
// return
|
||||
// }
|
||||
|
||||
// const filename = req
|
||||
// .getUrl()
|
||||
// .replace(this._path, "")
|
||||
// .replace(/\?.*$/, "")
|
||||
// .replace(/^\//, "")
|
||||
// const isMap = dotMapRegex.test(filename)
|
||||
// const type = isMap ? "map" : "source"
|
||||
|
||||
// // Per the standard, ETags must be quoted:
|
||||
// // https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
// const expectedEtag = '"' + clientVersion + '"'
|
||||
// const weakEtag = "W/" + expectedEtag
|
||||
|
||||
// const etag = req.getHeader("if-none-match")
|
||||
// if (etag) {
|
||||
// if (expectedEtag === etag || weakEtag === etag) {
|
||||
// debug("serve client %s 304", type)
|
||||
// res.writeStatus("304 Not Modified")
|
||||
// res.end()
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// debug("serve client %s", type)
|
||||
|
||||
// res.writeHeader("cache-control", "public, max-age=0")
|
||||
// res.writeHeader(
|
||||
// "content-type",
|
||||
// "application/" + (isMap ? "json" : "javascript")
|
||||
// )
|
||||
// res.writeHeader("etag", expectedEtag)
|
||||
|
||||
// const filepath = path.join(__dirname, "../client-dist/", filename)
|
||||
// serveFile(res, filepath)
|
||||
// })
|
||||
// }
|
||||
|
||||
// patchAdapter(app)
|
||||
// }
|
||||
|
||||
/**
|
||||
* Initialize engine
|
||||
*
|
||||
@@ -425,10 +468,14 @@ export class Server<
|
||||
* @param opts - options passed to engine.io
|
||||
* @private
|
||||
*/
|
||||
private initEngine(srv: any, opts: Partial<EngineAttachOptions>) {
|
||||
// // initialize engine
|
||||
console.debug("creating engine.io instance with opts", JSON.stringify(opts))
|
||||
this.eio = engine.attach(srv, opts)
|
||||
private initEngine(
|
||||
// srv: http.Server | HTTPSServer | Http2SecureServer,
|
||||
srv: any,
|
||||
opts: EngineOptions & AttachOptions
|
||||
): void {
|
||||
// initialize engine
|
||||
debug("creating engine.io instance with opts %j", opts)
|
||||
this.eio = attach(srv, opts)
|
||||
|
||||
// // attach static file serving
|
||||
// if (this._serveClient) this.attachServe(srv)
|
||||
@@ -446,13 +493,15 @@ export class Server<
|
||||
// * @param srv http server
|
||||
// * @private
|
||||
// */
|
||||
// private attachServe(srv: http.Server): void {
|
||||
// private attachServe(
|
||||
// srv: http.Server | HTTPSServer | Http2SecureServer
|
||||
// ): void {
|
||||
// debug("attaching client serving req handler")
|
||||
|
||||
// const evs = srv.listeners("request").slice(0)
|
||||
// srv.removeAllListeners("request")
|
||||
// srv.on("request", (req, res) => {
|
||||
// if (this.clientPathRegex.test(req.url)) {
|
||||
// if (this.clientPathRegex.test(req.url!)) {
|
||||
// this.serve(req, res)
|
||||
// } else {
|
||||
// for (let i = 0; i < evs.length; i++) {
|
||||
@@ -470,7 +519,7 @@ export class Server<
|
||||
// * @private
|
||||
// */
|
||||
// private serve(req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||
// const filename = req.url!.replace(this._path, "")
|
||||
// const filename = req.url!.replace(this._path, "").replace(/\?.*$/, "")
|
||||
// const isMap = dotMapRegex.test(filename)
|
||||
// const type = isMap ? "map" : "source"
|
||||
|
||||
@@ -547,11 +596,9 @@ export class Server<
|
||||
* Binds socket.io to an engine.io instance.
|
||||
*
|
||||
* @param {engine.Server} engine engine.io (or compatible) server
|
||||
* @return {Server} self
|
||||
* @public
|
||||
* @return self
|
||||
*/
|
||||
public bind(engine): Server {
|
||||
console.debug('engine.io', engine.constructor.name, 'bind to socket.io')
|
||||
public bind(engine): this {
|
||||
this.engine = engine
|
||||
this.engine.on("connection", this.onconnection.bind(this))
|
||||
return this
|
||||
@@ -561,12 +608,12 @@ export class Server<
|
||||
* Called with each incoming transport connection.
|
||||
*
|
||||
* @param {engine.Socket} conn
|
||||
* @return {Server} self
|
||||
* @return self
|
||||
* @private
|
||||
*/
|
||||
private onconnection(conn: EngineIOSocket): Server {
|
||||
console.debug(`socket.io index incoming connection with id ${conn.id}`)
|
||||
let client = new Client(this, conn)
|
||||
private onconnection(conn): this {
|
||||
debug("incoming connection with id %s", conn.id)
|
||||
const client = new Client(this, conn)
|
||||
if (conn.protocol === 3) {
|
||||
// @ts-ignore
|
||||
client.connect("/")
|
||||
@@ -577,17 +624,30 @@ export class Server<
|
||||
/**
|
||||
* Looks up a namespace.
|
||||
*
|
||||
* @param {String|RegExp|Function} name nsp name
|
||||
* @example
|
||||
* // with a simple string
|
||||
* const myNamespace = io.of("/my-namespace");
|
||||
*
|
||||
* // with a regex
|
||||
* const dynamicNsp = io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
|
||||
* const namespace = socket.nsp; // newNamespace.name === "/dynamic-101"
|
||||
*
|
||||
* // broadcast to all clients in the given sub-namespace
|
||||
* namespace.emit("hello");
|
||||
* });
|
||||
*
|
||||
* @param name - nsp name
|
||||
* @param fn optional, nsp `connection` ev handler
|
||||
* @public
|
||||
*/
|
||||
public of(
|
||||
name: string | RegExp | ParentNspNameMatchFn,
|
||||
fn?: (socket: Socket<ListenEvents, EmitEvents, ServerSideEvents>) => void
|
||||
): Namespace<ListenEvents, EmitEvents, ServerSideEvents> {
|
||||
fn?: (
|
||||
socket: Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData>
|
||||
) => void
|
||||
): Namespace<ListenEvents, EmitEvents, ServerSideEvents, SocketData> {
|
||||
if (typeof name === "function" || name instanceof RegExp) {
|
||||
const parentNsp = new ParentNamespace(this)
|
||||
console.debug(`initializing parent namespace ${parentNsp.name}`)
|
||||
debug("initializing parent namespace %s", parentNsp.name)
|
||||
if (typeof name === "function") {
|
||||
this.parentNsps.set(name, parentNsp)
|
||||
} else {
|
||||
@@ -607,7 +667,7 @@ export class Server<
|
||||
|
||||
let nsp = this._nsps.get(name)
|
||||
if (!nsp) {
|
||||
console.debug("initializing namespace", name)
|
||||
debug("initializing namespace %s", name)
|
||||
nsp = new Namespace(this, name)
|
||||
this._nsps.set(name, nsp)
|
||||
if (name !== "/") {
|
||||
@@ -623,7 +683,6 @@ export class Server<
|
||||
* Closes server connection
|
||||
*
|
||||
* @param [fn] optional, called as `fn([err])` on error OR all conns closed
|
||||
* @public
|
||||
*/
|
||||
public close(fn?: (err?: Error) => void): void {
|
||||
for (const socket of this.sockets.sockets.values()) {
|
||||
@@ -632,6 +691,9 @@ export class Server<
|
||||
|
||||
this.engine.close()
|
||||
|
||||
// // restore the Adapter prototype
|
||||
// restoreAdapter()
|
||||
|
||||
// if (this.httpServer) {
|
||||
// this.httpServer.close(fn)
|
||||
// } else {
|
||||
@@ -640,14 +702,19 @@ export class Server<
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up namespace middleware.
|
||||
* Registers a middleware, which is a function that gets executed for every incoming {@link Socket}.
|
||||
*
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* io.use((socket, next) => {
|
||||
* // ...
|
||||
* next();
|
||||
* });
|
||||
*
|
||||
* @param fn - the middleware function
|
||||
*/
|
||||
public use(
|
||||
fn: (
|
||||
socket: Socket<ListenEvents, EmitEvents, ServerSideEvents>,
|
||||
socket: Socket<ListenEvents, EmitEvents, ServerSideEvents, SocketData>,
|
||||
next: (err?: ExtendedError) => void
|
||||
) => void
|
||||
): this {
|
||||
@@ -658,41 +725,71 @@ export class Server<
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
*
|
||||
* @param room
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* // the “foo” event will be broadcast to all connected clients in the “room-101” room
|
||||
* io.to("room-101").emit("foo", "bar");
|
||||
*
|
||||
* // with an array of rooms (a client will be notified at most once)
|
||||
* io.to(["room-101", "room-102"]).emit("foo", "bar");
|
||||
*
|
||||
* // with multiple chained calls
|
||||
* io.to("room-101").to("room-102").emit("foo", "bar");
|
||||
*
|
||||
* @param room - a room, or an array of rooms
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public to(room: Room | Room[]): BroadcastOperator<EmitEvents> {
|
||||
public to(room: Room | Room[]) {
|
||||
return this.sockets.to(room)
|
||||
}
|
||||
|
||||
/**
|
||||
* Targets a room when emitting.
|
||||
* Targets a room when emitting. Similar to `to()`, but might feel clearer in some cases:
|
||||
*
|
||||
* @param room
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* // disconnect all clients in the "room-101" room
|
||||
* io.in("room-101").disconnectSockets();
|
||||
*
|
||||
* @param room - a room, or an array of rooms
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public in(room: Room | Room[]): BroadcastOperator<EmitEvents> {
|
||||
public in(room: Room | Room[]) {
|
||||
return this.sockets.in(room)
|
||||
}
|
||||
|
||||
/**
|
||||
* Excludes a room when emitting.
|
||||
*
|
||||
* @param name
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* // the "foo" event will be broadcast to all connected clients, except the ones that are in the "room-101" room
|
||||
* io.except("room-101").emit("foo", "bar");
|
||||
*
|
||||
* // with an array of rooms
|
||||
* io.except(["room-101", "room-102"]).emit("foo", "bar");
|
||||
*
|
||||
* // with multiple chained calls
|
||||
* io.except("room-101").except("room-102").emit("foo", "bar");
|
||||
*
|
||||
* @param room - a room, or an array of rooms
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public except(name: Room | Room[]): BroadcastOperator<EmitEvents> {
|
||||
return this.sockets.except(name)
|
||||
public except(room: Room | Room[]) {
|
||||
return this.sockets.except(room)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
*
|
||||
* This method mimics the WebSocket.send() method.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
|
||||
*
|
||||
* @example
|
||||
* io.send("hello");
|
||||
*
|
||||
* // this is equivalent to
|
||||
* io.emit("message", "hello");
|
||||
*
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public send(...args: EventParams<EmitEvents, "message">): this {
|
||||
this.sockets.emit("message", ...args)
|
||||
@@ -700,10 +797,9 @@ export class Server<
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a `message` event to all clients.
|
||||
* Sends a `message` event to all clients. Alias of {@link send}.
|
||||
*
|
||||
* @return self
|
||||
* @public
|
||||
*/
|
||||
public write(...args: EventParams<EmitEvents, "message">): this {
|
||||
this.sockets.emit("message", ...args)
|
||||
@@ -711,11 +807,30 @@ export class Server<
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a packet to other Socket.IO servers
|
||||
* Sends a message to the other Socket.IO servers of the cluster.
|
||||
*
|
||||
* @example
|
||||
* io.serverSideEmit("hello", "world");
|
||||
*
|
||||
* io.on("hello", (arg1) => {
|
||||
* console.log(arg1); // prints "world"
|
||||
* });
|
||||
*
|
||||
* // acknowledgements (without binary content) are supported too:
|
||||
* io.serverSideEmit("ping", (err, responses) => {
|
||||
* if (err) {
|
||||
* // some clients did not acknowledge the event in the given delay
|
||||
* } else {
|
||||
* console.log(responses); // one response per client
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* io.on("ping", (cb) => {
|
||||
* cb("pong");
|
||||
* });
|
||||
*
|
||||
* @param ev - the event name
|
||||
* @param args - an array of arguments, which may include an acknowledgement callback at the end
|
||||
* @public
|
||||
*/
|
||||
public serverSideEmit<Ev extends EventNames<ServerSideEvents>>(
|
||||
ev: Ev,
|
||||
@@ -727,7 +842,8 @@ export class Server<
|
||||
/**
|
||||
* Gets a list of socket ids.
|
||||
*
|
||||
* @public
|
||||
* @deprecated this method will be removed in the next major release, please use {@link Server#serverSideEmit} or
|
||||
* {@link Server#fetchSockets} instead.
|
||||
*/
|
||||
public allSockets(): Promise<Set<SocketId>> {
|
||||
return this.sockets.allSockets()
|
||||
@@ -736,11 +852,13 @@ export class Server<
|
||||
/**
|
||||
* Sets the compress flag.
|
||||
*
|
||||
* @example
|
||||
* io.compress(false).emit("hello");
|
||||
*
|
||||
* @param compress - if `true`, compresses the sending data
|
||||
* @return self
|
||||
* @public
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public compress(compress: boolean): BroadcastOperator<EmitEvents> {
|
||||
public compress(compress: boolean) {
|
||||
return this.sockets.compress(compress)
|
||||
}
|
||||
|
||||
@@ -749,59 +867,126 @@ export class Server<
|
||||
* receive messages (because of network slowness or other issues, or because they’re connected through long polling
|
||||
* and is in the middle of a request-response cycle).
|
||||
*
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* io.volatile.emit("hello"); // the clients may or may not receive it
|
||||
*
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public get volatile(): BroadcastOperator<EmitEvents> {
|
||||
public get volatile() {
|
||||
return this.sockets.volatile
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node.
|
||||
*
|
||||
* @return self
|
||||
* @public
|
||||
* @example
|
||||
* // the “foo” event will be broadcast to all connected clients on this node
|
||||
* io.local.emit("foo", "bar");
|
||||
*
|
||||
* @return a new {@link BroadcastOperator} instance for chaining
|
||||
*/
|
||||
public get local(): BroadcastOperator<EmitEvents> {
|
||||
public get local() {
|
||||
return this.sockets.local
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching socket instances
|
||||
* Adds a timeout in milliseconds for the next operation.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
* io.timeout(1000).emit("some-event", (err, responses) => {
|
||||
* if (err) {
|
||||
* // some clients did not acknowledge the event in the given delay
|
||||
* } else {
|
||||
* console.log(responses); // one response per client
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param timeout
|
||||
*/
|
||||
public fetchSockets(): Promise<RemoteSocket<EmitEvents>[]> {
|
||||
public timeout(timeout: number) {
|
||||
return this.sockets.timeout(timeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching socket instances.
|
||||
*
|
||||
* Note: this method also works within a cluster of multiple Socket.IO servers, with a compatible {@link Adapter}.
|
||||
*
|
||||
* @example
|
||||
* // return all Socket instances
|
||||
* const sockets = await io.fetchSockets();
|
||||
*
|
||||
* // return all Socket instances in the "room1" room
|
||||
* const sockets = await io.in("room1").fetchSockets();
|
||||
*
|
||||
* for (const socket of sockets) {
|
||||
* console.log(socket.id);
|
||||
* console.log(socket.handshake);
|
||||
* console.log(socket.rooms);
|
||||
* console.log(socket.data);
|
||||
*
|
||||
* socket.emit("hello");
|
||||
* socket.join("room1");
|
||||
* socket.leave("room2");
|
||||
* socket.disconnect();
|
||||
* }
|
||||
*/
|
||||
public fetchSockets(): Promise<RemoteSocket<EmitEvents, SocketData>[]> {
|
||||
return this.sockets.fetchSockets()
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances join the specified rooms
|
||||
* Makes the matching socket instances join the specified rooms.
|
||||
*
|
||||
* @param room
|
||||
* @public
|
||||
* Note: this method also works within a cluster of multiple Socket.IO servers, with a compatible {@link Adapter}.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* // make all socket instances join the "room1" room
|
||||
* io.socketsJoin("room1");
|
||||
*
|
||||
* // make all socket instances in the "room1" room join the "room2" and "room3" rooms
|
||||
* io.in("room1").socketsJoin(["room2", "room3"]);
|
||||
*
|
||||
* @param room - a room, or an array of rooms
|
||||
*/
|
||||
public socketsJoin(room: Room | Room[]): void {
|
||||
public socketsJoin(room: Room | Room[]) {
|
||||
return this.sockets.socketsJoin(room)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances leave the specified rooms
|
||||
* Makes the matching socket instances leave the specified rooms.
|
||||
*
|
||||
* @param room
|
||||
* @public
|
||||
* Note: this method also works within a cluster of multiple Socket.IO servers, with a compatible {@link Adapter}.
|
||||
*
|
||||
* @example
|
||||
* // make all socket instances leave the "room1" room
|
||||
* io.socketsLeave("room1");
|
||||
*
|
||||
* // make all socket instances in the "room1" room leave the "room2" and "room3" rooms
|
||||
* io.in("room1").socketsLeave(["room2", "room3"]);
|
||||
*
|
||||
* @param room - a room, or an array of rooms
|
||||
*/
|
||||
public socketsLeave(room: Room | Room[]): void {
|
||||
public socketsLeave(room: Room | Room[]) {
|
||||
return this.sockets.socketsLeave(room)
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the matching socket instances disconnect
|
||||
* Makes the matching socket instances disconnect.
|
||||
*
|
||||
* Note: this method also works within a cluster of multiple Socket.IO servers, with a compatible {@link Adapter}.
|
||||
*
|
||||
* @example
|
||||
* // make all socket instances disconnect (the connections might be kept alive for other namespaces)
|
||||
* io.disconnectSockets();
|
||||
*
|
||||
* // make all socket instances in the "room1" room disconnect and close the underlying connections
|
||||
* io.in("room1").disconnectSockets(true);
|
||||
*
|
||||
* @param close - whether to close the underlying connection
|
||||
* @public
|
||||
*/
|
||||
public disconnectSockets(close: boolean = false): void {
|
||||
public disconnectSockets(close: boolean = false) {
|
||||
return this.sockets.disconnectSockets(close)
|
||||
}
|
||||
}
|
||||
@@ -822,4 +1007,10 @@ emitterMethods.forEach(function (fn) {
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = (srv?, opts?) => new Server(srv, opts)
|
||||
module.exports.Server = Server
|
||||
module.exports.Namespace = Namespace
|
||||
module.exports.Socket = Socket
|
||||
|
||||
export { Socket, ServerOptions, Namespace, BroadcastOperator, RemoteSocket }
|
||||
export { Event } from "./socket"
|
||||
|
||||
Reference in New Issue
Block a user