| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- /* global io */
- class SocketShimBase {
- static connect(url, options) {
- return new SocketShimBase()
- }
- constructor(socket) {
- this._socket = socket
- }
- }
- const transparentMethods = [
- 'connect',
- 'disconnect',
- 'emit',
- 'on',
- 'removeListener',
- ]
- for (const method of transparentMethods) {
- SocketShimBase.prototype[method] = function () {
- this._socket[method].apply(this._socket, arguments)
- }
- }
- class SocketShimNoop extends SocketShimBase {
- static connect() {
- return new SocketShimNoop()
- }
- constructor(socket) {
- super(socket)
- this.socket = {
- get connected() {
- return false
- },
- get sessionid() {
- return undefined
- },
- get transport() {
- return {}
- },
- connect() {},
- disconnect(reason) {},
- }
- }
- connect() {}
- disconnect(reason) {}
- emit() {}
- on() {}
- removeListener() {}
- }
- class SocketShimV0 extends SocketShimBase {
- static connect(url, options) {
- return new SocketShimV0(io.connect(url, options))
- }
- constructor(socket) {
- super(socket)
- this.socket = this._socket.socket
- }
- }
- class SocketShimV2 extends SocketShimBase {
- static connect(url, options) {
- options.forceNew = options['force new connection']
- // .resource has no leading slash, path wants to see one.
- options.path = '/' + options.resource
- options.reconnection = options.reconnect
- options.timeout = options['connect timeout']
- return new SocketShimV2(url, options)
- }
- static get EVENT_MAP() {
- // Use the v2 event names transparently to the frontend.
- const connectionFailureEvents = [
- 'connect_error',
- 'connect_timeout',
- 'error',
- ]
- return new Map([
- ['connect_failed', connectionFailureEvents],
- ['error', connectionFailureEvents],
- ])
- }
- _on(event, handler) {
- // Keep track of our event listeners.
- // We move them to a new socket in ._replaceSocketWithNewInstance()
- if (!this._events.has(event)) {
- this._events.set(event, [handler])
- } else {
- this._events.get(event).push(handler)
- }
- this._socket.on(event, handler)
- }
- on(event, handler) {
- if (SocketShimV2.EVENT_MAP.has(event)) {
- for (const v2Event of SocketShimV2.EVENT_MAP.get(event)) {
- this._on(v2Event, handler)
- }
- } else {
- this._on(event, handler)
- }
- }
- _removeListener(event, handler) {
- // Keep track of our event listeners.
- // We move them to a new socket in ._replaceSocketWithNewInstance()
- if (this._events.has(event)) {
- const listeners = this._events.get(event)
- const pos = listeners.indexOf(handler)
- if (pos !== -1) {
- listeners.splice(pos, 1)
- }
- }
- this._socket.removeListener(event, handler)
- }
- removeListener(event, handler) {
- if (SocketShimV2.EVENT_MAP.has(event)) {
- for (const v2Event of SocketShimV2.EVENT_MAP.get(event)) {
- this._removeListener(v2Event, handler)
- }
- } else {
- this._removeListener(event, handler)
- }
- }
- static createNewSocket(url, options) {
- // open a brand new connection for the default namespace '/'
- // The old socket can still leak 'disconnect' events from the teardown
- // of the old transport. The leaking 'disconnect' events interfere with
- // the _new_ connection and cancel the new connect attempt.
- // Also skip the caching in these locations:
- // - `io.connect()` caches `io.Manager`s in `io.managers`
- // - `io.Manager().socket()` caches `io.Socket`s in its `this.nsps`
- return io.Manager(url, options).socket('/', options)
- }
- _replaceSocketWithNewInstance() {
- const oldSocket = this._socket
- const newSocket = SocketShimV2.createNewSocket(this._url, this._options)
- // move our existing event handlers to the new socket
- this._events.forEach((listeners, event) => {
- for (const listener of listeners) {
- oldSocket.removeListener(event, listener)
- newSocket.on(event, listener)
- }
- })
- if (oldSocket.connected) {
- // We overwrite the reference to oldSocket soon.
- // Make sure we are disconnected.
- oldSocket.disconnect()
- }
- this._socket = newSocket
- }
- connect() {
- // have the same logic behind socket.connect and socket.socket.connect
- this._replaceSocketWithNewInstance()
- }
- constructor(url, options) {
- super(SocketShimV2.createNewSocket(url, options))
- this._url = url
- this._options = options
- this._events = new Map()
- const self = this
- function _getEngine() {
- return (self._socket.io && self._socket.io.engine) || {}
- }
- this.socket = {
- get connected() {
- return self._socket.connected
- },
- get sessionid() {
- if (self._socket.id) {
- return self._socket.id
- }
- // socket.id is discarded upon disconnect
- // the id is still available in the internal state
- return _getEngine().id
- },
- get transport() {
- return _getEngine().transport
- },
- connect() {
- self._replaceSocketWithNewInstance()
- },
- disconnect(reason) {
- return self._socket.disconnect(reason)
- },
- }
- }
- }
- let current
- if (typeof io === 'undefined' || !io) {
- sl_console.log('[socket.io] Shim: socket.io is not loaded, returning noop')
- current = SocketShimNoop
- } else if (typeof io.version === 'string' && io.version.slice(0, 1) === '0') {
- sl_console.log('[socket.io] Shim: detected v0')
- current = SocketShimV0
- } else {
- // socket.io v2 does not have a global io.version attribute.
- sl_console.log('[socket.io] Shim: detected v2')
- current = SocketShimV2
- }
- export default {
- SocketShimNoop,
- SocketShimV0,
- SocketShimV2,
- current,
- connect: current.connect,
- stub: () => new SocketShimNoop(),
- }
|