mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-11 16:08:24 -05:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88b2cdb6ab | ||
|
|
d30630ba10 | ||
|
|
f927ba29ef | ||
|
|
baa6804440 | ||
|
|
f223178eb6 | ||
|
|
226cc16165 | ||
|
|
05e1278cfa | ||
|
|
22d4bdf00d | ||
|
|
dfded53593 | ||
|
|
e6b869738c | ||
|
|
a169050947 | ||
|
|
873fdc55ed | ||
|
|
f78a575f66 | ||
|
|
d33a619905 | ||
|
|
3951a79359 | ||
|
|
6fa026fc94 | ||
|
|
47161a65d4 | ||
|
|
cf39362014 | ||
|
|
4d01b2c84c | ||
|
|
82271921db | ||
|
|
1150eb50e9 | ||
|
|
9c1e73c752 | ||
|
|
df05b73bb9 | ||
|
|
b00ae50be6 | ||
|
|
d3c653d876 | ||
|
|
a7fbd1ac4a | ||
|
|
190d22b46e | ||
|
|
7b8fba7ea2 | ||
|
|
e5f0ceaee0 | ||
|
|
7e35f901b8 | ||
|
|
2dbec77a38 | ||
|
|
d97d873aee | ||
|
|
e0b2cb0c5a | ||
|
|
1decae341c | ||
|
|
0279c47c8c | ||
|
|
2917942b3e | ||
|
|
db831a3de4 | ||
|
|
ac945d1eba | ||
|
|
ad0c052eff | ||
|
|
1f1d64bab6 | ||
|
|
f4fc517e0f | ||
|
|
be61ba0a20 | ||
|
|
c0c79f019e | ||
|
|
dea5214f21 | ||
|
|
b1941d5dfe | ||
|
|
a23007a635 | ||
|
|
f48a06c040 | ||
|
|
0539a2c4fd | ||
|
|
c06ac071d0 | ||
|
|
52b09609db |
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,5 +1,9 @@
|
||||
|
||||
*Note*: for support questions, please use one of these channels: [stackoverflow](http://stackoverflow.com/questions/tagged/socket.io) or [slack](https://socketio.slack.com)
|
||||
**Note**: for support questions, please use one of these channels: [stackoverflow](http://stackoverflow.com/questions/tagged/socket.io) or [slack](https://socketio.slack.com)
|
||||
|
||||
For bug reports and feature requests for the **Swift client**, please open an issue [there](https://github.com/socketio/socket.io-client-swift).
|
||||
|
||||
For bug reports and feature requests for the **Java client**, please open an issue [there](https://github.com/socketio/socket.io-client-java).
|
||||
|
||||
### You want to:
|
||||
|
||||
@@ -8,13 +12,15 @@
|
||||
|
||||
### Current behaviour
|
||||
|
||||
*What is actually happening?*
|
||||
|
||||
### Steps to reproduce (if the current behaviour is a bug)
|
||||
|
||||
**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.
|
||||
**Note**: the best way (and by that we mean **the only way**) to get a quick answer is to provide a failing test case by forking the following [fiddle](https://github.com/socketio/socket.io-fiddle).
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
*What is expected?*
|
||||
|
||||
### Setup
|
||||
- OS:
|
||||
|
||||
24
.github/workflows/ci.yml
vendored
Normal file
24
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test-node:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
env:
|
||||
CI: true
|
||||
12
.travis.yml
12
.travis.yml
@@ -1,12 +0,0 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "4"
|
||||
- "6"
|
||||
- "7"
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
notifications:
|
||||
irc: "irc.freenode.org#socket.io"
|
||||
36
CHANGELOG.md
Normal file
36
CHANGELOG.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## [2.5.1](https://github.com/socketio/socket.io/compare/2.5.0...2.5.1) (2024-06-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add a noop handler for the error event ([d30630b](https://github.com/socketio/socket.io/commit/d30630ba10562bf987f4d2b42440fc41a828119c))
|
||||
|
||||
|
||||
|
||||
# [2.5.0](https://github.com/socketio/socket.io/compare/2.4.1...2.5.0) (2022-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix race condition in dynamic namespaces ([05e1278](https://github.com/socketio/socket.io/commit/05e1278cfa99f3ecf3f8f0531ffe57d850e9a05b))
|
||||
* ignore packet received after disconnection ([22d4bdf](https://github.com/socketio/socket.io/commit/22d4bdf00d1a03885dc0171125faddfaef730066))
|
||||
* only set 'connected' to true after middleware execution ([226cc16](https://github.com/socketio/socket.io/commit/226cc16165f9fe60f16ff4d295fb91c8971cde35))
|
||||
* prevent the socket from joining a room after disconnection ([f223178](https://github.com/socketio/socket.io/commit/f223178eb655a7713303b21a78f9ef9e161d6458))
|
||||
|
||||
|
||||
|
||||
## [2.4.1](https://github.com/socketio/socket.io/compare/2.4.0...2.4.1) (2021-01-07)
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* fix(security): do not allow all origins by default ([a169050](https://github.com/socketio/socket.io/commit/a1690509470e9dd5559cec4e60908ca6c23e9ba0))
|
||||
|
||||
|
||||
# [2.4.0](https://github.com/socketio/socket.io/compare/2.3.0...2.4.0) (2021-01-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** do not allow all origins by default ([f78a575](https://github.com/socketio/socket.io/commit/f78a575f66ab693c3ea96ea88429ddb1a44c86c7))
|
||||
* properly overwrite the query sent in the handshake ([d33a619](https://github.com/socketio/socket.io/commit/d33a619905a4905c153d4fec337c74da5b533a9e))
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 Automattic <dev@cloudup.com>
|
||||
Copyright (c) 2014-2018 Automattic <dev@cloudup.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
47
Readme.md
47
Readme.md
@@ -2,7 +2,7 @@
|
||||
# socket.io
|
||||
|
||||
[](#backers) [](#sponsors)
|
||||
[](https://travis-ci.org/socketio/socket.io)
|
||||
[](https://github.com/socketio/socket.io/actions)
|
||||
[](https://david-dm.org/socketio/socket.io)
|
||||
[](https://david-dm.org/socketio/socket.io#info=devDependencies)
|
||||
[](https://www.npmjs.com/package/socket.io)
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
## Features
|
||||
|
||||
Socket.IO enables real-time bidirectional event-based communication. It consists in:
|
||||
Socket.IO enables real-time bidirectional event-based communication. It consists of:
|
||||
|
||||
- a Node.js server (this repository)
|
||||
- a [Javascript client library](https://github.com/socketio/socket.io-client) for the browser (or a Node.js client)
|
||||
@@ -21,6 +21,7 @@ Some implementations in other languages are also available:
|
||||
- [Java](https://github.com/socketio/socket.io-client-java)
|
||||
- [C++](https://github.com/socketio/socket.io-client-cpp)
|
||||
- [Swift](https://github.com/socketio/socket.io-client-swift)
|
||||
- [Dart](https://github.com/rikulo/socket.io-client-dart)
|
||||
|
||||
Its main features are:
|
||||
|
||||
@@ -38,7 +39,7 @@ Unless instructed otherwise a disconnected client will try to reconnect forever,
|
||||
|
||||
#### Disconnection detection
|
||||
|
||||
An heartbeat mechanism is implemented at the Engine.IO level, allowing both the server and the client to know when the other one is not responding anymore.
|
||||
A heartbeat mechanism is implemented at the Engine.IO level, allowing both the server and the client to know when the other one is not responding anymore.
|
||||
|
||||
That functionality is achieved with timers set on both the server and the client, with timeout values (the `pingInterval` and `pingTimeout` parameters) shared during the connection handshake. Those timers require any subsequent client calls to be directed to the same server, hence the `sticky-session` requirement when using multiples nodes.
|
||||
|
||||
@@ -54,10 +55,10 @@ Any serializable data structures can be emitted, including:
|
||||
Sample code:
|
||||
|
||||
```js
|
||||
io.on('connection', function(socket){
|
||||
socket.emit('request', /* */); // emit an event to the socket
|
||||
io.emit('broadcast', /* */); // emit an event to all connected sockets
|
||||
socket.on('reply', function(){ /* */ }); // listen to the event
|
||||
io.on('connection', socket => {
|
||||
socket.emit('request', /* … */); // emit an event to the socket
|
||||
io.emit('broadcast', /* … */); // emit an event to all connected sockets
|
||||
socket.on('reply', () => { /* … */ }); // listen to the event
|
||||
});
|
||||
```
|
||||
|
||||
@@ -83,7 +84,7 @@ This is a useful feature to send notifications to a group of users, or to a give
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install socket.io --save
|
||||
npm install socket.io
|
||||
```
|
||||
|
||||
## How to use
|
||||
@@ -92,11 +93,11 @@ The following example attaches socket.io to a plain Node.JS
|
||||
HTTP server listening on port `3000`.
|
||||
|
||||
```js
|
||||
var server = require('http').createServer();
|
||||
var io = require('socket.io')(server);
|
||||
io.on('connection', function(client){
|
||||
client.on('event', function(data){});
|
||||
client.on('disconnect', function(){});
|
||||
const server = require('http').createServer();
|
||||
const io = require('socket.io')(server);
|
||||
io.on('connection', client => {
|
||||
client.on('event', data => { /* … */ });
|
||||
client.on('disconnect', () => { /* … */ });
|
||||
});
|
||||
server.listen(3000);
|
||||
```
|
||||
@@ -104,8 +105,8 @@ server.listen(3000);
|
||||
### Standalone
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.on('connection', function(client){});
|
||||
const io = require('socket.io')();
|
||||
io.on('connection', client => { ... });
|
||||
io.listen(3000);
|
||||
```
|
||||
|
||||
@@ -117,10 +118,10 @@ to pass the `Server` to `socket.io`, and not the express application
|
||||
function. Also make sure to call `.listen` on the `server`, not the `app`.
|
||||
|
||||
```js
|
||||
var app = require('express')();
|
||||
var server = require('http').createServer(app);
|
||||
var io = require('socket.io')(server);
|
||||
io.on('connection', function(){ /* … */ });
|
||||
const app = require('express')();
|
||||
const server = require('http').createServer(app);
|
||||
const io = require('socket.io')(server);
|
||||
io.on('connection', () => { /* … */ });
|
||||
server.listen(3000);
|
||||
```
|
||||
|
||||
@@ -130,10 +131,10 @@ Like Express.JS, Koa works by exposing an application as a request
|
||||
handler function, but only by calling the `callback` method.
|
||||
|
||||
```js
|
||||
var app = require('koa')();
|
||||
var server = require('http').createServer(app.callback());
|
||||
var io = require('socket.io')(server);
|
||||
io.on('connection', function(){ /* … */ });
|
||||
const app = require('koa')();
|
||||
const server = require('http').createServer(app.callback());
|
||||
const io = require('socket.io')(server);
|
||||
io.on('connection', () => { /* … */ });
|
||||
server.listen(3000);
|
||||
```
|
||||
|
||||
|
||||
61
docs/API.md
61
docs/API.md
@@ -33,6 +33,7 @@
|
||||
- [Event: 'connection'](#event-connect)
|
||||
- [Flag: 'volatile'](#flag-volatile)
|
||||
- [Flag: 'local'](#flag-local)
|
||||
- [Flag: 'binary'](#flag-binary)
|
||||
- [Class: Socket](#socket)
|
||||
- [socket.id](#socketid)
|
||||
- [socket.rooms](#socketrooms)
|
||||
@@ -57,6 +58,7 @@
|
||||
- [socket.disconnect(close)](#socketdisconnectclose)
|
||||
- [Flag: 'broadcast'](#flag-broadcast)
|
||||
- [Flag: 'volatile'](#flag-volatile-1)
|
||||
- [Flag: 'binary'](#flag-binary-1)
|
||||
- [Event: 'disconnect'](#event-disconnect)
|
||||
- [Event: 'error'](#event-error)
|
||||
- [Event: 'disconnecting'](#event-disconnecting)
|
||||
@@ -76,7 +78,7 @@ Exposed by `require('socket.io')`.
|
||||
- `path` _(String)_: name of the path to capture (`/socket.io`)
|
||||
- `serveClient` _(Boolean)_: whether to serve the client files (`true`)
|
||||
- `adapter` _(Adapter)_: the adapter to use. Defaults to an instance of the `Adapter` that ships with socket.io which is memory based. See [socket.io-adapter](https://github.com/socketio/socket.io-adapter)
|
||||
- `origins` _(String)_: the allowed origins (`*`)
|
||||
- `origins` _(String)_: the allowed origins (`*:*`)
|
||||
- `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).
|
||||
|
||||
Works with and without `new`:
|
||||
@@ -222,13 +224,13 @@ io.adapter(redis({ host: 'localhost', port: 6379 }));
|
||||
|
||||
#### server.origins([value])
|
||||
|
||||
- `value` _(String)_
|
||||
- `value` _(String|String[])_
|
||||
- **Returns** `Server|String`
|
||||
|
||||
Sets the allowed origins `value`. Defaults to any origins being allowed. If no arguments are supplied this method returns the current value.
|
||||
|
||||
```js
|
||||
io.origins(['foo.example.com:443']);
|
||||
io.origins(['https://foo.example.com:443']);
|
||||
```
|
||||
|
||||
#### server.origins(fn)
|
||||
@@ -236,7 +238,7 @@ io.origins(['foo.example.com:443']);
|
||||
- `fn` _(Function)_
|
||||
- **Returns** `Server`
|
||||
|
||||
Provides a function taking two arguments `origin:String` and `callback(error, success)`, where `success` is a boolean value indicating whether origin is allowed or not.
|
||||
Provides a function taking two arguments `origin:String` and `callback(error, success)`, where `success` is a boolean value indicating whether origin is allowed or not. If `success` is set to `false`, `error` must be provided as a string value that will be appended to the server response, e.g. "Origin not allowed".
|
||||
|
||||
__Potential drawbacks__:
|
||||
* in some situations, when it is not possible to determine `origin` it may have value of `*`
|
||||
@@ -290,7 +292,7 @@ Advanced use only. Creates a new `socket.io` client from the incoming engine.io
|
||||
|
||||
#### server.of(nsp)
|
||||
|
||||
- `nsp` _(String)_
|
||||
- `nsp` _(String|RegExp|Function)_
|
||||
- **Returns** `Namespace`
|
||||
|
||||
Initializes and retrieves the given `Namespace` by its pathname identifier `nsp`. If the namespace was already initialized it returns it immediately.
|
||||
@@ -299,6 +301,34 @@ Initializes and retrieves the given `Namespace` by its pathname identifier `nsp`
|
||||
const adminNamespace = io.of('/admin');
|
||||
```
|
||||
|
||||
A regex or a function can also be provided, in order to create namespace in a dynamic way:
|
||||
|
||||
```js
|
||||
const dynamicNsp = io.of(/^\/dynamic-\d+$/).on('connect', (socket) => {
|
||||
const newNamespace = socket.nsp; // newNamespace.name === '/dynamic-101'
|
||||
|
||||
// broadcast to all clients in the given sub-namespace
|
||||
newNamespace.emit('hello');
|
||||
});
|
||||
|
||||
// client-side
|
||||
const socket = io('/dynamic-101');
|
||||
|
||||
// broadcast to all clients in each sub-namespace
|
||||
dynamicNsp.emit('hello');
|
||||
|
||||
// use a middleware for each sub-namespace
|
||||
dynamicNsp.use((socket, next) => { /* ... */ });
|
||||
```
|
||||
|
||||
With a function:
|
||||
|
||||
```js
|
||||
io.of((name, query, next) => {
|
||||
next(null, checkToken(query.token));
|
||||
}).on('connect', (socket) => { /* ... */ });
|
||||
```
|
||||
|
||||
#### server.close([callback])
|
||||
|
||||
- `callback` _(Function)_
|
||||
@@ -470,6 +500,14 @@ Sets a modifier for a subsequent event emission that the event data may be lost
|
||||
io.volatile.emit('an event', { some: 'data' }); // the clients may or may not receive it
|
||||
```
|
||||
|
||||
#### Flag: 'binary'
|
||||
|
||||
Specifies whether there is binary data in the emitted data. Increases performance when specified. Can be `true` or `false`.
|
||||
|
||||
```js
|
||||
io.binary(false).emit('an event', { some: 'data' });
|
||||
```
|
||||
|
||||
#### Flag: 'local'
|
||||
|
||||
Sets a modifier for a subsequent event emission that the event data will only be _broadcast_ to the current node (when the [Redis adapter](https://github.com/socketio/socket.io-redis) is used).
|
||||
@@ -597,7 +635,7 @@ Emits an event to the socket identified by the string name. Any other parameters
|
||||
|
||||
```js
|
||||
socket.emit('hello', 'world');
|
||||
socket.emit('with-binary', 1, '2', { 3: '4', 5: new Buffer(6) });
|
||||
socket.emit('with-binary', 1, '2', { 3: '4', 5: Buffer.alloc(6) });
|
||||
```
|
||||
|
||||
The `ack` argument is optional and will be called with the client's answer.
|
||||
@@ -769,6 +807,17 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
```
|
||||
|
||||
#### Flag: 'binary'
|
||||
|
||||
Specifies whether there is binary data in the emitted data. Increases performance when specified. Can be `true` or `false`.
|
||||
|
||||
```js
|
||||
var io = require('socket.io')();
|
||||
io.on('connection', function(socket){
|
||||
socket.binary(false).emit('an event', { some: 'data' }); // The data to send has no binary data
|
||||
});
|
||||
```
|
||||
|
||||
#### Event: 'disconnect'
|
||||
|
||||
- `reason` _(String)_ the reason of the disconnection (either client or server-side)
|
||||
|
||||
@@ -29,7 +29,7 @@ function onConnect(socket){
|
||||
io.of('myNamespace').to('room').emit('event', 'message');
|
||||
|
||||
// sending to individual socketid (private message)
|
||||
socket.to(<socketid>).emit('hey', 'I just met you');
|
||||
io.to(<socketid>).emit('hey', 'I just met you');
|
||||
|
||||
// sending with acknowledgement
|
||||
socket.emit('question', 'do you think so?', function (answer) {});
|
||||
@@ -40,9 +40,12 @@ function onConnect(socket){
|
||||
// sending a message that might be dropped if the client is not ready to receive messages
|
||||
socket.volatile.emit('maybe', 'do you really need it?');
|
||||
|
||||
// specifying whether the data to send has binary data
|
||||
socket.binary(false).emit('what', 'I have no binaries!');
|
||||
|
||||
// sending to all clients on this node (when using multiple nodes)
|
||||
io.local.emit('hi', 'my lovely babies');
|
||||
|
||||
|
||||
// sending to all connected clients
|
||||
io.emit('an event sent to all connected clients');
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ var server = require('http').createServer(app);
|
||||
var io = require('../..')(server);
|
||||
var port = process.env.PORT || 3000;
|
||||
|
||||
server.listen(port, function () {
|
||||
server.listen(port, () => {
|
||||
console.log('Server listening at port %d', port);
|
||||
});
|
||||
|
||||
@@ -17,11 +17,11 @@ app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
var numUsers = 0;
|
||||
|
||||
io.on('connection', function (socket) {
|
||||
io.on('connection', (socket) => {
|
||||
var addedUser = false;
|
||||
|
||||
// when the client emits 'new message', this listens and executes
|
||||
socket.on('new message', function (data) {
|
||||
socket.on('new message', (data) => {
|
||||
// we tell the client to execute 'new message'
|
||||
socket.broadcast.emit('new message', {
|
||||
username: socket.username,
|
||||
@@ -30,7 +30,7 @@ io.on('connection', function (socket) {
|
||||
});
|
||||
|
||||
// when the client emits 'add user', this listens and executes
|
||||
socket.on('add user', function (username) {
|
||||
socket.on('add user', (username) => {
|
||||
if (addedUser) return;
|
||||
|
||||
// we store the username in the socket session for this client
|
||||
@@ -48,21 +48,21 @@ io.on('connection', function (socket) {
|
||||
});
|
||||
|
||||
// when the client emits 'typing', we broadcast it to others
|
||||
socket.on('typing', function () {
|
||||
socket.on('typing', () => {
|
||||
socket.broadcast.emit('typing', {
|
||||
username: socket.username
|
||||
});
|
||||
});
|
||||
|
||||
// when the client emits 'stop typing', we broadcast it to others
|
||||
socket.on('stop typing', function () {
|
||||
socket.on('stop typing', () => {
|
||||
socket.broadcast.emit('stop typing', {
|
||||
username: socket.username
|
||||
});
|
||||
});
|
||||
|
||||
// when the user disconnects.. perform this
|
||||
socket.on('disconnect', function () {
|
||||
socket.on('disconnect', () => {
|
||||
if (addedUser) {
|
||||
--numUsers;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ $(function() {
|
||||
|
||||
var socket = io();
|
||||
|
||||
function addParticipantsMessage (data) {
|
||||
const addParticipantsMessage = (data) => {
|
||||
var message = '';
|
||||
if (data.numUsers === 1) {
|
||||
message += "there's 1 participant";
|
||||
@@ -36,7 +36,7 @@ $(function() {
|
||||
}
|
||||
|
||||
// Sets the client's username
|
||||
function setUsername () {
|
||||
const setUsername = () => {
|
||||
username = cleanInput($usernameInput.val().trim());
|
||||
|
||||
// If the username is valid
|
||||
@@ -52,7 +52,7 @@ $(function() {
|
||||
}
|
||||
|
||||
// Sends a chat message
|
||||
function sendMessage () {
|
||||
const sendMessage = () => {
|
||||
var message = $inputMessage.val();
|
||||
// Prevent markup from being injected into the message
|
||||
message = cleanInput(message);
|
||||
@@ -69,13 +69,13 @@ $(function() {
|
||||
}
|
||||
|
||||
// Log a message
|
||||
function log (message, options) {
|
||||
const log = (message, options) => {
|
||||
var $el = $('<li>').addClass('log').text(message);
|
||||
addMessageElement($el, options);
|
||||
}
|
||||
|
||||
// Adds the visual chat message to the message list
|
||||
function addChatMessage (data, options) {
|
||||
const addChatMessage = (data, options) => {
|
||||
// Don't fade the message in if there is an 'X was typing'
|
||||
var $typingMessages = getTypingMessages(data);
|
||||
options = options || {};
|
||||
@@ -100,14 +100,14 @@ $(function() {
|
||||
}
|
||||
|
||||
// Adds the visual chat typing message
|
||||
function addChatTyping (data) {
|
||||
const addChatTyping = (data) => {
|
||||
data.typing = true;
|
||||
data.message = 'is typing';
|
||||
addChatMessage(data);
|
||||
}
|
||||
|
||||
// Removes the visual chat typing message
|
||||
function removeChatTyping (data) {
|
||||
const removeChatTyping = (data) => {
|
||||
getTypingMessages(data).fadeOut(function () {
|
||||
$(this).remove();
|
||||
});
|
||||
@@ -118,7 +118,7 @@ $(function() {
|
||||
// options.fade - If the element should fade-in (default = true)
|
||||
// options.prepend - If the element should prepend
|
||||
// all other messages (default = false)
|
||||
function addMessageElement (el, options) {
|
||||
const addMessageElement = (el, options) => {
|
||||
var $el = $(el);
|
||||
|
||||
// Setup default options
|
||||
@@ -145,12 +145,12 @@ $(function() {
|
||||
}
|
||||
|
||||
// Prevents input from having injected markup
|
||||
function cleanInput (input) {
|
||||
const cleanInput = (input) => {
|
||||
return $('<div/>').text(input).html();
|
||||
}
|
||||
|
||||
// Updates the typing event
|
||||
function updateTyping () {
|
||||
const updateTyping = () => {
|
||||
if (connected) {
|
||||
if (!typing) {
|
||||
typing = true;
|
||||
@@ -158,7 +158,7 @@ $(function() {
|
||||
}
|
||||
lastTypingTime = (new Date()).getTime();
|
||||
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
var typingTimer = (new Date()).getTime();
|
||||
var timeDiff = typingTimer - lastTypingTime;
|
||||
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
|
||||
@@ -170,14 +170,14 @@ $(function() {
|
||||
}
|
||||
|
||||
// Gets the 'X is typing' messages of a user
|
||||
function getTypingMessages (data) {
|
||||
const getTypingMessages = (data) => {
|
||||
return $('.typing.message').filter(function (i) {
|
||||
return $(this).data('username') === data.username;
|
||||
});
|
||||
}
|
||||
|
||||
// Gets the color of a username through our hash function
|
||||
function getUsernameColor (username) {
|
||||
const getUsernameColor = (username) => {
|
||||
// Compute hash code
|
||||
var hash = 7;
|
||||
for (var i = 0; i < username.length; i++) {
|
||||
@@ -190,7 +190,7 @@ $(function() {
|
||||
|
||||
// Keyboard events
|
||||
|
||||
$window.keydown(function (event) {
|
||||
$window.keydown(event => {
|
||||
// Auto-focus the current input when a key is typed
|
||||
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
|
||||
$currentInput.focus();
|
||||
@@ -207,26 +207,26 @@ $(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$inputMessage.on('input', function() {
|
||||
$inputMessage.on('input', () => {
|
||||
updateTyping();
|
||||
});
|
||||
|
||||
// Click events
|
||||
|
||||
// Focus input when clicking anywhere on login page
|
||||
$loginPage.click(function () {
|
||||
$loginPage.click(() => {
|
||||
$currentInput.focus();
|
||||
});
|
||||
|
||||
// Focus input when clicking on the message input's border
|
||||
$inputMessage.click(function () {
|
||||
$inputMessage.click(() => {
|
||||
$inputMessage.focus();
|
||||
});
|
||||
|
||||
// Socket events
|
||||
|
||||
// Whenever the server emits 'login', log the login message
|
||||
socket.on('login', function (data) {
|
||||
socket.on('login', (data) => {
|
||||
connected = true;
|
||||
// Display the welcome message
|
||||
var message = "Welcome to Socket.IO Chat – ";
|
||||
@@ -237,45 +237,45 @@ $(function() {
|
||||
});
|
||||
|
||||
// Whenever the server emits 'new message', update the chat body
|
||||
socket.on('new message', function (data) {
|
||||
socket.on('new message', (data) => {
|
||||
addChatMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'user joined', log it in the chat body
|
||||
socket.on('user joined', function (data) {
|
||||
socket.on('user joined', (data) => {
|
||||
log(data.username + ' joined');
|
||||
addParticipantsMessage(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'user left', log it in the chat body
|
||||
socket.on('user left', function (data) {
|
||||
socket.on('user left', (data) => {
|
||||
log(data.username + ' left');
|
||||
addParticipantsMessage(data);
|
||||
removeChatTyping(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'typing', show the typing message
|
||||
socket.on('typing', function (data) {
|
||||
socket.on('typing', (data) => {
|
||||
addChatTyping(data);
|
||||
});
|
||||
|
||||
// Whenever the server emits 'stop typing', kill the typing message
|
||||
socket.on('stop typing', function (data) {
|
||||
socket.on('stop typing', (data) => {
|
||||
removeChatTyping(data);
|
||||
});
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
socket.on('disconnect', () => {
|
||||
log('you have been disconnected');
|
||||
});
|
||||
|
||||
socket.on('reconnect', function () {
|
||||
socket.on('reconnect', () => {
|
||||
log('you have been reconnected');
|
||||
if (username) {
|
||||
socket.emit('add user', username);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('reconnect_error', function () {
|
||||
socket.on('reconnect_error', () => {
|
||||
log('attempt to reconnect has failed');
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"author": "Grant Timmerman",
|
||||
"private": true,
|
||||
"license": "BSD",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "4.13.4",
|
||||
"socket.io": "^1.7.2",
|
||||
|
||||
@@ -14,7 +14,7 @@ They are tested with various payloads:
|
||||
|
||||
- string: `['1', '2', ... '1000']`
|
||||
- numeric: `[1, 2, ... 1000]`
|
||||
- binary: `new Buffer(1000), where buf[i] = i`
|
||||
- binary: `Buffer.allocUnsafe(1000), where buf[i] = i`
|
||||
|
||||
## How to use
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ let server4 = io(3004, {
|
||||
|
||||
let string = [];
|
||||
let numeric = [];
|
||||
let binary = new Buffer(1e3);
|
||||
let binary = Buffer.allocUnsafe(1e3);
|
||||
for (var i = 0; i < 1e3; i++) {
|
||||
string.push('' + i);
|
||||
numeric.push(i);
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
canvas.addEventListener('mouseup', onMouseUp, false);
|
||||
canvas.addEventListener('mouseout', onMouseUp, false);
|
||||
canvas.addEventListener('mousemove', throttle(onMouseMove, 10), false);
|
||||
|
||||
//Touch support for mobile devices
|
||||
canvas.addEventListener('touchstart', onMouseDown, false);
|
||||
canvas.addEventListener('touchend', onMouseUp, false);
|
||||
canvas.addEventListener('touchcancel', onMouseUp, false);
|
||||
canvas.addEventListener('touchmove', throttle(onMouseMove, 10), false);
|
||||
|
||||
for (var i = 0; i < colors.length; i++){
|
||||
colors[i].addEventListener('click', onColorUpdate, false);
|
||||
@@ -51,21 +57,21 @@
|
||||
|
||||
function onMouseDown(e){
|
||||
drawing = true;
|
||||
current.x = e.clientX;
|
||||
current.y = e.clientY;
|
||||
current.x = e.clientX||e.touches[0].clientX;
|
||||
current.y = e.clientY||e.touches[0].clientY;
|
||||
}
|
||||
|
||||
function onMouseUp(e){
|
||||
if (!drawing) { return; }
|
||||
drawing = false;
|
||||
drawLine(current.x, current.y, e.clientX, e.clientY, current.color, true);
|
||||
drawLine(current.x, current.y, e.clientX||e.touches[0].clientX, e.clientY||e.touches[0].clientY, current.color, true);
|
||||
}
|
||||
|
||||
function onMouseMove(e){
|
||||
if (!drawing) { return; }
|
||||
drawLine(current.x, current.y, e.clientX, e.clientY, current.color, true);
|
||||
current.x = e.clientX;
|
||||
current.y = e.clientY;
|
||||
drawLine(current.x, current.y, e.clientX||e.touches[0].clientX, e.clientY||e.touches[0].clientY, current.color, true);
|
||||
current.x = e.clientX||e.touches[0].clientX;
|
||||
current.y = e.clientY||e.touches[0].clientY;
|
||||
}
|
||||
|
||||
function onColorUpdate(e){
|
||||
|
||||
@@ -56,17 +56,37 @@ Client.prototype.setup = function(){
|
||||
* Connects a client to a namespace.
|
||||
*
|
||||
* @param {String} name namespace
|
||||
* @param {Object} query the query parameters
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Client.prototype.connect = function(name, query){
|
||||
debug('connecting to namespace %s', name);
|
||||
var nsp = this.server.nsps[name];
|
||||
if (!nsp) {
|
||||
this.packet({ type: parser.ERROR, nsp: name, data : 'Invalid namespace'});
|
||||
return;
|
||||
if (this.server.nsps[name]) {
|
||||
debug('connecting to namespace %s', name);
|
||||
return this.doConnect(name, query);
|
||||
}
|
||||
|
||||
this.server.checkNamespace(name, query, (dynamicNsp) => {
|
||||
if (dynamicNsp) {
|
||||
this.doConnect(name, query);
|
||||
} else {
|
||||
debug('creation of namespace %s was denied', name);
|
||||
this.packet({ type: parser.ERROR, nsp: name, data: 'Invalid namespace' });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Connects a client to a namespace.
|
||||
*
|
||||
* @param {String} name namespace
|
||||
* @param {String} query the query parameters
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Client.prototype.doConnect = function(name, query){
|
||||
var nsp = this.server.of(name);
|
||||
|
||||
if ('/' != name && !this.nsps['/']) {
|
||||
this.connectBuffer.push(name);
|
||||
return;
|
||||
|
||||
61
lib/index.js
61
lib/index.js
@@ -1,3 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -12,6 +13,7 @@ var clientVersion = require('socket.io-client/package.json').version;
|
||||
var Client = require('./client');
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var Namespace = require('./namespace');
|
||||
var ParentNamespace = require('./parent-namespace');
|
||||
var Adapter = require('socket.io-adapter');
|
||||
var parser = require('socket.io-parser');
|
||||
var debug = require('debug')('socket.io:server');
|
||||
@@ -46,6 +48,7 @@ function Server(srv, opts){
|
||||
}
|
||||
opts = opts || {};
|
||||
this.nsps = {};
|
||||
this.parentNsps = new Map();
|
||||
this.path(opts.path || '/socket.io');
|
||||
this.serveClient(false !== opts.serveClient);
|
||||
this.parser = opts.parser || parser;
|
||||
@@ -79,9 +82,11 @@ Server.prototype.checkRequest = function(req, fn) {
|
||||
? parts.port
|
||||
: defaultPort;
|
||||
var ok =
|
||||
~this._origins.indexOf(parts.protocol + '//' + parts.hostname + ':' + parts.port) ||
|
||||
~this._origins.indexOf(parts.hostname + ':' + parts.port) ||
|
||||
~this._origins.indexOf(parts.hostname + ':*') ||
|
||||
~this._origins.indexOf('*:' + parts.port);
|
||||
debug('origin %s is %svalid', origin, !!ok ? '' : 'not ');
|
||||
return fn(null, !!ok);
|
||||
} catch (ex) {
|
||||
}
|
||||
@@ -157,6 +162,43 @@ Server.prototype.set = function(key, val){
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the middleware for an incoming namespace not already created on the server.
|
||||
*
|
||||
* @param {String} name name of incoming namespace
|
||||
* @param {Object} query the query parameters
|
||||
* @param {Function} fn callback
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.checkNamespace = function(name, query, fn){
|
||||
if (this.parentNsps.size === 0) return fn(false);
|
||||
|
||||
const keysIterator = this.parentNsps.keys();
|
||||
|
||||
const run = () => {
|
||||
let nextFn = keysIterator.next();
|
||||
if (nextFn.done) {
|
||||
return fn(false);
|
||||
}
|
||||
nextFn.value(name, query, (err, allow) => {
|
||||
if (err || !allow) {
|
||||
return run();
|
||||
}
|
||||
if (this.nsps[name]) {
|
||||
// the namespace was created in the meantime
|
||||
debug("dynamic namespace %s already exists", name);
|
||||
return fn(this.nsps[name]);
|
||||
}
|
||||
const namespace = this.parentNsps.get(nextFn.value).createChild(name);
|
||||
debug("dynamic namespace %s was created", name);
|
||||
fn(namespace);
|
||||
});
|
||||
};
|
||||
|
||||
run();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the client serving path.
|
||||
*
|
||||
@@ -193,7 +235,7 @@ Server.prototype.adapter = function(v){
|
||||
/**
|
||||
* Sets the allowed origins for requests.
|
||||
*
|
||||
* @param {String} v origins
|
||||
* @param {String|String[]} v origins
|
||||
* @return {Server|Adapter} self when setting or value when getting
|
||||
* @api public
|
||||
*/
|
||||
@@ -334,6 +376,7 @@ Server.prototype.serve = function(req, res){
|
||||
}
|
||||
|
||||
debug('serve client source');
|
||||
res.setHeader("Cache-Control", "public, max-age=0");
|
||||
res.setHeader('Content-Type', 'application/javascript');
|
||||
res.setHeader('ETag', expectedEtag);
|
||||
res.writeHead(200);
|
||||
@@ -402,12 +445,24 @@ Server.prototype.onconnection = function(conn){
|
||||
/**
|
||||
* Looks up a namespace.
|
||||
*
|
||||
* @param {String} name nsp name
|
||||
* @param {String|RegExp|Function} name nsp name
|
||||
* @param {Function} [fn] optional, nsp `connection` ev handler
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.of = function(name, fn){
|
||||
if (typeof name === 'function' || name instanceof RegExp) {
|
||||
const parentNsp = new ParentNamespace(this);
|
||||
debug('initializing parent namespace %s', parentNsp.name);
|
||||
if (typeof name === 'function') {
|
||||
this.parentNsps.set(name, parentNsp);
|
||||
} else {
|
||||
this.parentNsps.set((nsp, conn, next) => next(null, name.test(nsp)), parentNsp);
|
||||
}
|
||||
if (fn) parentNsp.on('connect', fn);
|
||||
return parentNsp;
|
||||
}
|
||||
|
||||
if (String(name)[0] !== '/') name = '/' + name;
|
||||
|
||||
var nsp = this.nsps[name];
|
||||
@@ -451,7 +506,7 @@ var emitterMethods = Object.keys(Emitter.prototype).filter(function(key){
|
||||
return typeof Emitter.prototype[key] === 'function';
|
||||
});
|
||||
|
||||
emitterMethods.concat(['to', 'in', 'use', 'send', 'write', 'clients', 'compress']).forEach(function(fn){
|
||||
emitterMethods.concat(['to', 'in', 'use', 'send', 'write', 'clients', 'compress', 'binary']).forEach(function(fn){
|
||||
Server.prototype[fn] = function(){
|
||||
return this.sockets[fn].apply(this.sockets, arguments);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var Socket = require('./socket');
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var parser = require('socket.io-parser');
|
||||
var hasBin = require('has-binary2');
|
||||
var debug = require('debug')('socket.io:namespace');
|
||||
|
||||
/**
|
||||
@@ -99,7 +100,7 @@ Namespace.prototype.initAdapter = function(){
|
||||
*/
|
||||
|
||||
Namespace.prototype.use = function(fn){
|
||||
if (this.server.eio) {
|
||||
if (this.server.eio && this.name === '/') {
|
||||
debug('removing initial packet');
|
||||
delete this.server.eio.initialPacket;
|
||||
}
|
||||
@@ -162,25 +163,31 @@ Namespace.prototype.add = function(client, query, fn){
|
||||
var self = this;
|
||||
this.run(socket, function(err){
|
||||
process.nextTick(function(){
|
||||
if ('open' == client.conn.readyState) {
|
||||
if (err) return socket.error(err.data || err.message);
|
||||
|
||||
// track socket
|
||||
self.sockets[socket.id] = socket;
|
||||
|
||||
// it's paramount that the internal `onconnect` logic
|
||||
// fires before user-set events to prevent state order
|
||||
// violations (such as a disconnection before the connection
|
||||
// logic is complete)
|
||||
socket.onconnect();
|
||||
if (fn) fn();
|
||||
|
||||
// fire user-set events
|
||||
self.emit('connect', socket);
|
||||
self.emit('connection', socket);
|
||||
} else {
|
||||
debug('next called after client was closed - ignoring socket');
|
||||
if ("open" !== client.conn.readyState) {
|
||||
debug("next called after client was closed - ignoring socket");
|
||||
socket._cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
debug("middleware error, sending CONNECT_ERROR packet to the client");
|
||||
socket._cleanup();
|
||||
return socket.error(err.data || err.message);
|
||||
}
|
||||
|
||||
// track socket
|
||||
self.sockets[socket.id] = socket;
|
||||
|
||||
// it's paramount that the internal `onconnect` logic
|
||||
// fires before user-set events to prevent state order
|
||||
// violations (such as a disconnection before the connection
|
||||
// logic is complete)
|
||||
socket.onconnect();
|
||||
if (fn) fn();
|
||||
|
||||
// fire user-set events
|
||||
self.emit('connect', socket);
|
||||
self.emit('connection', socket);
|
||||
});
|
||||
});
|
||||
return socket;
|
||||
@@ -214,7 +221,10 @@ Namespace.prototype.emit = function(ev){
|
||||
}
|
||||
// set up packet object
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var packet = { type: parser.EVENT, data: args };
|
||||
var packet = {
|
||||
type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT,
|
||||
data: args
|
||||
};
|
||||
|
||||
if ('function' == typeof args[args.length - 1]) {
|
||||
throw new Error('Callbacks are not supported when broadcasting');
|
||||
@@ -258,6 +268,9 @@ Namespace.prototype.write = function(){
|
||||
*/
|
||||
|
||||
Namespace.prototype.clients = function(fn){
|
||||
if(!this.adapter){
|
||||
throw new Error('No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?')
|
||||
}
|
||||
this.adapter.clients(this.rooms, fn);
|
||||
// reset rooms for scenario:
|
||||
// .in('room').clients() (GH-1978)
|
||||
@@ -277,3 +290,16 @@ Namespace.prototype.compress = function(compress){
|
||||
this.flags.compress = compress;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the binary flag
|
||||
*
|
||||
* @param {Boolean} Encode as if it has binary data if `true`, Encode as if it doesnt have binary data if `false`
|
||||
* @return {Socket} self
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Namespace.prototype.binary = function (binary) {
|
||||
this.flags.binary = binary;
|
||||
return this;
|
||||
};
|
||||
|
||||
39
lib/parent-namespace.js
Normal file
39
lib/parent-namespace.js
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
const Namespace = require('./namespace');
|
||||
|
||||
let count = 0;
|
||||
|
||||
class ParentNamespace extends Namespace {
|
||||
|
||||
constructor(server) {
|
||||
super(server, '/_' + (count++));
|
||||
this.children = new Set();
|
||||
}
|
||||
|
||||
initAdapter() {}
|
||||
|
||||
emit() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
|
||||
this.children.forEach(nsp => {
|
||||
nsp.rooms = this.rooms;
|
||||
nsp.flags = this.flags;
|
||||
nsp.emit.apply(nsp, args);
|
||||
});
|
||||
this.rooms = [];
|
||||
this.flags = {};
|
||||
}
|
||||
|
||||
createChild(name) {
|
||||
const namespace = new Namespace(this.server, name);
|
||||
namespace.fns = this.fns.slice(0);
|
||||
this.listeners('connect').forEach(listener => namespace.on('connect', listener));
|
||||
this.listeners('connection').forEach(listener => namespace.on('connection', listener));
|
||||
this.children.add(namespace);
|
||||
this.server.nsps[name] = namespace;
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ParentNamespace;
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
var Emitter = require('events').EventEmitter;
|
||||
var parser = require('socket.io-parser');
|
||||
var hasBin = require('has-binary2');
|
||||
var url = require('url');
|
||||
var debug = require('debug')('socket.io:socket');
|
||||
|
||||
@@ -38,7 +39,8 @@ exports.events = [
|
||||
var flags = [
|
||||
'json',
|
||||
'volatile',
|
||||
'broadcast'
|
||||
'broadcast',
|
||||
'local'
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -47,6 +49,8 @@ var flags = [
|
||||
|
||||
var emit = Emitter.prototype.emit;
|
||||
|
||||
function noop() {}
|
||||
|
||||
/**
|
||||
* Interface to a `Client` for a given `Namespace`.
|
||||
*
|
||||
@@ -64,12 +68,15 @@ function Socket(nsp, client, query){
|
||||
this.conn = client.conn;
|
||||
this.rooms = {};
|
||||
this.acks = {};
|
||||
this.connected = true;
|
||||
this.disconnected = false;
|
||||
this.connected = false;
|
||||
this.disconnected = true;
|
||||
this.handshake = this.buildHandshake(query);
|
||||
this.fns = [];
|
||||
this.flags = {};
|
||||
this._rooms = [];
|
||||
|
||||
// prevents crash when the socket receives an "error" event without listener
|
||||
this.on('error', noop);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +121,7 @@ Socket.prototype.buildHandshake = function(query){
|
||||
function buildQuery(){
|
||||
var requestQuery = url.parse(self.request.url, true).query;
|
||||
//if socket-specific query exist, replace query strings in requestQuery
|
||||
return Object.assign({}, query, requestQuery);
|
||||
return Object.assign({}, requestQuery, query);
|
||||
}
|
||||
return {
|
||||
headers: this.request.headers,
|
||||
@@ -143,7 +150,7 @@ Socket.prototype.emit = function(ev){
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var packet = {
|
||||
type: parser.EVENT,
|
||||
type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT,
|
||||
data: args
|
||||
};
|
||||
|
||||
@@ -298,6 +305,8 @@ Socket.prototype.leaveAll = function(){
|
||||
|
||||
Socket.prototype.onconnect = function(){
|
||||
debug('socket connected - writing packet');
|
||||
this.connected = true;
|
||||
this.disconnected = false;
|
||||
this.nsp.connected[this.id] = this;
|
||||
this.join(this.id);
|
||||
var skip = this.nsp.name === '/' && this.nsp.fns.length === 0;
|
||||
@@ -380,7 +389,7 @@ Socket.prototype.ack = function(id){
|
||||
|
||||
self.packet({
|
||||
id: id,
|
||||
type: parser.ACK,
|
||||
type: hasBin(args) ? parser.BINARY_ACK : parser.ACK,
|
||||
data: args
|
||||
});
|
||||
|
||||
@@ -423,12 +432,7 @@ Socket.prototype.ondisconnect = function(){
|
||||
*/
|
||||
|
||||
Socket.prototype.onerror = function(err){
|
||||
if (this.listeners('error').length) {
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
console.error('Missing error handler on `socket`.');
|
||||
console.error(err.stack);
|
||||
}
|
||||
this.emit('error', err);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -443,7 +447,7 @@ Socket.prototype.onclose = function(reason){
|
||||
if (!this.connected) return this;
|
||||
debug('closing socket - reason %s', reason);
|
||||
this.emit('disconnecting', reason);
|
||||
this.leaveAll();
|
||||
this._cleanup();
|
||||
this.nsp.remove(this);
|
||||
this.client.remove(this);
|
||||
this.connected = false;
|
||||
@@ -495,6 +499,19 @@ Socket.prototype.compress = function(compress){
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the binary flag
|
||||
*
|
||||
* @param {Boolean} Encode as if it has binary data if `true`, Encode as if it doesnt have binary data if `false`
|
||||
* @return {Socket} self
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Socket.prototype.binary = function (binary) {
|
||||
this.flags.binary = binary;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch incoming event to socket listeners.
|
||||
*
|
||||
@@ -510,7 +527,11 @@ Socket.prototype.dispatch = function(event){
|
||||
if (err) {
|
||||
return self.error(err.data || err.message);
|
||||
}
|
||||
emit.apply(self, event);
|
||||
if (self.connected) {
|
||||
emit.apply(self, event);
|
||||
} else {
|
||||
debug("ignore packet received after disconnection");
|
||||
}
|
||||
});
|
||||
}
|
||||
this.run(event, dispatchSocket);
|
||||
@@ -555,3 +576,8 @@ Socket.prototype.run = function(event, fn){
|
||||
|
||||
run(0);
|
||||
};
|
||||
|
||||
Socket.prototype._cleanup = function () {
|
||||
this.leaveAll();
|
||||
this.join = function noop() {};
|
||||
}
|
||||
|
||||
3352
package-lock.json
generated
Normal file
3352
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "socket.io",
|
||||
"version": "2.0.4",
|
||||
"version": "2.5.1",
|
||||
"description": "node.js realtime framework server",
|
||||
"keywords": [
|
||||
"realtime",
|
||||
@@ -24,18 +24,19 @@
|
||||
"test": "nyc mocha --reporter spec --slow 200 --bail --timeout 10000 test/socket.io.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": "~2.6.6",
|
||||
"engine.io": "~3.1.0",
|
||||
"debug": "~4.1.0",
|
||||
"engine.io": "~3.6.0",
|
||||
"has-binary2": "~1.0.2",
|
||||
"socket.io-adapter": "~1.1.0",
|
||||
"socket.io-client": "2.0.4",
|
||||
"socket.io-parser": "~3.1.1"
|
||||
"socket.io-client": "2.5.0",
|
||||
"socket.io-parser": "~3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expect.js": "0.3.1",
|
||||
"mocha": "^3.5.3",
|
||||
"nyc": "^11.2.1",
|
||||
"superagent": "1.6.1",
|
||||
"supertest": "1.1.0"
|
||||
"superagent": "^3.8.2",
|
||||
"supertest": "^3.0.0"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var http = require('http').Server;
|
||||
var io = require('../lib');
|
||||
var fs = require('fs');
|
||||
@@ -21,7 +23,7 @@ function client(srv, nsp, opts){
|
||||
|
||||
describe('socket.io', function(){
|
||||
|
||||
it('should be the same version as client', function(){
|
||||
it.skip('should be the same version as client', function(){
|
||||
var version = require('../package').version;
|
||||
expect(version).to.be(require('socket.io-client/package').version);
|
||||
});
|
||||
@@ -354,6 +356,17 @@ describe('socket.io', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow request when using an array of origins', function(done) {
|
||||
io({ origins: [ 'http://foo.example:54024' ] }).listen('54024');
|
||||
request.get('http://localhost:54024/socket.io/default/')
|
||||
.set('origin', 'http://foo.example:54024')
|
||||
.query({ transport: 'polling' })
|
||||
.end(function (err, res) {
|
||||
expect(res.status).to.be(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('close', function(){
|
||||
@@ -878,6 +891,97 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic namespaces', function () {
|
||||
it('should allow connections to dynamic namespaces with a regex', function(done){
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
let count = 0;
|
||||
srv.listen(function(){
|
||||
const socket = client(srv, '/dynamic-101');
|
||||
let dynamicNsp = sio.of(/^\/dynamic-\d+$/).on('connect', (socket) => {
|
||||
expect(socket.nsp.name).to.be('/dynamic-101');
|
||||
dynamicNsp.emit('hello', 1, '2', { 3: '4'});
|
||||
if (++count === 4) done();
|
||||
}).use((socket, next) => {
|
||||
next();
|
||||
if (++count === 4) done();
|
||||
});
|
||||
socket.on('error', function(err) {
|
||||
expect().fail();
|
||||
});
|
||||
socket.on('connect', () => {
|
||||
if (++count === 4) done();
|
||||
});
|
||||
socket.on('hello', (a, b, c) => {
|
||||
expect(a).to.eql(1);
|
||||
expect(b).to.eql('2');
|
||||
expect(c).to.eql({ 3: '4' });
|
||||
if (++count === 4) done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow connections to dynamic namespaces with a function', function(done){
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
srv.listen(function(){
|
||||
const socket = client(srv, '/dynamic-101');
|
||||
sio.of((name, query, next) => next(null, '/dynamic-101' === name));
|
||||
socket.on('connect', done);
|
||||
});
|
||||
});
|
||||
|
||||
it('should disallow connections when no dynamic namespace matches', function(done){
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
srv.listen(function(){
|
||||
const socket = client(srv, '/abc');
|
||||
sio.of(/^\/dynamic-\d+$/);
|
||||
sio.of((name, query, next) => next(null, '/dynamic-101' === name));
|
||||
socket.on('error', (err) => {
|
||||
expect(err).to.be('Invalid namespace');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle race conditions with dynamic namespaces (#4136)", (done) => {
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
const counters = {
|
||||
connected: 0,
|
||||
created: 0,
|
||||
events: 0,
|
||||
};
|
||||
const buffer = [];
|
||||
srv.listen(() => {
|
||||
const handler = () => {
|
||||
if (++counters.events === 2) {
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
sio
|
||||
.of((name, query, next) => {
|
||||
buffer.push(next);
|
||||
if (buffer.length === 2) {
|
||||
buffer.forEach((next) => next(null, true));
|
||||
}
|
||||
})
|
||||
.on("connection", (socket) => {
|
||||
if (++counters.connected === 2) {
|
||||
sio.of("/dynamic-101").emit("message");
|
||||
}
|
||||
});
|
||||
|
||||
let one = client(srv, "/dynamic-101");
|
||||
let two = client(srv, "/dynamic-101");
|
||||
one.on("message", handler);
|
||||
two.on("message", handler);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('socket', function(){
|
||||
@@ -1058,7 +1162,7 @@ describe('socket.io', function(){
|
||||
sio.on('connection', function(s){
|
||||
fs.readFile(join(__dirname, 'support', 'doge.jpg'), function(err, data){
|
||||
if (err) return done(err);
|
||||
var buf = new Buffer('asdfasdf', 'utf8');
|
||||
var buf = Buffer.from('asdfasdf', 'utf8');
|
||||
s.emit('multiple', 1, data, '3', [4], buf, [data, 'swag', buf]);
|
||||
});
|
||||
});
|
||||
@@ -1075,7 +1179,7 @@ describe('socket.io', function(){
|
||||
expect(Buffer.isBuffer(a)).to.be(true);
|
||||
done();
|
||||
});
|
||||
var buf = new Buffer('abcdefg', 'utf8');
|
||||
var buf = Buffer.from('abcdefg', 'utf8');
|
||||
socket.emit('buff', buf);
|
||||
});
|
||||
});
|
||||
@@ -1100,7 +1204,7 @@ describe('socket.io', function(){
|
||||
});
|
||||
fs.readFile(join(__dirname, 'support', 'doge.jpg'), function(err, data){
|
||||
if (err) return done(err);
|
||||
var buf = new Buffer('asdfasdf', 'utf8');
|
||||
var buf = Buffer.from('asdfasdf', 'utf8');
|
||||
socket.emit('multiple', 1, data, '3', [4], buf, [data, 'swag', buf]);
|
||||
});
|
||||
});
|
||||
@@ -1428,7 +1532,7 @@ describe('socket.io', function(){
|
||||
expect(Buffer.isBuffer(buf)).to.be(true);
|
||||
fn(1, 2);
|
||||
});
|
||||
socket.emit('woot', new Buffer(3), function(a, b){
|
||||
socket.emit('woot', Buffer.alloc(3), function(a, b){
|
||||
expect(a).to.be(1);
|
||||
expect(b).to.be(2);
|
||||
done();
|
||||
@@ -1447,7 +1551,7 @@ describe('socket.io', function(){
|
||||
expect(Buffer.isBuffer(a)).to.be(true);
|
||||
fn();
|
||||
});
|
||||
s.emit('hi', new Buffer(4), function(){
|
||||
s.emit('hi', Buffer.alloc(4), function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -1461,7 +1565,7 @@ describe('socket.io', function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
socket.on('hi', function(fn){
|
||||
fn(new Buffer(1));
|
||||
fn(Buffer.alloc(1));
|
||||
});
|
||||
s.emit('hi', function(a){
|
||||
expect(Buffer.isBuffer(a)).to.be(true);
|
||||
@@ -1478,7 +1582,7 @@ describe('socket.io', function(){
|
||||
var socket = client(srv);
|
||||
sio.on('connection', function(s){
|
||||
s.on('woot', function(fn){
|
||||
fn(new Buffer(2));
|
||||
fn(Buffer.alloc(2));
|
||||
});
|
||||
socket.emit('woot', function(a){
|
||||
expect(Buffer.isBuffer(a)).to.be(true);
|
||||
@@ -1553,8 +1657,25 @@ describe('socket.io', function(){
|
||||
expect(s.handshake.query.key2).to.be('&=bb');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should see the query options sent in the Socket.IO handshake (specific to the given socket)', (done) => {
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
const socket = client(srv, '/namespace',{ query: { key1: 'a', key2: 'b' }}); // manager-specific query option
|
||||
socket.query = { key2: 'c' }; // socket-specific query option
|
||||
|
||||
const success = () => {
|
||||
sio.close();
|
||||
socket.close();
|
||||
done();
|
||||
}
|
||||
|
||||
sio.of('/namespace').on('connection', (s) => {
|
||||
expect(s.handshake.query.key1).to.be('a'); // in the query params
|
||||
expect(s.handshake.query.key2).to.be('c'); // in the Socket.IO handshake
|
||||
success();
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle very large json', function(done){
|
||||
@@ -1673,7 +1794,7 @@ describe('socket.io', function(){
|
||||
var socket = client(srv, { reconnection: false });
|
||||
sio.on('connection', function(s){
|
||||
s.conn.on('upgrade', function(){
|
||||
console.log('\033[96mNote: warning expected and normal in test.\033[39m');
|
||||
console.log('\u001b[96mNote: warning expected and normal in test.\u001b[39m');
|
||||
socket.io.engine.write('5woooot');
|
||||
setTimeout(function(){
|
||||
done();
|
||||
@@ -1690,7 +1811,7 @@ describe('socket.io', function(){
|
||||
var socket = client(srv, { reconnection: false });
|
||||
sio.on('connection', function(s){
|
||||
s.conn.on('upgrade', function(){
|
||||
console.log('\033[96mNote: warning expected and normal in test.\033[39m');
|
||||
console.log('\u001b[96mNote: warning expected and normal in test.\u001b[39m');
|
||||
socket.io.engine.write('44["handle me please"]');
|
||||
setTimeout(function(){
|
||||
done();
|
||||
@@ -1737,7 +1858,7 @@ describe('socket.io', function(){
|
||||
it('should not crash when messing with Object prototype (and other globals)', function(done){
|
||||
Object.prototype.foo = 'bar';
|
||||
global.File = '';
|
||||
global.Blob = [];
|
||||
// global.Blob = [];
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
srv.listen(function(){
|
||||
@@ -1753,6 +1874,70 @@ describe('socket.io', function(){
|
||||
});
|
||||
});
|
||||
|
||||
it("should ignore a packet received after disconnection", (done) => {
|
||||
const srv = http();
|
||||
const sio = io(srv);
|
||||
|
||||
srv.listen(() => {
|
||||
const clientSocket = client(srv);
|
||||
|
||||
const success = () => {
|
||||
clientSocket.close();
|
||||
sio.close();
|
||||
done();
|
||||
};
|
||||
|
||||
sio.on("connection", (socket) => {
|
||||
socket.on("test", () => {
|
||||
done(new Error("should not happen"));
|
||||
});
|
||||
socket.on("disconnect", success);
|
||||
});
|
||||
|
||||
clientSocket.on("connect", () => {
|
||||
clientSocket.emit("test", Buffer.alloc(10));
|
||||
clientSocket.disconnect();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should leave all rooms joined after a middleware failure", (done) => {
|
||||
const srv = http().listen(0);
|
||||
const sio = io(srv);
|
||||
const clientSocket = client(srv, "/");
|
||||
|
||||
sio.use((socket, next) => {
|
||||
socket.join("room1");
|
||||
next(new Error("nope"));
|
||||
});
|
||||
|
||||
clientSocket.on("error", () => {
|
||||
expect(sio.of("/").adapter.rooms).to.eql(0);
|
||||
|
||||
clientSocket.disconnect();
|
||||
sio.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not join rooms after disconnection", (done) => {
|
||||
const srv = http().listen(0);
|
||||
const sio = io(srv);
|
||||
const clientSocket = client(srv, "/");
|
||||
|
||||
sio.on("connection", (socket) => {
|
||||
socket.disconnect();
|
||||
socket.join("room1");
|
||||
});
|
||||
|
||||
clientSocket.on("disconnect", () => {
|
||||
expect(sio.of("/").adapter.rooms).to.eql(0);
|
||||
|
||||
sio.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should always trigger the callback (if provided) when joining a room', function(done){
|
||||
var srv = http();
|
||||
var sio = io(srv);
|
||||
@@ -1831,7 +2016,7 @@ describe('socket.io', function(){
|
||||
});
|
||||
|
||||
function emit(){
|
||||
sio.emit('bin', new Buffer(10));
|
||||
sio.emit('bin', Buffer.alloc(10));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2015,8 +2200,8 @@ describe('socket.io', function(){
|
||||
socket.join(room, fn);
|
||||
});
|
||||
socket.on('broadcast', function(){
|
||||
socket.broadcast.to('test').emit('bin', new Buffer(5));
|
||||
socket.emit('bin2', new Buffer(5));
|
||||
socket.broadcast.to('test').emit('bin', Buffer.alloc(5));
|
||||
socket.emit('bin2', Buffer.alloc(5));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2277,6 +2462,42 @@ describe('socket.io', function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with a custom namespace', (done) => {
|
||||
var srv = http();
|
||||
var sio = io();
|
||||
sio.listen(srv);
|
||||
sio.of('/chat').use(function(socket, next){
|
||||
next();
|
||||
});
|
||||
|
||||
var count = 0;
|
||||
client(srv, '/').on('connect', () => {
|
||||
if (++count === 2) done();
|
||||
});
|
||||
client(srv, '/chat').on('connect', () => {
|
||||
if (++count === 2) done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should only set `connected` to true after the middleware execution", (done) => {
|
||||
const httpServer = http();
|
||||
const sio = io(httpServer);
|
||||
|
||||
const clientSocket = client(httpServer, "/");
|
||||
|
||||
sio.use((socket, next) => {
|
||||
expect(socket.connected).to.be(false);
|
||||
expect(socket.disconnected).to.be(true);
|
||||
next();
|
||||
});
|
||||
|
||||
sio.on("connection", (socket) => {
|
||||
expect(socket.connected).to.be(true);
|
||||
expect(socket.disconnected).to.be(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('socket middleware', function(done){
|
||||
|
||||
Reference in New Issue
Block a user