├── .gitignore ├── docs ├── README.md └── API.md ├── support ├── noop.js ├── webpack.config.slim.js └── webpack.config.js ├── test ├── .eslintrc.json ├── index.js ├── support │ ├── env.js │ └── server.js ├── url.js ├── socket.js └── connection.js ├── .eslintrc.json ├── Makefile ├── lib ├── on.js ├── url.js ├── index.js ├── socket.js └── manager.js ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── zuul.config.js ├── LICENSE ├── .travis.yml ├── README.md ├── package.json ├── gulpfile.js └── dist ├── socket.io.slim.js └── socket.io.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Table of Contents 3 | 4 | - [API](API.md) 5 | -------------------------------------------------------------------------------- /support/noop.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function () { return function () {}; }; 3 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "mocha": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | require('./support/env'); 3 | 4 | // whitelist some globals to avoid warnings 5 | global.___eio = null; 6 | 7 | require('./url'); 8 | 9 | // browser only tests 10 | require('./connection'); 11 | require('./socket'); 12 | 13 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "parser": "babel-eslint", 4 | "env": { 5 | // Note: mocha env is defined inside test/.eslintrc.json 6 | "node": true 7 | }, 8 | "rules": { 9 | "yoda": 0, 10 | "semi": [2, "always"], 11 | "no-extra-semi": 2, 12 | "semi-spacing": [2, { "before": false, "after": true }] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | REPORTER = dot 3 | 4 | build: socket.io.js 5 | 6 | socket.io.js socket.io.min.js: lib/*.js package.json 7 | @./node_modules/.bin/gulp 8 | 9 | test: 10 | @./node_modules/.bin/gulp test 11 | 12 | test-node: 13 | @./node_modules/.bin/gulp test-node 14 | 15 | test-zuul: 16 | @./node_modules/.bin/gulp test-zuul 17 | 18 | test-cov: 19 | @./node_modules/.bin/gulp test-cov 20 | 21 | .PHONY: test 22 | -------------------------------------------------------------------------------- /lib/on.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module exports. 4 | */ 5 | 6 | module.exports = on; 7 | 8 | /** 9 | * Helper for subscriptions. 10 | * 11 | * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter` 12 | * @param {String} event name 13 | * @param {Function} callback 14 | * @api public 15 | */ 16 | 17 | function on (obj, ev, fn) { 18 | obj.on(ev, fn); 19 | return { 20 | destroy: function () { 21 | obj.removeListener(ev, fn); 22 | } 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /test/support/env.js: -------------------------------------------------------------------------------- 1 | // WARNING this is bad practice 2 | // we only do this in our tests because we need to test engine.io-client 3 | // support in browsers and in node.js 4 | // some tests do not yet work in both 5 | exports.browser = !!global.window; 6 | exports.node = !exports.browser; 7 | 8 | if (!global.location) { 9 | global.location = { 10 | protocol: 'http:', 11 | host: 'localhost:3210', 12 | hostname: 'localhost', 13 | port: '3210' 14 | }; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | *Note*: the `socket.io.js` file is the generated output of `make socket.io.js`, and should not be manually modified. 3 | 4 | ### The kind of change this PR does introduce 5 | 6 | * [x] a bug fix 7 | * [ ] a new feature 8 | * [ ] an update to the documentation 9 | * [ ] a code change that improves performance 10 | * [ ] other 11 | 12 | ### Current behaviour 13 | 14 | 15 | ### New behaviour 16 | 17 | 18 | ### Other information (e.g. related issues) 19 | 20 | 21 | -------------------------------------------------------------------------------- /zuul.config.js: -------------------------------------------------------------------------------- 1 | 2 | var zuulConfig = module.exports = { 3 | ui: 'mocha-bdd', 4 | server: './test/support/server.js', 5 | local: true, // test on localhost by default 6 | builder: 'zuul-builder-webpack', 7 | webpack: require('./support/webpack.config.js') 8 | }; 9 | 10 | if (process.env.CI === 'true') { 11 | zuulConfig.local = false; 12 | zuulConfig.tunnel = { 13 | type: 'ngrok', 14 | authtoken: '6Aw8vTgcG5EvXdQywVvbh_3fMxvd4Q7dcL2caAHAFjV', 15 | proto: 'tcp' 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | *Note*: for support questions, please use one of these channels: [stackoverflow](http://stackoverflow.com/questions/tagged/socket.io) or [slack](https://socketio.slack.com) 3 | 4 | ### You want to: 5 | 6 | * [x] report a *bug* 7 | * [ ] request a *feature* 8 | 9 | ### Current behaviour 10 | 11 | 12 | ### Steps to reproduce (if the current behaviour is a bug) 13 | 14 | **Note**: the best way to get a quick answer is to provide a failing test case, by forking the following [fiddle](https://github.com/darrachequesne/socket.io-fiddle) for example. 15 | 16 | ### Expected behaviour 17 | 18 | 19 | ### Setup 20 | - OS: 21 | - browser: 22 | - socket.io version: 23 | 24 | ### Other information (e.g. stacktraces, related issues, suggestions how to fix) 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Guillermo Rauch 4 | 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /support/webpack.config.slim.js: -------------------------------------------------------------------------------- 1 | 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | name: 'slim', 6 | entry: './lib/index.js', 7 | output: { 8 | library: 'io', 9 | libraryTarget: 'umd', 10 | filename: 'socket.io.slim.js' 11 | }, 12 | externals: { 13 | global: glob(), 14 | json3: 'JSON' 15 | }, 16 | devtool: 'source-map', 17 | plugins: [ 18 | new webpack.NormalModuleReplacementPlugin(/debug/, process.cwd() + '/support/noop.js'), 19 | new webpack.optimize.UglifyJsPlugin() 20 | ], 21 | module: { 22 | loaders: [{ 23 | test: /\.js$/, 24 | exclude: /(node_modules|bower_components)/, 25 | loader: 'babel', // 'babel-loader' is also a legal name to reference 26 | query: { presets: ['es2015'] } 27 | }, { 28 | test: /\json3.js/, 29 | loader: 'imports?define=>false' 30 | }, { 31 | test: /\.js$/, 32 | loader: 'strip-loader?strip[]=debug' 33 | }] 34 | } 35 | }; 36 | 37 | /** 38 | * Populates `global`. 39 | * 40 | * @api private 41 | */ 42 | 43 | function glob () { 44 | return 'typeof self !== "undefined" ? self : ' + 45 | 'typeof window !== "undefined" ? window : ' + 46 | 'typeof global !== "undefined" ? global : {}'; 47 | } 48 | -------------------------------------------------------------------------------- /support/webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | name: 'default', 6 | entry: './lib/index.js', 7 | output: { 8 | library: 'io', 9 | libraryTarget: 'umd', 10 | filename: 'socket.io.js' 11 | }, 12 | externals: { 13 | global: glob() 14 | }, 15 | devtool: 'source-map', 16 | plugins: [ 17 | new webpack.optimize.UglifyJsPlugin({ 18 | compress: { 19 | screw_ie8: false 20 | }, 21 | mangle: { 22 | screw_ie8: false 23 | }, 24 | output: { 25 | screw_ie8: false, 26 | beautify: false 27 | } 28 | }) 29 | ], 30 | module: { 31 | loaders: [{ 32 | test: /\.js$/, 33 | exclude: /(node_modules|bower_components)/, 34 | loader: 'babel', // 'babel-loader' is also a legal name to reference 35 | query: { presets: ['es2015'] } 36 | }, { 37 | test: /\json3.js/, 38 | loader: 'imports?define=>false' 39 | }] 40 | } 41 | }; 42 | 43 | /** 44 | * Populates `global`. 45 | * 46 | * @api private 47 | */ 48 | 49 | function glob () { 50 | return 'typeof self !== "undefined" ? self : ' + 51 | 'typeof window !== "undefined" ? window : ' + 52 | 'typeof global !== "undefined" ? global : {}'; 53 | } 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '4' 4 | - '6' 5 | - '8' 6 | sudo: false 7 | git: 8 | depth: 1 9 | notifications: 10 | irc: irc.freenode.org#socket.io 11 | matrix: 12 | include: 13 | - node_js: 'node' 14 | env: BROWSER_NAME=chrome BROWSER_VERSION=latest 15 | - node_js: 'node' 16 | env: BROWSER_NAME=safari BROWSER_VERSION=latest 17 | - node_js: 'node' 18 | env: BROWSER_NAME=firefox BROWSER_VERSION=latest 19 | - node_js: 'node' 20 | env: BROWSER_NAME=ie BROWSER_VERSION=8 21 | - node_js: 'node' 22 | env: BROWSER_NAME=ie BROWSER_VERSION=9 23 | - node_js: 'node' 24 | env: BROWSER_NAME=ie BROWSER_VERSION=10 25 | - node_js: 'node' 26 | env: BROWSER_NAME=ie BROWSER_VERSION=11 27 | - node_js: 'node' 28 | env: BROWSER_NAME=microsoftedge BROWSER_VERSION=latest 29 | - node_js: 'node' 30 | env: BROWSER_NAME=iphone BROWSER_VERSION=8.4 31 | - node_js: 'node' 32 | env: BROWSER_NAME=iphone BROWSER_VERSION=9.2 33 | - node_js: 'node' 34 | env: BROWSER_NAME=iphone BROWSER_VERSION=10.0 35 | - node_js: 'node' 36 | env: BROWSER_NAME=android BROWSER_VERSION=4.4 37 | - node_js: 'node' 38 | env: BROWSER_NAME=android BROWSER_VERSION=5.1 39 | - node_js: 'node' 40 | env: BROWSER_NAME=ipad BROWSER_VERSION=9.3 41 | - node_js: 'node' 42 | env: BROWSER_NAME=ipad BROWSER_VERSION=10.0 43 | -------------------------------------------------------------------------------- /lib/url.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var parseuri = require('parseuri'); 7 | var debug = require('debug')('socket.io-client:url'); 8 | 9 | /** 10 | * Module exports. 11 | */ 12 | 13 | module.exports = url; 14 | 15 | /** 16 | * URL parser. 17 | * 18 | * @param {String} url 19 | * @param {Object} An object meant to mimic window.location. 20 | * Defaults to window.location. 21 | * @api public 22 | */ 23 | 24 | function url (uri, loc) { 25 | var obj = uri; 26 | 27 | // default to window.location 28 | loc = loc || global.location; 29 | if (null == uri) uri = loc.protocol + '//' + loc.host; 30 | 31 | // relative path support 32 | if ('string' === typeof uri) { 33 | if ('/' === uri.charAt(0)) { 34 | if ('/' === uri.charAt(1)) { 35 | uri = loc.protocol + uri; 36 | } else { 37 | uri = loc.host + uri; 38 | } 39 | } 40 | 41 | if (!/^(https?|wss?):\/\//.test(uri)) { 42 | debug('protocol-less url %s', uri); 43 | if ('undefined' !== typeof loc) { 44 | uri = loc.protocol + '//' + uri; 45 | } else { 46 | uri = 'https://' + uri; 47 | } 48 | } 49 | 50 | // parse 51 | debug('parse %s', uri); 52 | obj = parseuri(uri); 53 | } 54 | 55 | // make sure we treat `localhost:80` and `localhost` equally 56 | if (!obj.port) { 57 | if (/^(http|ws)$/.test(obj.protocol)) { 58 | obj.port = '80'; 59 | } else if (/^(http|ws)s$/.test(obj.protocol)) { 60 | obj.port = '443'; 61 | } 62 | } 63 | 64 | obj.path = obj.path || '/'; 65 | 66 | var ipv6 = obj.host.indexOf(':') !== -1; 67 | var host = ipv6 ? '[' + obj.host + ']' : obj.host; 68 | 69 | // define unique id 70 | obj.id = obj.protocol + '://' + host + ':' + obj.port; 71 | // define href 72 | obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : (':' + obj.port)); 73 | 74 | return obj; 75 | } 76 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var url = require('./url'); 7 | var parser = require('socket.io-parser'); 8 | var Manager = require('./manager'); 9 | var debug = require('debug')('socket.io-client'); 10 | 11 | /** 12 | * Module exports. 13 | */ 14 | 15 | module.exports = exports = lookup; 16 | 17 | /** 18 | * Managers cache. 19 | */ 20 | 21 | var cache = exports.managers = {}; 22 | 23 | /** 24 | * Looks up an existing `Manager` for multiplexing. 25 | * If the user summons: 26 | * 27 | * `io('http://localhost/a');` 28 | * `io('http://localhost/b');` 29 | * 30 | * We reuse the existing instance based on same scheme/port/host, 31 | * and we initialize sockets for each namespace. 32 | * 33 | * @api public 34 | */ 35 | 36 | function lookup (uri, opts) { 37 | if (typeof uri === 'object') { 38 | opts = uri; 39 | uri = undefined; 40 | } 41 | 42 | opts = opts || {}; 43 | 44 | var parsed = url(uri); 45 | var source = parsed.source; 46 | var id = parsed.id; 47 | var path = parsed.path; 48 | var sameNamespace = cache[id] && path in cache[id].nsps; 49 | var newConnection = opts.forceNew || opts['force new connection'] || 50 | false === opts.multiplex || sameNamespace; 51 | 52 | var io; 53 | 54 | if (newConnection) { 55 | debug('ignoring socket cache for %s', source); 56 | io = Manager(source, opts); 57 | } else { 58 | if (!cache[id]) { 59 | debug('new io instance for %s', source); 60 | cache[id] = Manager(source, opts); 61 | } 62 | io = cache[id]; 63 | } 64 | if (parsed.query && !opts.query) { 65 | opts.query = parsed.query; 66 | } 67 | return io.socket(parsed.path, opts); 68 | } 69 | 70 | /** 71 | * Protocol version. 72 | * 73 | * @api public 74 | */ 75 | 76 | exports.protocol = parser.protocol; 77 | 78 | /** 79 | * `connect`. 80 | * 81 | * @param {String} uri 82 | * @api public 83 | */ 84 | 85 | exports.connect = lookup; 86 | 87 | /** 88 | * Expose constructors for standalone build. 89 | * 90 | * @api public 91 | */ 92 | 93 | exports.Manager = require('./manager'); 94 | exports.Socket = require('./socket'); 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # socket.io-client 3 | 4 | [![Build Status](https://secure.travis-ci.org/socketio/socket.io-client.svg?branch=master)](http://travis-ci.org/socketio/socket.io-client) 5 | [![Dependency Status](https://david-dm.org/socketio/socket.io-client.svg)](https://david-dm.org/socketio/socket.io-client) 6 | [![devDependency Status](https://david-dm.org/socketio/socket.io-client/dev-status.svg)](https://david-dm.org/socketio/socket.io-client#info=devDependencies) 7 | ![NPM version](https://badge.fury.io/js/socket.io-client.svg) 8 | ![Downloads](http://img.shields.io/npm/dm/socket.io-client.svg?style=flat) 9 | [![](http://slack.socket.io/badge.svg?)](http://slack.socket.io) 10 | 11 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/socket.svg)](https://saucelabs.com/u/socket) 12 | 13 | ## How to use 14 | 15 | A standalone build of `socket.io-client` is exposed automatically by the 16 | socket.io server as `/socket.io/socket.io.js`. Alternatively you can 17 | serve the file `socket.io.js` found in the `dist` folder. 18 | 19 | ```html 20 | 21 | 27 | ``` 28 | 29 | ```js 30 | // with ES6 import 31 | import io from 'socket.io-client'; 32 | 33 | const socket = io('http://localhost'); 34 | ``` 35 | 36 | A slim build (without `JSON3`, a JSON polyfill for IE6/IE7, and `debug`) is also available: `socket.io.slim.js`. 37 | 38 | Socket.IO is compatible with [browserify](http://browserify.org/) and [webpack](https://webpack.js.org/) (see example [there](https://github.com/socketio/socket.io/tree/2.0.3/examples/webpack-build)). 39 | 40 | ### Node.JS (server-side usage) 41 | 42 | Add `socket.io-client` to your `package.json` and then: 43 | 44 | ```js 45 | var socket = require('socket.io-client')('http://localhost'); 46 | socket.on('connect', function(){}); 47 | socket.on('event', function(data){}); 48 | socket.on('disconnect', function(){}); 49 | ``` 50 | 51 | ## API 52 | 53 | See [API](/docs/API.md) 54 | 55 | ## License 56 | 57 | [MIT](/LICENSE) 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io-client", 3 | "version": "2.0.4", 4 | "keywords": [ 5 | "realtime", 6 | "framework", 7 | "websocket", 8 | "tcp", 9 | "events", 10 | "client" 11 | ], 12 | "main": "./lib/index", 13 | "files": [ 14 | "lib/", 15 | "dist/" 16 | ], 17 | "dependencies": { 18 | "backo2": "1.0.2", 19 | "base64-arraybuffer": "0.1.5", 20 | "component-bind": "1.0.0", 21 | "component-emitter": "1.2.1", 22 | "debug": "~2.6.4", 23 | "engine.io-client": "~3.1.0", 24 | "has-cors": "1.1.0", 25 | "indexof": "0.0.1", 26 | "object-component": "0.0.3", 27 | "parseqs": "0.0.5", 28 | "parseuri": "0.0.5", 29 | "socket.io-parser": "~3.1.1", 30 | "to-array": "0.1.4" 31 | }, 32 | "devDependencies": { 33 | "babel-core": "^6.24.1", 34 | "babel-eslint": "4.1.7", 35 | "babel-loader": "7.0.0", 36 | "babel-preset-es2015": "6.24.1", 37 | "concat-stream": "^1.6.0", 38 | "derequire": "^2.0.6", 39 | "eslint-config-standard": "4.4.0", 40 | "eslint-plugin-standard": "1.3.1", 41 | "expect.js": "0.3.1", 42 | "gulp": "^3.9.1", 43 | "gulp-eslint": "1.1.1", 44 | "gulp-file": "^0.3.0", 45 | "gulp-istanbul": "^1.1.1", 46 | "gulp-mocha": "^4.3.1", 47 | "gulp-task-listing": "1.0.1", 48 | "imports-loader": "^0.7.1", 49 | "istanbul": "^0.4.5", 50 | "mocha": "^3.3.0", 51 | "socket.io": "2.0.4", 52 | "strip-loader": "0.1.2", 53 | "text-blob-builder": "0.0.1", 54 | "webpack-stream": "3.2.0", 55 | "zuul": "^3.11.1 ", 56 | "zuul-builder-webpack": "^1.2.0", 57 | "zuul-ngrok": "4.0.0" 58 | }, 59 | "scripts": { 60 | "test": "gulp test" 61 | }, 62 | "contributors": [ 63 | { 64 | "name": "Guillermo Rauch", 65 | "email": "rauchg@gmail.com" 66 | }, 67 | { 68 | "name": "Arnout Kazemier", 69 | "email": "info@3rd-eden.com" 70 | }, 71 | { 72 | "name": "Vladimir Dronnikov", 73 | "email": "dronnikov@gmail.com" 74 | }, 75 | { 76 | "name": "Einar Otto Stangvik", 77 | "email": "einaros@gmail.com" 78 | } 79 | ], 80 | "repository": { 81 | "type": "git", 82 | "url": "https://github.com/Automattic/socket.io-client.git" 83 | }, 84 | "license": "MIT" 85 | } 86 | -------------------------------------------------------------------------------- /test/url.js: -------------------------------------------------------------------------------- 1 | 2 | var loc = {}; 3 | var url = require('../lib/url'); 4 | var expect = require('expect.js'); 5 | 6 | describe('url', function () { 7 | it('works with undefined', function () { 8 | loc.hostname = 'woot.com'; 9 | loc.protocol = 'https:'; 10 | loc.port = 4005; 11 | loc.host = loc.hostname + ':' + loc.port; 12 | var parsed = url(undefined, loc); 13 | expect(parsed.host).to.be('woot.com'); 14 | expect(parsed.protocol).to.be('https'); 15 | expect(parsed.port).to.be('4005'); 16 | }); 17 | 18 | it('works with relative paths', function () { 19 | loc.hostname = 'woot.com'; 20 | loc.protocol = 'https:'; 21 | loc.port = 3000; 22 | loc.host = loc.hostname + ':' + loc.port; 23 | var parsed = url('/test', loc); 24 | expect(parsed.host).to.be('woot.com'); 25 | expect(parsed.protocol).to.be('https'); 26 | expect(parsed.port).to.be('3000'); 27 | }); 28 | 29 | it('works with no protocol', function () { 30 | loc.protocol = 'http:'; 31 | var parsed = url('localhost:3000', loc); 32 | expect(parsed.host).to.be('localhost'); 33 | expect(parsed.port).to.be('3000'); 34 | expect(parsed.protocol).to.be('http'); 35 | }); 36 | 37 | it('works with no schema', function () { 38 | loc.protocol = 'http:'; 39 | var parsed = url('//localhost:3000', loc); 40 | expect(parsed.host).to.be('localhost'); 41 | expect(parsed.port).to.be('3000'); 42 | expect(parsed.protocol).to.be('http'); 43 | }); 44 | 45 | it('forces ports for unique url ids', function () { 46 | var id1 = url('http://google.com:80/'); 47 | var id2 = url('http://google.com/'); 48 | var id3 = url('https://google.com/'); 49 | expect(id1.id).to.be(id2.id); 50 | expect(id1.id).to.not.be(id3.id); 51 | expect(id2.id).to.not.be(id3.id); 52 | }); 53 | 54 | it('identifies the namespace', function () { 55 | loc.protocol = 'http:'; 56 | loc.hostname = 'woot.com'; 57 | 58 | expect(url('/woot', loc).path).to.be('/woot'); 59 | expect(url('http://google.com').path).to.be('/'); 60 | expect(url('http://google.com/').path).to.be('/'); 61 | }); 62 | 63 | it('works with ipv6', function () { 64 | var parsed = url('http://[::1]'); 65 | expect(parsed.protocol).to.be('http'); 66 | expect(parsed.host).to.be('::1'); 67 | expect(parsed.port).to.be('80'); 68 | expect(parsed.id).to.be('http://[::1]:80'); 69 | }); 70 | 71 | it('works with ipv6 location', function () { 72 | loc.protocol = 'http:'; 73 | loc.hostname = '[::1]'; 74 | loc.port = ''; 75 | loc.host = loc.hostname + ':' + loc.port; 76 | 77 | var parsed = url(undefined, loc); 78 | expect(parsed.protocol).to.be('http'); 79 | expect(parsed.host).to.be('::1'); 80 | expect(parsed.port).to.be('80'); 81 | expect(parsed.id).to.be('http://[::1]:80'); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const mocha = require('gulp-mocha'); 3 | const istanbul = require('gulp-istanbul'); 4 | const webpack = require('webpack-stream'); 5 | const child = require('child_process'); 6 | const help = require('gulp-task-listing'); 7 | const eslint = require('gulp-eslint'); 8 | 9 | gulp.task('help', help); 10 | 11 | gulp.task('default', ['build']); 12 | 13 | // ////////////////////////////////////// 14 | // BUILDING 15 | // ////////////////////////////////////// 16 | 17 | const BUILD_TARGET_DIR = './dist/'; 18 | 19 | gulp.task('build', function () { 20 | return gulp.src('lib/*.js') 21 | .pipe(webpack({ 22 | config: [ 23 | require('./support/webpack.config.js'), 24 | require('./support/webpack.config.slim.js') 25 | ] 26 | })) 27 | .pipe(gulp.dest(BUILD_TARGET_DIR)); 28 | }); 29 | 30 | // ////////////////////////////////////// 31 | // TESTING 32 | // ////////////////////////////////////// 33 | 34 | const REPORTER = 'dot'; 35 | const TEST_FILE = './test/index.js'; 36 | const TEST_SUPPORT_SERVER_FILE = './test/support/server.js'; 37 | 38 | gulp.task('test', ['lint'], function () { 39 | if (process.env.hasOwnProperty('BROWSER_NAME')) { 40 | return testZuul(); 41 | } else { 42 | return testNode(); 43 | } 44 | }); 45 | 46 | gulp.task('test-node', testNode); 47 | gulp.task('test-zuul', testZuul); 48 | 49 | gulp.task('lint', function () { 50 | return gulp.src([ 51 | '*.js', 52 | 'lib/**/*.js', 53 | 'test/**/*.js', 54 | 'support/**/*.js' 55 | ]) 56 | .pipe(eslint()) 57 | .pipe(eslint.format()) 58 | .pipe(eslint.failAfterError()); 59 | }); 60 | 61 | // runs zuul through shell process 62 | function testZuul () { 63 | const ZUUL_CMD = './node_modules/zuul/bin/zuul'; 64 | const args = [ 65 | '--browser-name', 66 | process.env.BROWSER_NAME, 67 | '--browser-version', 68 | process.env.BROWSER_VERSION 69 | ]; 70 | if (process.env.hasOwnProperty('BROWSER_PLATFORM')) { 71 | args.push('--browser-platform'); 72 | args.push(process.env.BROWSER_PLATFORM); 73 | } 74 | args.push(TEST_FILE); 75 | const zuulChild = child.spawn(ZUUL_CMD, args, { stdio: 'inherit' }); 76 | zuulChild.on('exit', function (code) { process.exit(code); }); 77 | return zuulChild; 78 | } 79 | 80 | function testNode () { 81 | const MOCHA_OPTS = { 82 | reporter: REPORTER, 83 | require: [TEST_SUPPORT_SERVER_FILE], 84 | bail: true 85 | }; 86 | return gulp.src(TEST_FILE, { read: false }) 87 | .pipe(mocha(MOCHA_OPTS)) 88 | // following lines to fix gulp-mocha not terminating (see gulp-mocha webpage) 89 | .once('error', function (err) { 90 | console.error(err.stack); 91 | process.exit(1); 92 | }) 93 | .once('end', function () { 94 | process.exit(); 95 | }); 96 | } 97 | 98 | gulp.task('istanbul-pre-test', function () { 99 | return gulp.src(['lib/**/*.js']) 100 | // Covering files 101 | .pipe(istanbul()) 102 | // Force `require` to return covered files 103 | .pipe(istanbul.hookRequire()); 104 | }); 105 | 106 | gulp.task('test-cov', ['istanbul-pre-test'], function () { 107 | gulp.src(['test/*.js', 'test/support/*.js']) 108 | .pipe(mocha({ 109 | reporter: REPORTER 110 | })) 111 | .pipe(istanbul.writeReports()) 112 | .once('error', function (err) { 113 | console.error(err); 114 | process.exit(1); 115 | }) 116 | .once('end', function () { 117 | process.exit(); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /test/support/server.js: -------------------------------------------------------------------------------- 1 | 2 | // this is a test server to support tests which make requests 3 | 4 | var io = require('socket.io'); 5 | var server = io(process.env.ZUUL_PORT || 3210, { pingInterval: 2000 }); 6 | var expect = require('expect.js'); 7 | 8 | server.of('/foo').on('connection', function () { 9 | // register namespace 10 | }); 11 | 12 | server.of('/timeout_socket').on('connection', function () { 13 | // register namespace 14 | }); 15 | 16 | server.of('/valid').on('connection', function () { 17 | // register namespace 18 | }); 19 | 20 | server.of('/asd').on('connection', function () { 21 | // register namespace 22 | }); 23 | 24 | server.of('/abc').on('connection', function (socket) { 25 | socket.emit('handshake', socket.handshake); 26 | }); 27 | 28 | server.on('connection', function (socket) { 29 | // simple test 30 | socket.on('hi', function () { 31 | socket.emit('hi'); 32 | }); 33 | 34 | // ack tests 35 | socket.on('ack', function () { 36 | socket.emit('ack', function (a, b) { 37 | if (a === 5 && b.test) { 38 | socket.emit('got it'); 39 | } 40 | }); 41 | }); 42 | 43 | socket.on('getAckDate', function (data, cb) { 44 | cb(new Date()); 45 | }); 46 | 47 | socket.on('getDate', function () { 48 | socket.emit('takeDate', new Date()); 49 | }); 50 | 51 | socket.on('getDateObj', function () { 52 | socket.emit('takeDateObj', { date: new Date() }); 53 | }); 54 | 55 | socket.on('getUtf8', function () { 56 | socket.emit('takeUtf8', 'てすと'); 57 | socket.emit('takeUtf8', 'Я Б Г Д Ж Й'); 58 | socket.emit('takeUtf8', 'Ä ä Ü ü ß'); 59 | socket.emit('takeUtf8', 'utf8 — string'); 60 | socket.emit('takeUtf8', 'utf8 — string'); 61 | }); 62 | 63 | // false test 64 | socket.on('false', function () { 65 | socket.emit('false', false); 66 | }); 67 | 68 | // binary test 69 | socket.on('doge', function () { 70 | var buf = new Buffer('asdfasdf', 'utf8'); 71 | socket.emit('doge', buf); 72 | }); 73 | 74 | // expect receiving binary to be buffer 75 | socket.on('buffa', function (a) { 76 | if (Buffer.isBuffer(a)) socket.emit('buffack'); 77 | }); 78 | 79 | // expect receiving binary with mixed JSON 80 | socket.on('jsonbuff', function (a) { 81 | expect(a.hello).to.eql('lol'); 82 | expect(Buffer.isBuffer(a.message)).to.be(true); 83 | expect(a.goodbye).to.eql('gotcha'); 84 | socket.emit('jsonbuff-ack'); 85 | }); 86 | 87 | // expect receiving buffers in order 88 | var receivedAbuff1 = false; 89 | socket.on('abuff1', function (a) { 90 | expect(Buffer.isBuffer(a)).to.be(true); 91 | receivedAbuff1 = true; 92 | }); 93 | socket.on('abuff2', function (a) { 94 | expect(receivedAbuff1).to.be(true); 95 | socket.emit('abuff2-ack'); 96 | }); 97 | 98 | // expect sent blob to be buffer 99 | socket.on('blob', function (a) { 100 | if (Buffer.isBuffer(a)) socket.emit('back'); 101 | }); 102 | 103 | // expect sent blob mixed with json to be buffer 104 | socket.on('jsonblob', function (a) { 105 | expect(a.hello).to.eql('lol'); 106 | expect(Buffer.isBuffer(a.message)).to.be(true); 107 | expect(a.goodbye).to.eql('gotcha'); 108 | socket.emit('jsonblob-ack'); 109 | }); 110 | 111 | // expect blobs sent in order to arrive in correct order 112 | var receivedblob1 = false; 113 | var receivedblob2 = false; 114 | socket.on('blob1', function (a) { 115 | expect(Buffer.isBuffer(a)).to.be(true); 116 | receivedblob1 = true; 117 | }); 118 | socket.on('blob2', function (a) { 119 | expect(receivedblob1).to.be(true); 120 | expect(a).to.eql('second'); 121 | receivedblob2 = true; 122 | }); 123 | socket.on('blob3', function (a) { 124 | expect(Buffer.isBuffer(a)).to.be(true); 125 | expect(receivedblob1).to.be(true); 126 | expect(receivedblob2).to.be(true); 127 | socket.emit('blob3-ack'); 128 | }); 129 | 130 | // emit buffer to base64 receiving browsers 131 | socket.on('getbin', function () { 132 | var buf = new Buffer('asdfasdf', 'utf8'); 133 | socket.emit('takebin', buf); 134 | }); 135 | 136 | socket.on('getHandshake', function (cb) { 137 | cb(socket.handshake); 138 | }); 139 | }); 140 | -------------------------------------------------------------------------------- /test/socket.js: -------------------------------------------------------------------------------- 1 | var expect = require('expect.js'); 2 | var io = require('../'); 3 | 4 | describe('socket', function () { 5 | this.timeout(70000); 6 | 7 | it('should have an accessible socket id equal to the server-side socket id (default namespace)', function (done) { 8 | var socket = io({ forceNew: true }); 9 | socket.on('connect', function () { 10 | expect(socket.id).to.be.ok(); 11 | expect(socket.id).to.eql(socket.io.engine.id); 12 | socket.disconnect(); 13 | done(); 14 | }); 15 | }); 16 | 17 | it('should have an accessible socket id equal to the server-side socket id (custom namespace)', function (done) { 18 | var socket = io('/foo', { forceNew: true }); 19 | socket.on('connect', function () { 20 | expect(socket.id).to.be.ok(); 21 | expect(socket.id).to.eql('/foo#' + socket.io.engine.id); 22 | socket.disconnect(); 23 | done(); 24 | }); 25 | }); 26 | 27 | it('clears socket.id upon disconnection', function (done) { 28 | var socket = io({ forceNew: true }); 29 | socket.on('connect', function () { 30 | socket.on('disconnect', function () { 31 | expect(socket.id).to.not.be.ok(); 32 | done(); 33 | }); 34 | 35 | socket.disconnect(); 36 | }); 37 | }); 38 | 39 | it('doesn\'t fire a connect_error if we force disconnect in opening state', function (done) { 40 | var socket = io({ forceNew: true, timeout: 100 }); 41 | socket.disconnect(); 42 | socket.on('connect_error', function () { 43 | throw new Error('Unexpected'); 44 | }); 45 | setTimeout(function () { 46 | done(); 47 | }, 300); 48 | }); 49 | 50 | it('should ping and pong with latency', function (done) { 51 | var socket = io({ forceNew: true }); 52 | socket.on('connect', function () { 53 | var pinged; 54 | socket.once('ping', function () { 55 | pinged = true; 56 | }); 57 | socket.once('pong', function (ms) { 58 | expect(pinged).to.be(true); 59 | expect(ms).to.be.a('number'); 60 | socket.disconnect(); 61 | done(); 62 | }); 63 | }); 64 | }); 65 | 66 | it('should change socket.id upon reconnection', function (done) { 67 | var socket = io({ forceNew: true }); 68 | socket.on('connect', function () { 69 | var id = socket.id; 70 | 71 | socket.on('reconnect_attempt', function () { 72 | expect(socket.id).to.not.be.ok(); 73 | }); 74 | 75 | socket.on('reconnect', function () { 76 | expect(socket.id).to.not.eql(id); 77 | socket.disconnect(); 78 | done(); 79 | }); 80 | 81 | socket.io.engine.close(); 82 | }); 83 | }); 84 | 85 | it('should enable compression by default', function (done) { 86 | var socket = io({ forceNew: true }); 87 | socket.on('connect', function () { 88 | socket.io.engine.once('packetCreate', function (packet) { 89 | expect(packet.options.compress).to.be(true); 90 | socket.disconnect(); 91 | done(); 92 | }); 93 | socket.emit('hi'); 94 | }); 95 | }); 96 | 97 | it('should disable compression', function (done) { 98 | var socket = io({ forceNew: true }); 99 | socket.on('connect', function () { 100 | socket.io.engine.once('packetCreate', function (packet) { 101 | expect(packet.options.compress).to.be(false); 102 | socket.disconnect(); 103 | done(); 104 | }); 105 | socket.compress(false).emit('hi'); 106 | }); 107 | }); 108 | 109 | describe('query option', function () { 110 | it('should accept an object (default namespace)', function (done) { 111 | var socket = io('/', { forceNew: true, query: { e: 'f' } }); 112 | 113 | socket.emit('getHandshake', function (handshake) { 114 | expect(handshake.query.e).to.be('f'); 115 | socket.disconnect(); 116 | done(); 117 | }); 118 | }); 119 | 120 | it('should accept a query string (default namespace)', function (done) { 121 | var socket = io('/?c=d', { forceNew: true }); 122 | 123 | socket.emit('getHandshake', function (handshake) { 124 | expect(handshake.query.c).to.be('d'); 125 | socket.disconnect(); 126 | done(); 127 | }); 128 | }); 129 | 130 | it('should accept an object', function (done) { 131 | var socket = io('/abc', {query: {a: 'b'}}); 132 | 133 | socket.on('handshake', function (handshake) { 134 | expect(handshake.query.a).to.be('b'); 135 | socket.disconnect(); 136 | done(); 137 | }); 138 | }); 139 | 140 | it('should accept a query string', function (done) { 141 | var socket = io('/abc?b=c&d=e'); 142 | 143 | socket.on('handshake', function (handshake) { 144 | expect(handshake.query.b).to.be('c'); 145 | expect(handshake.query.d).to.be('e'); 146 | socket.disconnect(); 147 | done(); 148 | }); 149 | }); 150 | 151 | it('should properly encode the parameters', function (done) { 152 | var socket = io('/abc', {query: {'&a': '&=?a'}}); 153 | 154 | socket.on('handshake', function (handshake) { 155 | expect(handshake.query['&a']).to.be('&=?a'); 156 | socket.disconnect(); 157 | done(); 158 | }); 159 | }); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /lib/socket.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var parser = require('socket.io-parser'); 7 | var Emitter = require('component-emitter'); 8 | var toArray = require('to-array'); 9 | var on = require('./on'); 10 | var bind = require('component-bind'); 11 | var debug = require('debug')('socket.io-client:socket'); 12 | var parseqs = require('parseqs'); 13 | 14 | /** 15 | * Module exports. 16 | */ 17 | 18 | module.exports = exports = Socket; 19 | 20 | /** 21 | * Internal events (blacklisted). 22 | * These events can't be emitted by the user. 23 | * 24 | * @api private 25 | */ 26 | 27 | var events = { 28 | connect: 1, 29 | connect_error: 1, 30 | connect_timeout: 1, 31 | connecting: 1, 32 | disconnect: 1, 33 | error: 1, 34 | reconnect: 1, 35 | reconnect_attempt: 1, 36 | reconnect_failed: 1, 37 | reconnect_error: 1, 38 | reconnecting: 1, 39 | ping: 1, 40 | pong: 1 41 | }; 42 | 43 | /** 44 | * Shortcut to `Emitter#emit`. 45 | */ 46 | 47 | var emit = Emitter.prototype.emit; 48 | 49 | /** 50 | * `Socket` constructor. 51 | * 52 | * @api public 53 | */ 54 | 55 | function Socket (io, nsp, opts) { 56 | this.io = io; 57 | this.nsp = nsp; 58 | this.json = this; // compat 59 | this.ids = 0; 60 | this.acks = {}; 61 | this.receiveBuffer = []; 62 | this.sendBuffer = []; 63 | this.connected = false; 64 | this.disconnected = true; 65 | if (opts && opts.query) { 66 | this.query = opts.query; 67 | } 68 | if (this.io.autoConnect) this.open(); 69 | } 70 | 71 | /** 72 | * Mix in `Emitter`. 73 | */ 74 | 75 | Emitter(Socket.prototype); 76 | 77 | /** 78 | * Subscribe to open, close and packet events 79 | * 80 | * @api private 81 | */ 82 | 83 | Socket.prototype.subEvents = function () { 84 | if (this.subs) return; 85 | 86 | var io = this.io; 87 | this.subs = [ 88 | on(io, 'open', bind(this, 'onopen')), 89 | on(io, 'packet', bind(this, 'onpacket')), 90 | on(io, 'close', bind(this, 'onclose')) 91 | ]; 92 | }; 93 | 94 | /** 95 | * "Opens" the socket. 96 | * 97 | * @api public 98 | */ 99 | 100 | Socket.prototype.open = 101 | Socket.prototype.connect = function () { 102 | if (this.connected) return this; 103 | 104 | this.subEvents(); 105 | this.io.open(); // ensure open 106 | if ('open' === this.io.readyState) this.onopen(); 107 | this.emit('connecting'); 108 | return this; 109 | }; 110 | 111 | /** 112 | * Sends a `message` event. 113 | * 114 | * @return {Socket} self 115 | * @api public 116 | */ 117 | 118 | Socket.prototype.send = function () { 119 | var args = toArray(arguments); 120 | args.unshift('message'); 121 | this.emit.apply(this, args); 122 | return this; 123 | }; 124 | 125 | /** 126 | * Override `emit`. 127 | * If the event is in `events`, it's emitted normally. 128 | * 129 | * @param {String} event name 130 | * @return {Socket} self 131 | * @api public 132 | */ 133 | 134 | Socket.prototype.emit = function (ev) { 135 | if (events.hasOwnProperty(ev)) { 136 | emit.apply(this, arguments); 137 | return this; 138 | } 139 | 140 | var args = toArray(arguments); 141 | var packet = { type: parser.EVENT, data: args }; 142 | 143 | packet.options = {}; 144 | packet.options.compress = !this.flags || false !== this.flags.compress; 145 | 146 | // event ack callback 147 | if ('function' === typeof args[args.length - 1]) { 148 | debug('emitting packet with ack id %d', this.ids); 149 | this.acks[this.ids] = args.pop(); 150 | packet.id = this.ids++; 151 | } 152 | 153 | if (this.connected) { 154 | this.packet(packet); 155 | } else { 156 | this.sendBuffer.push(packet); 157 | } 158 | 159 | delete this.flags; 160 | 161 | return this; 162 | }; 163 | 164 | /** 165 | * Sends a packet. 166 | * 167 | * @param {Object} packet 168 | * @api private 169 | */ 170 | 171 | Socket.prototype.packet = function (packet) { 172 | packet.nsp = this.nsp; 173 | this.io.packet(packet); 174 | }; 175 | 176 | /** 177 | * Called upon engine `open`. 178 | * 179 | * @api private 180 | */ 181 | 182 | Socket.prototype.onopen = function () { 183 | debug('transport is open - connecting'); 184 | 185 | // write connect packet if necessary 186 | if ('/' !== this.nsp) { 187 | if (this.query) { 188 | var query = typeof this.query === 'object' ? parseqs.encode(this.query) : this.query; 189 | debug('sending connect packet with query %s', query); 190 | this.packet({type: parser.CONNECT, query: query}); 191 | } else { 192 | this.packet({type: parser.CONNECT}); 193 | } 194 | } 195 | }; 196 | 197 | /** 198 | * Called upon engine `close`. 199 | * 200 | * @param {String} reason 201 | * @api private 202 | */ 203 | 204 | Socket.prototype.onclose = function (reason) { 205 | debug('close (%s)', reason); 206 | this.connected = false; 207 | this.disconnected = true; 208 | delete this.id; 209 | this.emit('disconnect', reason); 210 | }; 211 | 212 | /** 213 | * Called with socket packet. 214 | * 215 | * @param {Object} packet 216 | * @api private 217 | */ 218 | 219 | Socket.prototype.onpacket = function (packet) { 220 | if (packet.nsp !== this.nsp) return; 221 | 222 | switch (packet.type) { 223 | case parser.CONNECT: 224 | this.onconnect(); 225 | break; 226 | 227 | case parser.EVENT: 228 | this.onevent(packet); 229 | break; 230 | 231 | case parser.BINARY_EVENT: 232 | this.onevent(packet); 233 | break; 234 | 235 | case parser.ACK: 236 | this.onack(packet); 237 | break; 238 | 239 | case parser.BINARY_ACK: 240 | this.onack(packet); 241 | break; 242 | 243 | case parser.DISCONNECT: 244 | this.ondisconnect(); 245 | break; 246 | 247 | case parser.ERROR: 248 | this.emit('error', packet.data); 249 | break; 250 | } 251 | }; 252 | 253 | /** 254 | * Called upon a server event. 255 | * 256 | * @param {Object} packet 257 | * @api private 258 | */ 259 | 260 | Socket.prototype.onevent = function (packet) { 261 | var args = packet.data || []; 262 | debug('emitting event %j', args); 263 | 264 | if (null != packet.id) { 265 | debug('attaching ack callback to event'); 266 | args.push(this.ack(packet.id)); 267 | } 268 | 269 | if (this.connected) { 270 | emit.apply(this, args); 271 | } else { 272 | this.receiveBuffer.push(args); 273 | } 274 | }; 275 | 276 | /** 277 | * Produces an ack callback to emit with an event. 278 | * 279 | * @api private 280 | */ 281 | 282 | Socket.prototype.ack = function (id) { 283 | var self = this; 284 | var sent = false; 285 | return function () { 286 | // prevent double callbacks 287 | if (sent) return; 288 | sent = true; 289 | var args = toArray(arguments); 290 | debug('sending ack %j', args); 291 | 292 | self.packet({ 293 | type: parser.ACK, 294 | id: id, 295 | data: args 296 | }); 297 | }; 298 | }; 299 | 300 | /** 301 | * Called upon a server acknowlegement. 302 | * 303 | * @param {Object} packet 304 | * @api private 305 | */ 306 | 307 | Socket.prototype.onack = function (packet) { 308 | var ack = this.acks[packet.id]; 309 | if ('function' === typeof ack) { 310 | debug('calling ack %s with %j', packet.id, packet.data); 311 | ack.apply(this, packet.data); 312 | delete this.acks[packet.id]; 313 | } else { 314 | debug('bad ack %s', packet.id); 315 | } 316 | }; 317 | 318 | /** 319 | * Called upon server connect. 320 | * 321 | * @api private 322 | */ 323 | 324 | Socket.prototype.onconnect = function () { 325 | this.connected = true; 326 | this.disconnected = false; 327 | this.emit('connect'); 328 | this.emitBuffered(); 329 | }; 330 | 331 | /** 332 | * Emit buffered events (received and emitted). 333 | * 334 | * @api private 335 | */ 336 | 337 | Socket.prototype.emitBuffered = function () { 338 | var i; 339 | for (i = 0; i < this.receiveBuffer.length; i++) { 340 | emit.apply(this, this.receiveBuffer[i]); 341 | } 342 | this.receiveBuffer = []; 343 | 344 | for (i = 0; i < this.sendBuffer.length; i++) { 345 | this.packet(this.sendBuffer[i]); 346 | } 347 | this.sendBuffer = []; 348 | }; 349 | 350 | /** 351 | * Called upon server disconnect. 352 | * 353 | * @api private 354 | */ 355 | 356 | Socket.prototype.ondisconnect = function () { 357 | debug('server disconnect (%s)', this.nsp); 358 | this.destroy(); 359 | this.onclose('io server disconnect'); 360 | }; 361 | 362 | /** 363 | * Called upon forced client/server side disconnections, 364 | * this method ensures the manager stops tracking us and 365 | * that reconnections don't get triggered for this. 366 | * 367 | * @api private. 368 | */ 369 | 370 | Socket.prototype.destroy = function () { 371 | if (this.subs) { 372 | // clean subscriptions to avoid reconnections 373 | for (var i = 0; i < this.subs.length; i++) { 374 | this.subs[i].destroy(); 375 | } 376 | this.subs = null; 377 | } 378 | 379 | this.io.destroy(this); 380 | }; 381 | 382 | /** 383 | * Disconnects the socket manually. 384 | * 385 | * @return {Socket} self 386 | * @api public 387 | */ 388 | 389 | Socket.prototype.close = 390 | Socket.prototype.disconnect = function () { 391 | if (this.connected) { 392 | debug('performing disconnect (%s)', this.nsp); 393 | this.packet({ type: parser.DISCONNECT }); 394 | } 395 | 396 | // remove socket from pool 397 | this.destroy(); 398 | 399 | if (this.connected) { 400 | // fire events 401 | this.onclose('io client disconnect'); 402 | } 403 | return this; 404 | }; 405 | 406 | /** 407 | * Sets the compress flag. 408 | * 409 | * @param {Boolean} if `true`, compresses the sending data 410 | * @return {Socket} self 411 | * @api public 412 | */ 413 | 414 | Socket.prototype.compress = function (compress) { 415 | this.flags = this.flags || {}; 416 | this.flags.compress = compress; 417 | return this; 418 | }; 419 | -------------------------------------------------------------------------------- /lib/manager.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var eio = require('engine.io-client'); 7 | var Socket = require('./socket'); 8 | var Emitter = require('component-emitter'); 9 | var parser = require('socket.io-parser'); 10 | var on = require('./on'); 11 | var bind = require('component-bind'); 12 | var debug = require('debug')('socket.io-client:manager'); 13 | var indexOf = require('indexof'); 14 | var Backoff = require('backo2'); 15 | 16 | /** 17 | * IE6+ hasOwnProperty 18 | */ 19 | 20 | var has = Object.prototype.hasOwnProperty; 21 | 22 | /** 23 | * Module exports 24 | */ 25 | 26 | module.exports = Manager; 27 | 28 | /** 29 | * `Manager` constructor. 30 | * 31 | * @param {String} engine instance or engine uri/opts 32 | * @param {Object} options 33 | * @api public 34 | */ 35 | 36 | function Manager (uri, opts) { 37 | if (!(this instanceof Manager)) return new Manager(uri, opts); 38 | if (uri && ('object' === typeof uri)) { 39 | opts = uri; 40 | uri = undefined; 41 | } 42 | opts = opts || {}; 43 | 44 | opts.path = opts.path || '/socket.io'; 45 | this.nsps = {}; 46 | this.subs = []; 47 | this.opts = opts; 48 | this.reconnection(opts.reconnection !== false); 49 | this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); 50 | this.reconnectionDelay(opts.reconnectionDelay || 1000); 51 | this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); 52 | this.randomizationFactor(opts.randomizationFactor || 0.5); 53 | this.backoff = new Backoff({ 54 | min: this.reconnectionDelay(), 55 | max: this.reconnectionDelayMax(), 56 | jitter: this.randomizationFactor() 57 | }); 58 | this.timeout(null == opts.timeout ? 20000 : opts.timeout); 59 | this.readyState = 'closed'; 60 | this.uri = uri; 61 | this.connecting = []; 62 | this.lastPing = null; 63 | this.encoding = false; 64 | this.packetBuffer = []; 65 | var _parser = opts.parser || parser; 66 | this.encoder = new _parser.Encoder(); 67 | this.decoder = new _parser.Decoder(); 68 | this.autoConnect = opts.autoConnect !== false; 69 | if (this.autoConnect) this.open(); 70 | } 71 | 72 | /** 73 | * Propagate given event to sockets and emit on `this` 74 | * 75 | * @api private 76 | */ 77 | 78 | Manager.prototype.emitAll = function () { 79 | this.emit.apply(this, arguments); 80 | for (var nsp in this.nsps) { 81 | if (has.call(this.nsps, nsp)) { 82 | this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); 83 | } 84 | } 85 | }; 86 | 87 | /** 88 | * Update `socket.id` of all sockets 89 | * 90 | * @api private 91 | */ 92 | 93 | Manager.prototype.updateSocketIds = function () { 94 | for (var nsp in this.nsps) { 95 | if (has.call(this.nsps, nsp)) { 96 | this.nsps[nsp].id = this.generateId(nsp); 97 | } 98 | } 99 | }; 100 | 101 | /** 102 | * generate `socket.id` for the given `nsp` 103 | * 104 | * @param {String} nsp 105 | * @return {String} 106 | * @api private 107 | */ 108 | 109 | Manager.prototype.generateId = function (nsp) { 110 | return (nsp === '/' ? '' : (nsp + '#')) + this.engine.id; 111 | }; 112 | 113 | /** 114 | * Mix in `Emitter`. 115 | */ 116 | 117 | Emitter(Manager.prototype); 118 | 119 | /** 120 | * Sets the `reconnection` config. 121 | * 122 | * @param {Boolean} true/false if it should automatically reconnect 123 | * @return {Manager} self or value 124 | * @api public 125 | */ 126 | 127 | Manager.prototype.reconnection = function (v) { 128 | if (!arguments.length) return this._reconnection; 129 | this._reconnection = !!v; 130 | return this; 131 | }; 132 | 133 | /** 134 | * Sets the reconnection attempts config. 135 | * 136 | * @param {Number} max reconnection attempts before giving up 137 | * @return {Manager} self or value 138 | * @api public 139 | */ 140 | 141 | Manager.prototype.reconnectionAttempts = function (v) { 142 | if (!arguments.length) return this._reconnectionAttempts; 143 | this._reconnectionAttempts = v; 144 | return this; 145 | }; 146 | 147 | /** 148 | * Sets the delay between reconnections. 149 | * 150 | * @param {Number} delay 151 | * @return {Manager} self or value 152 | * @api public 153 | */ 154 | 155 | Manager.prototype.reconnectionDelay = function (v) { 156 | if (!arguments.length) return this._reconnectionDelay; 157 | this._reconnectionDelay = v; 158 | this.backoff && this.backoff.setMin(v); 159 | return this; 160 | }; 161 | 162 | Manager.prototype.randomizationFactor = function (v) { 163 | if (!arguments.length) return this._randomizationFactor; 164 | this._randomizationFactor = v; 165 | this.backoff && this.backoff.setJitter(v); 166 | return this; 167 | }; 168 | 169 | /** 170 | * Sets the maximum delay between reconnections. 171 | * 172 | * @param {Number} delay 173 | * @return {Manager} self or value 174 | * @api public 175 | */ 176 | 177 | Manager.prototype.reconnectionDelayMax = function (v) { 178 | if (!arguments.length) return this._reconnectionDelayMax; 179 | this._reconnectionDelayMax = v; 180 | this.backoff && this.backoff.setMax(v); 181 | return this; 182 | }; 183 | 184 | /** 185 | * Sets the connection timeout. `false` to disable 186 | * 187 | * @return {Manager} self or value 188 | * @api public 189 | */ 190 | 191 | Manager.prototype.timeout = function (v) { 192 | if (!arguments.length) return this._timeout; 193 | this._timeout = v; 194 | return this; 195 | }; 196 | 197 | /** 198 | * Starts trying to reconnect if reconnection is enabled and we have not 199 | * started reconnecting yet 200 | * 201 | * @api private 202 | */ 203 | 204 | Manager.prototype.maybeReconnectOnOpen = function () { 205 | // Only try to reconnect if it's the first time we're connecting 206 | if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { 207 | // keeps reconnection from firing twice for the same reconnection loop 208 | this.reconnect(); 209 | } 210 | }; 211 | 212 | /** 213 | * Sets the current transport `socket`. 214 | * 215 | * @param {Function} optional, callback 216 | * @return {Manager} self 217 | * @api public 218 | */ 219 | 220 | Manager.prototype.open = 221 | Manager.prototype.connect = function (fn, opts) { 222 | debug('readyState %s', this.readyState); 223 | if (~this.readyState.indexOf('open')) return this; 224 | 225 | debug('opening %s', this.uri); 226 | this.engine = eio(this.uri, this.opts); 227 | var socket = this.engine; 228 | var self = this; 229 | this.readyState = 'opening'; 230 | this.skipReconnect = false; 231 | 232 | // emit `open` 233 | var openSub = on(socket, 'open', function () { 234 | self.onopen(); 235 | fn && fn(); 236 | }); 237 | 238 | // emit `connect_error` 239 | var errorSub = on(socket, 'error', function (data) { 240 | debug('connect_error'); 241 | self.cleanup(); 242 | self.readyState = 'closed'; 243 | self.emitAll('connect_error', data); 244 | if (fn) { 245 | var err = new Error('Connection error'); 246 | err.data = data; 247 | fn(err); 248 | } else { 249 | // Only do this if there is no fn to handle the error 250 | self.maybeReconnectOnOpen(); 251 | } 252 | }); 253 | 254 | // emit `connect_timeout` 255 | if (false !== this._timeout) { 256 | var timeout = this._timeout; 257 | debug('connect attempt will timeout after %d', timeout); 258 | 259 | // set timer 260 | var timer = setTimeout(function () { 261 | debug('connect attempt timed out after %d', timeout); 262 | openSub.destroy(); 263 | socket.close(); 264 | socket.emit('error', 'timeout'); 265 | self.emitAll('connect_timeout', timeout); 266 | }, timeout); 267 | 268 | this.subs.push({ 269 | destroy: function () { 270 | clearTimeout(timer); 271 | } 272 | }); 273 | } 274 | 275 | this.subs.push(openSub); 276 | this.subs.push(errorSub); 277 | 278 | return this; 279 | }; 280 | 281 | /** 282 | * Called upon transport open. 283 | * 284 | * @api private 285 | */ 286 | 287 | Manager.prototype.onopen = function () { 288 | debug('open'); 289 | 290 | // clear old subs 291 | this.cleanup(); 292 | 293 | // mark as open 294 | this.readyState = 'open'; 295 | this.emit('open'); 296 | 297 | // add new subs 298 | var socket = this.engine; 299 | this.subs.push(on(socket, 'data', bind(this, 'ondata'))); 300 | this.subs.push(on(socket, 'ping', bind(this, 'onping'))); 301 | this.subs.push(on(socket, 'pong', bind(this, 'onpong'))); 302 | this.subs.push(on(socket, 'error', bind(this, 'onerror'))); 303 | this.subs.push(on(socket, 'close', bind(this, 'onclose'))); 304 | this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); 305 | }; 306 | 307 | /** 308 | * Called upon a ping. 309 | * 310 | * @api private 311 | */ 312 | 313 | Manager.prototype.onping = function () { 314 | this.lastPing = new Date(); 315 | this.emitAll('ping'); 316 | }; 317 | 318 | /** 319 | * Called upon a packet. 320 | * 321 | * @api private 322 | */ 323 | 324 | Manager.prototype.onpong = function () { 325 | this.emitAll('pong', new Date() - this.lastPing); 326 | }; 327 | 328 | /** 329 | * Called with data. 330 | * 331 | * @api private 332 | */ 333 | 334 | Manager.prototype.ondata = function (data) { 335 | this.decoder.add(data); 336 | }; 337 | 338 | /** 339 | * Called when parser fully decodes a packet. 340 | * 341 | * @api private 342 | */ 343 | 344 | Manager.prototype.ondecoded = function (packet) { 345 | this.emit('packet', packet); 346 | }; 347 | 348 | /** 349 | * Called upon socket error. 350 | * 351 | * @api private 352 | */ 353 | 354 | Manager.prototype.onerror = function (err) { 355 | debug('error', err); 356 | this.emitAll('error', err); 357 | }; 358 | 359 | /** 360 | * Creates a new socket for the given `nsp`. 361 | * 362 | * @return {Socket} 363 | * @api public 364 | */ 365 | 366 | Manager.prototype.socket = function (nsp, opts) { 367 | var socket = this.nsps[nsp]; 368 | if (!socket) { 369 | socket = new Socket(this, nsp, opts); 370 | this.nsps[nsp] = socket; 371 | var self = this; 372 | socket.on('connecting', onConnecting); 373 | socket.on('connect', function () { 374 | socket.id = self.generateId(nsp); 375 | }); 376 | 377 | if (this.autoConnect) { 378 | // manually call here since connecting event is fired before listening 379 | onConnecting(); 380 | } 381 | } 382 | 383 | function onConnecting () { 384 | if (!~indexOf(self.connecting, socket)) { 385 | self.connecting.push(socket); 386 | } 387 | } 388 | 389 | return socket; 390 | }; 391 | 392 | /** 393 | * Called upon a socket close. 394 | * 395 | * @param {Socket} socket 396 | */ 397 | 398 | Manager.prototype.destroy = function (socket) { 399 | var index = indexOf(this.connecting, socket); 400 | if (~index) this.connecting.splice(index, 1); 401 | if (this.connecting.length) return; 402 | 403 | this.close(); 404 | }; 405 | 406 | /** 407 | * Writes a packet. 408 | * 409 | * @param {Object} packet 410 | * @api private 411 | */ 412 | 413 | Manager.prototype.packet = function (packet) { 414 | debug('writing packet %j', packet); 415 | var self = this; 416 | if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query; 417 | 418 | if (!self.encoding) { 419 | // encode, then write to engine with result 420 | self.encoding = true; 421 | this.encoder.encode(packet, function (encodedPackets) { 422 | for (var i = 0; i < encodedPackets.length; i++) { 423 | self.engine.write(encodedPackets[i], packet.options); 424 | } 425 | self.encoding = false; 426 | self.processPacketQueue(); 427 | }); 428 | } else { // add packet to the queue 429 | self.packetBuffer.push(packet); 430 | } 431 | }; 432 | 433 | /** 434 | * If packet buffer is non-empty, begins encoding the 435 | * next packet in line. 436 | * 437 | * @api private 438 | */ 439 | 440 | Manager.prototype.processPacketQueue = function () { 441 | if (this.packetBuffer.length > 0 && !this.encoding) { 442 | var pack = this.packetBuffer.shift(); 443 | this.packet(pack); 444 | } 445 | }; 446 | 447 | /** 448 | * Clean up transport subscriptions and packet buffer. 449 | * 450 | * @api private 451 | */ 452 | 453 | Manager.prototype.cleanup = function () { 454 | debug('cleanup'); 455 | 456 | var subsLength = this.subs.length; 457 | for (var i = 0; i < subsLength; i++) { 458 | var sub = this.subs.shift(); 459 | sub.destroy(); 460 | } 461 | 462 | this.packetBuffer = []; 463 | this.encoding = false; 464 | this.lastPing = null; 465 | 466 | this.decoder.destroy(); 467 | }; 468 | 469 | /** 470 | * Close the current socket. 471 | * 472 | * @api private 473 | */ 474 | 475 | Manager.prototype.close = 476 | Manager.prototype.disconnect = function () { 477 | debug('disconnect'); 478 | this.skipReconnect = true; 479 | this.reconnecting = false; 480 | if ('opening' === this.readyState) { 481 | // `onclose` will not fire because 482 | // an open event never happened 483 | this.cleanup(); 484 | } 485 | this.backoff.reset(); 486 | this.readyState = 'closed'; 487 | if (this.engine) this.engine.close(); 488 | }; 489 | 490 | /** 491 | * Called upon engine close. 492 | * 493 | * @api private 494 | */ 495 | 496 | Manager.prototype.onclose = function (reason) { 497 | debug('onclose'); 498 | 499 | this.cleanup(); 500 | this.backoff.reset(); 501 | this.readyState = 'closed'; 502 | this.emit('close', reason); 503 | 504 | if (this._reconnection && !this.skipReconnect) { 505 | this.reconnect(); 506 | } 507 | }; 508 | 509 | /** 510 | * Attempt a reconnection. 511 | * 512 | * @api private 513 | */ 514 | 515 | Manager.prototype.reconnect = function () { 516 | if (this.reconnecting || this.skipReconnect) return this; 517 | 518 | var self = this; 519 | 520 | if (this.backoff.attempts >= this._reconnectionAttempts) { 521 | debug('reconnect failed'); 522 | this.backoff.reset(); 523 | this.emitAll('reconnect_failed'); 524 | this.reconnecting = false; 525 | } else { 526 | var delay = this.backoff.duration(); 527 | debug('will wait %dms before reconnect attempt', delay); 528 | 529 | this.reconnecting = true; 530 | var timer = setTimeout(function () { 531 | if (self.skipReconnect) return; 532 | 533 | debug('attempting reconnect'); 534 | self.emitAll('reconnect_attempt', self.backoff.attempts); 535 | self.emitAll('reconnecting', self.backoff.attempts); 536 | 537 | // check again for the case socket closed in above events 538 | if (self.skipReconnect) return; 539 | 540 | self.open(function (err) { 541 | if (err) { 542 | debug('reconnect attempt error'); 543 | self.reconnecting = false; 544 | self.reconnect(); 545 | self.emitAll('reconnect_error', err.data); 546 | } else { 547 | debug('reconnect success'); 548 | self.onreconnect(); 549 | } 550 | }); 551 | }, delay); 552 | 553 | this.subs.push({ 554 | destroy: function () { 555 | clearTimeout(timer); 556 | } 557 | }); 558 | } 559 | }; 560 | 561 | /** 562 | * Called upon successful reconnect. 563 | * 564 | * @api private 565 | */ 566 | 567 | Manager.prototype.onreconnect = function () { 568 | var attempt = this.backoff.attempts; 569 | this.reconnecting = false; 570 | this.backoff.reset(); 571 | this.updateSocketIds(); 572 | this.emitAll('reconnect', attempt); 573 | }; 574 | -------------------------------------------------------------------------------- /test/connection.js: -------------------------------------------------------------------------------- 1 | var expect = require('expect.js'); 2 | var io = require('../'); 3 | var hasCORS = require('has-cors'); 4 | var textBlobBuilder = require('text-blob-builder'); 5 | var env = require('./support/env'); 6 | 7 | describe('connection', function () { 8 | this.timeout(70000); 9 | 10 | it('should connect to localhost', function (done) { 11 | var socket = io({ forceNew: true }); 12 | socket.emit('hi'); 13 | socket.on('hi', function (data) { 14 | socket.disconnect(); 15 | done(); 16 | }); 17 | }); 18 | 19 | it('should not connect when autoConnect option set to false', function () { 20 | var socket = io({ forceNew: true, autoConnect: false }); 21 | expect(socket.io.engine).to.not.be.ok(); 22 | socket.disconnect(); 23 | }); 24 | 25 | it('should start two connections with same path', function () { 26 | var s1 = io('/'); 27 | var s2 = io('/'); 28 | 29 | expect(s1.io).to.not.be(s2.io); 30 | s1.disconnect(); 31 | s2.disconnect(); 32 | }); 33 | 34 | it('should start two connections with same path and different querystrings', function () { 35 | var s1 = io('/?woot'); 36 | var s2 = io('/'); 37 | 38 | expect(s1.io).to.not.be(s2.io); 39 | s1.disconnect(); 40 | s2.disconnect(); 41 | }); 42 | 43 | it('should work with acks', function (done) { 44 | var socket = io({ forceNew: true }); 45 | socket.emit('ack'); 46 | socket.on('ack', function (fn) { 47 | fn(5, { test: true }); 48 | }); 49 | socket.on('got it', function () { 50 | socket.disconnect(); 51 | done(); 52 | }); 53 | }); 54 | 55 | it('should receive date with ack', function (done) { 56 | var socket = io({ forceNew: true }); 57 | socket.emit('getAckDate', { test: true }, function (data) { 58 | expect(data).to.be.a('string'); 59 | socket.disconnect(); 60 | done(); 61 | }); 62 | }); 63 | 64 | it('should work with false', function (done) { 65 | var socket = io({ forceNew: true }); 66 | socket.emit('false'); 67 | socket.on('false', function (f) { 68 | expect(f).to.be(false); 69 | socket.disconnect(); 70 | done(); 71 | }); 72 | }); 73 | 74 | it('should receive utf8 multibyte characters', function (done) { 75 | var correct = [ 76 | 'てすと', 77 | 'Я Б Г Д Ж Й', 78 | 'Ä ä Ü ü ß', 79 | 'utf8 — string', 80 | 'utf8 — string' 81 | ]; 82 | 83 | var socket = io({ forceNew: true }); 84 | var i = 0; 85 | socket.on('takeUtf8', function (data) { 86 | expect(data).to.be(correct[i]); 87 | i++; 88 | if (i === correct.length) { 89 | socket.disconnect(); 90 | done(); 91 | } 92 | }); 93 | socket.emit('getUtf8'); 94 | }); 95 | 96 | it('should connect to a namespace after connection established', function (done) { 97 | var manager = io.Manager(); 98 | var socket = manager.socket('/'); 99 | socket.on('connect', function () { 100 | var foo = manager.socket('/foo'); 101 | foo.on('connect', function () { 102 | foo.close(); 103 | socket.close(); 104 | manager.close(); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | 110 | it('should open a new namespace after connection gets closed', function (done) { 111 | var manager = io.Manager(); 112 | var socket = manager.socket('/'); 113 | socket.on('connect', function () { 114 | socket.disconnect(); 115 | }).on('disconnect', function () { 116 | var foo = manager.socket('/foo'); 117 | foo.on('connect', function () { 118 | foo.disconnect(); 119 | manager.close(); 120 | done(); 121 | }); 122 | }); 123 | }); 124 | 125 | it('should reconnect by default', function (done) { 126 | var socket = io({ forceNew: true }); 127 | socket.io.on('reconnect', function () { 128 | socket.disconnect(); 129 | done(); 130 | }); 131 | 132 | setTimeout(function () { 133 | socket.io.engine.close(); 134 | }, 500); 135 | }); 136 | 137 | it('should reconnect manually', function (done) { 138 | var socket = io({ forceNew: true }); 139 | socket.once('connect', function () { 140 | socket.disconnect(); 141 | }).once('disconnect', function () { 142 | socket.once('connect', function () { 143 | socket.disconnect(); 144 | done(); 145 | }); 146 | socket.connect(); 147 | }); 148 | }); 149 | 150 | it('should reconnect automatically after reconnecting manually', function (done) { 151 | var socket = io({ forceNew: true }); 152 | socket.once('connect', function () { 153 | socket.disconnect(); 154 | }).once('disconnect', function () { 155 | socket.on('reconnect', function () { 156 | socket.disconnect(); 157 | done(); 158 | }); 159 | socket.connect(); 160 | setTimeout(function () { 161 | socket.io.engine.close(); 162 | }, 500); 163 | }); 164 | }); 165 | 166 | it('should attempt reconnects after a failed reconnect', function (done) { 167 | var manager = io.Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, reconnectionDelay: 10 }); 168 | var socket = manager.socket('/timeout'); 169 | socket.once('reconnect_failed', function () { 170 | var reconnects = 0; 171 | var reconnectCb = function () { 172 | reconnects++; 173 | }; 174 | 175 | manager.on('reconnect_attempt', reconnectCb); 176 | manager.on('reconnect_failed', function failed () { 177 | expect(reconnects).to.be(2); 178 | socket.close(); 179 | manager.close(); 180 | done(); 181 | }); 182 | socket.connect(); 183 | }); 184 | }); 185 | 186 | it('reconnect delay should increase every time', function (done) { 187 | var manager = io.Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 3, reconnectionDelay: 100, randomizationFactor: 0.2 }); 188 | var socket = manager.socket('/timeout'); 189 | var reconnects = 0; 190 | var increasingDelay = true; 191 | var startTime; 192 | var prevDelay = 0; 193 | 194 | socket.on('connect_error', function () { 195 | startTime = new Date().getTime(); 196 | }); 197 | socket.on('reconnect_attempt', function () { 198 | reconnects++; 199 | var currentTime = new Date().getTime(); 200 | var delay = currentTime - startTime; 201 | if (delay <= prevDelay) { 202 | increasingDelay = false; 203 | } 204 | prevDelay = delay; 205 | }); 206 | 207 | socket.on('reconnect_failed', function failed () { 208 | expect(reconnects).to.be(3); 209 | expect(increasingDelay).to.be.ok(); 210 | socket.close(); 211 | manager.close(); 212 | done(); 213 | }); 214 | }); 215 | 216 | it('reconnect event should fire in socket', function (done) { 217 | var socket = io({ forceNew: true }); 218 | 219 | socket.on('reconnect', function () { 220 | socket.disconnect(); 221 | done(); 222 | }); 223 | 224 | setTimeout(function () { 225 | socket.io.engine.close(); 226 | }, 500); 227 | }); 228 | 229 | it('should not reconnect when force closed', function (done) { 230 | var socket = io('/invalid', { forceNew: true, timeout: 0, reconnectionDelay: 10 }); 231 | socket.on('connect_error', function () { 232 | socket.on('reconnect_attempt', function () { 233 | expect().fail(); 234 | }); 235 | socket.disconnect(); 236 | // set a timeout to let reconnection possibly fire 237 | setTimeout(function () { 238 | done(); 239 | }, 500); 240 | }); 241 | }); 242 | 243 | it('should stop reconnecting when force closed', function (done) { 244 | var socket = io('/invalid', { forceNew: true, timeout: 0, reconnectionDelay: 10 }); 245 | socket.once('reconnect_attempt', function () { 246 | socket.on('reconnect_attempt', function () { 247 | expect().fail(); 248 | }); 249 | socket.disconnect(); 250 | // set a timeout to let reconnection possibly fire 251 | setTimeout(function () { 252 | done(); 253 | }, 500); 254 | }); 255 | }); 256 | 257 | it('should reconnect after stopping reconnection', function (done) { 258 | var socket = io('/invalid', { forceNew: true, timeout: 0, reconnectionDelay: 10 }); 259 | socket.once('reconnect_attempt', function () { 260 | socket.on('reconnect_attempt', function () { 261 | socket.disconnect(); 262 | done(); 263 | }); 264 | socket.disconnect(); 265 | socket.connect(); 266 | }); 267 | }); 268 | 269 | it('should stop reconnecting on a socket and keep to reconnect on another', function (done) { 270 | var manager = io.Manager(); 271 | var socket1 = manager.socket('/'); 272 | var socket2 = manager.socket('/asd'); 273 | 274 | manager.on('reconnect_attempt', function () { 275 | socket1.on('connect', function () { 276 | expect().fail(); 277 | }); 278 | socket2.on('connect', function () { 279 | setTimeout(function () { 280 | socket2.disconnect(); 281 | manager.disconnect(); 282 | done(); 283 | }, 500); 284 | }); 285 | socket1.disconnect(); 286 | }); 287 | 288 | setTimeout(function () { 289 | manager.engine.close(); 290 | }, 1000); 291 | }); 292 | 293 | it('should try to reconnect twice and fail when requested two attempts with immediate timeout and reconnect enabled', function (done) { 294 | var manager = io.Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, reconnectionDelay: 10 }); 295 | var socket; 296 | 297 | var reconnects = 0; 298 | var reconnectCb = function () { 299 | reconnects++; 300 | }; 301 | 302 | manager.on('reconnect_attempt', reconnectCb); 303 | manager.on('reconnect_failed', function failed () { 304 | expect(reconnects).to.be(2); 305 | socket.close(); 306 | manager.close(); 307 | done(); 308 | }); 309 | 310 | socket = manager.socket('/timeout'); 311 | }); 312 | 313 | it('should fire reconnect_* events on socket', function (done) { 314 | var manager = io.Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, reconnectionDelay: 10 }); 315 | var socket = manager.socket('/timeout_socket'); 316 | 317 | var reconnects = 0; 318 | var reconnectCb = function (attempts) { 319 | reconnects++; 320 | expect(attempts).to.be(reconnects); 321 | }; 322 | 323 | socket.on('reconnect_attempt', reconnectCb); 324 | socket.on('reconnect_failed', function failed () { 325 | expect(reconnects).to.be(2); 326 | socket.close(); 327 | manager.close(); 328 | done(); 329 | }); 330 | }); 331 | 332 | it('should fire error on socket', function (done) { 333 | var manager = io.Manager({ reconnection: true }); 334 | var socket = manager.socket('/timeout_socket'); 335 | 336 | socket.on('error', function (data) { 337 | expect(data.code).to.be('test'); 338 | socket.close(); 339 | manager.close(); 340 | done(); 341 | }); 342 | 343 | socket.on('connect', function () { 344 | manager.engine.onPacket({ type: 'error', data: 'test' }); 345 | }); 346 | }); 347 | 348 | it('should fire reconnecting (on socket) with attempts number when reconnecting twice', function (done) { 349 | var manager = io.Manager({ reconnection: true, timeout: 0, reconnectionAttempts: 2, reconnectionDelay: 10 }); 350 | var socket = manager.socket('/timeout_socket'); 351 | 352 | var reconnects = 0; 353 | var reconnectCb = function (attempts) { 354 | reconnects++; 355 | expect(attempts).to.be(reconnects); 356 | }; 357 | 358 | socket.on('reconnecting', reconnectCb); 359 | socket.on('reconnect_failed', function failed () { 360 | expect(reconnects).to.be(2); 361 | socket.close(); 362 | manager.close(); 363 | done(); 364 | }); 365 | }); 366 | 367 | it('should not try to reconnect and should form a connection when connecting to correct port with default timeout', function (done) { 368 | var manager = io.Manager({ reconnection: true, reconnectionDelay: 10 }); 369 | var cb = function () { 370 | socket.close(); 371 | expect().fail(); 372 | }; 373 | manager.on('reconnect_attempt', cb); 374 | 375 | var socket = manager.socket('/valid'); 376 | socket.on('connect', function () { 377 | // set a timeout to let reconnection possibly fire 378 | setTimeout(function () { 379 | socket.close(); 380 | manager.close(); 381 | done(); 382 | }, 1000); 383 | }); 384 | }); 385 | 386 | it('should connect while disconnecting another socket', function (done) { 387 | var manager = io.Manager(); 388 | var socket1 = manager.socket('/foo'); 389 | socket1.on('connect', function () { 390 | var socket2 = manager.socket('/asd'); 391 | socket2.on('connect', done); 392 | socket1.disconnect(); 393 | }); 394 | }); 395 | 396 | // Ignore incorrect connection test for old IE due to no support for 397 | // `script.onerror` (see: http://requirejs.org/docs/api.html#ieloadfail) 398 | if (!global.document || hasCORS) { 399 | it('should try to reconnect twice and fail when requested two attempts with incorrect address and reconnect enabled', function (done) { 400 | var manager = io.Manager('http://localhost:3940', { reconnection: true, reconnectionAttempts: 2, reconnectionDelay: 10 }); 401 | var socket = manager.socket('/asd'); 402 | var reconnects = 0; 403 | var cb = function () { 404 | reconnects++; 405 | }; 406 | 407 | manager.on('reconnect_attempt', cb); 408 | 409 | manager.on('reconnect_failed', function () { 410 | expect(reconnects).to.be(2); 411 | socket.disconnect(); 412 | manager.close(); 413 | done(); 414 | }); 415 | }); 416 | 417 | it('should not try to reconnect with incorrect port when reconnection disabled', function (done) { 418 | var manager = io.Manager('http://localhost:9823', { reconnection: false }); 419 | var cb = function () { 420 | socket.close(); 421 | expect().fail(); 422 | }; 423 | manager.on('reconnect_attempt', cb); 424 | 425 | manager.on('connect_error', function () { 426 | // set a timeout to let reconnection possibly fire 427 | setTimeout(function () { 428 | socket.disconnect(); 429 | manager.close(); 430 | done(); 431 | }, 1000); 432 | }); 433 | 434 | var socket = manager.socket('/invalid'); 435 | }); 436 | } 437 | 438 | it('should emit date as string', function (done) { 439 | var socket = io({ forceNew: true }); 440 | socket.on('takeDate', function (data) { 441 | socket.close(); 442 | expect(data).to.be.a('string'); 443 | done(); 444 | }); 445 | socket.emit('getDate'); 446 | }); 447 | 448 | it('should emit date in object', function (done) { 449 | var socket = io({ forceNew: true }); 450 | socket.on('takeDateObj', function (data) { 451 | socket.close(); 452 | expect(data).to.be.an('object'); 453 | expect(data.date).to.be.a('string'); 454 | done(); 455 | }); 456 | socket.emit('getDateObj'); 457 | }); 458 | 459 | if (!global.Blob && !global.ArrayBuffer) { 460 | it('should get base64 data as a last resort', function (done) { 461 | var socket = io({ forceNew: true }); 462 | socket.on('takebin', function (a) { 463 | socket.disconnect(); 464 | expect(a.base64).to.be(true); 465 | expect(a.data).to.eql('YXNkZmFzZGY='); 466 | done(); 467 | }); 468 | socket.emit('getbin'); 469 | }); 470 | } 471 | 472 | if (global.ArrayBuffer) { 473 | var base64 = require('base64-arraybuffer'); 474 | 475 | it('should get binary data (as an ArrayBuffer)', function (done) { 476 | var socket = io({ forceNew: true }); 477 | if (env.node) { 478 | socket.io.engine.binaryType = 'arraybuffer'; 479 | } 480 | socket.emit('doge'); 481 | socket.on('doge', function (buffer) { 482 | expect(buffer instanceof ArrayBuffer).to.be(true); 483 | socket.disconnect(); 484 | done(); 485 | }); 486 | }); 487 | 488 | it('should send binary data (as an ArrayBuffer)', function (done) { 489 | var socket = io({ forceNew: true }); 490 | socket.on('buffack', function () { 491 | socket.disconnect(); 492 | done(); 493 | }); 494 | var buf = base64.decode('asdfasdf'); 495 | socket.emit('buffa', buf); 496 | }); 497 | 498 | it('should send binary data (as an ArrayBuffer) mixed with json', function (done) { 499 | var socket = io({ forceNew: true }); 500 | socket.on('jsonbuff-ack', function () { 501 | socket.disconnect(); 502 | done(); 503 | }); 504 | var buf = base64.decode('howdy'); 505 | socket.emit('jsonbuff', {hello: 'lol', message: buf, goodbye: 'gotcha'}); 506 | }); 507 | 508 | it('should send events with ArrayBuffers in the correct order', function (done) { 509 | var socket = io({ forceNew: true }); 510 | socket.on('abuff2-ack', function () { 511 | socket.disconnect(); 512 | done(); 513 | }); 514 | var buf = base64.decode('abuff1'); 515 | socket.emit('abuff1', buf); 516 | socket.emit('abuff2', 'please arrive second'); 517 | }); 518 | } 519 | 520 | if (global.Blob && null != textBlobBuilder('xxx')) { 521 | it('should send binary data (as a Blob)', function (done) { 522 | var socket = io({ forceNew: true }); 523 | socket.on('back', function () { 524 | socket.disconnect(); 525 | done(); 526 | }); 527 | var blob = textBlobBuilder('hello world'); 528 | socket.emit('blob', blob); 529 | }); 530 | 531 | it('should send binary data (as a Blob) mixed with json', function (done) { 532 | var socket = io({ forceNew: true }); 533 | socket.on('jsonblob-ack', function () { 534 | socket.disconnect(); 535 | done(); 536 | }); 537 | var blob = textBlobBuilder('EEEEEEEEE'); 538 | socket.emit('jsonblob', {hello: 'lol', message: blob, goodbye: 'gotcha'}); 539 | }); 540 | 541 | it('should send events with Blobs in the correct order', function (done) { 542 | var socket = io({ forceNew: true }); 543 | socket.on('blob3-ack', function () { 544 | socket.disconnect(); 545 | done(); 546 | }); 547 | var blob = textBlobBuilder('BLOBBLOB'); 548 | socket.emit('blob1', blob); 549 | socket.emit('blob2', 'second'); 550 | socket.emit('blob3', blob); 551 | }); 552 | } 553 | }); 554 | -------------------------------------------------------------------------------- /docs/API.md: -------------------------------------------------------------------------------- 1 | 2 | ## Table of Contents 3 | 4 | - [IO](#io) 5 | - [io.protocol](#ioprotocol) 6 | - [io([url][, options])](#iourl-options) 7 | - [Initialization examples](#initialization-examples) 8 | - [With multiplexing](#with-multiplexing) 9 | - [With custom path](#with-custom-path) 10 | - [With query parameters](#with-query-parameters) 11 | - [With query option](#with-query-option) 12 | - [With extraHeaders](#with-extraheaders) 13 | - [With websocket transport only](#with-websocket-transport-only) 14 | - [With a custom parser](#with-a-custom-parser) 15 | - [Class: io.Manager](#manager) 16 | - [new Manager(url[, options])](#new-managerurl-options) 17 | - [manager.reconnection([value])](#managerreconnectionvalue) 18 | - [manager.reconnectionAttempts([value])](#managerreconnectionattemptsvalue) 19 | - [manager.reconnectionDelay([value])](#managerreconnectiondelayvalue) 20 | - [manager.reconnectionDelayMax([value])](#managerreconnectiondelaymaxvalue) 21 | - [manager.timeout([value])](#managertimeoutvalue) 22 | - [manager.open([callback])](#manageropencallback) 23 | - [manager.connect([callback])](#managerconnectcallback) 24 | - [manager.socket(nsp, options)](#managersocketnsp-options) 25 | - [Event: 'connect_error'](#event-connect_error) 26 | - [Event: 'connect_timeout'](#event-connect_timeout) 27 | - [Event: 'reconnect'](#event-reconnect) 28 | - [Event: 'reconnect_attempt'](#event-reconnect_attempt) 29 | - [Event: 'reconnecting'](#event-reconnecting) 30 | - [Event: 'reconnect_error'](#event-reconnect_error) 31 | - [Event: 'reconnect_failed'](#event-reconnect_failed) 32 | - [Event: 'ping'](#event-ping) 33 | - [Event: 'pong'](#event-pong) 34 | - [Class: io.Socket](#socket) 35 | - [socket.id](#socketid) 36 | - [socket.open()](#socketopen) 37 | - [socket.connect()](#socketconnect) 38 | - [socket.send([...args][, ack])](#socketsendargs-ack) 39 | - [socket.emit(eventName[, ...args][, ack])](#socketemiteventname-args-ack) 40 | - [socket.on(eventName, callback)](#socketoneventname-callback) 41 | - [socket.compress(value)](#socketcompressvalue) 42 | - [socket.close()](#socketclose) 43 | - [socket.disconnect()](#socketdisconnect) 44 | - [Event: 'connect'](#event-connect) 45 | - [Event: 'connect_error'](#event-connect_error-1) 46 | - [Event: 'connect_timeout'](#event-connect_timeout-1) 47 | - [Event: 'error'](#event-error) 48 | - [Event: 'disconnect'](#event-disconnect) 49 | - [Event: 'reconnect'](#event-reconnect-1) 50 | - [Event: 'reconnect_attempt'](#event-reconnect_attempt-1) 51 | - [Event: 'reconnecting'](#event-reconnecting-1) 52 | - [Event: 'reconnect_error'](#event-reconnect_error-1) 53 | - [Event: 'reconnect_failed'](#event-reconnect_failed-1) 54 | - [Event: 'ping'](#event-ping-1) 55 | - [Event: 'pong'](#event-pong-1) 56 | 57 | 58 | ### IO 59 | 60 | Exposed as the `io` namespace in the standalone build, or the result of calling `require('socket.io-client')`. 61 | 62 | ```html 63 | 64 | 67 | ``` 68 | 69 | ```js 70 | const io = require('socket.io-client'); 71 | // or with import syntax 72 | import io from 'socket.io-client'; 73 | ``` 74 | 75 | #### io.protocol 76 | 77 | * _(Number)_ 78 | 79 | The protocol revision number. 80 | 81 | #### io([url][, options]) 82 | 83 | - `url` _(String)_ (defaults to `window.location`) 84 | - `options` _(Object)_ 85 | - `forceNew` _(Boolean)_ whether to reuse an existing connection 86 | - **Returns** `Socket` 87 | 88 | Creates a new `Manager` for the given URL, and attempts to reuse an existing `Manager` for subsequent calls, unless the `multiplex` option is passed with `false`. Passing this option is the equivalent of passing `'force new connection': true` or `forceNew: true`. 89 | 90 | A new `Socket` instance is returned for the namespace specified by the pathname in the URL, defaulting to `/`. For example, if the `url` is `http://localhost/users`, a transport connection will be established to `http://localhost` and a Socket.IO connection will be established to `/users`. 91 | 92 | Query parameters can also be provided, either with the `query` option or directly in the url (example: `http://localhost/users?token=abc`). 93 | 94 | See [new Manager(url[, options])](#new-managerurl-options) for available `options`. 95 | 96 | #### Initialization examples 97 | 98 | ##### With multiplexing 99 | 100 | By default, a single connection is used when connecting to different namespaces (to minimize resources): 101 | 102 | ```js 103 | const socket = io(); 104 | const adminSocket = io('/admin'); 105 | // a single connection will be established 106 | ``` 107 | 108 | That behaviour can be disabled with the `forceNew` option: 109 | 110 | ```js 111 | const socket = io(); 112 | const adminSocket = io('/admin', { forceNew: true }); 113 | // will create two distinct connections 114 | ``` 115 | 116 | Note: reusing the same namespace will also create two connections 117 | 118 | ```js 119 | const socket = io(); 120 | const socket2 = io(); 121 | // will also create two distinct connections 122 | ``` 123 | 124 | ##### With custom `path` 125 | 126 | ```js 127 | const socket = io('http://localhost', { 128 | path: '/myownpath' 129 | }); 130 | 131 | // server-side 132 | const io = require('socket.io')({ 133 | path: '/myownpath' 134 | }); 135 | ``` 136 | 137 | The request URLs will look like: `localhost/myownpath/?EIO=3&transport=polling&sid=` 138 | 139 | ```js 140 | const socket = io('http://localhost/admin', { 141 | path: '/mypath' 142 | }); 143 | ``` 144 | 145 | Here, the socket connects to the `admin` namespace, with the custom path `mypath`. 146 | 147 | The request URLs will look like: `localhost/mypath/?EIO=3&transport=polling&sid=` (the namespace is sent as part of the payload). 148 | 149 | ##### With query parameters 150 | 151 | ```js 152 | const socket = io('http://localhost?token=abc'); 153 | 154 | // server-side 155 | const io = require('socket.io')(); 156 | 157 | // middleware 158 | io.use((socket, next) => { 159 | let token = socket.handshake.query.token; 160 | if (isValid(token)) { 161 | return next(); 162 | } 163 | return next(new Error('authentication error')); 164 | }); 165 | 166 | // then 167 | io.on('connection', (socket) => { 168 | let token = socket.handshake.query.token; 169 | // ... 170 | }); 171 | ``` 172 | 173 | ##### With query option 174 | 175 | ```js 176 | const socket = io({ 177 | query: { 178 | token: 'cde' 179 | } 180 | }); 181 | ``` 182 | 183 | The query content can also be updated on reconnection: 184 | 185 | ```js 186 | socket.on('reconnect_attempt', () => { 187 | socket.io.opts.query = { 188 | token: 'fgh' 189 | } 190 | }); 191 | ``` 192 | 193 | ##### With `extraHeaders` 194 | 195 | This only works if `polling` transport is enabled (which is the default). Custom headers will not be appended when using `websocket` as the transport. This happens because the WebSocket handshake does not honor custom headers. (For background see the [WebSocket protocol RFC](https://tools.ietf.org/html/rfc6455#section-4)) 196 | 197 | ```js 198 | const socket = io({ 199 | transportOptions: { 200 | polling: { 201 | extraHeaders: { 202 | 'x-clientid': 'abc' 203 | } 204 | } 205 | } 206 | }); 207 | 208 | // server-side 209 | const io = require('socket.io')(); 210 | 211 | // middleware 212 | io.use((socket, next) => { 213 | let clientId = socket.handshake.headers['x-clientid']; 214 | if (isValid(clientId)) { 215 | return next(); 216 | } 217 | return next(new Error('authentication error')); 218 | }); 219 | ``` 220 | 221 | ##### With `websocket` transport only 222 | 223 | By default, a long-polling connection is established first, then upgraded to "better" transports (like WebSocket). If you like to live dangerously, this part can be skipped: 224 | 225 | ```js 226 | const socket = io({ 227 | transports: ['websocket'] 228 | }); 229 | 230 | // on reconnection, reset the transports option, as the Websocket 231 | // connection may have failed (caused by proxy, firewall, browser, ...) 232 | socket.on('reconnect_attempt', () => { 233 | socket.io.opts.transports = ['polling', 'websocket']; 234 | }); 235 | ``` 236 | 237 | ##### With a custom parser 238 | 239 | The default [parser](https://github.com/socketio/socket.io-parser) promotes compatibility (support for `Blob`, `File`, binary check) at the expense of performance. A custom parser can be provided to match the needs of your application. Please see the example [here](https://github.com/socketio/socket.io/tree/master/examples/custom-parsers). 240 | 241 | ```js 242 | const parser = require('socket.io-msgpack-parser'); // or require('socket.io-json-parser') 243 | const socket = io({ 244 | parser: parser 245 | }); 246 | 247 | // the server-side must have the same parser, to be able to communicate 248 | const io = require('socket.io')({ 249 | parser: parser 250 | }); 251 | ``` 252 | 253 | ### Manager 254 | 255 | #### new Manager(url[, options]) 256 | 257 | - `url` _(String)_ 258 | - `options` _(Object)_ 259 | - `path` _(String)_ name of the path that is captured on the server side (`/socket.io`) 260 | - `reconnection` _(Boolean)_ whether to reconnect automatically (`true`) 261 | - `reconnectionAttempts` _(Number)_ number of reconnection attempts before giving up (`Infinity`) 262 | - `reconnectionDelay` _(Number)_ how long to initially wait before attempting a new 263 | reconnection (`1000`). Affected by +/- `randomizationFactor`, 264 | for example the default initial delay will be between 500 to 1500ms. 265 | - `reconnectionDelayMax` _(Number)_ maximum amount of time to wait between 266 | reconnections (`5000`). Each attempt increases the reconnection delay by 2x 267 | along with a randomization as above 268 | - `randomizationFactor` _(Number)_ (`0.5`), 0 <= randomizationFactor <= 1 269 | - `timeout` _(Number)_ connection timeout before a `connect_error` 270 | and `connect_timeout` events are emitted (`20000`) 271 | - `autoConnect` _(Boolean)_ by setting this false, you have to call `manager.open` 272 | whenever you decide it's appropriate 273 | - `query` _(Object)_: additional query parameters that are sent when connecting a namespace (then found in `socket.handshake.query` object on the server-side) 274 | - `parser` _(Parser)_: the parser to use. Defaults to an instance of the `Parser` that ships with socket.io. See [socket.io-parser](https://github.com/socketio/socket.io-parser). 275 | - **Returns** `Manager` 276 | 277 | The `options` are also passed to `engine.io-client` upon initialization of the underlying `Socket`. See the available `options` [here](https://github.com/socketio/engine.io-client#methods). 278 | 279 | #### manager.reconnection([value]) 280 | 281 | - `value` _(Boolean)_ 282 | - **Returns** `Manager|Boolean` 283 | 284 | Sets the `reconnection` option, or returns it if no parameters are passed. 285 | 286 | #### manager.reconnectionAttempts([value]) 287 | 288 | - `value` _(Number)_ 289 | - **Returns** `Manager|Number` 290 | 291 | Sets the `reconnectionAttempts` option, or returns it if no parameters are passed. 292 | 293 | #### manager.reconnectionDelay([value]) 294 | 295 | - `value` _(Number)_ 296 | - **Returns** `Manager|Number` 297 | 298 | Sets the `reconnectionDelay` option, or returns it if no parameters are passed. 299 | 300 | #### manager.reconnectionDelayMax([value]) 301 | 302 | - `value` _(Number)_ 303 | - **Returns** `Manager|Number` 304 | 305 | Sets the `reconnectionDelayMax` option, or returns it if no parameters are passed. 306 | 307 | #### manager.timeout([value]) 308 | 309 | - `value` _(Number)_ 310 | - **Returns** `Manager|Number` 311 | 312 | Sets the `timeout` option, or returns it if no parameters are passed. 313 | 314 | #### manager.open([callback]) 315 | 316 | - `callback` _(Function)_ 317 | - **Returns** `Manager` 318 | 319 | If the manager was initiated with `autoConnect` to `false`, launch a new connection attempt. 320 | 321 | The `callback` argument is optional and will be called once the attempt fails/succeeds. 322 | 323 | #### manager.connect([callback]) 324 | 325 | Synonym of [manager.open([callback])](#manageropencallback). 326 | 327 | #### manager.socket(nsp, options) 328 | 329 | - `nsp` _(String)_ 330 | - `options` _(Object)_ 331 | - **Returns** `Socket` 332 | 333 | Creates a new `Socket` for the given namespace. 334 | 335 | #### Event: 'connect_error' 336 | 337 | - `error` _(Object)_ error object 338 | 339 | Fired upon a connection error. 340 | 341 | #### Event: 'connect_timeout' 342 | 343 | Fired upon a connection timeout. 344 | 345 | #### Event: 'reconnect' 346 | 347 | - `attempt` _(Number)_ reconnection attempt number 348 | 349 | Fired upon a successful reconnection. 350 | 351 | #### Event: 'reconnect_attempt' 352 | 353 | Fired upon an attempt to reconnect. 354 | 355 | #### Event: 'reconnecting' 356 | 357 | - `attempt` _(Number)_ reconnection attempt number 358 | 359 | Fired upon a successful reconnection. 360 | 361 | #### Event: 'reconnect_error' 362 | 363 | - `error` _(Object)_ error object 364 | 365 | Fired upon a reconnection attempt error. 366 | 367 | #### Event: 'reconnect_failed' 368 | 369 | Fired when couldn't reconnect within `reconnectionAttempts`. 370 | 371 | #### Event: 'ping' 372 | 373 | Fired when a ping packet is written out to the server. 374 | 375 | #### Event: 'pong' 376 | 377 | - `ms` _(Number)_ number of ms elapsed since `ping` packet (i.e.: latency). 378 | 379 | Fired when a pong is received from the server. 380 | 381 | ### Socket 382 | 383 | #### socket.id 384 | 385 | - _(String)_ 386 | 387 | An unique identifier for the socket session. Set after the `connect` event is triggered, and updated after the `reconnect` event. 388 | 389 | ```js 390 | const socket = io('http://localhost'); 391 | 392 | console.log(socket.id); // undefined 393 | 394 | socket.on('connect', () => { 395 | console.log(socket.id); // 'G5p5...' 396 | }); 397 | ``` 398 | 399 | #### socket.open() 400 | 401 | - **Returns** `Socket` 402 | 403 | Manually opens the socket. 404 | 405 | ```js 406 | const socket = io({ 407 | autoConnect: false 408 | }); 409 | 410 | // ... 411 | socket.open(); 412 | ``` 413 | 414 | It can also be used to manually reconnect: 415 | 416 | ```js 417 | socket.on('disconnect', () => { 418 | socket.open(); 419 | }); 420 | ``` 421 | 422 | #### socket.connect() 423 | 424 | Synonym of [socket.open()](#socketopen). 425 | 426 | #### socket.send([...args][, ack]) 427 | 428 | - `args` 429 | - `ack` _(Function)_ 430 | - **Returns** `Socket` 431 | 432 | Sends a `message` event. See [socket.emit(eventName[, ...args][, ack])](#socketemiteventname-args-ack). 433 | 434 | #### socket.emit(eventName[, ...args][, ack]) 435 | 436 | - `eventName` _(String)_ 437 | - `args` 438 | - `ack` _(Function)_ 439 | - **Returns** `Socket` 440 | 441 | Emits an event to the socket identified by the string name. Any other parameters can be included. All serializable datastructures are supported, including `Buffer`. 442 | 443 | ```js 444 | socket.emit('hello', 'world'); 445 | socket.emit('with-binary', 1, '2', { 3: '4', 5: new Buffer(6) }); 446 | ``` 447 | 448 | The `ack` argument is optional and will be called with the server answer. 449 | 450 | ```js 451 | socket.emit('ferret', 'tobi', (data) => { 452 | console.log(data); // data will be 'woot' 453 | }); 454 | 455 | // server: 456 | // io.on('connection', (socket) => { 457 | // socket.on('ferret', (name, fn) => { 458 | // fn('woot'); 459 | // }); 460 | // }); 461 | ``` 462 | 463 | #### socket.on(eventName, callback) 464 | 465 | - `eventName` _(String)_ 466 | - `callback` _(Function)_ 467 | - **Returns** `Socket` 468 | 469 | Register a new handler for the given event. 470 | 471 | ```js 472 | socket.on('news', (data) => { 473 | console.log(data); 474 | }); 475 | 476 | // with multiple arguments 477 | socket.on('news', (arg1, arg2, arg3, arg4) => { 478 | // ... 479 | }); 480 | // with callback 481 | socket.on('news', (cb) => { 482 | cb(0); 483 | }); 484 | ``` 485 | 486 | The socket actually inherits every method of the [Emitter](https://github.com/component/emitter) class, like `hasListeners`, `once` or `off` (to remove an event listener). 487 | 488 | #### socket.compress(value) 489 | 490 | - `value` _(Boolean)_ 491 | - **Returns** `Socket` 492 | 493 | Sets a modifier for a subsequent event emission that the event data will only be _compressed_ if the value is `true`. Defaults to `true` when you don't call the method. 494 | 495 | ```js 496 | socket.compress(false).emit('an event', { some: 'data' }); 497 | ``` 498 | 499 | #### socket.close() 500 | 501 | - **Returns** `Socket` 502 | 503 | Disconnects the socket manually. 504 | 505 | #### socket.disconnect() 506 | 507 | Synonym of [socket.close()](#socketclose). 508 | 509 | #### Event: 'connect' 510 | 511 | Fired upon a connection including a successful reconnection. 512 | 513 | ```js 514 | socket.on('connect', () => { 515 | // ... 516 | }); 517 | 518 | // note: you should register event handlers outside of connect, 519 | // so they are not registered again on reconnection 520 | socket.on('myevent', () => { 521 | // ... 522 | }); 523 | ``` 524 | 525 | #### Event: 'connect_error' 526 | 527 | - `error` _(Object)_ error object 528 | 529 | Fired upon a connection error. 530 | 531 | ```js 532 | socket.on('connect_error', (error) => { 533 | // ... 534 | }); 535 | ``` 536 | 537 | #### Event: 'connect_timeout' 538 | 539 | Fired upon a connection timeout. 540 | 541 | ```js 542 | socket.on('connect_timeout', (timeout) => { 543 | // ... 544 | }); 545 | ``` 546 | 547 | #### Event: 'error' 548 | 549 | - `error` _(Object)_ error object 550 | 551 | Fired when an error occurs. 552 | 553 | ```js 554 | socket.on('error', (error) => { 555 | // ... 556 | }); 557 | ``` 558 | 559 | #### Event: 'disconnect' 560 | 561 | - `reason` _(String)_ either 'io server disconnect' or 'io client disconnect' 562 | 563 | Fired upon a disconnection. 564 | 565 | ```js 566 | socket.on('disconnect', (reason) => { 567 | // ... 568 | }); 569 | ``` 570 | 571 | #### Event: 'reconnect' 572 | 573 | - `attempt` _(Number)_ reconnection attempt number 574 | 575 | Fired upon a successful reconnection. 576 | 577 | ```js 578 | socket.on('reconnect', (attemptNumber) => { 579 | // ... 580 | }); 581 | ``` 582 | 583 | #### Event: 'reconnect_attempt' 584 | 585 | - `attempt` _(Number)_ reconnection attempt number 586 | 587 | Fired upon an attempt to reconnect. 588 | 589 | ```js 590 | socket.on('reconnect_attempt', (attemptNumber) => { 591 | // ... 592 | }); 593 | ``` 594 | 595 | #### Event: 'reconnecting' 596 | 597 | - `attempt` _(Number)_ reconnection attempt number 598 | 599 | Fired upon an attempt to reconnect. 600 | 601 | ```js 602 | socket.on('reconnecting', (attemptNumber) => { 603 | // ... 604 | }); 605 | ``` 606 | 607 | #### Event: 'reconnect_error' 608 | 609 | - `error` _(Object)_ error object 610 | 611 | Fired upon a reconnection attempt error. 612 | 613 | ```js 614 | socket.on('reconnect_error', (error) => { 615 | // ... 616 | }); 617 | ``` 618 | 619 | #### Event: 'reconnect_failed' 620 | 621 | Fired when couldn't reconnect within `reconnectionAttempts`. 622 | 623 | ```js 624 | socket.on('reconnect_failed', () => { 625 | // ... 626 | }); 627 | ``` 628 | 629 | #### Event: 'ping' 630 | 631 | Fired when a ping packet is written out to the server. 632 | 633 | ```js 634 | socket.on('ping', () => { 635 | // ... 636 | }); 637 | ``` 638 | 639 | #### Event: 'pong' 640 | 641 | - `ms` _(Number)_ number of ms elapsed since `ping` packet (i.e.: latency). 642 | 643 | Fired when a pong is received from the server. 644 | 645 | ```js 646 | socket.on('pong', (latency) => { 647 | // ... 648 | }); 649 | ``` 650 | -------------------------------------------------------------------------------- /dist/socket.io.slim.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.io=e():t.io=e()}(this,function(){return function(t){function e(n){if(r[n])return r[n].exports;var o=r[n]={exports:{},id:n,loaded:!1};return t[n].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function n(t,e){"object"===("undefined"==typeof t?"undefined":o(t))&&(e=t,t=void 0),e=e||{};var r,n=i(t),s=n.source,h=n.id,p=n.path,u=c[h]&&p in c[h].nsps,f=e.forceNew||e["force new connection"]||!1===e.multiplex||u;return f?r=a(s,e):(c[h]||(c[h]=a(s,e)),r=c[h]),n.query&&!e.query&&(e.query=n.query),r.socket(n.path,e)}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=r(1),s=r(4),a=r(10);r(3)("socket.io-client");t.exports=e=n;var c=e.managers={};e.protocol=s.protocol,e.connect=n,e.Manager=r(10),e.Socket=r(34)},function(t,e,r){(function(e){"use strict";function n(t,r){var n=t;r=r||e.location,null==t&&(t=r.protocol+"//"+r.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?r.protocol+t:r.host+t),/^(https?|wss?):\/\//.test(t)||(t="undefined"!=typeof r?r.protocol+"//"+t:"https://"+t),n=o(t)),n.port||(/^(http|ws)$/.test(n.protocol)?n.port="80":/^(http|ws)s$/.test(n.protocol)&&(n.port="443")),n.path=n.path||"/";var i=n.host.indexOf(":")!==-1,s=i?"["+n.host+"]":n.host;return n.id=n.protocol+"://"+s+":"+n.port,n.href=n.protocol+"://"+s+(r&&r.port===n.port?"":":"+n.port),n}var o=r(2);r(3)("socket.io-client:url");t.exports=n}).call(e,function(){return this}())},function(t,e){var r=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,n=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");o!=-1&&i!=-1&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s=r.exec(t||""),a={},c=14;c--;)a[n[c]]=s[c]||"";return o!=-1&&i!=-1&&(a.source=e,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a}},function(t,e){"use strict";t.exports=function(){return function(){}}},function(t,e,r){function n(){}function o(t){var r=""+t.type;return e.BINARY_EVENT!==t.type&&e.BINARY_ACK!==t.type||(r+=t.attachments+"-"),t.nsp&&"/"!==t.nsp&&(r+=t.nsp+","),null!=t.id&&(r+=t.id),null!=t.data&&(r+=JSON.stringify(t.data)),r}function i(t,e){function r(t){var r=l.deconstructPacket(t),n=o(r.packet),i=r.buffers;i.unshift(n),e(i)}l.removeBlobs(t,r)}function s(){this.reconstructor=null}function a(t){var r=0,n={type:Number(t.charAt(0))};if(null==e.types[n.type])return p();if(e.BINARY_EVENT===n.type||e.BINARY_ACK===n.type){for(var o="";"-"!==t.charAt(++r)&&(o+=t.charAt(r),r!=t.length););if(o!=Number(o)||"-"!==t.charAt(r))throw new Error("Illegal attachments");n.attachments=Number(o)}if("/"===t.charAt(r+1))for(n.nsp="";++r;){var i=t.charAt(r);if(","===i)break;if(n.nsp+=i,r===t.length)break}else n.nsp="/";var s=t.charAt(r+1);if(""!==s&&Number(s)==s){for(n.id="";++r;){var i=t.charAt(r);if(null==i||Number(i)!=i){--r;break}if(n.id+=t.charAt(r),r===t.length)break}n.id=Number(n.id)}return t.charAt(++r)&&(n=c(n,t.substr(r))),n}function c(t,e){try{t.data=JSON.parse(e)}catch(t){return p()}return t}function h(t){this.reconPack=t,this.buffers=[]}function p(){return{type:e.ERROR,data:"parser error"}}var u=(r(3)("socket.io-parser"),r(5)),f=r(6),l=r(8),d=r(9);e.protocol=4,e.types=["CONNECT","DISCONNECT","EVENT","ACK","ERROR","BINARY_EVENT","BINARY_ACK"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=n,e.Decoder=s,n.prototype.encode=function(t,r){if(t.type!==e.EVENT&&t.type!==e.ACK||!f(t.data)||(t.type=t.type===e.EVENT?e.BINARY_EVENT:e.BINARY_ACK),e.BINARY_EVENT===t.type||e.BINARY_ACK===t.type)i(t,r);else{var n=o(t);r([n])}},u(s.prototype),s.prototype.add=function(t){var r;if("string"==typeof t)r=a(t),e.BINARY_EVENT===r.type||e.BINARY_ACK===r.type?(this.reconstructor=new h(r),0===this.reconstructor.reconPack.attachments&&this.emit("decoded",r)):this.emit("decoded",r);else{if(!d(t)&&!t.base64)throw new Error("Unknown type: "+t);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");r=this.reconstructor.takeBinaryData(t),r&&(this.reconstructor=null,this.emit("decoded",r))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},h.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=l.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},h.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,r){function n(t){if(t)return o(t)}function o(t){for(var e in n.prototype)t[e]=n.prototype[e];return t}t.exports=n,n.prototype.on=n.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},n.prototype.once=function(t,e){function r(){this.off(t,r),e.apply(this,arguments)}return r.fn=e,this.on(t,r),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var r=this._callbacks["$"+t];if(!r)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var n,o=0;o0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},n.prototype.cleanup=function(){for(var t=this.subs.length,e=0;e=this._reconnectionAttempts)this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var e=this.backoff.duration();this.reconnecting=!0;var r=setTimeout(function(){t.skipReconnect||(t.emitAll("reconnect_attempt",t.backoff.attempts),t.emitAll("reconnecting",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(t.reconnecting=!1,t.reconnect(),t.emitAll("reconnect_error",e.data)):t.onreconnect()}))},e);this.subs.push({destroy:function(){clearTimeout(r)}})}},n.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",t)}},function(t,e,r){t.exports=r(12),t.exports.parser=r(19)},function(t,e,r){(function(e){function n(t,r){if(!(this instanceof n))return new n(t,r);r=r||{},t&&"object"==typeof t&&(r=t,t=null),t?(t=h(t),r.hostname=t.host,r.secure="https"===t.protocol||"wss"===t.protocol,r.port=t.port,t.query&&(r.query=t.query)):r.host&&(r.hostname=h(r.host).host),this.secure=null!=r.secure?r.secure:e.location&&"https:"===location.protocol,r.hostname&&!r.port&&(r.port=this.secure?"443":"80"),this.agent=r.agent||!1,this.hostname=r.hostname||(e.location?location.hostname:"localhost"),this.port=r.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=r.query||{},"string"==typeof this.query&&(this.query=p.decode(this.query)),this.upgrade=!1!==r.upgrade,this.path=(r.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!r.forceJSONP,this.jsonp=!1!==r.jsonp,this.forceBase64=!!r.forceBase64,this.enablesXDR=!!r.enablesXDR,this.timestampParam=r.timestampParam||"t",this.timestampRequests=r.timestampRequests,this.transports=r.transports||["polling","websocket"],this.transportOptions=r.transportOptions||{},this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=r.policyPort||843,this.rememberUpgrade=r.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=r.onlyBinaryUpgrades,this.perMessageDeflate=!1!==r.perMessageDeflate&&(r.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=r.pfx||null,this.key=r.key||null,this.passphrase=r.passphrase||null,this.cert=r.cert||null,this.ca=r.ca||null,this.ciphers=r.ciphers||null,this.rejectUnauthorized=void 0===r.rejectUnauthorized||r.rejectUnauthorized,this.forceNode=!!r.forceNode;var o="object"==typeof e&&e;o.global===o&&(r.extraHeaders&&Object.keys(r.extraHeaders).length>0&&(this.extraHeaders=r.extraHeaders),r.localAddress&&(this.localAddress=r.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}var i=r(13),s=r(5),a=(r(3)("engine.io-client:socket"),r(33)),c=r(19),h=r(2),p=r(27);t.exports=n,n.priorWebsocketSuccess=!1,s(n.prototype),n.protocol=c.protocol,n.Socket=n,n.Transport=r(18),n.transports=r(13),n.parser=r(19),n.prototype.createTransport=function(t){var e=o(this.query);e.EIO=c.protocol,e.transport=t;var r=this.transportOptions[t]||{};this.id&&(e.sid=this.id);var n=new i[t]({query:e,socket:this,agent:r.agent||this.agent,hostname:r.hostname||this.hostname,port:r.port||this.port,secure:r.secure||this.secure,path:r.path||this.path,forceJSONP:r.forceJSONP||this.forceJSONP,jsonp:r.jsonp||this.jsonp,forceBase64:r.forceBase64||this.forceBase64,enablesXDR:r.enablesXDR||this.enablesXDR,timestampRequests:r.timestampRequests||this.timestampRequests,timestampParam:r.timestampParam||this.timestampParam,policyPort:r.policyPort||this.policyPort,pfx:r.pfx||this.pfx,key:r.key||this.key,passphrase:r.passphrase||this.passphrase,cert:r.cert||this.cert,ca:r.ca||this.ca,ciphers:r.ciphers||this.ciphers,rejectUnauthorized:r.rejectUnauthorized||this.rejectUnauthorized,perMessageDeflate:r.perMessageDeflate||this.perMessageDeflate,extraHeaders:r.extraHeaders||this.extraHeaders,forceNode:r.forceNode||this.forceNode,localAddress:r.localAddress||this.localAddress,requestTimeout:r.requestTimeout||this.requestTimeout,protocols:r.protocols||void 0});return n},n.prototype.open=function(){var t;if(this.rememberUpgrade&&n.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1)t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit("error","No transports available")},0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},n.prototype.setTransport=function(t){var e=this;this.transport&&this.transport.removeAllListeners(),this.transport=t,t.on("drain",function(){e.onDrain()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},n.prototype.probe=function(t){function e(){if(u.onlyBinaryUpgrades){var t=!this.supportsBinary&&u.transport.supportsBinary;p=p||t}p||(h.send([{type:"ping",data:"probe"}]),h.once("packet",function(t){if(!p)if("pong"===t.type&&"probe"===t.data){if(u.upgrading=!0,u.emit("upgrading",h),!h)return;n.priorWebsocketSuccess="websocket"===h.name,u.transport.pause(function(){p||"closed"!==u.readyState&&(c(),u.setTransport(h),h.send([{type:"upgrade"}]),u.emit("upgrade",h),h=null,u.upgrading=!1,u.flush())})}else{var e=new Error("probe error");e.transport=h.name,u.emit("upgradeError",e)}}))}function r(){p||(p=!0,c(),h.close(),h=null)}function o(t){var e=new Error("probe error: "+t);e.transport=h.name,r(),u.emit("upgradeError",e)}function i(){o("transport closed")}function s(){o("socket closed")}function a(t){h&&t.name!==h.name&&r()}function c(){h.removeListener("open",e),h.removeListener("error",o),h.removeListener("close",i),u.removeListener("close",s),u.removeListener("upgrading",a)}var h=this.createTransport(t,{probe:1}),p=!1,u=this;n.priorWebsocketSuccess=!1,h.once("open",e),h.once("error",o),h.once("close",i),this.once("close",s),this.once("upgrading",a),h.open()},n.prototype.onOpen=function(){if(this.readyState="open",n.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.upgrade&&this.transport.pause)for(var t=0,e=this.upgrades.length;t1?{type:b[o],data:t.substring(1)}:{type:b[o]}:k}var i=new Uint8Array(t),o=i[0],s=f(t,1);return w&&"blob"===r&&(s=new w([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var r=b[t.charAt(0)];if(!h)return{type:r,data:{base64:!0,data:t.substr(1)}};var n=h.decode(t.substr(1));return"blob"===e&&w&&(n=new w([n])),{type:r,data:n}},e.encodePayload=function(t,r,n){function o(t){return t.length+":"+t}function i(t,n){e.encodePacket(t,!!s&&r,!1,function(t){n(null,o(t))})}"function"==typeof r&&(n=r,r=null);var s=u(t);return r&&s?w&&!g?e.encodePayloadAsBlob(t,n):e.encodePayloadAsArrayBuffer(t,n):t.length?void c(t,i,function(t,e){return n(e.join(""))}):n("0:")},e.decodePayload=function(t,r,n){if("string"!=typeof t)return e.decodePayloadAsBinary(t,r,n);"function"==typeof r&&(n=r,r=null);var o;if(""===t)return n(k,0,1);for(var i,s,a="",c=0,h=t.length;c0;){for(var s=new Uint8Array(o),a=0===s[0],c="",h=1;255!==s[h];h++){if(c.length>310)return n(k,0,1);c+=s[h]}o=f(o,2+c.length),c=parseInt(c);var p=f(o,0,c);if(a)try{p=String.fromCharCode.apply(null,new Uint8Array(p))}catch(t){var u=new Uint8Array(p);p="";for(var h=0;hn&&(r=n),e>=n||e>=r||0===n)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(r-e),s=e,a=0;s=55296&&e<=56319&&o65535&&(e-=65536,o+=k(e>>>10&1023|55296),e=56320|1023&e),o+=k(e);return o}function c(t,e){if(t>=55296&&t<=57343){if(e)throw Error("Lone surrogate U+"+t.toString(16).toUpperCase()+" is not a scalar value");return!1}return!0}function h(t,e){return k(t>>e&63|128)}function p(t,e){if(0==(4294967168&t))return k(t);var r="";return 0==(4294965248&t)?r=k(t>>6&31|192):0==(4294901760&t)?(c(t,e)||(t=65533),r=k(t>>12&15|224),r+=h(t,6)):0==(4292870144&t)&&(r=k(t>>18&7|240),r+=h(t,12),r+=h(t,6)),r+=k(63&t|128)}function u(t,e){e=e||{};for(var r,n=!1!==e.strict,o=s(t),i=o.length,a=-1,c="";++a=v)throw Error("Invalid byte index");var t=255&g[b];if(b++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function l(t){var e,r,n,o,i;if(b>v)throw Error("Invalid byte index");if(b==v)return!1;if(e=255&g[b],b++,0==(128&e))return e;if(192==(224&e)){if(r=f(),i=(31&e)<<6|r,i>=128)return i;throw Error("Invalid continuation byte")}if(224==(240&e)){if(r=f(),n=f(),i=(15&e)<<12|r<<6|n,i>=2048)return c(i,t)?i:65533;throw Error("Invalid continuation byte")}if(240==(248&e)&&(r=f(),n=f(),o=f(),i=(7&e)<<18|r<<12|n<<6|o,i>=65536&&i<=1114111))return i;throw Error("Invalid UTF-8 detected")}function d(t,e){e=e||{};var r=!1!==e.strict;g=s(t),v=g.length,b=0;for(var n,o=[];(n=l(r))!==!1;)o.push(n);return a(o)}var y="object"==typeof e&&e,m=("object"==typeof t&&t&&t.exports==y&&t,"object"==typeof o&&o);m.global!==m&&m.window!==m||(i=m);var g,v,b,k=String.fromCharCode,w={version:"2.1.2",encode:u,decode:d};n=function(){return w}.call(e,r,e,t),!(void 0!==n&&(t.exports=n))}(this)}).call(e,r(24)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){!function(){"use strict";for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r=new Uint8Array(256),n=0;n>2],i+=t[(3&n[r])<<4|n[r+1]>>4],i+=t[(15&n[r+1])<<2|n[r+2]>>6],i+=t[63&n[r+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(t){var e,n,o,i,s,a=.75*t.length,c=t.length,h=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var p=new ArrayBuffer(a),u=new Uint8Array(p);for(e=0;e>4,u[h++]=(15&o)<<4|i>>2,u[h++]=(3&i)<<6|63&s;return p}}()},function(t,e){(function(e){function r(t){for(var e=0;e0);return e}function n(t){var e=0;for(p=0;p';i=document.createElement(t)}catch(t){i=document.createElement("iframe"),i.name=o.iframeId,i.src="javascript:0"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement("form"),a=document.createElement("textarea"),p=this.iframeId="eio_iframe_"+this.index;s.className="socketio",s.style.position="absolute",s.style.top="-1000px",s.style.left="-1000px",s.target=p,s.method="POST",s.setAttribute("accept-charset","utf-8"),a.name="d",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),n(),t=t.replace(h,"\\\n"),this.area.value=t.replace(c,"\\n");try{this.form.submit()}catch(t){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===o.iframe.readyState&&r()}:this.iframe.onload=r}}).call(e,function(){return this}())},function(t,e,r){(function(e){function n(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=p&&!t.forceNode,this.protocols=t.protocols,this.usingBrowserWebSocket||(u=o),i.call(this,t)}var o,i=r(18),s=r(19),a=r(27),c=r(28),h=r(29),p=(r(3)("engine.io-client:websocket"),e.WebSocket||e.MozWebSocket);if("undefined"==typeof window)try{o=r(32)}catch(t){}var u=p;u||"undefined"!=typeof window||(u=o),t.exports=n,c(n,i),n.prototype.name="websocket",n.prototype.supportsBinary=!0,n.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=this.protocols,r={agent:this.agent,perMessageDeflate:this.perMessageDeflate};r.pfx=this.pfx,r.key=this.key,r.passphrase=this.passphrase,r.cert=this.cert,r.ca=this.ca,r.ciphers=this.ciphers,r.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(r.headers=this.extraHeaders),this.localAddress&&(r.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?e?new u(t,e):new u(t):new u(t,e,r)}catch(t){return this.emit("error",t)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType="nodebuffer"):this.ws.binaryType="arraybuffer",this.addEventListeners()}},n.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError("websocket error",e)}},n.prototype.write=function(t){function r(){n.emit("flush"),setTimeout(function(){n.writable=!0,n.emit("drain")},0)}var n=this;this.writable=!1;for(var o=t.length,i=0,a=o;i0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=r,r.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),r=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-r:t+r}return 0|Math.min(t,this.max)},r.prototype.reset=function(){this.attempts=0},r.prototype.setMin=function(t){this.ms=t},r.prototype.setMax=function(t){this.max=t},r.prototype.setJitter=function(t){this.jitter=t}}])}); 3 | //# sourceMappingURL=socket.io.slim.js.map -------------------------------------------------------------------------------- /dist/socket.io.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.io=e():t.io=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t,e){"object"===("undefined"==typeof t?"undefined":o(t))&&(e=t,t=void 0),e=e||{};var n,r=i(t),s=r.source,u=r.id,h=r.path,f=p[u]&&h in p[u].nsps,l=e.forceNew||e["force new connection"]||!1===e.multiplex||f;return l?(c("ignoring socket cache for %s",s),n=a(s,e)):(p[u]||(c("new io instance for %s",s),p[u]=a(s,e)),n=p[u]),r.query&&!e.query&&(e.query=r.query),n.socket(r.path,e)}var o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=n(1),s=n(7),a=n(13),c=n(3)("socket.io-client");t.exports=e=r;var p=e.managers={};e.protocol=s.protocol,e.connect=r,e.Manager=n(13),e.Socket=n(37)},function(t,e,n){(function(e){"use strict";function r(t,n){var r=t;n=n||e.location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(i("protocol-less url %s",t),t="undefined"!=typeof n?n.protocol+"//"+t:"https://"+t),i("parse %s",t),r=o(t)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";var s=r.host.indexOf(":")!==-1,a=s?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+a+":"+r.port,r.href=r.protocol+"://"+a+(n&&n.port===r.port?"":":"+r.port),r}var o=n(2),i=n(3)("socket.io-client:url");t.exports=r}).call(e,function(){return this}())},function(t,e){var n=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,r=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");o!=-1&&i!=-1&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s=n.exec(t||""),a={},c=14;c--;)a[r[c]]=s[c]||"";return o!=-1&&i!=-1&&(a.source=e,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a}},function(t,e,n){(function(r){function o(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type)||("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))}function i(t){var n=this.useColors;if(t[0]=(n?"%c":"")+this.namespace+(n?" %c":" ")+t[0]+(n?"%c ":" ")+"+"+e.humanize(this.diff),n){var r="color: "+this.color;t.splice(1,0,r,"color: inherit");var o=0,i=0;t[0].replace(/%[a-zA-Z%]/g,function(t){"%%"!==t&&(o++,"%c"===t&&(i=o))}),t.splice(i,0,r)}}function s(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(t){try{null==t?e.storage.removeItem("debug"):e.storage.debug=t}catch(n){}}function c(){var t;try{t=e.storage.debug}catch(n){}return!t&&"undefined"!=typeof r&&"env"in r&&(t=r.env.DEBUG),t}function p(){try{return window.localStorage}catch(t){}}e=t.exports=n(5),e.log=s,e.formatArgs=i,e.save=a,e.load=c,e.useColors=o,e.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:p(),e.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],e.formatters.j=function(t){try{return JSON.stringify(t)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}},e.enable(c())}).call(e,n(4))},function(t,e){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(t){if(u===setTimeout)return setTimeout(t,0);if((u===n||!u)&&setTimeout)return u=setTimeout,setTimeout(t,0);try{return u(t,0)}catch(e){try{return u.call(null,t,0)}catch(e){return u.call(this,t,0)}}}function i(t){if(h===clearTimeout)return clearTimeout(t);if((h===r||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(t);try{return h(t)}catch(e){try{return h.call(null,t)}catch(e){return h.call(this,t)}}}function s(){y&&l&&(y=!1,l.length?d=l.concat(d):m=-1,d.length&&a())}function a(){if(!y){var t=o(s);y=!0;for(var e=d.length;e;){for(l=d,d=[];++m1)for(var n=1;n100)){var e=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var n=parseFloat(e[1]),r=(e[2]||"ms").toLowerCase();switch(r){case"years":case"year":case"yrs":case"yr":case"y":return n*u;case"days":case"day":case"d":return n*p;case"hours":case"hour":case"hrs":case"hr":case"h":return n*c;case"minutes":case"minute":case"mins":case"min":case"m":return n*a;case"seconds":case"second":case"secs":case"sec":case"s":return n*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return n;default:return}}}}function r(t){return t>=p?Math.round(t/p)+"d":t>=c?Math.round(t/c)+"h":t>=a?Math.round(t/a)+"m":t>=s?Math.round(t/s)+"s":t+"ms"}function o(t){return i(t,p,"day")||i(t,c,"hour")||i(t,a,"minute")||i(t,s,"second")||t+" ms"}function i(t,e,n){if(!(t0)return n(t);if("number"===i&&isNaN(t)===!1)return e["long"]?o(t):r(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))}},function(t,e,n){function r(){}function o(t){var n=""+t.type;return e.BINARY_EVENT!==t.type&&e.BINARY_ACK!==t.type||(n+=t.attachments+"-"),t.nsp&&"/"!==t.nsp&&(n+=t.nsp+","),null!=t.id&&(n+=t.id),null!=t.data&&(n+=JSON.stringify(t.data)),h("encoded %j as %s",t,n),n}function i(t,e){function n(t){var n=d.deconstructPacket(t),r=o(n.packet),i=n.buffers;i.unshift(r),e(i)}d.removeBlobs(t,n)}function s(){this.reconstructor=null}function a(t){var n=0,r={type:Number(t.charAt(0))};if(null==e.types[r.type])return u();if(e.BINARY_EVENT===r.type||e.BINARY_ACK===r.type){for(var o="";"-"!==t.charAt(++n)&&(o+=t.charAt(n),n!=t.length););if(o!=Number(o)||"-"!==t.charAt(n))throw new Error("Illegal attachments");r.attachments=Number(o)}if("/"===t.charAt(n+1))for(r.nsp="";++n;){var i=t.charAt(n);if(","===i)break;if(r.nsp+=i,n===t.length)break}else r.nsp="/";var s=t.charAt(n+1);if(""!==s&&Number(s)==s){for(r.id="";++n;){var i=t.charAt(n);if(null==i||Number(i)!=i){--n;break}if(r.id+=t.charAt(n),n===t.length)break}r.id=Number(r.id)}return t.charAt(++n)&&(r=c(r,t.substr(n))),h("decoded %s as %j",t,r),r}function c(t,e){try{t.data=JSON.parse(e)}catch(n){return u()}return t}function p(t){this.reconPack=t,this.buffers=[]}function u(){return{type:e.ERROR,data:"parser error"}}var h=n(3)("socket.io-parser"),f=n(8),l=n(9),d=n(11),y=n(12);e.protocol=4,e.types=["CONNECT","DISCONNECT","EVENT","ACK","ERROR","BINARY_EVENT","BINARY_ACK"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=r,e.Decoder=s,r.prototype.encode=function(t,n){if(t.type!==e.EVENT&&t.type!==e.ACK||!l(t.data)||(t.type=t.type===e.EVENT?e.BINARY_EVENT:e.BINARY_ACK),h("encoding packet %j",t),e.BINARY_EVENT===t.type||e.BINARY_ACK===t.type)i(t,n);else{var r=o(t);n([r])}},f(s.prototype),s.prototype.add=function(t){var n;if("string"==typeof t)n=a(t),e.BINARY_EVENT===n.type||e.BINARY_ACK===n.type?(this.reconstructor=new p(n),0===this.reconstructor.reconPack.attachments&&this.emit("decoded",n)):this.emit("decoded",n);else{if(!y(t)&&!t.base64)throw new Error("Unknown type: "+t);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");n=this.reconstructor.takeBinaryData(t),n&&(this.reconstructor=null,this.emit("decoded",n))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},p.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=d.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},p.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,n){function r(t){if(t)return o(t)}function o(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},r.prototype.once=function(t,e){function n(){this.off(t,n),e.apply(this,arguments)}return n.fn=e,this.on(t,n),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var r,o=0;o0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},r.prototype.cleanup=function(){h("cleanup");for(var t=this.subs.length,e=0;e=this._reconnectionAttempts)h("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var e=this.backoff.duration();h("will wait %dms before reconnect attempt",e),this.reconnecting=!0;var n=setTimeout(function(){t.skipReconnect||(h("attempting reconnect"),t.emitAll("reconnect_attempt",t.backoff.attempts),t.emitAll("reconnecting",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(h("reconnect attempt error"),t.reconnecting=!1,t.reconnect(),t.emitAll("reconnect_error",e.data)):(h("reconnect success"),t.onreconnect())}))},e);this.subs.push({destroy:function(){clearTimeout(n)}})}},r.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",t)}},function(t,e,n){t.exports=n(15),t.exports.parser=n(22)},function(t,e,n){(function(e){function r(t,n){if(!(this instanceof r))return new r(t,n);n=n||{},t&&"object"==typeof t&&(n=t,t=null),t?(t=u(t),n.hostname=t.host,n.secure="https"===t.protocol||"wss"===t.protocol,n.port=t.port,t.query&&(n.query=t.query)):n.host&&(n.hostname=u(n.host).host),this.secure=null!=n.secure?n.secure:e.location&&"https:"===location.protocol,n.hostname&&!n.port&&(n.port=this.secure?"443":"80"),this.agent=n.agent||!1,this.hostname=n.hostname||(e.location?location.hostname:"localhost"),this.port=n.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=n.query||{},"string"==typeof this.query&&(this.query=h.decode(this.query)),this.upgrade=!1!==n.upgrade,this.path=(n.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!n.forceJSONP,this.jsonp=!1!==n.jsonp,this.forceBase64=!!n.forceBase64,this.enablesXDR=!!n.enablesXDR,this.timestampParam=n.timestampParam||"t",this.timestampRequests=n.timestampRequests,this.transports=n.transports||["polling","websocket"],this.transportOptions=n.transportOptions||{},this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=n.policyPort||843,this.rememberUpgrade=n.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=n.onlyBinaryUpgrades,this.perMessageDeflate=!1!==n.perMessageDeflate&&(n.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=n.pfx||null,this.key=n.key||null,this.passphrase=n.passphrase||null,this.cert=n.cert||null,this.ca=n.ca||null,this.ciphers=n.ciphers||null,this.rejectUnauthorized=void 0===n.rejectUnauthorized||n.rejectUnauthorized,this.forceNode=!!n.forceNode;var o="object"==typeof e&&e;o.global===o&&(n.extraHeaders&&Object.keys(n.extraHeaders).length>0&&(this.extraHeaders=n.extraHeaders),n.localAddress&&(this.localAddress=n.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}var i=n(16),s=n(8),a=n(3)("engine.io-client:socket"),c=n(36),p=n(22),u=n(2),h=n(30);t.exports=r,r.priorWebsocketSuccess=!1,s(r.prototype),r.protocol=p.protocol,r.Socket=r,r.Transport=n(21),r.transports=n(16),r.parser=n(22),r.prototype.createTransport=function(t){a('creating transport "%s"',t);var e=o(this.query);e.EIO=p.protocol,e.transport=t;var n=this.transportOptions[t]||{};this.id&&(e.sid=this.id);var r=new i[t]({query:e,socket:this,agent:n.agent||this.agent,hostname:n.hostname||this.hostname,port:n.port||this.port,secure:n.secure||this.secure,path:n.path||this.path,forceJSONP:n.forceJSONP||this.forceJSONP,jsonp:n.jsonp||this.jsonp,forceBase64:n.forceBase64||this.forceBase64,enablesXDR:n.enablesXDR||this.enablesXDR,timestampRequests:n.timestampRequests||this.timestampRequests,timestampParam:n.timestampParam||this.timestampParam,policyPort:n.policyPort||this.policyPort,pfx:n.pfx||this.pfx,key:n.key||this.key,passphrase:n.passphrase||this.passphrase,cert:n.cert||this.cert,ca:n.ca||this.ca,ciphers:n.ciphers||this.ciphers,rejectUnauthorized:n.rejectUnauthorized||this.rejectUnauthorized,perMessageDeflate:n.perMessageDeflate||this.perMessageDeflate,extraHeaders:n.extraHeaders||this.extraHeaders,forceNode:n.forceNode||this.forceNode,localAddress:n.localAddress||this.localAddress,requestTimeout:n.requestTimeout||this.requestTimeout,protocols:n.protocols||void 0});return r},r.prototype.open=function(){var t;if(this.rememberUpgrade&&r.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1)t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit("error","No transports available")},0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(n){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},r.prototype.setTransport=function(t){a("setting transport %s",t.name);var e=this;this.transport&&(a("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=t,t.on("drain",function(){e.onDrain()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},r.prototype.probe=function(t){function e(){if(f.onlyBinaryUpgrades){var e=!this.supportsBinary&&f.transport.supportsBinary;h=h||e}h||(a('probe transport "%s" opened',t),u.send([{type:"ping",data:"probe"}]),u.once("packet",function(e){if(!h)if("pong"===e.type&&"probe"===e.data){if(a('probe transport "%s" pong',t),f.upgrading=!0,f.emit("upgrading",u),!u)return;r.priorWebsocketSuccess="websocket"===u.name,a('pausing current transport "%s"',f.transport.name),f.transport.pause(function(){h||"closed"!==f.readyState&&(a("changing transport and sending upgrade packet"),p(),f.setTransport(u),u.send([{type:"upgrade"}]),f.emit("upgrade",u),u=null,f.upgrading=!1,f.flush())})}else{a('probe transport "%s" failed',t);var n=new Error("probe error");n.transport=u.name,f.emit("upgradeError",n)}}))}function n(){h||(h=!0,p(),u.close(),u=null)}function o(e){var r=new Error("probe error: "+e);r.transport=u.name,n(),a('probe transport "%s" failed because of error: %s',t,e),f.emit("upgradeError",r)}function i(){o("transport closed")}function s(){o("socket closed")}function c(t){u&&t.name!==u.name&&(a('"%s" works - aborting "%s"',t.name,u.name),n())}function p(){u.removeListener("open",e),u.removeListener("error",o),u.removeListener("close",i),f.removeListener("close",s),f.removeListener("upgrading",c)}a('probing transport "%s"',t);var u=this.createTransport(t,{probe:1}),h=!1,f=this;r.priorWebsocketSuccess=!1,u.once("open",e),u.once("error",o),u.once("close",i),this.once("close",s),this.once("upgrading",c),u.open()},r.prototype.onOpen=function(){if(a("socket open"),this.readyState="open",r.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.upgrade&&this.transport.pause){a("starting upgrade probes");for(var t=0,e=this.upgrades.length;t1?{type:b[o],data:t.substring(1)}:{type:b[o]}:w}var i=new Uint8Array(t),o=i[0],s=f(t,1);return k&&"blob"===n&&(s=new k([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var n=b[t.charAt(0)];if(!p)return{type:n,data:{base64:!0,data:t.substr(1)}};var r=p.decode(t.substr(1));return"blob"===e&&k&&(r=new k([r])),{type:n,data:r}},e.encodePayload=function(t,n,r){function o(t){return t.length+":"+t}function i(t,r){e.encodePacket(t,!!s&&n,!1,function(t){r(null,o(t))})}"function"==typeof n&&(r=n,n=null);var s=h(t);return n&&s?k&&!g?e.encodePayloadAsBlob(t,r):e.encodePayloadAsArrayBuffer(t,r):t.length?void c(t,i,function(t,e){return r(e.join(""))}):r("0:")},e.decodePayload=function(t,n,r){if("string"!=typeof t)return e.decodePayloadAsBinary(t,n,r);"function"==typeof n&&(r=n,n=null);var o;if(""===t)return r(w,0,1);for(var i,s,a="",c=0,p=t.length;c0;){for(var s=new Uint8Array(o),a=0===s[0],c="",p=1;255!==s[p];p++){if(c.length>310)return r(w,0,1);c+=s[p]}o=f(o,2+c.length),c=parseInt(c);var u=f(o,0,c);if(a)try{u=String.fromCharCode.apply(null,new Uint8Array(u))}catch(h){var l=new Uint8Array(u);u="";for(var p=0;pr&&(n=r),e>=r||e>=n||0===r)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(n-e),s=e,a=0;s=55296&&e<=56319&&o65535&&(e-=65536,o+=w(e>>>10&1023|55296),e=56320|1023&e),o+=w(e);return o}function c(t,e){if(t>=55296&&t<=57343){if(e)throw Error("Lone surrogate U+"+t.toString(16).toUpperCase()+" is not a scalar value");return!1}return!0}function p(t,e){return w(t>>e&63|128)}function u(t,e){if(0==(4294967168&t))return w(t);var n="";return 0==(4294965248&t)?n=w(t>>6&31|192):0==(4294901760&t)?(c(t,e)||(t=65533),n=w(t>>12&15|224),n+=p(t,6)):0==(4292870144&t)&&(n=w(t>>18&7|240),n+=p(t,12),n+=p(t,6)),n+=w(63&t|128)}function h(t,e){e=e||{};for(var n,r=!1!==e.strict,o=s(t),i=o.length,a=-1,c="";++a=v)throw Error("Invalid byte index");var t=255&g[b];if(b++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function l(t){var e,n,r,o,i;if(b>v)throw Error("Invalid byte index");if(b==v)return!1;if(e=255&g[b],b++,0==(128&e))return e;if(192==(224&e)){if(n=f(),i=(31&e)<<6|n,i>=128)return i;throw Error("Invalid continuation byte")}if(224==(240&e)){if(n=f(),r=f(),i=(15&e)<<12|n<<6|r,i>=2048)return c(i,t)?i:65533;throw Error("Invalid continuation byte")}if(240==(248&e)&&(n=f(),r=f(),o=f(),i=(7&e)<<18|n<<12|r<<6|o,i>=65536&&i<=1114111))return i;throw Error("Invalid UTF-8 detected")}function d(t,e){e=e||{};var n=!1!==e.strict;g=s(t),v=g.length,b=0;for(var r,o=[];(r=l(n))!==!1;)o.push(r);return a(o)}var y="object"==typeof e&&e,m=("object"==typeof t&&t&&t.exports==y&&t,"object"==typeof o&&o);m.global!==m&&m.window!==m||(i=m);var g,v,b,w=String.fromCharCode,k={version:"2.1.2",encode:h,decode:d};r=function(){return k}.call(e,n,e,t),!(void 0!==r&&(t.exports=r))}(this)}).call(e,n(27)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){!function(){"use strict";for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n=new Uint8Array(256),r=0;r>2],i+=t[(3&r[n])<<4|r[n+1]>>4],i+=t[(15&r[n+1])<<2|r[n+2]>>6],i+=t[63&r[n+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(t){var e,r,o,i,s,a=.75*t.length,c=t.length,p=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var u=new ArrayBuffer(a),h=new Uint8Array(u);for(e=0;e>4,h[p++]=(15&o)<<4|i>>2,h[p++]=(3&i)<<6|63&s;return u}}()},function(t,e){(function(e){function n(t){for(var e=0;e0);return e}function r(t){var e=0;for(u=0;u';i=document.createElement(e)}catch(t){i=document.createElement("iframe"),i.name=o.iframeId,i.src="javascript:0"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement("form"),a=document.createElement("textarea"),u=this.iframeId="eio_iframe_"+this.index;s.className="socketio",s.style.position="absolute",s.style.top="-1000px",s.style.left="-1000px",s.target=u,s.method="POST",s.setAttribute("accept-charset","utf-8"),a.name="d",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),r(),t=t.replace(p,"\\\n"),this.area.value=t.replace(c,"\\n");try{this.form.submit()}catch(h){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===o.iframe.readyState&&n()}:this.iframe.onload=n}}).call(e,function(){return this}())},function(t,e,n){(function(e){function r(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=h&&!t.forceNode,this.protocols=t.protocols,this.usingBrowserWebSocket||(l=o),i.call(this,t)}var o,i=n(21),s=n(22),a=n(30),c=n(31),p=n(32),u=n(3)("engine.io-client:websocket"),h=e.WebSocket||e.MozWebSocket;if("undefined"==typeof window)try{o=n(35)}catch(f){}var l=h;l||"undefined"!=typeof window||(l=o),t.exports=r,c(r,i),r.prototype.name="websocket",r.prototype.supportsBinary=!0,r.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=this.protocols,n={agent:this.agent,perMessageDeflate:this.perMessageDeflate};n.pfx=this.pfx,n.key=this.key,n.passphrase=this.passphrase,n.cert=this.cert,n.ca=this.ca,n.ciphers=this.ciphers,n.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(n.headers=this.extraHeaders),this.localAddress&&(n.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?e?new l(t,e):new l(t):new l(t,e,n)}catch(r){return this.emit("error",r)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType="nodebuffer"):this.ws.binaryType="arraybuffer",this.addEventListeners()}},r.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError("websocket error",e)}},r.prototype.write=function(t){function n(){r.emit("flush"),setTimeout(function(){r.writable=!0,r.emit("drain")},0)}var r=this;this.writable=!1;for(var o=t.length,i=0,a=o;i0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=n,n.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(t){this.ms=t},n.prototype.setMax=function(t){this.max=t},n.prototype.setJitter=function(t){this.jitter=t}}])}); 3 | //# sourceMappingURL=socket.io.js.map --------------------------------------------------------------------------------