@@ -7,4 +7,4 @@ export { Transport } from "./transport"
|
||||
export { transports } from "./transports/index"
|
||||
export { installTimerFunctions } from "./util"
|
||||
export { parse } from "./contrib/parseuri"
|
||||
export { nextTick } from "./transports/websocket-constructor.js"
|
||||
export { nextTick } from "./transports/websocket-constructor"
|
||||
|
||||
@@ -7,7 +7,8 @@ import { parse } from "./contrib/parseuri"
|
||||
import { Emitter } from "@socket.io/component-emitter"
|
||||
// import { protocol } from "engine.io-parser";
|
||||
import { protocol } from "../engine.io-parser"
|
||||
import { CloseDetails } from "./transport"
|
||||
import type { Packet, BinaryType, PacketType, RawData } from "../engine.io-parser"
|
||||
import { CloseDetails, Transport } from "./transport"
|
||||
|
||||
// const debug = debugModule("engine.io-client:socket"); // debug()
|
||||
const debug = require('../debug')('engine.io-client:socket')
|
||||
@@ -209,6 +210,12 @@ export interface SocketOptions {
|
||||
*/
|
||||
path: string
|
||||
|
||||
/**
|
||||
* Whether we should add a trailing slash to the request path.
|
||||
* @default true
|
||||
*/
|
||||
addTrailingSlash: boolean
|
||||
|
||||
/**
|
||||
* Either a single protocol string or an array of protocol strings. These strings are used to indicate sub-protocols,
|
||||
* so that a single server can implement multiple WebSocket sub-protocols (for example, you might want one server to
|
||||
@@ -218,11 +225,19 @@ export interface SocketOptions {
|
||||
protocols: string | string[]
|
||||
}
|
||||
|
||||
interface HandshakeData {
|
||||
sid: string
|
||||
upgrades: string[]
|
||||
pingInterval: number
|
||||
pingTimeout: number
|
||||
maxPayload: number
|
||||
}
|
||||
|
||||
interface SocketReservedEvents {
|
||||
open: () => void
|
||||
handshake: (data) => void
|
||||
packet: (packet) => void
|
||||
packetCreate: (packet) => void
|
||||
handshake: (data: HandshakeData) => void
|
||||
packet: (packet: Packet) => void
|
||||
packetCreate: (packet: Packet) => void
|
||||
data: (data) => void
|
||||
message: (data) => void
|
||||
drain: () => void
|
||||
@@ -237,13 +252,15 @@ interface SocketReservedEvents {
|
||||
close: (reason: string, description?: CloseDetails | Error) => void
|
||||
}
|
||||
|
||||
type SocketState = "opening" | "open" | "closing" | "closed"
|
||||
|
||||
export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
public id: string
|
||||
public transport: any
|
||||
public binaryType: string
|
||||
public transport: Transport
|
||||
public binaryType: BinaryType
|
||||
public readyState: SocketState
|
||||
public writeBuffer: Packet[] = [];
|
||||
|
||||
private readyState: string
|
||||
private writeBuffer
|
||||
private prevBufferLen: number
|
||||
private upgrades
|
||||
private pingInterval: number
|
||||
@@ -314,7 +331,6 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
: "80")
|
||||
|
||||
this.transports = opts.transports || ["polling", "websocket"]
|
||||
this.readyState = ""
|
||||
this.writeBuffer = []
|
||||
this.prevBufferLen = 0
|
||||
|
||||
@@ -326,6 +342,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
upgrade: true,
|
||||
timestampParam: "t",
|
||||
rememberUpgrade: false,
|
||||
addTrailingSlash: true,
|
||||
rejectUnauthorized: true,
|
||||
perMessageDeflate: {
|
||||
threshold: 1024
|
||||
@@ -336,7 +353,9 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
opts
|
||||
)
|
||||
|
||||
this.opts.path = this.opts.path.replace(/\/$/, "") + "/"
|
||||
this.opts.path =
|
||||
this.opts.path.replace(/\/$/, "") +
|
||||
(this.opts.addTrailingSlash ? "/" : "")
|
||||
|
||||
if (typeof this.opts.query === "string") {
|
||||
this.opts.query = decode(this.opts.query)
|
||||
@@ -368,7 +387,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
if (this.hostname !== "localhost") {
|
||||
this.offlineEventListener = () => {
|
||||
this.onClose("transport close", {
|
||||
description: "network connection lost"
|
||||
description: "network connection lost",
|
||||
})
|
||||
}
|
||||
addEventListener("offline", this.offlineEventListener, false)
|
||||
@@ -381,9 +400,9 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
/**
|
||||
* Creates transport of the given type.
|
||||
*
|
||||
* @param {String} transport name
|
||||
* @param {String} name - transport name
|
||||
* @return {Transport}
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
private createTransport(name) {
|
||||
debug('creating transport "%s"', name)
|
||||
@@ -407,7 +426,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
socket: this,
|
||||
hostname: this.hostname,
|
||||
secure: this.secure,
|
||||
port: this.port
|
||||
port: this.port,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -417,10 +436,10 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes transport to use and starts probe.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
* Initializes transport to use and starts probe.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private open() {
|
||||
let transport
|
||||
if (
|
||||
@@ -457,7 +476,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
/**
|
||||
* Sets the current transport. Disables the existing one (if any).
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
private setTransport(transport) {
|
||||
debug("setting transport %s", transport.name)
|
||||
@@ -475,7 +494,7 @@ export class Socket extends Emitter<{}, {}, SocketReservedEvents> {
|
||||
.on("drain", this.onDrain.bind(this))
|
||||
.on("packet", this.onPacket.bind(this))
|
||||
.on("error", this.onError.bind(this))
|
||||
.on("close", reason => this.onClose("transport close", reason))
|
||||
.on("close", (reason) => this.onClose("transport close", reason))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,7 @@ class TransportError extends Error {
|
||||
|
||||
export interface CloseDetails {
|
||||
description: string
|
||||
context?: CloseEvent | XMLHttpRequest
|
||||
context?: unknown // context should be typed as CloseEvent | XMLHttpRequest, but these types are not available on non-browser platforms
|
||||
}
|
||||
|
||||
interface TransportReservedEvents {
|
||||
@@ -35,32 +35,34 @@ interface TransportReservedEvents {
|
||||
drain: () => void
|
||||
}
|
||||
|
||||
type TransportState = "opening" | "open" | "closed" | "pausing" | "paused"
|
||||
|
||||
export abstract class Transport extends Emitter<
|
||||
{},
|
||||
{},
|
||||
Record<never, never>,
|
||||
Record<never, never>,
|
||||
TransportReservedEvents
|
||||
> {
|
||||
public query: Record<string, string>
|
||||
public writable: boolean = false;
|
||||
|
||||
protected opts: SocketOptions
|
||||
protected supportsBinary: boolean
|
||||
protected query: object
|
||||
protected readyState: string
|
||||
protected writable: boolean = false;
|
||||
protected readyState: TransportState
|
||||
protected socket: any
|
||||
protected setTimeoutFn: typeof setTimeout
|
||||
|
||||
/**
|
||||
* Transport abstract constructor.
|
||||
*
|
||||
* @param {Object} options.
|
||||
* @api private
|
||||
*/
|
||||
* Transport abstract constructor.
|
||||
*
|
||||
* @param {Object} opts - options
|
||||
* @protected
|
||||
*/
|
||||
constructor(opts) {
|
||||
super()
|
||||
installTimerFunctions(this, opts)
|
||||
|
||||
this.opts = opts
|
||||
this.query = opts.query
|
||||
this.readyState = ""
|
||||
this.socket = opts.socket
|
||||
}
|
||||
|
||||
@@ -71,7 +73,7 @@ export abstract class Transport extends Emitter<
|
||||
* @param description
|
||||
* @param context - the error context
|
||||
* @return {Transport} for chaining
|
||||
* @api protected
|
||||
* @protected
|
||||
*/
|
||||
protected onError(reason: string, description: any, context?: any) {
|
||||
super.emitReserved(
|
||||
@@ -83,25 +85,19 @@ export abstract class Transport extends Emitter<
|
||||
|
||||
/**
|
||||
* Opens the transport.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
private open() {
|
||||
if ("closed" === this.readyState || "" === this.readyState) {
|
||||
this.readyState = "opening"
|
||||
this.doOpen()
|
||||
}
|
||||
public open() {
|
||||
this.readyState = "opening"
|
||||
this.doOpen()
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the transport.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
public close() {
|
||||
if ("opening" === this.readyState || "open" === this.readyState) {
|
||||
if (this.readyState === "opening" || this.readyState === "open") {
|
||||
this.doClose()
|
||||
this.onClose()
|
||||
}
|
||||
@@ -110,13 +106,12 @@ export abstract class Transport extends Emitter<
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends multiple packets.
|
||||
*
|
||||
* @param {Array} packets
|
||||
* @api public
|
||||
*/
|
||||
* Sends multiple packets.
|
||||
*
|
||||
* @param {Array} packets
|
||||
*/
|
||||
public send(packets) {
|
||||
if ("open" === this.readyState) {
|
||||
if (this.readyState === "open") {
|
||||
this.write(packets)
|
||||
} else {
|
||||
// this might happen if the transport was silently closed in the beforeunload event handler
|
||||
@@ -127,7 +122,7 @@ export abstract class Transport extends Emitter<
|
||||
/**
|
||||
* Called upon open
|
||||
*
|
||||
* @api protected
|
||||
* @protected
|
||||
*/
|
||||
protected onOpen() {
|
||||
this.readyState = "open"
|
||||
@@ -139,17 +134,18 @@ export abstract class Transport extends Emitter<
|
||||
* Called with data.
|
||||
*
|
||||
* @param {String} data
|
||||
* @api protected
|
||||
* @protected
|
||||
*/
|
||||
protected onData(data: RawData) {
|
||||
const packet = decodePacket(data, this.socket.binaryType)
|
||||
this.onPacket(packet)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called with a decoded packet.
|
||||
*
|
||||
* @api protected
|
||||
* @protected
|
||||
*/
|
||||
protected onPacket(packet: Packet) {
|
||||
super.emitReserved("packet", packet)
|
||||
@@ -158,14 +154,26 @@ export abstract class Transport extends Emitter<
|
||||
/**
|
||||
* Called upon close.
|
||||
*
|
||||
* @api protected
|
||||
* @protected
|
||||
*/
|
||||
protected onClose(details?: CloseDetails) {
|
||||
this.readyState = "closed"
|
||||
super.emitReserved("close", details)
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the transport
|
||||
*/
|
||||
public abstract get name(): string
|
||||
|
||||
/**
|
||||
* Pauses the transport, in order not to lose packets during an upgrade.
|
||||
*
|
||||
* @param onPause
|
||||
*/
|
||||
public pause(onPause: () => void) { }
|
||||
|
||||
protected abstract doOpen()
|
||||
protected abstract doClose()
|
||||
protected abstract write(packets)
|
||||
protected abstract write(packets: Packet[])
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ export class WS extends Transport {
|
||||
/**
|
||||
* WebSocket transport constructor.
|
||||
*
|
||||
* @api {Object} connection options
|
||||
* @api public
|
||||
* @param {Object} opts - connection options
|
||||
* @protected
|
||||
*/
|
||||
constructor(opts) {
|
||||
super(opts)
|
||||
@@ -35,21 +35,11 @@ export class WS extends Transport {
|
||||
this.supportsBinary = !opts.forceBase64
|
||||
}
|
||||
|
||||
/**
|
||||
* Transport name.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
get name() {
|
||||
override get name() {
|
||||
return "websocket"
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens socket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
doOpen() {
|
||||
override doOpen() {
|
||||
if (!this.check()) {
|
||||
// let probe timeout
|
||||
return
|
||||
@@ -103,31 +93,25 @@ export class WS extends Transport {
|
||||
/**
|
||||
* Adds event listeners to the socket
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
addEventListeners() {
|
||||
private addEventListeners() {
|
||||
this.ws.onopen = () => {
|
||||
if (this.opts.autoUnref) {
|
||||
this.ws._socket.unref()
|
||||
}
|
||||
this.onOpen()
|
||||
}
|
||||
this.ws.onclose = closeEvent =>
|
||||
this.ws.onclose = (closeEvent) =>
|
||||
this.onClose({
|
||||
description: "websocket connection closed",
|
||||
context: closeEvent
|
||||
context: closeEvent,
|
||||
})
|
||||
this.ws.onmessage = ev => this.onData(ev.data)
|
||||
this.ws.onerror = e => this.onError("websocket error", e)
|
||||
this.ws.onmessage = (ev) => this.onData(ev.data)
|
||||
this.ws.onerror = (e) => this.onError("websocket error", e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to socket.
|
||||
*
|
||||
* @param {Array} array of packets.
|
||||
* @api private
|
||||
*/
|
||||
write(packets) {
|
||||
override write(packets) {
|
||||
this.writable = false
|
||||
|
||||
// encodePacket efficient as it uses WS framing
|
||||
@@ -136,7 +120,7 @@ export class WS extends Transport {
|
||||
const packet = packets[i]
|
||||
const lastPacket = i === packets.length - 1
|
||||
|
||||
encodePacket(packet, this.supportsBinary, data => {
|
||||
encodePacket(packet, this.supportsBinary, (data) => {
|
||||
// always create a new object (GH-437)
|
||||
const opts: { compress?: boolean } = {}
|
||||
if (!usingBrowserWebSocket) {
|
||||
@@ -180,12 +164,7 @@ export class WS extends Transport {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes socket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
doClose() {
|
||||
override doClose() {
|
||||
if (typeof this.ws !== "undefined") {
|
||||
this.ws.close()
|
||||
this.ws = null
|
||||
@@ -195,7 +174,7 @@ export class WS extends Transport {
|
||||
/**
|
||||
* Generates uri for connection.
|
||||
*
|
||||
* @api private
|
||||
* @private
|
||||
*/
|
||||
uri() {
|
||||
let query: { b64?: number } = this.query || {}
|
||||
@@ -238,9 +217,9 @@ export class WS extends Transport {
|
||||
* Feature detection for WebSocket.
|
||||
*
|
||||
* @return {Boolean} whether this transport is available.
|
||||
* @api public
|
||||
* @private
|
||||
*/
|
||||
check() {
|
||||
private check() {
|
||||
return !!WebSocket
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,16 +10,16 @@ export function pick(obj, ...attr) {
|
||||
}
|
||||
|
||||
// Keep a reference to the real timeout functions so they can be used when overridden
|
||||
const NATIVE_SET_TIMEOUT = setTimeout
|
||||
const NATIVE_CLEAR_TIMEOUT = clearTimeout
|
||||
const NATIVE_SET_TIMEOUT = globalThis.setTimeout
|
||||
const NATIVE_CLEAR_TIMEOUT = globalThis.clearTimeout
|
||||
|
||||
export function installTimerFunctions(obj, opts) {
|
||||
if (opts.useNativeTimers) {
|
||||
obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThis)
|
||||
obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThis)
|
||||
} else {
|
||||
obj.setTimeoutFn = setTimeout.bind(globalThis)
|
||||
obj.clearTimeoutFn = clearTimeout.bind(globalThis)
|
||||
obj.setTimeoutFn = globalThis.setTimeout.bind(globalThis)
|
||||
obj.clearTimeoutFn = globalThis.clearTimeout.bind(globalThis)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user