Compare commits

...

392 Commits
1.3.6 ... 3.0.0

Author SHA1 Message Date
Damien Arrachequesne
1af3267e3f chore(release): 3.0.0
Diff: https://github.com/socketio/socket.io/compare/2.3.0...3.0.0
2020-11-05 22:07:47 +01:00
Damien Arrachequesne
02951c4391 chore(release): 3.0.0-rc4
Diff: https://github.com/socketio/socket.io/compare/3.0.0-rc3...3.0.0-rc4
2020-10-30 23:02:43 +01:00
Damien Arrachequesne
54bf4a44e9 feat: emit an Error object upon middleware error
This commit restores the ability to send additional data in the
middleware functions, which was removed during the rewrite to
Typescript ([1]).

The only difference with the previous implementation is that the client
will now emit a "connect_error" (previously, "error") event with an
actual Error object, with both the message and an optional "data"
attribute.

```js
// server-side
io.use((socket, next) => {
  const err = new Error("not authorized");
  err.data = { content: "Please retry later" };
  next(err);
});

// client-side
socket.on("connect_error", err => {
  console.log(err.message); // not authorized
  console.log(err.data.content); // Please retry later
});
```

[1]: a5581a9789
2020-10-30 22:52:08 +01:00
Damien Arrachequesne
aa7574f884 feat: serve msgpack bundle
See 71d60480af
2020-10-27 23:17:12 +01:00
Damien Arrachequesne
64056d6616 docs(examples): update TypeScript example 2020-10-27 22:18:07 +01:00
Damien Arrachequesne
cacad7029a chore(release): 3.0.0-rc3
Diff: https://github.com/socketio/socket.io/compare/3.0.0-rc2...3.0.0-rc3
2020-10-27 00:44:30 +01:00
Damien Arrachequesne
d16c035d25 refactor: rename ERROR to CONNECT_ERROR
The meaning is not modified: this packet type is still used by the
server when the connection to a namespace is refused.
2020-10-26 00:29:11 +01:00
Damien Arrachequesne
5c73733985 feat: add support for catch-all listeners
Inspired from EventEmitter2 [1]

```js
io.on("connect", socket => {

  socket.onAny((event, ...args) => {});

  socket.prependAny((event, ...args) => {});

  socket.offAny(); // remove all listeners

  socket.offAny(listener);

  const listeners = socket.listenersAny();
});
```

Breaking change: the socket.use() method is removed

This method was introduced in [2] for the same feature (having a
catch-all listener), but there were two issues:

- the API is not very user-friendly, since the user has to know the structure of the packet argument
- it uses an ERROR packet, which is reserved for Namespace authentication issues (see [3])

[1]: https://github.com/EventEmitter2/EventEmitter2
[2]: https://github.com/socketio/socket.io/issues/434
[3]: https://github.com/socketio/socket.io-protocol
2020-10-25 23:44:01 +01:00
Damien Arrachequesne
129c6417bd feat: make Socket#join() and Socket#leave() synchronous
Depending on the adapter, Socket#join() may return:

- nothing (in-memory and Redis adapters)
- a promise (custom adapters)

Breaking change: Socket#join() and Socket#leave() do not accept a
callback argument anymore.

Before:

```js
socket.join("room1", () => {
 io.to("room1").emit("hello");
});
```

After:

```
socket.join("room1");
io.to("room1").emit("hello");
// or await socket.join("room1"); for custom adapters
```

Note: the need for an asynchronous method came from the Redis adapter,
which did override the Adapter#add() method in earlier versions, but
this is not the case anymore.

Reference:

- https://github.com/socketio/socket.io/blob/2.3.0/lib/socket.js#L236-L258
- https://github.com/socketio/socket.io-adapter/blob/1.1.2/index.js#L56-L65
- 05f926e13e

Related: https://github.com/socketio/socket.io/issues/3662
2020-10-22 01:50:13 +02:00
Damien Arrachequesne
0d74f290cd refactor(typings): export Socket class
In order to be able to cast it on the argument of the "connect" event:

```js
import { Socket } from "socket.io";

io.on("connect", (socket: Socket) => {
  // ...
});
```
2020-10-17 03:36:15 +02:00
Damien Arrachequesne
7603da71a5 feat: remove prod dependency to socket.io-client
The client bundles are included in the repository in order to remove
socket.io-client from the list of production dependencies and thus to
reduce the total number of dependencies when installing the server.

This means the release of the client and the server must now be in sync
(which is almost always the case actually).

The minified build is now served:

- /<path>/socket.io.js
- /<path>/socket.io.js.map
- /<path>/socket.io.min.js
- /<path>/socket.io.min.js.map

The content will now be compressed as well.
2020-10-17 02:11:15 +02:00
Damien Arrachequesne
a81b9f31cf docs(examples): add example with TypeScript
There are two issues with the typings:

- on the client-side, the Emitter class is not properly imported (hence the @ts-ignore)
- on the server-side, the Socket class is not exported (in order to cast it in the "connect" event)
2020-10-15 13:38:46 +02:00
Damien Arrachequesne
20ea6bd277 docs(examples): add example with ES modules 2020-10-15 13:26:55 +02:00
Damien Arrachequesne
0ce5b4ca68 chore(release): 3.0.0-rc2
Diff: https://github.com/socketio/socket.io/compare/3.0.0-rc1...3.0.0-rc2
2020-10-15 13:02:59 +02:00
Damien Arrachequesne
8a5db7fa36 refactor: remove duplicate _sockets map
Both the "connected" and the "_sockets" maps were used to track the
Socket instances in the namespace.

Let's merge them into "sockets". It's a breaking change, but:

- the "sockets" object did already exist in Socket.IO v2 (and appears in some examples/tutorials)
- "sockets" makes more sense than "connected" in my opinion
- there was already a breaking change regarding the "connected" property (from object to Map)

Breaking change: the "connected" map is renamed to "sockets"
2020-10-15 12:45:42 +02:00
Damien Arrachequesne
2a05042e2c refactor: add additional typings 2020-10-15 12:04:42 +02:00
Damien Arrachequesne
91cd255ba7 fix: close clients with no namespace
After a given timeout, a client that did not join any namespace will be
closed in order to prevent malicious clients from using the server
resources.

The timeout defaults to 45 seconds, in order not to interfere with the
Engine.IO heartbeat mechanism (30 seconds).
2020-10-15 11:54:06 +02:00
Damien Arrachequesne
58b66f8089 refactor: hide internal methods and properties
There is no concept of package-private methods in TypeScript, so we'll
just prefix them with "_" and mark them as private in the JSDoc.
2020-10-15 11:54:06 +02:00
Damien Arrachequesne
669592d120 feat: move binary detection back to the parser
See 285e7cd0d8

Breaking change: the Socket#binary() method is removed, as this use
case is now covered by the ability to provide your own parser.
2020-10-15 10:45:56 +02:00
Damien Arrachequesne
2d2a31e5c0 chore: publish the wrapper.mjs file 2020-10-14 01:53:37 +02:00
Damien Arrachequesne
ebb0575fa8 chore(release): 3.0.0-rc1
Diff: https://github.com/socketio/socket.io/compare/2.3.0...3.0.0-rc1
2020-10-13 23:36:21 +02:00
Damien Arrachequesne
c0d171f728 test: use the reconnect event of the Manager
Following 132f8ec918
2020-10-13 23:02:09 +02:00
Damien Arrachequesne
9c7a48d866 test: use the complete export name
Following fa7d41f55d

Ref: https://nodejs.org/dist/latest-v14.x/docs/api/packages.html#packages_subpath_exports
2020-10-13 23:02:09 +02:00
Damien Arrachequesne
4bd5b2339a feat: throw upon reserved event names
These events cannot be used by the end users, because they are part of
the Socket.IO public API, so using them will now throw an error
explicitly.
2020-10-13 23:02:09 +02:00
Damien Arrachequesne
a8c0600609 feat: remove the 'origins' option
The underlying Engine.IO server now supports a 'cors' option, which
will be forwarded to the cors module.

Breaking change: the 'origins' option is removed

Before:

```js
new Server(3000, {
  origins: ["https://example.com"]
});
```

The 'origins' option was used in the allowRequest method, in order to
determine whether the request should pass or not. And the Engine.IO
server would implicitly add the necessary Access-Control-Allow-xxx
headers.

After:

```js
new Server(3000, {
  cors: {
    origin: "https://example.com",
    methods: ["GET", "POST"],
    allowedHeaders: ["content-type"]
  }
});
```

The already existing 'allowRequest' option can be used for validation:

```js
new Server(3000, {
  allowRequest: (req, callback) => {
    callback(null, req.headers.referer.startsWith("https://example.com"));
  }
});
```
2020-10-13 23:02:08 +02:00
Damien Arrachequesne
8b6b100c28 feat: add ES6 module export
Both CommonJS and ES6 import are now supported:

- with `{ "type": "commonjs" }` in the package.json file

```js
const io = require("socket.io")(8080);
// or
const { Server } = require("socket.io");
const io = new Server(8080);
```

- with `{ "type": "module" }`

```js
import { Server } from "socket.io";

const io = new Server(8080);
```

Related: https://nodejs.org/api/packages.html#packages_dual_commonjs_es_module_packages
2020-10-13 23:02:08 +02:00
Damien Arrachequesne
83a2356648 refactor: properly delegate to the main namespace 2020-10-13 23:02:08 +02:00
Damien Arrachequesne
2875d2cfdf feat: do not reuse the Engine.IO id
In previous versions, the Socket#id attribute was equal (or derived,
for a non-default namespace) to the underlying Engine.IO id, which is
used as a mean to authenticate the user throughout the Engine.IO
session and thus is sensitive information that should be kept secret.

The problem with reusing the Engine.IO id is that users could be
tempted to transmit this id to other clients, in order to implement
private messaging for example.

So we'll now generate a new random id for each new socket.

Please note that this id will now be different from the one found in
the query parameters of the HTTP requests.
2020-10-13 23:02:07 +02:00
Damien Arrachequesne
3289f7ec37 feat: remove the implicit connection to the default namespace
In previous versions, a client was always connected to the default
namespace, even if it requested access to another namespace.

This meant that the middlewares registered for the default namespace
were triggered in any case, which is a surprising behavior for end
users.

This also meant that the query option of the Socket on the client-side
was not sent in the Socket.IO CONNECT packet for the default namespace:

```js
// default namespace: query sent in the query params
const socket = io({
  query: {
    abc: "def"
  }
});

// another namespace: query sent in the query params + the CONNECT packet
const socket = io("/admin", {
  query: {
    abc: "def"
  }
});
```

The client will now send a CONNECT packet in any case, and the query
option of the Socket is renamed to "auth", in order to make a clear
distinction with the query option of the Manager (included in the query
parameters of the HTTP requests).

```js
// server-side
io.use((socket, next) => {
  // not triggered anymore
});

io.of("/admin").use((socket, next => {
  // triggered
  console.log(socket.handshake.query.abc); // "def"
  console.log(socket.handshake.auth.abc); // "123"
});

// client-side
const socket = io("/admin", {
  query: {
    abc: "def"
  },
  auth: {
    abc: "123"
  }
});
```
2020-10-13 23:02:07 +02:00
Damien Arrachequesne
7a51c76413 refactor: migrate tests to TypeScript 2020-10-13 23:02:06 +02:00
Damien Arrachequesne
64bd9fb01a chore: include Engine.IO v4
Release notes: https://github.com/socketio/engine.io/releases/tag/4.0.0
2020-10-13 23:02:06 +02:00
Damien Arrachequesne
4396bd0b3d chore: point towards the develop branch of the client
The package-lock.json file is temporarily removed in order to include
the latest commits to the client and make the tests pass.
2020-10-13 23:02:06 +02:00
Alessandro Magionami
bb43ff2988 docs: add example with fastify (#3654)
See https://github.com/alemagio/fastify-socket.io
2020-10-04 23:20:05 +02:00
Damien Arrachequesne
0540c36510 refactor(typings): add server options
Greatly inspired from:

- https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/engine.io
- https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/socket.io
2020-09-29 02:04:27 +02:00
Damien Arrachequesne
1108ede120 chore: bump socket.io-parser
Breaking change:

- the encode() method is now synchronous

Please note that the exchange [protocol][1] is left untouched and thus
stays in version 4.

Diff: https://github.com/socketio/socket.io-parser/compare/3.4.1...4.0.0

[1] https://github.com/socketio/socket.io-protocol
2020-09-28 16:07:09 +02:00
Damien Arrachequesne
029f478992 feat: remove Server#set() method
This method was kept for backward-compatibility with pre-1.0 versions.
2020-09-26 01:47:17 +02:00
Damien Arrachequesne
424a473c22 refactor: use ES6 Maps instead of plain objects
These attributes were not part of the public API, so there's no
breaking change.
2020-09-26 01:21:51 +02:00
Damien Arrachequesne
1507b416d5 feat: remove Socket#rooms object
The value stored in the adapter will now be used, instead of
duplicating it in the Socket class.

Breaking change: Socket#rooms is now a Set instead of an object

Closes https://github.com/socketio/socket.io/issues/2890
2020-09-26 00:48:55 +02:00
Damien Arrachequesne
84437dc2a6 chore: bump socket.io-adapter
Breaking changes:

- Namespace#connected is now a Map instead of an object.

- Namespace#clients() is renamed to Namespace#allSockets() and now
returns a Promise

Diff: https://github.com/socketio/socket.io-adapter/compare/1.1.2...2.0.0
2020-09-26 00:24:54 +02:00
Damien Arrachequesne
2464de7d2b refactor: use prettier to format tests 2020-09-25 23:47:23 +02:00
Damien Arrachequesne
a5581a9789 refactor: migrate to TypeScript 2020-09-25 23:41:53 +02:00
Diego Molina
af165ae1c2 docs: adjusting Sauce Labs name (#3578) 2020-09-19 03:01:19 +02:00
Damien Arrachequesne
3d760b71d7 refactor: use ES6 syntax 2020-09-17 14:48:46 +02:00
Damien Arrachequesne
13cc07d6ad refactor: use prettier to format code 2020-09-17 14:31:06 +02:00
Damien Arrachequesne
d9bfcaeedb test: add Node.js 12 and 14 in the build matrix
Node.js 8 is removed, as it is now EOL.

Note: the node_modules folder is cached by default
2020-09-17 14:30:34 +02:00
Damien Arrachequesne
1238ddb995 chore: add package-lock.json file 2020-09-17 12:14:57 +02:00
Damien Arrachequesne
e0b35d054f docs: points towards the website
The website is now much more stable, so there's no need to keep two
copies of the same content (which must be manually kept in sync).
2020-09-01 09:37:29 +02:00
Damien Arrachequesne
a66f083d3e docs(examples): add create-react-app example
Related: https://github.com/socketio/socket.io-client/issues/1330
2020-07-09 11:32:48 +02:00
Damien Arrachequesne
f5a8f52f19 docs(examples/react-native): update video 2020-06-05 21:36:15 +02:00
Damien Arrachequesne
7a219f9459 docs(examples/react-native): add example with React Native
Includes e5bc1063cc
2020-06-05 08:57:04 +02:00
Damien Arrachequesne
5d16319692 docs(examples/webpack-build-server): update engine.io version
In order to include [0], which fixes the following error:

ERROR in ./node_modules/engine.io/lib/server.js
Module not found: Error: Can't resolve 'uws' in '/media/damien/git/other-bets/socket.io-parent/socket.io/examples/webpack-build-server/node_modules/engine.io/lib'
 @ ./node_modules/engine.io/lib/server.js 107:27-41
 @ ./node_modules/engine.io/lib/engine.io.js
 @ ./node_modules/socket.io/lib/index.js
 @ ./lib/index.js

[0] 85e544afd9
2020-06-04 16:36:19 +02:00
Damien Arrachequesne
8f90ba9c67 docs(examples): add example with passport authentication 2020-05-22 08:36:43 +02:00
Damien Arrachequesne
2a1aa1c59c docs(examples): bump dependencies
In order to include https://github.com/socketio/engine.io/releases/tag/3.4.1
2020-04-17 14:37:31 +02:00
Damien Arrachequesne
17747e4d69 docs(chat-example): bump dependencies
In order to include https://github.com/socketio/engine.io/releases/tag/3.4.1
2020-04-17 11:21:10 +02:00
Damien Arrachequesne
281de9ed47 docs(tweet-stream-example): migrate example
From https://github.com/darrachequesne/socket.io-tweet-stream
2020-04-14 10:17:47 +02:00
Damien Arrachequesne
edb95ea221 docs(whiteboard-example): update dependencies 2020-04-14 10:04:27 +02:00
Damien Arrachequesne
b74bb80122 docs(chat-example): remove dependency to the parent project 2020-04-14 09:56:42 +02:00
Damien Arrachequesne
47161a65d4 [chore] Release 2.3.0
Diff: https://github.com/socketio/socket.io/compare/2.2.0...2.3.0
2019-09-20 12:18:39 +02:00
Damien Arrachequesne
cf39362014 [chore] Bump socket.io-parser to version 3.4.0
Diff: https://github.com/socketio/socket.io-parser/compare/3.3.0...3.4.0
2019-09-20 11:04:11 +02:00
flaambe
4d01b2c84c test: remove deprecated Buffer usage (#3481) 2019-09-20 10:50:12 +02:00
Jonatan Juárez
82271921db [docs] Fix the default value of the 'origins' parameter (#3464)
Fix documentation about default origins value. The default should read `*:*` instead of `*
2019-09-20 10:37:54 +02:00
Damien Arrachequesne
1150eb50e9 [chore] Bump engine.io to version 3.4.0
Diff: https://github.com/socketio/engine.io/compare/3.3.1...3.4.0
2019-09-20 10:31:25 +02:00
Grant Timmerman
9c1e73c752 [chore] Update the license of the chat example (#3410)
There was no obvious reason to use BSD instead of MIT for that very basic chat app.

Closes #3411
2019-03-15 22:22:22 +01:00
Damien Arrachequesne
df05b73bb9 [chore] Release 2.2.0 2018-11-29 00:00:45 +01:00
Markko Legonkov
b00ae50be6 [feat] Add cache-control header when serving the client source (#2907) 2018-11-20 08:02:04 +01:00
Nadir Hussain Laskar
d3c653d876 [docs] Add Touch Support to the whiteboard example (#3104) 2018-11-20 08:01:09 +01:00
Antonio
a7fbd1ac4a [fix] Throw an error when trying to access the clients of a dynamic namespace (#3355)
Accessing the clients of a dynamic namespace throws because doing `io.of(/your-regex/g)` returns a namespace with no adapter and the clients methods tries to access `namespace.adapter.clients`.
2018-11-20 07:40:11 +01:00
Damien Arrachequesne
190d22b46e [chore] Bump dependencies
- engine.io: https://github.com/socketio/engine.io/compare/3.2.0...3.3.1
- socket.io-parser: https://github.com/socketio/socket.io-parser/compare/3.2.0..3.3.0
2018-11-20 07:33:41 +01:00
Damien Arrachequesne
7b8fba7ea2 [test] Update Travis configuration
Reference: https://github.com/nodejs/Release
2018-11-20 07:32:39 +01:00
Emmanuel DEMEY
e5f0ceaee0 [docs] Use new JavaScript syntax inside the README (#3360) 2018-11-08 00:26:54 +01:00
Damien Arrachequesne
7e35f901b8 [docs] fix this scope in the chat example
`user is typing` messages were not properly removed

Closes #3291
2018-08-28 09:05:44 +02:00
Damien Arrachequesne
2dbec77a38 [chore] Update issue template 2018-08-21 13:21:14 +02:00
Andrew Stelmach
d97d873aee [docs] update README.md (#3309) 2018-08-18 23:32:07 +02:00
Damien Arrachequesne
e0b2cb0c5a [chore] Release 2.1.1 2018-05-17 23:22:49 +02:00
Sleiman Sleiman
1decae341c [feat] Add local flag to the socket object (#3219)
To match the behaviour on the namespace (see #2628).
2018-04-27 13:03:25 +02:00
Donut
0279c47c8c [docs] Convert the chat example to ES6 (#3227) 2018-04-27 13:00:27 +02:00
Damien Arrachequesne
2917942b3e [docs] Clarify private messaging in the emit cheatsheet (#3232)
The previous version was confusing, as `socket.to(socket.id).emit()` does nothing.

Fixes #3220
2018-04-27 12:50:31 +02:00
Damien Arrachequesne
db831a3de4 [chore] Release 2.1.0 2018-03-29 23:30:03 +02:00
Damien Arrachequesne
ac945d1eba [feat] Add support for dynamic namespaces (#3195)
This follows #3187, with a slightly different API.

A dynamic namespace can be created with:

```js
io.of(/^\/dynamic-\d+$/).on('connect', (socket) => { /* ... */ });
```
2018-03-29 23:08:08 +02:00
Scott Gress
ad0c052eff [docs] Add note in docs for origins(fn) about error needing to be a string. (#2895) 2018-03-10 09:03:28 +01:00
Damien Arrachequesne
1f1d64bab6 [fix] Include the protocol in the origins check (#3198)
Previously, the protocol was not taken in account, which caused the following behaviour:

```js
io.origins('https://foo.example.com:443'); // ok as a string
io.origins(['https://foo.example.com:443'); // not ok as an array
```

Fixes #3190
2018-03-10 08:56:42 +01:00
Damien Arrachequesne
f4fc517e0f [fix] Properly emit 'connect' when using a custom namespace (#3197)
When using a custom namespace with a middleware, the client did not receive the 'connect' event.

Fixes #3082
2018-03-10 08:51:22 +01:00
Jumper Chen
be61ba0a20 [docs] Add link to a Dart client implementation (#2940) 2018-03-01 00:23:45 +01:00
Damien Arrachequesne
c0c79f019e [feat] Add support for dynamic namespaces (#3187) 2018-03-01 00:22:16 +01:00
Damien Arrachequesne
dea5214f21 [chore] Bump superagent and supertest versions (#3186) 2018-02-28 23:19:19 +01:00
Damien Arrachequesne
b1941d5dfe [chore] Bump engine.io to version 3.2.0 2018-02-28 23:10:40 +01:00
Miguel Piedrafita
a23007a635 [docs] Update license year (#3153) 2018-02-28 23:03:02 +01:00
Damien Arrachequesne
f48a06c040 [feat] Add a 'binary' flag (#3185)
So that the call to the `has-binary` method can be skipped. Usage:

```
// with binary data
socket.binary(true).emit("binary", obj);

// without binary data
socket.binary(false).emit("string", obj);

// call to hasBin
socket.emit("guess", obj);
```
2018-02-28 23:00:16 +01:00
Damien Arrachequesne
0539a2c4fd [test] Update travis configuration 2018-02-28 22:56:28 +01:00
Devlin Pajaron
c06ac071d0 [docs] Fix typo (#3157) 2018-02-25 09:26:24 +01:00
Damien Arrachequesne
52b09609db [chore] Bump debug to version 3.1.0 2018-02-25 09:22:40 +01:00
Damien Arrachequesne
1c108a35e4 [chore] Release 2.0.4 2017-10-22 15:16:29 +02:00
Thiago Santos
f333479080 [test] Use npm scripts instead of gulp (#3078) 2017-10-16 07:46:42 +02:00
Carson McKinstry
3f611654f2 [docs] Fix a grammar mistake in the API docs (#3076) 2017-10-07 15:08:14 +02:00
Vyacheslav Aristov
e26b71c78e [docs] Fix typo in API docs (#3066) 2017-10-07 14:36:02 +02:00
Haku
3386e155a8 [docs] Actually prevent input from having injected markup in chat example (#2987) 2017-08-27 08:40:26 +02:00
Stanley288
3684d590f5 [docs] Use path.join instead of concatenating paths (#3014) 2017-08-26 07:44:25 +02:00
Damien Arrachequesne
dd69abbeee [fix] Reset rooms object before broadcasting from namespace (#3039) 2017-08-26 07:36:26 +02:00
Damien Arrachequesne
1f0e64a6da [fix] Do not throw when receiving an unhandled error packet (#3038) 2017-08-26 07:35:57 +02:00
Gabriel Hautclocq
9d170a75d0 [docs] Add io.emit in the cheat sheet (#2992) 2017-07-01 07:57:34 +02:00
Nicolas Coden
7199d1b6ef [docs] Fix misnamed 'Object.keys' in API docs (#2979) 2017-06-19 12:16:26 +02:00
Damien Arrachequesne
bf7afb14cb [docs] Update Webpack server example (#2976) 2017-06-15 23:40:43 +02:00
Damien Arrachequesne
410f5bcb8e [docs] Update API documentation (#2973) 2017-06-13 23:05:26 +02:00
Damien Arrachequesne
65ece01135 [chore] Release 2.0.3 2017-06-12 14:05:46 +02:00
Damien Arrachequesne
db0c69969e [fix] Reset rooms object before broadcasting (#2970)
It seems packets could be delivered to wrong room in some case, if the
_rooms array was not reset before the next emit.
2017-06-12 14:01:35 +02:00
Damien Arrachequesne
94df7bcdfd [fix] Fix middleware initialization (#2969)
Fix "TypeError: Cannot convert undefined or null to object" when a
middleware is added before the engine is properly attached.
2017-06-12 14:00:48 +02:00
Ra'Shaun Stovall
9a014e2df4 [docs] Update slack badge (#2961)
Update badge to https://slackin-socketio.now.sh/badge.svg
2017-06-08 12:28:56 +02:00
Damien Arrachequesne
2b10f1b3a4 [docs] Update webpack example (#2960) 2017-06-03 13:53:03 +02:00
Damien Arrachequesne
a10dc8d92d [chore] Release 2.0.2 2017-06-01 14:01:24 +02:00
Damien Arrachequesne
2b216902e1 [fix] Fix timing issues with middleware (#2948)
Using a middleware could previously lead to a connecting client
receiving a connect event from the server before the server triggers
its own connect event.
2017-05-22 13:01:59 +02:00
Damien Arrachequesne
832b8fc6d9 [chore] Release 2.0.1 2017-05-09 01:40:46 +02:00
Damien Arrachequesne
a0056904c1 [fix] Update path of client file (#2934) 2017-05-09 01:36:40 +02:00
Damien Arrachequesne
3367eaa948 [chore] Release 2.0.0 2017-05-09 01:09:19 +02:00
Damien Arrachequesne
6c0705f733 [docs] Add an example of custom parser (#2929) 2017-05-09 00:46:01 +02:00
Damien Arrachequesne
1980fb4a03 [chore] Merge history of 1.7.x and 0.9.x branches (#2930) 2017-05-09 00:45:17 +02:00
Xavier Damman
0d07c47f81 [chore] Added backers and sponsors on the README (#2933) 2017-05-09 00:41:46 +02:00
Damien Arrachequesne
a086588747 [chore] Bump dependencies (#2926) 2017-04-28 22:33:40 +02:00
Damien Arrachequesne
87b06ad362 [feat] Move binary detection to the parser (#2923) 2017-04-25 00:38:07 +02:00
Caleb
199eec648e [docs] Replace non-breaking space with proper whitespace (#2913) 2017-04-19 22:19:57 +02:00
Faraz Patankar
f1b39a6b1d [docs] Update emit cheatsheet (#2906) 2017-04-03 07:20:38 +02:00
Damien Arrachequesne
240b154960 [docs] Explicitly document that Server extends EventEmitter (#2874) 2017-03-29 14:13:04 +02:00
efkan
c5b7738730 [docs] Add server.engine.generateId attribute (#2880) 2017-03-29 13:52:07 +02:00
Simen Bekkhus
03f3bc9ab3 [docs] Fix wrong space character in README (#2900) 2017-03-27 13:15:19 +02:00
SwhGo_oN
e40accf7a1 [docs] Fix documentation for 'connect' event (#2898) 2017-03-19 00:33:59 +01:00
Damien Arrachequesne
01a4623613 [feat] Allow to join several rooms at once (#2879) 2017-02-26 09:15:32 +01:00
Damien Arrachequesne
2d5b0026c5 [docs] Add webpack build example (#2828) 2017-02-22 12:48:46 +01:00
Damien Arrachequesne
5ae06e6285 [chore] Bump socket.io-adapter to version 1.0.0 (#2867) 2017-02-17 07:33:43 +01:00
sgress454
4d8f68c7dc [chore] Bump engine.io to version 2.0.2 (#2864)
Includes the following (from engine.io changelog):

* [fix] Initialize the WebSocket server in the Server constructor (#476)
* [chore] Bump ws to version 1.1.2 (vulnerability fix) (#480)
2017-02-16 22:01:53 +01:00
Tim Ruffles
5b79ab1af1 [docs] Update the wording to match the code example (#2853) 2017-02-13 23:52:07 +01:00
Damien Arrachequesne
54ff591b07 [feature] Merge Engine.IO and Socket.IO handshake packets (#2833) 2017-02-01 15:28:14 +01:00
H3rby7
e1facd5155 [docs] Small addition to the Express Readme Part (#2846) 2017-02-01 15:23:56 +01:00
Damien Arrachequesne
3b92cc2b26 [feature] Allow the use of custom parsers (#2829) 2017-01-24 07:10:19 +01:00
Damien Arrachequesne
3d695c60f1 [chore] Bump engine.io to version 2.0.0 (#2832) 2017-01-23 23:16:51 +01:00
Antoine LUCAS
3b5f4339a7 [fix] Use path.resolve by default and require.resolve as a fallback (#2797)
Browserify doesn't support require.resolve, and as a consequence, makes nexe fail the compilation. This PR attempts to get the path of the socket.io-client file via path.resolve and falls back to the original require.resolve if this file cannot be found.
2017-01-22 07:21:45 +01:00
Damien Arrachequesne
23c9dd34d5 [docs] Add a 'Features' section in the README (#2824) 2017-01-16 23:16:46 +01:00
Damien Arrachequesne
e28b475428 [docs] Add httpd cluster example (#2819) 2017-01-16 08:33:22 +01:00
Damien Arrachequesne
f7eed6e397 [docs] Add haproxy cluster example (#2818) 2017-01-16 08:33:09 +01:00
Damien Arrachequesne
988852986a [docs] Add nginx cluster example (#2817) 2017-01-16 08:32:53 +01:00
Damien Arrachequesne
8eaba085de [docs] Implement whiteboard example (#2810) 2017-01-16 08:32:33 +01:00
Tom Atkinson
2258a6a6e3 [fix] Properly close the connection on error (#2681) 2017-01-16 08:29:11 +01:00
Tom Atkinson
ad658b8cc2 [perf] Use shared instance of the encoder (#2825) 2017-01-16 08:25:52 +01:00
Tom Atkinson
e24434a0a0 [perf] Reset properties instead of deleting them (#2826)
Deleting properties is a v8 'optimization killer'
2017-01-16 08:23:22 +01:00
Damien Arrachequesne
b754cff2b4 [docs] Fix documentation for local flag (#2816) 2017-01-10 18:10:56 +01:00
Damien Arrachequesne
706ca2a0c1 [docs] Add emit cheatsheet (#2815) 2017-01-10 17:54:09 +01:00
Damien Arrachequesne
accd0bd64a [docs] Add pingInterval/pingTimeout/transports options in the API documentation (#2814) 2017-01-10 17:50:38 +01:00
Damien Arrachequesne
131a2befec [docs] Add an example for socket.join() method (#2813) 2017-01-10 17:46:54 +01:00
Damien Arrachequesne
0be865f614 [docs] Fix a typo on clients method in the API documentation (#2812) 2017-01-10 17:41:31 +01:00
Damien Arrachequesne
01b262fbe0 [chore] Update issue template with fiddle (#2811) 2017-01-10 10:16:27 +01:00
Bella
f260439933 [chore] Update copyright year LICENSE to 2017 (#2803) 2017-01-06 17:11:03 +01:00
Andrea Accardo
e04de3c2c8 [docs] Fix wrong argument name in API.md (#2802) 2017-01-04 06:37:12 +01:00
Ian Bro
a27802e19e [fix] Prevent null from being accepted as argument (#2606) 2016-12-30 16:22:16 +01:00
Gatsbill
c46d4481bd [perf] micro-optimisations (#2793) 2016-12-30 16:18:09 +01:00
bananaapple
d82190016f [docs] Add install script on Readme.md (#2780) 2016-12-30 16:11:26 +01:00
Damien Arrachequesne
255b845596 [docs] API documentation (#2784) 2016-12-30 16:10:54 +01:00
Damien Arrachequesne
1f59e4526a [chore] Release 1.7.2 (#2783) 2016-12-11 02:04:21 +01:00
Damien Arrachequesne
0a7afa85ea [chore] Bump engine.io to version 1.8.2 (#2782) 2016-12-11 01:27:19 +01:00
Serhii Sol
1e31769062 [fix] Fixes socket.use error packet (#2772)
* fix(socket): Fixes socket.use error packet which drops nodejs due to nuances of Nodejs' EventEmitter

* fix(socket): Fixes missing error event on socket

* fix(socket): test fix, should listen for clientSocket instead of server socket

* minor update
2016-12-01 02:25:13 +01:00
Damien Arrachequesne
797c9a3498 [chore] Release 1.7.1 (#2768) 2016-11-28 00:06:18 +01:00
Damien Arrachequesne
4f93a0b429 [chore] Release 1.7.0 (#2767) 2016-11-27 08:27:39 +01:00
Damien Arrachequesne
3c98130f15 [chore] Update client location and serve minified file (#2766)
Following https://github.com/socketio/socket.io-client/pull/1025, the
output files are now generated in the dist directory.
2016-11-27 08:24:27 +01:00
Damien Arrachequesne
9c23308c6e [chore] Bump engine.io to version 1.8.1 (#2765) 2016-11-27 07:55:02 +01:00
Tal Beja
955e5e0d91 [feature] Add a local flag (#2628)
That new flag will prevent the adapter (redis) from publishing the emit to the pub/sub server.

When several instances of a server receive the same event from a third party (not from a client), each server instance broadcasts the event to all his clients. With the local flag, and the change in the redis adapter, each server instance send the event only to his client, so each client receive only one unique event.
2016-11-24 23:44:52 +01:00
Zhu Liang
0ef55b26d4 [feature] serve sourcemap for socket.io-client (#2482) 2016-11-24 23:39:43 +01:00
Robbie Ferguson
4d8e2d342c [docs] Fixed grammar issues in the README.md (#2159)
Added a few periods and commas which were missing. Pluralised the word
'parameter' where it was incorrectly specified to singular on line 281.
Very minor edit. No source code changed.
2016-11-23 15:05:15 +01:00
Jérémy Lal
d48f848bb4 [docs] Comment connected socket availability for adapters (#2081) 2016-11-23 15:00:07 +01:00
Damien Arrachequesne
57b386385e [chore] Release 1.6.0 (#2757) 2016-11-20 04:26:56 +01:00
Damien Arrachequesne
9e7567daee [chore] Bump socket.io-adapter to version 0.5.0 (#2756) 2016-11-20 03:51:57 +01:00
Damien Arrachequesne
2e36799b17 [chore] Bump engine.io to version 1.8.0 (#2755) 2016-11-20 03:50:32 +01:00
Damien Arrachequesne
9bb5e9de2f [chore] Bump debug to version 2.3.3 (#2754) 2016-11-20 03:42:09 +01:00
Luca Tabone
ff2c15de68 [perf] Minor code optimizations (#2219) 2016-11-18 02:03:06 +01:00
Damien Arrachequesne
a483658607 [example] Add disconnection/reconnection logs to the chat example (#2675) 2016-11-18 01:52:45 +01:00
Julian Grinblat
4c5dbd8824 [fix] Don't drop query variables on handshake (#2745)
Parameters passed during handshake, such as tokens, were being dropped.
2016-11-16 01:17:19 +01:00
mhmeadows63
e14a10b7ce [feature] add support for Server#close(callback) (#2748) 2016-11-15 21:52:56 +01:00
Alex
5a123beea5 [feature] Add support for socket middleware (#2306) 2016-11-05 01:51:29 +01:00
Philip YoonShin
2ed5f0f5fb [chore] Update year to 2016 (#2456) 2016-11-05 01:19:15 +01:00
Julian Grinblat
e9f980c475 [feature] Add support for all event emitter methods (#2601) 2016-11-05 01:14:21 +01:00
Jamie Davis
6f44f3a8ef [test] Fix leaking clientSocket (#2721)
Test 'should be able to close sio sending a port' defined a clientSocket
but didn't set 'reconnection: false'.

Now, the default behavior of a clientSocket is 'reconnection: true'.
As a result, the clientSocket was "leaked" from the test case
and seemed to intermittently connect to the servers in subsequent
test cases. This would cause other tests to timeout unexpectedly.

It's not clear to me why this would happen, since the test case
assigns a unique port number to the socket.
However, if you go into socket.io-client and assign and log
unique IDs to each socket, then you'll see that this clientSocket
shows up in other test cases if the reconnectionDelay strikes
unluckily.
2016-11-05 01:06:04 +01:00
Atanas Palavrov
04fc0f3677 [feature] Loading client script on demand. (#2567)
Support for the serving of the client script mess with packagers like
browserify, webpack. Especcialy in projects where it is not used at all.
This patch is workaround to avoid that problem in the cases when client
script is not served.
2016-10-30 14:22:00 +01:00
Kenton Varda
d026c00d05 [fix] Make ETag header comply with standard. (#2603)
The standard says that an ETag must be surrounded in double quotes:

https://tools.ietf.org/html/rfc7232#section-2.3

Although browsers tend to be lenient, omitting the quotes can confuse/break some kinds of proxies and other tools that demand compliant formatting. For example, Sandstorm.io enforces strict HTTP usage for security reasons and will block responses with invalid ETags.
2016-10-30 14:19:00 +01:00
Damien Arrachequesne
fdf64cc38f [chore] Release 1.5.1 (#2735) 2016-10-24 03:31:32 +02:00
Damien Arrachequesne
5badb6436e [chore] Bump socket.io-parser to 2.3.1 (#2734) 2016-10-24 03:23:03 +02:00
Damien Arrachequesne
c20e0b26c9 [chore] Bump engine.io to 1.7.2 (#2729) 2016-10-24 03:21:11 +02:00
bananaapple
5c10c5439b [test] Use client function to unify client in test script (#2731) 2016-10-24 02:35:01 +02:00
Damien Arrachequesne
8182ecc61c [chore] Add Github issue and PR templates (#2733) 2016-10-22 09:19:08 +02:00
Feram Bot
ccd3376627 [docs] Minor fixes (#2526) 2016-10-21 03:21:45 +02:00
Damien Arrachequesne
58a73d39e9 [chore] Bump socket.io-parser to 2.3.0 (#2730) 2016-10-21 03:18:49 +02:00
Tom Atkinson
e60bd5a4da [fix] Avoid swallowing exceptions thrown by user event handlers (#2682) 2016-10-21 03:16:36 +02:00
Paul Meinhardt
21dffa4b58 [docs] Link readme npm package badge to npm registry page (#2612) 2016-10-21 03:10:33 +02:00
Marcio Puga
94852e3d23 [docs] Fix typo (#2724) 2016-10-21 03:06:59 +02:00
davidlondono
b8c60506a6 [docs] Fix JSDoc of optional parameters (#2465)
Making proper optional Docs for socketIO
2016-10-21 03:02:59 +02:00
syuilo⭐️
ecc76f48bc [docs] Add link to LICENSE (#2221) 2016-10-21 03:00:38 +02:00
Damien Arrachequesne
c94058f9b0 [chore] release 1.5.0 (#2711) 2016-10-07 00:13:33 +02:00
Damien Arrachequesne
43d9a4b55d [feature] Add a 'disconnecting' event to access to socket.rooms upon disconnection (#2332) 2016-10-06 23:23:26 +02:00
Alejandro Oviedo
df916172dd [chore] Link build badge to master branch (#2549) 2016-10-06 23:12:15 +02:00
Damien Arrachequesne
271c625243 [chore] Restrict files included in npm package (#2709) 2016-10-06 23:11:33 +02:00
Damien Arrachequesne
628fe8f1b2 [chore] Point towards current master of socket.io-client (#2710) 2016-10-06 23:10:54 +02:00
Damien Arrachequesne
db62e1bf67 [chore] Remove unused zuul-ngrok dependency (#2708) 2016-10-06 23:10:33 +02:00
YasharF
ba4c7921ef [chore] Bump engine.io to 1.7.0 (#2707) 2016-10-06 10:03:55 +02:00
Damien Arrachequesne
30ea0b8d7d Merge pull request #2599 from boazsegev/patch-1
Documentation update to help new programmers
2016-08-05 21:07:30 +02:00
Bo
2d141aff7c Prevent mixup for new programmers
The documentation, inadvertently, causes confusion for newer programmers, hindering their learning curve and ability to learn `socket.io` faster.

This proposed change aims at preventing this confusion to allow new programmers easier access to resources, especially when searching the web for more information.

The name `socket` in the documentation refers to the API gateway, or abstraction layer, related to the `client` and other properties. However, in the rest of the programming world, `sockets` usually refer to Berklee sockets or the lower-level communication channel that is used to implement Protocols such as the Websocket Protocol and HTTP.

New programmers often search for more informations using the `sockets` keyword when they are grappling with learning the `socket.io` API.

A good example is the stack overflow site, where newer programmers incorrectly tag their questions `sockets` and in turn don't get a fast response to their questions.

By simply changing the name of the variable used to describe socket.io's "Socket" class instances from `socket` to `client`, this mixup should be mitigated considerably.
2016-06-26 14:32:37 -04:00
Guillermo Rauch
40763d3962 1.4.8 2016-06-23 19:24:38 -07:00
Guillermo Rauch
2a092bd2fb Release 1.4.8 2016-06-23 19:22:41 -07:00
Guillermo Rauch
4137eb5c43 package: bump engine.io 2016-06-23 19:12:28 -07:00
Guillermo Rauch
e3207005da 1.4.7 2016-06-23 17:31:33 -07:00
Guillermo Rauch
42aa77614e Release 1.4.7 2016-06-23 17:25:46 -07:00
Guillermo Rauch
1491a96c95 package: bump engine.io 2016-06-23 17:11:15 -07:00
Guillermo Rauch
dcca01f5a4 Release 1.4.6 2016-05-02 20:57:44 -07:00
Guillermo Rauch
3b58fa04d5 package: bump engine.io 2016-05-02 20:56:15 -07:00
Naoyuki Kanezawa
0c6d50d9c0 Merge pull request #2416 from nus-fboa2016-si/chatexample
updated express version to express 4
2016-04-12 03:58:08 +09:00
Naoyuki Kanezawa
881f16553c Merge pull request #2508 from tw0517tw/quote-exec
add quote to exec paths, prevent error when spaces in path
2016-04-12 03:57:43 +09:00
Guillermo Rauch
e5a8d4d2d9 Merge pull request #2520 from nkzawa/fix/ignore-dist
Ignore dist directory for now
2016-04-11 08:45:11 -07:00
Guillermo Rauch
fb0253edea Merge pull request #2422 from nus-fboa2016-si/queryStringFix
Fix for Issue #331 on socket.io-client
2016-04-11 08:44:59 -07:00
tw0517tw
3c5f5a0864 use a bigger timeout for slower machine 2016-04-11 22:31:47 +08:00
tw0517tw
a23d26a617 add quote to exec paths, prevent error when spaces in path 2016-04-11 22:21:34 +08:00
Zhu Liang
910b5d77a6 fixed version notation 2016-04-11 22:19:49 +08:00
Naoyuki Kanezawa
438ad63cdf Merge pull request #2420 from revington/test-node-v5-stable
[Travis] Add Node 5.x (Stable) to the test matrix
2016-04-11 23:09:41 +09:00
Naoyuki Kanezawa
7e9a67d8ee Merge pull request #2415 from nus-fboa2016-si/documentation
Documentation
2016-04-11 23:05:33 +09:00
Naoyuki Kanezawa
0ae070885d Merge pull request #2477 from callahad/chat-layout-fix
Fix chat display in Firefox
2016-04-11 23:00:07 +09:00
Naoyuki Kanezawa
36d99d8d84 Merge pull request #2492 from westy92/master
Update package.json
2016-04-11 22:20:46 +09:00
nkzawa
0e63b0910e ignore dist for now 2016-04-11 22:11:19 +09:00
Guillermo Rauch
3c7350fa58 Merge pull request #2509 from tw0517tw/patch-1
stop prepend /# before id when no namespace
2016-04-10 11:24:42 -07:00
Guillermo Rauch
aadd5da655 Merge pull request #2496 from nus-fboa2016-si/compat-test
Added test for compat version transformed by babel
2016-04-02 18:31:30 -07:00
吳東曄 Wu, Dung-Ie
6d4128750b stop append /# before id when no namespace 2016-04-02 01:55:53 +08:00
Zhu Liang
6edcd1c6ba styling fixes 2016-03-31 12:55:42 +08:00
Zhu Liang
6b2394e612 disable loading lib by default to avoid syntax error due to lib containing new es6 syntax 2016-03-31 03:54:32 +08:00
Zhu Liang
677af3fa11 changed env var name, added testing documentation to readme 2016-03-21 13:11:03 +08:00
Zhu Liang
a1ff739b36 babel transpile before test, print error 2016-03-19 20:19:08 +08:00
Zhu Liang
de5b588e17 explicit distinction between current and compat in test so we can serve either compat or current as default 2016-03-19 20:13:57 +08:00
Zhu Liang
5a20c1195b set env vars in travis 2016-03-19 19:49:41 +08:00
Zhu Liang
5253bc400b use multiple npm scripts 2016-03-19 19:41:14 +08:00
Zhu Liang
1245a5639e added compat test 2016-03-19 19:21:54 +08:00
Zhu Liang
88161539a1 implement compat test using env variable 2016-03-17 13:42:29 +08:00
Zhu Liang
d99d4d15ae add compat test for babel-transformed codes 2016-03-17 13:11:20 +08:00
Seth Westphal
06ecfe5444 Explicit local gulp is unnecesary 2016-03-16 13:01:49 -05:00
Seth Westphal
e90b4eba1e Update repository URL 2016-03-16 12:58:59 -05:00
Guillermo Rauch
c7de1a1adf Merge pull request #2471 from nus-fboa2016-si/gulp-rebased
Add gulp and babel
2016-03-12 20:04:48 -08:00
Dan Callahan
7bae6ac636 Fix chat display in Firefox 2016-03-08 19:50:55 -06:00
Diga Widyaprana
355b5156fe Remove node 0.8 from travis 2016-03-09 09:36:51 +08:00
Diga Widyaprana
3e168ee0b8 Redirect make tasks to gulp 2016-03-06 11:40:57 +08:00
Diga Widyaprana
ed9ab77dcb Minor fixes to gulp tasks
- Only provide entry point as first argument of gulp.src
- {read:false} option for mocha tests
- change timeout:2000 to slow:200 to mirror makefile's mocha options
- replace var with const where appropriate
2016-03-06 11:40:57 +08:00
Diga Widyaprana
1104cd135e Use local instead of global gulp in npm test 2016-03-06 11:40:57 +08:00
Diga Widyaprana
0be915cd0f Add gulp task clean to clean transpile output
- Work done by @YijinL. Part of @05ad308
2016-03-06 11:40:57 +08:00
Diga Widyaprana
c6dd41b915 Minor refactoring to transpile task
- Transpiling is now gulp transpile
- Default redirects to transpile
- Refactor transpile output directory to a separate constant
- Work by @YijinL in @05ad308
2016-03-06 11:32:40 +08:00
Diga Widyaprana
0f14312d7b Add gulp help
Work was done by @YijinL in 2 separate commits (@9204125 and @05ad308)
2016-03-06 11:32:09 +08:00
Diga Widyaprana
97bd95f036 Don't use dynamic dependencies 2016-03-04 18:06:00 +08:00
Diga Widyaprana
11e0f19272 Inline babel configuration 2016-03-04 18:06:00 +08:00
Yijin
e388a3319d npm test script to use gulp 2016-03-04 18:06:00 +08:00
Zheng Weihan
b551ce9835 added test-cov task. 2016-03-04 18:06:00 +08:00
Zhu Liang
0bac96a6b2 added comment for babel 2016-03-04 14:46:08 +08:00
Zhu Liang
1d07b10339 integrated babel into gulp 2016-03-04 14:46:08 +08:00
Zheng Weihan
398b5479f0 delete unused dependency 2016-03-04 14:45:55 +08:00
Zheng Weihan
d9eb729eab implement gulp build system for test. 2016-03-04 14:45:03 +08:00
whattokingu
f9db72997f Merge pull request #10 from whattokingu/queryStringFix
Query string fix
2016-02-14 13:57:06 +08:00
Zheng Weihan
1293505dc2 update tests. 2016-02-14 13:48:28 +08:00
Zheng Weihan
f4e9e71c56 Merge branch 'queryStringFix' of https://github.com/whattokingu/socket.io into queryStringFix
# By whattokingu
# Via whattokingu
* 'queryStringFix' of https://github.com/whattokingu/socket.io:
  conform to style guide

Conflicts:
	lib/client.js
2016-02-14 13:47:59 +08:00
Zheng Weihan
045674de97 remove unnecessary decoder. url.parse already decodes string.
add test to test decoding function.
2016-02-14 13:46:26 +08:00
whattokingu
7091acf24c Merge pull request #9 from whattokingu/queryStringFix
Query string fix
2016-02-13 14:41:15 +08:00
whattokingu
19341051e8 conform to style guide 2016-02-13 14:35:12 +08:00
Zheng Weihan
e141e09aaf change decodeUriComponents calls 2016-02-13 14:28:21 +08:00
Zheng Weihan
5fabe4e780 call decodeURIComponent on query received from client.
update package.son to point to latest commit in socket.o-client package (with URI encoder)
2016-02-13 14:10:20 +08:00
whattokingu
7760a71d90 Merge pull request #8 from whattokingu/queryStringFix
add comments to code
2016-02-04 13:28:29 +08:00
Zheng Weihan
c077357eff add comments to code 2016-02-04 13:27:36 +08:00
whattokingu
2f26a2bdb4 Merge pull request #7 from whattokingu/queryStringFix
add test for issue #331 on socket.io-client.
2016-01-31 15:45:35 +08:00
Zheng Weihan
13af610f6d add test for issue #331 on socket.io-client. 2016-01-31 15:44:39 +08:00
whattokingu
abcedf53ec Merge pull request #6 from whattokingu/queryStringFix
sync to master branch
2016-01-31 11:23:02 +08:00
Zheng Weihan
24456fdcbe Merge branch 'master' into queryStringFix
* master:
  Release 1.4.5
  package: bump `engine.io`
  add a test for graceful shutdown
2016-01-31 11:21:30 +08:00
Pedro Narciso García Revington
25116ab179 test node 5 (stable) 2016-01-30 21:53:04 +01:00
Zhu Liang
a116d52e30 updated express version to express 4 2016-01-30 11:57:31 +08:00
whattokingu
cecf5f127e Merge pull request #5 from whattokingu/documentation
Added Documentation
2016-01-30 11:31:11 +08:00
Zheng Weihan
1c467e15e6 Issue #2400, added documentation 2016-01-30 11:28:40 +08:00
whattokingu
a110542563 Merge pull request #4 from whattokingu/queryStringFix
Fix for Issue #331 on socket.io-client
2016-01-30 10:16:19 +08:00
Zheng Weihan
7d55724468 refactor parsing of URL in CONNECT packet to use 'url' library. 2016-01-30 09:47:28 +08:00
Zheng Weihan
1d84c55743 fix to issue #331 on socket.o-client.
Parses CONNECT packet for query string and replaces query in socket.handshake.
2016-01-28 14:40:05 +08:00
Guillermo Rauch
b3fc530abe Release 1.4.5 2016-01-26 13:26:17 -08:00
Guillermo Rauch
d0dfa54dcb package: bump engine.io 2016-01-26 10:42:41 -08:00
Guillermo Rauch
198b836759 Merge pull request #2399 from nkzawa/graceful-close
Add a test for graceful shutdown
2016-01-21 09:15:36 -08:00
nkzawa
a75f46f06f add a test for graceful shutdown 2016-01-21 20:50:44 +09:00
Naoyuki Kanezawa
2a91fd57c7 Merge pull request #2344 from denghongcai/patch-2
fix wrong jsdoc
2016-01-17 15:25:32 +09:00
Naoyuki Kanezawa
fe891293b2 Merge pull request #2353 from faizalpribadi/patch-1
fix typo word :)
2016-01-17 15:23:35 +09:00
Naoyuki Kanezawa
6e450f75b4 Merge pull request #2374 from darrachequesne/patch-4
Add badges showing dependency status
2016-01-17 15:22:52 +09:00
Naoyuki Kanezawa
22c985cae6 Merge pull request #2380 from darrachequesne/patch-5
Document type change of Socket#rooms
2016-01-17 15:22:05 +09:00
Guillermo Rauch
fea3d79a2c Release 1.4.4 2016-01-10 22:36:31 -08:00
Guillermo Rauch
d3dde130b7 package: bump engine.io 2016-01-10 22:27:44 -08:00
Damien Arrachequesne
899de7f873 Document type change of Socket#rooms 2016-01-09 22:08:30 +01:00
Guillermo Rauch
f1a3e8db2a Release 1.4.3 2016-01-08 08:17:50 -08:00
Guillermo Rauch
dad82c3343 Release 1.4.2 2016-01-07 18:58:05 -08:00
Guillermo Rauch
a6cbf6b205 package: bump engine.io 2016-01-07 18:14:39 -08:00
Damien Arrachequesne
580100d491 Bump dev dependencies 2016-01-07 22:59:28 +01:00
Damien Arrachequesne
acb9cb421d Add badges showing dependency status 2016-01-07 22:58:59 +01:00
Guillermo Rauch
6d5d7e4411 Release 1.4.1 2016-01-07 12:52:27 -08:00
Guillermo Rauch
ddb3445f3d Release 1.4.0 2016-01-05 15:44:48 -08:00
Guillermo Rauch
6eb9807f17 package: bump engine.io for sec advisory 2016-01-05 13:40:54 -08:00
Faizal Pribadi
abe2394a24 fix typo word :)
update docs source code
2015-12-20 16:16:42 +07:00
Hongcai Deng
7012ba6c64 fix wrong jsdoc 2015-12-14 15:31:42 +08:00
Guillermo Rauch
7b0073c00f Merge pull request #2335 from nkzawa/patch-7
Improve the chat demo
2015-12-04 17:10:23 -08:00
nkzawa
0313ad0ea3 improve the chat demo 2015-12-05 10:07:08 +09:00
Guillermo Rauch
d310d42472 package: bump engine.io 2015-12-04 10:56:08 -08:00
Guillermo Rauch
709ceba00a package: bump socket.io-adapter for release 2015-12-03 10:28:33 -08:00
Guillermo Rauch
b29312bc61 package: bump engine.io for node 0.8 fix 2015-12-01 12:15:36 -08:00
Guillermo Rauch
51901160df package: temporarily revert version for tests 2015-12-01 11:24:06 -08:00
Guillermo Rauch
ab1b36e13f package: bump engine.io for ws memory fix 2015-11-30 08:16:21 -08:00
Guillermo Rauch
f7a2f35590 package: bump engine.io 2015-11-28 16:34:44 -08:00
Guillermo Rauch
c348737fe6 socket.io: increase large binary data test timeout 2015-11-28 15:13:13 -08:00
Guillermo Rauch
dde3737165 package: bump engine.io for release 2015-11-28 12:07:08 -08:00
Guillermo Rauch
8df4c7931b Merge pull request #2325 from darrachequesne/patch-2
Trigger callback even when joining an already joined room
2015-11-28 10:56:46 -08:00
Damien Arrachequesne
1dfacc6647 Trigger callback even when joining an already joined room 2015-11-27 22:08:18 +01:00
Guillermo Rauch
35a0fe0377 package: bump parser 2015-11-25 10:14:56 -08:00
Guillermo Rauch
3b00312fa7 namespace: clear rooms flag after a clients call (fixes #1978) 2015-11-23 09:40:57 -08:00
Guillermo Rauch
7b420295ef Merge pull request #2249 from pmq20/minqi-another
[Travis] Add Node 4.x to the test matrix
2015-11-23 09:31:48 -08:00
Guillermo Rauch
c504315982 Merge pull request #2322 from darrachequesne/issue-2199-clean
Add hasOwnProperty checks
2015-11-23 09:19:35 -08:00
Guillermo Rauch
520b5c37d5 Merge pull request #2321 from nkzawa/patch-6
Update redirects in README
2015-11-23 09:19:03 -08:00
Damien Arrachequesne
09f446eca0 Add hasOwnProperty checks 2015-11-23 15:12:21 +01:00
Naoyuki Kanezawa
aecf3bf71c Merge pull request #2218 from futbalguy/master
Clarified documentation of Socket.in
2015-11-23 17:37:07 +09:00
nkzawa
6c0a79e2b9 update redirects in README 2015-11-23 17:28:11 +09:00
Guillermo Rauch
8683206e31 Merge pull request #2239 from darrachequesne/issue-2199
converted arrays to objects
2015-11-22 10:34:57 -08:00
Guillermo Rauch
988d9d2346 Merge pull request #1999 from nkzawa/patch-3
Fix a few assertions are not executed properly
2015-11-22 10:17:02 -08:00
Damien Arrachequesne
b73d9bea4e converted arrays to objects 2015-11-22 11:12:40 +01:00
Naoyuki Kanezawa
d4fb6a5904 Merge pull request #2316 from tripu/master
Update “debug” to avoid vulnerability in “ms”
2015-11-22 10:39:23 +09:00
tripu
0517c609ba Avoid ^ in "debug" dependency (fixes #2314) 2015-11-21 23:59:16 +01:00
tripu
aa9438fee2 Update package "debug" to avoid "ms" vulnerability 2015-11-21 23:55:00 +01:00
Guillermo Rauch
aee466cc71 Update Readme.md 2015-11-21 10:54:19 -08:00
Guillermo Rauch
65f75d7732 Merge branch 'master' of github.com:socketio/socket.io 2015-11-21 10:46:26 -08:00
Guillermo Rauch
279c52a4c5 package: bump socket.io-parser 2015-11-21 10:45:43 -08:00
Naoyuki Kanezawa
67d1a67b1c Merge pull request #2319 from darrachequesne/patch-3
Minor fix
2015-11-21 23:21:39 +09:00
Damien Arrachequesne
aae68d74b1 Minor performance optimization 2015-11-20 22:03:03 +01:00
Damien Arrachequesne
bf5897cedc Fixed tests with large data 2015-11-20 22:01:16 +01:00
Damien Arrachequesne
a1a1c6657a Removed unused variable 2015-11-20 22:00:42 +01:00
Guillermo Rauch
2a4f7ae161 Merge pull request #2136 from nkzawa/patch-5
Don't set request property
2015-11-20 12:59:05 -08:00
Naoyuki Kanezawa
dba0576ca9 Merge pull request #2317 from ChaofengZhou/master
fixed a typo in the example code
2015-11-20 13:13:10 +09:00
Chaofeng
ba90f0991b fixed a typo in the example code 2015-11-19 21:09:34 -07:00
Guillermo Rauch
9a54e9cace package: bump mocha 2015-11-18 19:53:25 -08:00
Guillermo Rauch
9df4cb64b8 bump zuul-ngrok 2015-11-18 19:51:47 -08:00
Guillermo Rauch
c8f525868a package: bump has-binary 2015-11-18 16:56:06 -08:00
Guillermo Rauch
a39d7c2ae4 Merge pull request #2171 from KevinMartin/patch-1
Actually prevent double callbacks...
2015-11-18 12:56:01 -08:00
Guillermo Rauch
4ba266d404 Merge pull request #2307 from darrachequesne/patch-1
Upgrade npm version in order to fix node v0.8 build
2015-11-16 15:55:20 -08:00
Damien Arrachequesne
6b49779420 Upgrade npm version in order to fix node v0.8 build 2015-11-17 00:44:42 +01:00
Guillermo Rauch
b1775b1cb8 Merge pull request #2051 from tobalsgithub/master
Allowing a custom function for opts.allowRequest
2015-11-16 13:15:08 -08:00
Guillermo Rauch
ef52967301 Merge pull request #2271 from dazorni/master
Working travis build status
2015-11-16 13:12:26 -08:00
Guillermo Rauch
fde38b1bd7 Merge pull request #2208 from odeke-em/fix-legacy-repo-info
README: fixed up legacy repo links
2015-11-16 13:10:24 -08:00
Sebastian Zorn
75da57abb2 Working travis build status 2015-10-06 21:42:33 +02:00
Guillermo Rauch
21ea31a34e Merge branch '1.3.7-patch' 2015-09-21 04:30:59 +04:00
Guillermo Rauch
e2ebd4349b Release 1.3.7 2015-09-21 04:21:11 +04:00
Guillermo Rauch
b771f74a22 package: bump socket.io-client for node4 compatibility 2015-09-21 04:16:59 +04:00
Guillermo Rauch
8744007dc7 package: bump engine.io for node4 compatibility 2015-09-21 04:10:19 +04:00
P.S.V.R
3658928ee7 [Travis] Add Node 4.x to the test matrix 2015-09-16 17:39:26 +08:00
Guillermo Rauch
210e688732 package: bump engine.io 2015-09-09 15:47:14 -07:00
Guillermo Rauch
ae5420b727 package: bump engine.io and socket.io-client 2015-08-30 10:51:25 -07:00
futbalguy
d5b3bb46f8 Clarified documentation of Socket.in 2015-08-23 10:12:23 -07:00
Emmanuel Odeke
575d35a371 README: fixed up legacy repo links 2015-08-16 20:36:39 -06:00
Guillermo Rauch
e41483be9e Merge branch '1.3.6-patch' 2015-07-14 20:04:44 -07:00
Guillermo Rauch
c79e787671 test: better timeout for stress test 2015-07-14 19:58:42 -07:00
Guillermo Rauch
a2f6bb99d6 Merge branch '1.3.6-patch' 2015-07-14 18:02:56 -07:00
Kevin Jose Martin
514aeb4fcd Actually prevent double callbacks...
We declare `sent = false` and even check `if (sent)` is truthy, but we never set it to truthy, therefore it never really *prevents double callbacks*...
2015-07-06 11:36:00 -04:00
Nathan Rajlich
876833198d Merge pull request #2111 from kemitchell/spdx-license
Use a Standard-Compliant License Identifier
2015-06-05 11:51:30 -07:00
Naoyuki Kanezawa
4d36d6c01a don't set request property 2015-05-30 03:34:05 +09:00
Guillermo Rauch
43fa075ec4 bump client 2015-05-27 22:56:15 -07:00
Guillermo Rauch
91c7bde34b Merge commit '342faf219793fc5c1f130fc33e3fb5422dab516d' 2015-05-27 22:55:15 -07:00
Guillermo Rauch
874a4a8535 package: bump client 2015-05-27 08:19:15 -07:00
Guillermo Rauch
93b571406e package: bump engine.io to fix build issues 2015-05-25 14:34:10 -07:00
Kyle Mitchell
98af793f07 use a valid SPDX license identifier 2015-05-05 00:08:44 +00:00
Guillermo Rauch
3f72dd3322 Merge pull request #2063 from kapouer/patch-1
Let adapter.add find connected socket
2015-04-10 21:38:41 -07:00
Guillermo Rauch
d0d38220fb Update Readme.md 2015-04-08 16:53:04 -07:00
Guillermo Rauch
f8aa157d5d remove proxy index file 2015-04-05 16:53:28 -07:00
Guillermo Rauch
0e13e9a268 bumped adapter 2015-04-05 15:52:59 -07:00
Guillermo Rauch
916872587f Merge pull request #2013 from nkzawa/namespace-flags
Supports flags on namespace
2015-04-05 15:51:54 -07:00
Guillermo Rauch
a7072845ea Merge pull request #2006 from nkzawa/patch-4
improve Socket#packet and Client#packet and enable volatile and compress when broadcast
2015-04-05 15:51:49 -07:00
Jérémy Lal
425409945b Let adapter.add find connected socket
When a socket connects, it joins its own room, resulting in a call to adapter.add.
The adapter in turn should be able to find the socket by id.
2015-03-24 14:54:52 +01:00
Guillermo Rauch
5826ef0bb5 Merge pull request #2059 from rase-/update/debug
Bump debug to 2.1.3
2015-03-17 16:42:00 -07:00
Tony Kovanen
14e32a9bad Bump debug to 2.1.3 2015-03-18 01:40:19 +02:00
TC
1dde0f3947 Allowing a custom function for opts.allowRequest 2015-03-12 15:50:31 -06:00
Guillermo Rauch
b31ac18554 test with 0.12 2015-02-19 15:38:35 -08:00
Naoyuki Kanezawa
53cdd8f1fc support flags on namespace 2015-02-20 03:34:09 +09:00
Naoyuki Kanezawa
81aea995ed improve Socket#packet and Client#packet 2015-02-15 04:33:20 +09:00
Naoyuki Kanezawa
56fe26661d fix a few assertions 2015-02-12 06:09:30 +09:00
Guillermo Rauch
a93d05a9f3 package: bump parser 2015-02-03 16:30:49 -08:00
Guillermo Rauch
5ce06d3088 socket: warn node_redis-style about missing error 2015-02-03 16:26:21 -08:00
Guillermo Rauch
816bfec783 added failing tests 2015-02-03 16:26:08 -08:00
Guillermo Rauch
0a17c90d7a test: added failing test 2015-02-03 15:10:46 -08:00
Guillermo Rauch
3645741b86 test: increase timeout for large binary data test 2015-01-31 09:35:37 -08:00
Guillermo Rauch
f2a7322b5a update deps 2015-01-31 08:46:32 -08:00
Guillermo Rauch
97c6568f65 Merge pull request #1966 from cha0s/clients
Suggestion for implementation of clients API
2015-01-31 08:34:43 -08:00
Ruben Rodriguez II
8814825a35 Suggestion for implementation of clients API 2015-01-31 02:17:49 -06:00
Guillermo Rauch
58eaecad27 Merge branch 'master' of github.com:Automattic/socket.io 2015-01-29 20:39:00 -08:00
Guillermo Rauch
94157e650e point to master 2015-01-29 20:38:36 -08:00
Guillermo Rauch
0935b81da2 Merge pull request #1963 from michael-luo/bugfix/1956
bugfix/1956 test for don't reuse same-namespace connections
2015-01-29 20:34:38 -08:00
Michael Luo
afa871bb8a bugfix/1956 test for don't reuse same-namspace connections 2015-01-24 20:19:16 -08:00
Guillermo Rauch
1b01e16a6c fix broken previous commit 2015-01-24 08:02:05 -08:00
Guillermo Rauch
bd6f638c8f package: bump to work with all objects (fixes #1955) 2015-01-24 07:06:30 -08:00
Guillermo Rauch
83b36e54ac Merge pull request #1958 from nkzawa/compress
Support compression
2015-01-23 10:04:43 -08:00
Guillermo Rauch
2f0d9d05af fix origin verification default https port [evanlucas]
for PR #1693
2015-01-22 21:41:59 +00:00
Naoyuki Kanezawa
429eb0cb7c point to the specific commit 2015-01-22 05:26:37 +09:00
Naoyuki Kanezawa
ac8e8598d7 support compression 2015-01-22 05:01:45 +09:00
151 changed files with 40414 additions and 4072 deletions

32
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,32 @@
**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:
* [x] report a *bug*
* [ ] request a *feature*
### Current behaviour
*What is actually happening?*
### Steps to reproduce (if the current behaviour is a bug)
**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:
- browser:
- socket.io version:
### Other information (e.g. stacktraces, related issues, suggestions how to fix)

18
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,18 @@
### The kind of change this PR does introduce
* [x] a bug fix
* [ ] a new feature
* [ ] an update to the documentation
* [ ] a code change that improves performance
* [ ] other
### Current behaviour
### New behaviour
### Other information (e.g. related issues)

3
.gitignore vendored
View File

@@ -9,3 +9,6 @@ lib-cov
benchmarks/*.png
node_modules
coverage
.idea
.nyc_output
dist/

View File

@@ -1,4 +0,0 @@
support
test
examples
.gitignore

View File

@@ -1,16 +1,10 @@
language: node_js
sudo: false
node_js:
- "0.8"
- "0.10"
- "0.11"
git:
depth: 1
matrix:
fast_finish: true
allow_failures:
- node_js: "0.11"
- '10'
- '12'
- '14'
notifications:
irc: "irc.freenode.org#socket.io"
git:
depth: 1

262
CHANGELOG.md Normal file
View File

@@ -0,0 +1,262 @@
# [3.0.0](https://github.com/socketio/socket.io/compare/2.3.0...3.0.0) (2020-11-05)
### Bug Fixes
* close clients with no namespace ([91cd255](https://github.com/socketio/socket.io/commit/91cd255ba76ff6a780c62740f9f5cd3a76f5d7c7))
### Features
* emit an Error object upon middleware error ([54bf4a4](https://github.com/socketio/socket.io/commit/54bf4a44e9e896dfb64764ee7bd4e8823eb7dc7b))
* serve msgpack bundle ([aa7574f](https://github.com/socketio/socket.io/commit/aa7574f88471aa30ae472a5cddf1000a8baa70fd))
* add support for catch-all listeners ([5c73733](https://github.com/socketio/socket.io/commit/5c737339858d59eab4b5ee2dd6feff0e82c4fe5a))
* make Socket#join() and Socket#leave() synchronous ([129c641](https://github.com/socketio/socket.io/commit/129c6417bd818bc8b4e1b831644323876e627c13))
* remove prod dependency to socket.io-client ([7603da7](https://github.com/socketio/socket.io/commit/7603da71a535481e3fc60e38b013abf78516d322))
* move binary detection back to the parser ([669592d](https://github.com/socketio/socket.io/commit/669592d120409a5cf00f128070dee6d22259ba4f))
* add ES6 module export ([8b6b100](https://github.com/socketio/socket.io/commit/8b6b100c284ccce7d85e55659e3397f533916847))
* do not reuse the Engine.IO id ([2875d2c](https://github.com/socketio/socket.io/commit/2875d2cfdfa463e64cb520099749f543bbc4eb15))
* remove Server#set() method ([029f478](https://github.com/socketio/socket.io/commit/029f478992f59b1eb5226453db46363a570eea46))
* remove Socket#rooms object ([1507b41](https://github.com/socketio/socket.io/commit/1507b416d584381554d1ed23c9aaf3b650540071))
* remove the 'origins' option ([a8c0600](https://github.com/socketio/socket.io/commit/a8c06006098b512ba1b8b8df82777349db486f41))
* remove the implicit connection to the default namespace ([3289f7e](https://github.com/socketio/socket.io/commit/3289f7ec376e9ec88c2f90e2735c8ca8d01c0e97))
* throw upon reserved event names ([4bd5b23](https://github.com/socketio/socket.io/commit/4bd5b2339a66a5a675e20f689fff2e70ff12d236))
### BREAKING CHANGES
* the Socket#use() method is removed (see [5c73733](https://github.com/socketio/socket.io/commit/5c737339858d59eab4b5ee2dd6feff0e82c4fe5a))
* Socket#join() and Socket#leave() do not accept a callback argument anymore.
Before:
```js
socket.join("room1", () => {
io.to("room1").emit("hello");
});
```
After:
```js
socket.join("room1");
io.to("room1").emit("hello");
// or await socket.join("room1"); for custom adapters
```
* the "connected" map is renamed to "sockets"
* the Socket#binary() method is removed, as this use case is now covered by the ability to provide your own parser.
* the 'origins' option is removed
Before:
```js
new Server(3000, {
origins: ["https://example.com"]
});
```
The 'origins' option was used in the allowRequest method, in order to
determine whether the request should pass or not. And the Engine.IO
server would implicitly add the necessary Access-Control-Allow-xxx
headers.
After:
```js
new Server(3000, {
cors: {
origin: "https://example.com",
methods: ["GET", "POST"],
allowedHeaders: ["content-type"]
}
});
```
The already existing 'allowRequest' option can be used for validation:
```js
new Server(3000, {
allowRequest: (req, callback) => {
callback(null, req.headers.referer.startsWith("https://example.com"));
}
});
```
* Socket#rooms is now a Set instead of an object
* Namespace#connected is now a Map instead of an object
* there is no more implicit connection to the default namespace:
```js
// client-side
const socket = io("/admin");
// server-side
io.on("connect", socket => {
// not triggered anymore
})
io.use((socket, next) => {
// not triggered anymore
});
io.of("/admin").use((socket, next) => {
// triggered
});
```
* the Server#set() method was removed
This method was kept for backward-compatibility with pre-1.0 versions.
# [3.0.0-rc4](https://github.com/socketio/socket.io/compare/3.0.0-rc3...3.0.0-rc4) (2020-10-30)
### Features
* emit an Error object upon middleware error ([54bf4a4](https://github.com/socketio/socket.io/commit/54bf4a44e9e896dfb64764ee7bd4e8823eb7dc7b))
* serve msgpack bundle ([aa7574f](https://github.com/socketio/socket.io/commit/aa7574f88471aa30ae472a5cddf1000a8baa70fd))
# [3.0.0-rc3](https://github.com/socketio/socket.io/compare/3.0.0-rc2...3.0.0-rc3) (2020-10-26)
### Features
* add support for catch-all listeners ([5c73733](https://github.com/socketio/socket.io/commit/5c737339858d59eab4b5ee2dd6feff0e82c4fe5a))
* make Socket#join() and Socket#leave() synchronous ([129c641](https://github.com/socketio/socket.io/commit/129c6417bd818bc8b4e1b831644323876e627c13))
* remove prod dependency to socket.io-client ([7603da7](https://github.com/socketio/socket.io/commit/7603da71a535481e3fc60e38b013abf78516d322))
### BREAKING CHANGES
* the Socket#use() method is removed (see [5c73733](https://github.com/socketio/socket.io/commit/5c737339858d59eab4b5ee2dd6feff0e82c4fe5a))
* Socket#join() and Socket#leave() do not accept a callback argument anymore.
Before:
```js
socket.join("room1", () => {
io.to("room1").emit("hello");
});
```
After:
```js
socket.join("room1");
io.to("room1").emit("hello");
// or await socket.join("room1"); for custom adapters
```
# [3.0.0-rc2](https://github.com/socketio/socket.io/compare/3.0.0-rc1...3.0.0-rc2) (2020-10-15)
### Bug Fixes
* close clients with no namespace ([91cd255](https://github.com/socketio/socket.io/commit/91cd255ba76ff6a780c62740f9f5cd3a76f5d7c7))
### Code Refactoring
* remove duplicate _sockets map ([8a5db7f](https://github.com/socketio/socket.io/commit/8a5db7fa36a075da75cde43cd4fb6382b7659953))
### Features
* move binary detection back to the parser ([669592d](https://github.com/socketio/socket.io/commit/669592d120409a5cf00f128070dee6d22259ba4f))
### BREAKING CHANGES
* the "connected" map is renamed to "sockets"
* the Socket#binary() method is removed, as this use case is now covered by the ability to provide your own parser.
# [3.0.0-rc1](https://github.com/socketio/socket.io/compare/2.3.0...3.0.0-rc1) (2020-10-13)
### Features
* add ES6 module export ([8b6b100](https://github.com/socketio/socket.io/commit/8b6b100c284ccce7d85e55659e3397f533916847))
* do not reuse the Engine.IO id ([2875d2c](https://github.com/socketio/socket.io/commit/2875d2cfdfa463e64cb520099749f543bbc4eb15))
* remove Server#set() method ([029f478](https://github.com/socketio/socket.io/commit/029f478992f59b1eb5226453db46363a570eea46))
* remove Socket#rooms object ([1507b41](https://github.com/socketio/socket.io/commit/1507b416d584381554d1ed23c9aaf3b650540071))
* remove the 'origins' option ([a8c0600](https://github.com/socketio/socket.io/commit/a8c06006098b512ba1b8b8df82777349db486f41))
* remove the implicit connection to the default namespace ([3289f7e](https://github.com/socketio/socket.io/commit/3289f7ec376e9ec88c2f90e2735c8ca8d01c0e97))
* throw upon reserved event names ([4bd5b23](https://github.com/socketio/socket.io/commit/4bd5b2339a66a5a675e20f689fff2e70ff12d236))
### BREAKING CHANGES
* the 'origins' option is removed
Before:
```js
new Server(3000, {
origins: ["https://example.com"]
});
```
The 'origins' option was used in the allowRequest method, in order to
determine whether the request should pass or not. And the Engine.IO
server would implicitly add the necessary Access-Control-Allow-xxx
headers.
After:
```js
new Server(3000, {
cors: {
origin: "https://example.com",
methods: ["GET", "POST"],
allowedHeaders: ["content-type"]
}
});
```
The already existing 'allowRequest' option can be used for validation:
```js
new Server(3000, {
allowRequest: (req, callback) => {
callback(null, req.headers.referer.startsWith("https://example.com"));
}
});
```
* Socket#rooms is now a Set instead of an object
* Namespace#connected is now a Map instead of an object
* there is no more implicit connection to the default namespace:
```js
// client-side
const socket = io("/admin");
// server-side
io.on("connect", socket => {
// not triggered anymore
})
io.use((socket, next) => {
// not triggered anymore
});
io.of("/admin").use((socket, next) => {
// triggered
});
```
* the Server#set() method was removed
This method was kept for backward-compatibility with pre-1.0 versions.

View File

@@ -1,496 +0,0 @@
1.3.6 / 2015-07-14
==================
* package: bump `engine.io` to fix build on windows
1.3.5 / 2015-03-03
==================
* package: bump `socket.io-parser`
1.3.4 / 2015-02-14
==================
* package: bump `socket.io-client`
1.3.3 / 2015-02-03
==================
* socket: warn node_redis-style about missing `error`
* package: bump parser to better handle bad binary packets
1.3.2 / 2015-01-19
==================
* no change on this release
1.3.1 / 2015-01-19
==================
* no change on this release
* package: bump `engine.io`
1.3.0 / 2015-01-19
==================
* package: bump `engine.io`
* add test for reconnection after server restarts [rase-]
* update license with up-to-date year range [fay-jai]
* fix leaving unknown rooms [defunctzombie]
* allow null origins when allowed origins is a function [drewblaisdell]
* fix tests on node 0.11
* package: fix `npm test` to run on windows
* package: bump `debug` v2.1.0 [coderaiser]
* added tests for volatile [rase-]
1.2.1 / 2014-11-21
==================
* fix protocol violations and improve error handling (GH-1880)
* package: bump `engine.io` for websocket leak fix [3rd-Eden]
* style tweaks
1.2.0 / 2014-10-27
==================
* package: bump `engine.io`
* downloads badge
* add test to check that empty rooms are autopruned
* added Server#origins(v:Function) description for dynamic CORS
* added test coverage for Server#origins(function) for dynamic CORS
* added optional Server#origins(function) for dynamic CORS
* fix usage example for Server#close
* package: fix main file for example application 'chat'
* package: bump `socket.io-parser`
* update README http ctor to createServer()
* bump adapter with a lot of fixes for room bookkeeping
1.1.0 / 2014-09-04
==================
* examples: minor fix of escaping
* testing for equivalence of namespaces starting with / or without
* update index.js
* added relevant tests
* take "" and "/" as equivalent namespaces on server
* use svg instead of png to get better image quality in readme
* make CI build faster
* fix splice arguments and `socket.rooms` value update in `socket.leaveAll`.
* client cannot connect to non-existing namespaces
* bump engine.io version to get the cached IP address
* fixed handshake object address property and made the test case more strict.
* package: bump `engine.io`
* fixed the failing test where server crashes on disconnect involving connectBuffer
* npmignore: ignore `.gitignore` (fixes #1607)
* test: added failing case for `socket.disconnect` and nsps
* fix repo in package.json
* improve Close documentation
* use ephemeral ports
* fix: We should use the standard http protocol to handler the etag header.
* override default browser font-family for inputs
* update has-binary-data to 1.0.3
* add close specs
* add ability to stop the http server even if not created inside socket.io
* make sure server gets close
* Add test case for checking that reconnect_failed is fired only once upon failure
* package: bump `socket.io-parser` for `component-emitter` dep fix
1.0.6 / 2014-06-19
==================
* package: bump `socket.io-client`
1.0.5 / 2014-06-16
==================
* package: bump `engine.io` to fix jsonp `\n` bug and CORS warnings
* index: fix typo [yanatan16]
* add `removeListener` to blacklisted events
* examples: clearer instructions to install chat example
* index: fix namespace `connectBuffer` issue
1.0.4 / 2014-06-02
==================
* package: bump socket.io-client
1.0.3 / 2014-05-31
==================
* package: bump `socket.io-client`
* package: bump `socket.io-parser` for binary ACK fix
* package: bump `engine.io` for binary UTF8 fix
* example: fix XSS in chat example
1.0.2 / 2014-05-28
==================
* package: bump `socket.io-parser` for windows fix
1.0.1 / 2014-05-28
==================
* bump due to bad npm tag
1.0.0 / 2014-05-28
==================
* stable release
1.0.0-pre5 / 2014-05-22
=======================
* package: bump `socket.io-client` for parser fixes
* package: bump `engine.io`
1.0.0-pre4 / 2014-05-19
=======================
* package: bump client
1.0.0-pre3 / 2014-05-17
=======================
* package: bump parser
* package: bump engine.io
1.0.0-pre2 / 2014-04-27
=======================
* package: bump `engine.io`
* added backwards compatible of engine.io maxHttpBufferSize
* added test that server and client using same protocol
* added support for setting allowed origins
* added information about logging
* the set function in server can be used to set some attributes for BC
* fix error in callback call 'done' instead of 'next' in docs
* package: bump `socket.io-parser`
* package: bump `expect.js`
* added some new tests, including binary with acks
1.0.0-pre / 2014-03-14
======================
* implemented `engine.io`
* implemented `socket.io-adapter`
* implemented `socket.io-protocol`
* implemented `debug` and improved instrumentation
* added binary support
* added new `require('io')(srv)` signature
* simplified `socket.io-client` serving
0.9.14 / 2013-03-29
===================
* manager: fix memory leak with SSL [jpallen]
0.9.13 / 2012-12-13
===================
* package: fixed `base64id` requirement
0.9.12 / 2012-12-13
===================
* manager: fix for latest node which is returning a clone with `listeners` [viirya]
0.9.11 / 2012-11-02
===================
* package: move redis to optionalDependenices [3rd-Eden]
* bumped client
0.9.10 / 2012-08-10
===================
* Don't lowercase log messages
* Always set the HTTP response in case an error should be returned to the client
* Create or destroy the flash policy server on configuration change
* Honour configuration to disable flash policy server
* Add express 3.0 instructions on Readme.md
* Bump client
0.9.9 / 2012-08-01
==================
* Fixed sync disconnect xhrs handling
* Put license text in its own file (#965)
* Add warning to .listen() to ease the migration to Express 3.x
* Restored compatibility with node 0.4.x
0.9.8 / 2012-07-24
==================
* Bumped client.
0.9.7 / 2012-07-24
==================
* Prevent crash when socket leaves a room twice.
* Corrects unsafe usage of for..in
* Fix for node 0.8 with `gzip compression` [vadimi]
* Update redis to support Node 0.8.x
* Made ID generation securely random
* Fix Redis Store race condition in manager onOpen unsubscribe callback
* Fix for EventEmitters always reusing the same Array instance for listeners
0.9.6 / 2012-04-17
==================
* Fixed XSS in jsonp-polling.
0.9.5 / 2012-04-05
==================
* Added test for polling and socket close.
* Ensure close upon request close.
* Fix disconnection reason being lost for polling transports.
* Ensure that polling transports work with Connection: close.
* Log disconnection reason.
0.9.4 / 2012-04-01
==================
* Disconnecting from namespace improvement (#795) [DanielBaulig]
* Bumped client with polling reconnection loop (#438)
0.9.3 / 2012-03-28
==================
* Fix "Syntax error" on FF Web Console with XHR Polling [mikito]
0.9.2 / 2012-03-13
==================
* More sensible close `timeout default` (fixes disconnect issue)
0.9.1-1 / 2012-03-02
====================
* Bumped client with NPM dependency fix.
0.9.1 / 2012-03-02
==================
* Changed heartbeat timeout and interval defaults (60 and 25 seconds)
* Make tests work both on 0.4 and 0.6
* Updated client (improvements + bug fixes).
0.9.0 / 2012-02-26
==================
* Make it possible to use a regexp to match the socket.io resource URL.
We need this because we have to prefix the socket.io URL with a variable ID.
* Supplemental fix to gavinuhma/authfix, it looks like the same Access-Control-Origin logic is needed in the http and xhr-polling transports
* Updated express dep for windows compatibility.
* Combine two substr calls into one in decodePayload to improve performance
* Minor documentation fix
* Minor. Conform to style of other files.
* Switching setting to 'match origin protocol'
* Revert "Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect()."
* Revert "Handle leaked dispatch:[id] subscription."
* Merge pull request #667 from dshaw/patch/redis-disconnect
* Handle leaked dispatch:[id] subscription.
* Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect().
* Prevent memory leaking on uncompleted requests & add max post size limitation
* Fix for testcase
* Set Access-Control-Allow-Credentials true, regardless of cookie
* Remove assertvarnish from package as it breaks on 0.6
* Correct irc channel
* Added proper return after reserved field error
* Fixes manager.js failure to close connection after transport error has happened
* Added implicit port 80 for origin checks. fixes #638
* Fixed bug #432 in 0.8.7
* Set Access-Control-Allow-Origin header to origin to enable withCredentials
* Adding configuration variable matchOriginProtocol
* Fixes location mismatch error in Safari.
* Use tty to detect if we should add colors or not by default.
* Updated the package location.
0.8.7 / 2011-11-05
==================
* Fixed memory leaks in closed clients.
* Fixed memory leaks in namespaces.
* Fixed websocket handling for malformed requests from proxies. [einaros]
* Node 0.6 compatibility. [einaros] [3rd-Eden]
* Adapted tests and examples.
0.8.6 / 2011-10-27
==================
* Added JSON decoding on jsonp-polling transport.
* Fixed README example.
* Major speed optimizations [3rd-Eden] [einaros] [visionmedia]
* Added decode/encode benchmarks [visionmedia]
* Added support for black-listing client sent events.
* Fixed logging options, closes #540 [3rd-Eden]
* Added vary header for gzip [3rd-Eden]
* Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client
* Patched to properly shut down when a finishClose call is made during connection establishment
* Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify]
* Began IE10 compatibility [einaros] [tbranyen]
* Misc WebSocket fixes [einaros]
* Added UTF8 to respone headers for htmlfile [3rd-Eden]
0.8.5 / 2011-10-07
==================
* Added websocket draft HyBi-16 support. [einaros]
* Fixed websocket continuation bugs. [einaros]
* Fixed flashsocket transport name.
* Fixed websocket tests.
* Ensured `parser#decodePayload` doesn't choke.
* Added http referrer verification to manager verifyOrigin.
* Added access control for cross domain xhr handshakes [3rd-Eden]
* Added support for automatic generation of socket.io files [3rd-Eden]
* Added websocket binary support [einaros]
* Added gzip support for socket.io.js [3rd-Eden]
* Expose socket.transport [3rd-Eden]
* Updated client.
0.8.4 / 2011-09-06
==================
* Client build
0.8.3 / 2011-09-03
==================
* Fixed `\n` parsing for non-JSON packets (fixes #479).
* Fixed parsing of certain unicode characters (fixes #451).
* Fixed transport message packet logging.
* Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476).
* Fixed; allow for falsy values as the configuration value of `log level` (fixes #491).
* Fixed repository URI in `package.json`. Fixes #504.
* Added text/plain content-type to handshake responses [einaros]
* Improved single byte writes [einaros]
* Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden]
* Updated client.
0.8.2 / 2011-08-29
==================
* Updated client.
0.8.1 / 2011-08-29
==================
* Fixed utf8 bug in send framing in websocket [einaros]
* Fixed typo in docs [Znarkus]
* Fixed bug in send framing for over 64kB of data in websocket [einaros]
* Corrected ping handling in websocket transport [einaros]
0.8.0 / 2011-08-28
==================
* Updated to work with two-level websocket versioning. [einaros]
* Added hybi07 support. [einaros]
* Added hybi10 support. [einaros]
* Added http referrer verification to manager.js verifyOrigin. [einaors]
0.7.11 / 2011-08-27
===================
* Updated socket.io-client.
0.7.10 / 2011-08-27
===================
* Updated socket.io-client.
0.7.9 / 2011-08-12
==================
* Updated socket.io-client.
* Make sure we only do garbage collection when the server we receive is actually run.
0.7.8 / 2011-08-08
==================
* Changed; make sure sio#listen passes options to both HTTP server and socket.io manager.
* Added docs for sio#listen.
* Added options parameter support for Manager constructor.
* Added memory leaks tests and test-leaks Makefile task.
* Removed auto npm-linking from make test.
* Make sure that you can disable heartbeats. [3rd-Eden]
* Fixed rooms memory leak [3rd-Eden]
* Send response once we got all POST data, not immediately [Pita]
* Fixed onLeave behavior with missing clientsk [3rd-Eden]
* Prevent duplicate references in rooms.
* Added alias for `to` to `in` and `in` to `to`.
* Fixed roomClients definition.
* Removed dependency on redis for installation without npm [3rd-Eden]
* Expose path and querystring in handshakeData [3rd-Eden]
0.7.7 / 2011-07-12
==================
* Fixed double dispatch handling with emit to closed clients.
* Added test for emitting to closed clients to prevent regression.
* Fixed race condition in redis test.
* Changed Transport#end instrumentation.
* Leveraged $emit instead of emit internally.
* Made tests faster.
* Fixed double disconnect events.
* Fixed disconnect logic
* Simplified remote events handling in Socket.
* Increased testcase timeout.
* Fixed unknown room emitting (GH-291). [3rd-Eden]
* Fixed `address` in handshakeData. [3rd-Eden]
* Removed transports definition in chat example.
* Fixed room cleanup
* Fixed; make sure the client is cleaned up after booting.
* Make sure to mark the client as non-open if the connection is closed.
* Removed unneeded `buffer` declarations.
* Fixed; make sure to clear socket handlers and subscriptions upon transport close.
0.7.6 / 2011-06-30
==================
* Fixed general dispatching when a client has closed.
0.7.5 / 2011-06-30
==================
* Fixed dispatching to clients that are disconnected.
0.7.4 / 2011-06-30
==================
* Fixed; only clear handlers if they were set. [level09]
0.7.3 / 2011-06-30
==================
* Exposed handshake data to clients.
* Refactored dispatcher interface.
* Changed; Moved id generation method into the manager.
* Added sub-namespace authorization. [3rd-Eden]
* Changed; normalized SocketNamespace local eventing [dvv]
* Changed; Use packet.reason or default to 'packet' [3rd-Eden]
* Changed console.error to console.log.
* Fixed; bind both servers at the same time do that the test never times out.
* Added 304 support.
* Removed `Transport#name` for abstract interface.
* Changed; lazily require http and https module only when needed. [3rd-Eden]
0.7.2 / 2011-06-22
==================
* Make sure to write a packet (of type `noop`) when closing a poll.
This solves a problem with cross-domain requests being flagged as aborted and
reconnection being triggered.
* Added `noop` message type.
0.7.1 / 2011-06-21
==================
* Fixed cross-domain XHR.
* Added CORS test to xhr-polling suite.
0.7.0 / 2010-06-21
==================
* http://socket.io/announcement.html

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2014-2015 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

View File

@@ -1,15 +0,0 @@
REPORTER = dot
test:
@./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--slow 200ms \
--bail
test-cov:
@./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- \
--reporter $(REPORTER) \
test/
.PHONY: test

516
Readme.md
View File

@@ -1,9 +1,91 @@
# socket.io
[![Build Status](https://secure.travis-ci.org/Automattic/socket.io.svg)](http://travis-ci.org/Automattic/socket.io)
![NPM version](https://badge.fury.io/js/socket.io.svg)
![Downloads](http://img.shields.io/npm/dm/socket.io.svg?style=flat)
[![Backers on Open Collective](https://opencollective.com/socketio/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/socketio/sponsors/badge.svg)](#sponsors)
[![Build Status](https://secure.travis-ci.org/socketio/socket.io.svg?branch=master)](https://travis-ci.org/socketio/socket.io)
[![Dependency Status](https://david-dm.org/socketio/socket.io.svg)](https://david-dm.org/socketio/socket.io)
[![devDependency Status](https://david-dm.org/socketio/socket.io/dev-status.svg)](https://david-dm.org/socketio/socket.io#info=devDependencies)
[![NPM version](https://badge.fury.io/js/socket.io.svg)](https://www.npmjs.com/package/socket.io)
![Downloads](https://img.shields.io/npm/dm/socket.io.svg?style=flat)
[![](https://slackin-socketio.now.sh/badge.svg)](https://slackin-socketio.now.sh)
## Features
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)
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:
#### Reliability
Connections are established even in the presence of:
- proxies and load balancers.
- personal firewall and antivirus software.
For this purpose, it relies on [Engine.IO](https://github.com/socketio/engine.io), which first establishes a long-polling connection, then tries to upgrade to better transports that are "tested" on the side, like WebSocket. Please see the [Goals](https://github.com/socketio/engine.io#goals) section for more information.
#### Auto-reconnection support
Unless instructed otherwise a disconnected client will try to reconnect forever, until the server is available again. Please see the available reconnection options [here](https://github.com/socketio/socket.io-client/blob/master/docs/API.md#new-managerurl-options).
#### Disconnection detection
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.
#### Binary support
Any serializable data structures can be emitted, including:
- [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) and [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) in the browser
- [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) and [Buffer](https://nodejs.org/api/buffer.html) in Node.js
#### Simple and convenient API
Sample code:
```js
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
});
```
#### Cross-browser
Browser support is tested in Sauce Labs:
[![Sauce Test Status](https://saucelabs.com/browser-matrix/socket.svg)](https://saucelabs.com/u/socket)
#### Multiplexing support
In order to create separation of concerns within your application (for example per module, or based on permissions), Socket.IO allows you to create several `Namespaces`, which will act as separate communication channels but will share the same underlying connection.
#### Room support
Within each `Namespace`, you can define arbitrary channels, called `Rooms`, that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it.
This is a useful feature to send notifications to a group of users, or to a given user connected on several devices for example.
**Note:** Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server (like `ws://echo.websocket.org`) either. Please see the protocol specification [here](https://github.com/socketio/socket.io-protocol).
## Installation
```bash
npm install socket.io
```
## How to use
@@ -11,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(socket){
socket.on('event', function(data){});
socket.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);
```
@@ -23,8 +105,8 @@ server.listen(3000);
### Standalone
```js
var io = require('socket.io')();
io.on('connection', function(socket){});
const io = require('socket.io')();
io.on('connection', client => { ... });
io.listen(3000);
```
@@ -33,13 +115,13 @@ io.listen(3000);
Starting with **3.0**, express applications have become request handler
functions that you pass to `http` or `http` `Server` instances. You need
to pass the `Server` to `socket.io`, and not the express application
function.
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);
```
@@ -49,317 +131,35 @@ 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);
```
## API
### In conjunction with Fastify
### Server
To integrate Socket.io in your Fastify application you just need to
register `fastify-socket.io` plugin. It will create a `decorator`
called `io`.
Exposed by `require('socket.io')`.
```js
const app = require('fastify')();
app.register(require('fastify-socket.io'));
app.io.on('connection', () => { /* … */ });
app.listen(3000);
```
### Server()
## Documentation
Creates a new `Server`. Works with and without `new`:
Please see the documentation [here](https://socket.io/docs/).
```js
var io = require('socket.io')();
// or
var Server = require('socket.io');
var io = new Server();
```
### Server(opts:Object)
Optionally, the first or second argument (see below) of the `Server`
constructor can be an options object.
The following options are supported:
- `serveClient` sets the value for Server#serveClient()
- `path` sets the value for Server#path()
The same options passed to socket.io are always passed to
the `engine.io` `Server` that gets created. See engine.io
[options](https://github.com/learnboost/engine.io#methods-1)
as reference.
### Server(srv:http#Server, opts:Object)
Creates a new `Server` and attaches it to the given `srv`. Optionally
`opts` can be passed.
### Server(port:Number, opts:Object)
Binds socket.io to a new `http.Server` that listens on `port`.
### Server#serveClient(v:Boolean):Server
If `v` is `true` the attached server (see `Server#attach`) will serve
the client files. Defaults to `true`.
This method has no effect after `attach` is called.
```js
// pass a server and the `serveClient` option
var io = require('socket.io')(http, { serveClient: false });
// or pass no server and then you can call the method
var io = require('socket.io')();
io.serveClient(false);
io.attach(http);
```
If no arguments are supplied this method returns the current value.
### Server#path(v:String):Server
Sets the path `v` under which `engine.io` and the static files will be
served. Defaults to `/socket.io`.
If no arguments are supplied this method returns the current value.
### Server#adapter(v:Adapter):Server
Sets the adapter `v`. Defaults to an instance of the `Adapter` that
ships with socket.io which is memory based. See
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
If no arguments are supplied this method returns the current value.
### Server#origins(v:String):Server
Sets the allowed origins `v`. Defaults to any origins being allowed.
If no arguments are supplied this method returns the current value.
### Server#origins(v:Function):Server
Sets the allowed origins as dynamic function. Function takes two arguments `origin:String` and `callback(error, success)`, where `success` is a boolean value indicating whether origin is allowed or not.
__Potential drawbacks__:
* in some situations, when it is not possible to determine `origin` it may have value of `*`
* As this function will be executed for every request, it is advised to make this function work as fast as possible
* If `socket.io` is used together with `Express`, the CORS headers will be affected only for `socket.io` requests. For Express can use [cors](https://github.com/troygoode/node-cors/)
### Server#sockets:Namespace
The default (`/`) namespace.
### Server#attach(srv:http#Server, opts:Object):Server
Attaches the `Server` to an engine.io instance on `srv` with the
supplied `opts` (optionally).
### Server#attach(port:Number, opts:Object):Server
Attaches the `Server` to an engine.io instance that is bound to `port`
with the given `opts` (optionally).
### Server#listen
Synonym of `Server#attach`.
### Server#bind(srv:engine#Server):Server
Advanced use only. Binds the server to a specific engine.io `Server`
(or compatible API) instance.
### Server#onconnection(socket:engine#Socket):Server
Advanced use only. Creates a new `socket.io` client from the incoming
engine.io (or compatible API) `socket`.
### Server#of(nsp:String):Namespace
Initializes and retrieves the given `Namespace` by its pathname
identifier `nsp`.
If the namespace was already initialized it returns it right away.
### Server#emit
Emits an event to all connected clients. The following two are
equivalent:
```js
var io = require('socket.io')();
io.sockets.emit('an event sent to all connected clients');
io.emit('an event sent to all connected clients');
```
For other available methods, see `Namespace` below.
### Server#close
Closes socket server
```js
var Server = require('socket.io');
var PORT = 3030;
var server = require('http').Server();
var io = Server(PORT);
io.close(); // Close current server
server.listen(PORT); // PORT is free to use
io = Server(server);
```
### Server#use
See `Namespace#use` below.
### Namespace
Represents a pool of sockets connected under a given scope identified
by a pathname (eg: `/chat`).
By default the client always connects to `/`.
#### Events
- `connection` / `connect`. Fired upon a connection.
Parameters:
- `Socket` the incoming socket.
### Namespace#name:String
The namespace identifier property.
### Namespace#connected:Object<Socket>
Hash of `Socket` objects that are connected to this namespace indexed
by `id`.
### Namespace#use(fn:Function):Namespace
Registers a middleware, which is a function that gets executed for
every incoming `Socket` and receives as parameter the socket and a
function to optionally defer execution to the next registered
middleware.
```js
var io = require('socket.io')();
io.use(function(socket, next){
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
});
```
Errors passed to middleware callbacks are sent as special `error`
packets to clients.
### Socket
A `Socket` is the fundamental class for interacting with browser
clients. A `Socket` belongs to a certain `Namespace` (by default `/`)
and uses an underlying `Client` to communicate.
### Socket#rooms:Array
A list of strings identifying the rooms this socket is in.
### Socket#client:Client
A reference to the underlying `Client` object.
### Socket#conn:Socket
A reference to the underlying `Client` transport connection (engine.io
`Socket` object).
### Socket#request:Request
A getter proxy that returns the reference to the `request` that
originated the underlying engine.io `Client`. Useful for accessing
request headers such as `Cookie` or `User-Agent`.
### Socket#id:String
A unique identifier for the socket session, that comes from the
underlying `Client`.
### Socket#emit(name:String[, …]):Socket
Emits an event to the socket identified by the string `name`. Any
other parameters can be included.
All datastructures are supported, including `Buffer`. JavaScript
functions can't be serialized/deserialized.
```js
var io = require('socket.io')();
io.on('connection', function(socket){
socket.emit('an event', { some: 'data' });
});
```
### Socket#join(name:String[, fn:Function]):Socket
Adds the socket to the `room`, and fires optionally a callback `fn`
with `err` signature (if any).
The socket is automatically a member of a room identified with its
session id (see `Socket#id`).
The mechanics of joining rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#leave(name:String[, fn:Function]):Socket
Removes the socket from `room`, and fires optionally a callback `fn`
with `err` signature (if any).
**Rooms are left automatically upon disconnection**.
The mechanics of leaving rooms are handled by the `Adapter`
that has been configured (see `Server#adapter` above), defaulting to
[socket.io-adapter](https://github.com/Automattic/socket.io-adapter).
### Socket#to(room:String):Socket
### Socket#in(room:String):Socket
Sets a modifier for a subsequent event emission that the event will
only be _broadcasted_ to sockets that have joined the given `room`.
To emit to multiple rooms, you can call `to` several times.
```js
var io = require('socket.io')();
io.on('connection', function(socket){
socket.to('others').emit('an event', { some: 'data' });
});
```
### Client
The `Client` class represents an incoming transport (engine.io)
connection. A `Client` can be associated with many multiplexed `Socket`
that belong to different `Namespace`s.
### Client#conn
A reference to the underlying `engine.io` `Socket` connection.
### Client#request
A getter proxy that returns the reference to the `request` that
originated the engine.io connection. Useful for accessing
request headers such as `Cookie` or `User-Agent`.
The source code of the website can be found [here](https://github.com/socketio/socket.io-website). Contributions are welcome!
## Debug / logging
Socket.IO is powered by [debug](http://github.com/visionmedia/debug).
Socket.IO is powered by [debug](https://github.com/visionmedia/debug).
In order to see all the debug output, run your app with the environment variable
`DEBUG` including the desired scope.
@@ -369,6 +169,90 @@ To see the output from all of Socket.IO's debugging scopes you can use:
DEBUG=socket.io* node myapp
```
## Testing
```
npm test
```
This runs the `gulp` task `test`. By default the test will be run with the source code in `lib` directory.
Set the environmental variable `TEST_VERSION` to `compat` to test the transpiled es5-compat version of the code.
The `gulp` task `test` will always transpile the source code into es5 and export to `dist` first before running the test.
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/socketio#backer)]
<a href="https://opencollective.com/socketio/backer/0/website" target="_blank"><img src="https://opencollective.com/socketio/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/1/website" target="_blank"><img src="https://opencollective.com/socketio/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/2/website" target="_blank"><img src="https://opencollective.com/socketio/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/3/website" target="_blank"><img src="https://opencollective.com/socketio/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/4/website" target="_blank"><img src="https://opencollective.com/socketio/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/5/website" target="_blank"><img src="https://opencollective.com/socketio/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/6/website" target="_blank"><img src="https://opencollective.com/socketio/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/7/website" target="_blank"><img src="https://opencollective.com/socketio/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/8/website" target="_blank"><img src="https://opencollective.com/socketio/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/9/website" target="_blank"><img src="https://opencollective.com/socketio/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/10/website" target="_blank"><img src="https://opencollective.com/socketio/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/11/website" target="_blank"><img src="https://opencollective.com/socketio/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/12/website" target="_blank"><img src="https://opencollective.com/socketio/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/13/website" target="_blank"><img src="https://opencollective.com/socketio/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/14/website" target="_blank"><img src="https://opencollective.com/socketio/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/15/website" target="_blank"><img src="https://opencollective.com/socketio/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/16/website" target="_blank"><img src="https://opencollective.com/socketio/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/17/website" target="_blank"><img src="https://opencollective.com/socketio/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/18/website" target="_blank"><img src="https://opencollective.com/socketio/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/19/website" target="_blank"><img src="https://opencollective.com/socketio/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/20/website" target="_blank"><img src="https://opencollective.com/socketio/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/21/website" target="_blank"><img src="https://opencollective.com/socketio/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/22/website" target="_blank"><img src="https://opencollective.com/socketio/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/23/website" target="_blank"><img src="https://opencollective.com/socketio/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/24/website" target="_blank"><img src="https://opencollective.com/socketio/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/25/website" target="_blank"><img src="https://opencollective.com/socketio/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/26/website" target="_blank"><img src="https://opencollective.com/socketio/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/27/website" target="_blank"><img src="https://opencollective.com/socketio/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/28/website" target="_blank"><img src="https://opencollective.com/socketio/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/socketio/backer/29/website" target="_blank"><img src="https://opencollective.com/socketio/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/socketio#sponsor)]
<a href="https://opencollective.com/socketio/sponsor/0/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/1/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/2/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/3/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/4/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/5/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/6/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/7/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/8/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/9/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/10/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/11/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/12/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/13/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/14/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/15/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/16/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/17/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/18/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/19/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/20/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/21/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/22/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/23/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/24/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/25/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/26/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/27/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/28/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/socketio/sponsor/29/website" target="_blank"><img src="https://opencollective.com/socketio/sponsor/29/avatar.svg"></a>
## License
MIT
[MIT](LICENSE)

6161
client-dist/socket.io.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
client-dist/socket.io.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
client-dist/socket.io.msgpack.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
docs/README.md Normal file
View File

@@ -0,0 +1,2 @@
The documentation has been moved to the website [here](https://socket.io/docs/).

View File

@@ -6,11 +6,8 @@ A simple chat demo for socket.io
## How to use
```
$ cd socket.io
$ npm install
$ cd examples/chat
$ npm install
$ node .
$ npm ci
$ npm start
```
And point your browser to `http://localhost:3000`. Optionally, specify

View File

@@ -1,28 +1,27 @@
// Setup basic express server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var io = require('../..')(server);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
server.listen(port, function () {
server.listen(port, () => {
console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));
app.use(express.static(path.join(__dirname, 'public')));
// Chatroom
// usernames which are currently connected to the chat
var usernames = {};
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,
@@ -31,11 +30,11 @@ 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
socket.username = username;
// add the client's username to the global list
usernames[username] = username;
++numUsers;
addedUser = true;
socket.emit('login', {
@@ -49,24 +48,22 @@ 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 () {
// remove the username from global usernames list
socket.on('disconnect', () => {
if (addedUser) {
delete usernames[socket.username];
--numUsers;
// echo globally that this client has left

706
examples/chat/package-lock.json generated Normal file
View File

@@ -0,0 +1,706 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
"integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "0.3.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "^7.1.2"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"engine.io-client": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.1.tgz",
"integrity": "sha512-RJNmA+A9Js+8Aoq815xpGAsgWH1VoSYM//2VgIiu9lNOaHFfLpTjH4tOzktBpjIs5lvOfiNY1dwf+NuU6D38Mw==",
"requires": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.43.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
},
"mime-types": {
"version": "2.1.26",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
"requires": {
"mime-db": "1.43.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"socket.io": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
"integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"requires": {
"debug": "~4.1.0",
"engine.io": "~3.4.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"socket.io-parser": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
}
}
},
"socket.io-parser": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
"integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -7,6 +7,10 @@
"private": true,
"license": "BSD",
"dependencies": {
"express": "3.4.8"
"express": "~4.17.1",
"socket.io": "~2.3.0"
},
"scripts": {
"start": "node index.js"
}
}
}

View File

@@ -7,7 +7,7 @@ $(function() {
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize varibles
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
@@ -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) {
return $('<div/>').text(input).text();
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,30 +237,46 @@ $(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', () => {
log('you have been disconnected');
});
socket.on('reconnect', () => {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', () => {
log('attempt to reconnect has failed');
});
});

View File

@@ -128,7 +128,6 @@ ul {
}
.username {
float: left;
font-weight: 700;
overflow: hidden;
padding-right: 15px;

View File

@@ -0,0 +1,31 @@
# Socket.IO Chat with haproxy & redis
A simple chat demo for socket.io
## How to use
Install [Docker Compose](https://docs.docker.com/compose/install/), then:
```
$ docker-compose up -d
```
And then point your browser to `http://localhost:3000`.
This will start four Socket.IO nodes, behind a haproxy instance which will loadbalance the requests (using a cookie for sticky sessions, see [cookie](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-cookie)).
Each node connects to the redis backend, which will enable to broadcast to every client, no matter which node it is currently connected to.
```
# you can kill a given node, the client should reconnect to another node
$ docker-compose stop server-george
```
## Features
- Multiple users can join a chat room by each entering a unique username
on website load.
- Users can type chat messages to the chat room.
- A notification is sent to all users when a user joins or leaves
the chatroom.

View File

@@ -0,0 +1,51 @@
haproxy:
build: ./haproxy
links:
- server-john
- server-paul
- server-george
- server-ringo
ports:
- "3000:80"
server-john:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=John
server-paul:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Paul
server-george:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=George
server-ringo:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Ringo
redis:
image: redis:alpine
expose:
- "6379"

View File

@@ -0,0 +1,2 @@
FROM haproxy:1.7-alpine
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

View File

@@ -0,0 +1,31 @@
# Reference: http://blog.haproxy.com/2012/11/07/websockets-load-balancing-with-haproxy/
global
daemon
maxconn 4096
nbproc 2
defaults
mode http
balance roundrobin
option http-server-close
timeout connect 5s
timeout client 30s
timeout client-fin 30s
timeout server 30s
timeout tunnel 1h
default-server inter 1s rise 2 fall 1 on-marked-down shutdown-sessions
option forwardfor
listen chat
bind *:80
default_backend nodes
backend nodes
option httpchk HEAD /health
http-check expect status 200
cookie serverid insert
server john server-john:3000 cookie john check
server paul server-paul:3000 cookie paul check
server george server-george:3000 cookie george check
server ringo server-ringo:3000 cookie ringo check

View File

@@ -0,0 +1,15 @@
FROM mhart/alpine-node:6
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Bundle app source
COPY . /usr/src/app
EXPOSE 3000
CMD [ "npm", "start" ]

View File

@@ -0,0 +1,87 @@
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var port = process.env.PORT || 3000;
var serverName = process.env.NAME || 'Unknown';
io.adapter(redis({ host: 'redis', port: 6379 }));
server.listen(port, function () {
console.log('Server listening at port %d', port);
console.log('Hello, I\'m %s, how can I help?', serverName);
});
// Routing
app.use(express.static(__dirname + '/public'));
// Health check
app.head('/health', function (req, res) {
res.sendStatus(200);
});
// Chatroom
var numUsers = 0;
io.on('connection', function (socket) {
socket.emit('my-name-is', serverName);
var addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function () {
if (addedUser) {
--numUsers;
// echo globally that this client has left
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: numUsers
});
}
});
});

View File

@@ -0,0 +1,17 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"description": "A simple chat client using socket.io",
"main": "index.js",
"author": "Grant Timmerman",
"private": true,
"license": "BSD",
"dependencies": {
"express": "4.13.4",
"socket.io": "^1.7.2",
"socket.io-redis": "^3.0.0"
},
"scripts": {
"start": "node index.js"
}
}

View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO Chat Example</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul class="pages">
<li class="chat page">
<div class="chatArea">
<ul class="messages"></ul>
</div>
<input class="inputMessage" placeholder="Type here..."/>
</li>
<li class="login page">
<div class="form">
<h3 class="title">What's your nickname?</h3>
<input class="usernameInput" type="text" maxlength="14" />
</div>
</li>
</ul>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,286 @@
$(function() {
var FADE_TIME = 150; // ms
var TYPING_TIMER_LENGTH = 400; // ms
var COLORS = [
'#e21400', '#91580f', '#f8a700', '#f78b00',
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
var $inputMessage = $('.inputMessage'); // Input message input box
var $loginPage = $('.login.page'); // The login page
var $chatPage = $('.chat.page'); // The chatroom page
// Prompt for setting a username
var username;
var connected = false;
var typing = false;
var lastTypingTime;
var $currentInput = $usernameInput.focus();
var socket = io();
function addParticipantsMessage (data) {
var message = '';
if (data.numUsers === 1) {
message += "there's 1 participant";
} else {
message += "there are " + data.numUsers + " participants";
}
log(message);
}
// Sets the client's username
function setUsername () {
username = cleanInput($usernameInput.val().trim());
// If the username is valid
if (username) {
$loginPage.fadeOut();
$chatPage.show();
$loginPage.off('click');
$currentInput = $inputMessage.focus();
// Tell the server your username
socket.emit('add user', username);
}
}
// Sends a chat message
function sendMessage () {
var message = $inputMessage.val();
// Prevent markup from being injected into the message
message = cleanInput(message);
// if there is a non-empty message and a socket connection
if (message && connected) {
$inputMessage.val('');
addChatMessage({
username: username,
message: message
});
// tell server to execute 'new message' and send along one parameter
socket.emit('new message', message);
}
}
// Log a message
function 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) {
// Don't fade the message in if there is an 'X was typing'
var $typingMessages = getTypingMessages(data);
options = options || {};
if ($typingMessages.length !== 0) {
options.fade = false;
$typingMessages.remove();
}
var $usernameDiv = $('<span class="username"/>')
.text(data.username)
.css('color', getUsernameColor(data.username));
var $messageBodyDiv = $('<span class="messageBody">')
.text(data.message);
var typingClass = data.typing ? 'typing' : '';
var $messageDiv = $('<li class="message"/>')
.data('username', data.username)
.addClass(typingClass)
.append($usernameDiv, $messageBodyDiv);
addMessageElement($messageDiv, options);
}
// Adds the visual chat typing message
function addChatTyping (data) {
data.typing = true;
data.message = 'is typing';
addChatMessage(data);
}
// Removes the visual chat typing message
function removeChatTyping (data) {
getTypingMessages(data).fadeOut(function () {
$(this).remove();
});
}
// Adds a message element to the messages and scrolls to the bottom
// el - The element to add as a message
// 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) {
var $el = $(el);
// Setup default options
if (!options) {
options = {};
}
if (typeof options.fade === 'undefined') {
options.fade = true;
}
if (typeof options.prepend === 'undefined') {
options.prepend = false;
}
// Apply options
if (options.fade) {
$el.hide().fadeIn(FADE_TIME);
}
if (options.prepend) {
$messages.prepend($el);
} else {
$messages.append($el);
}
$messages[0].scrollTop = $messages[0].scrollHeight;
}
// Prevents input from having injected markup
function cleanInput (input) {
return $('<div/>').text(input).text();
}
// Updates the typing event
function updateTyping () {
if (connected) {
if (!typing) {
typing = true;
socket.emit('typing');
}
lastTypingTime = (new Date()).getTime();
setTimeout(function () {
var typingTimer = (new Date()).getTime();
var timeDiff = typingTimer - lastTypingTime;
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
socket.emit('stop typing');
typing = false;
}
}, TYPING_TIMER_LENGTH);
}
}
// Gets the 'X is typing' messages of a user
function 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) {
// Compute hash code
var hash = 7;
for (var i = 0; i < username.length; i++) {
hash = username.charCodeAt(i) + (hash << 5) - hash;
}
// Calculate color
var index = Math.abs(hash % COLORS.length);
return COLORS[index];
}
// Keyboard events
$window.keydown(function (event) {
// Auto-focus the current input when a key is typed
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
$currentInput.focus();
}
// When the client hits ENTER on their keyboard
if (event.which === 13) {
if (username) {
sendMessage();
socket.emit('stop typing');
typing = false;
} else {
setUsername();
}
}
});
$inputMessage.on('input', function() {
updateTyping();
});
// Click events
// Focus input when clicking anywhere on login page
$loginPage.click(function () {
$currentInput.focus();
});
// Focus input when clicking on the message input's border
$inputMessage.click(function () {
$inputMessage.focus();
});
// Socket events
// Whenever the server emits 'login', log the login message
socket.on('login', function (data) {
connected = true;
// Display the welcome message
var message = "Welcome to Socket.IO Chat ";
log(message, {
prepend: true
});
addParticipantsMessage(data);
});
// Whenever the server emits 'new message', update the chat body
socket.on('new message', function (data) {
addChatMessage(data);
});
// Whenever the server emits 'user joined', log it in the chat body
socket.on('user joined', function (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) {
log(data.username + ' left');
addParticipantsMessage(data);
removeChatTyping(data);
});
// Whenever the server emits 'typing', show the typing message
socket.on('typing', function (data) {
addChatTyping(data);
});
// Whenever the server emits 'stop typing', kill the typing message
socket.on('stop typing', function (data) {
removeChatTyping(data);
});
socket.on('disconnect', function () {
log('you have been disconnected');
});
socket.on('reconnect', function () {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', function () {
log('attempt to reconnect has failed');
});
socket.on('my-name-is', function (serverName) {
log('host is now ' + serverName);
})
});

View File

@@ -0,0 +1,149 @@
/* Fix user-agent */
* {
box-sizing: border-box;
}
html {
font-weight: 300;
-webkit-font-smoothing: antialiased;
}
html, input {
font-family:
"HelveticaNeue-Light",
"Helvetica Neue Light",
"Helvetica Neue",
Helvetica,
Arial,
"Lucida Grande",
sans-serif;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
ul {
list-style: none;
word-wrap: break-word;
}
/* Pages */
.pages {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
.page {
height: 100%;
position: absolute;
width: 100%;
}
/* Login Page */
.login.page {
background-color: #000;
}
.login.page .form {
height: 100px;
margin-top: -100px;
position: absolute;
text-align: center;
top: 50%;
width: 100%;
}
.login.page .form .usernameInput {
background-color: transparent;
border: none;
border-bottom: 2px solid #fff;
outline: none;
padding-bottom: 15px;
text-align: center;
width: 400px;
}
.login.page .title {
font-size: 200%;
}
.login.page .usernameInput {
font-size: 200%;
letter-spacing: 3px;
}
.login.page .title, .login.page .usernameInput {
color: #fff;
font-weight: 100;
}
/* Chat page */
.chat.page {
display: none;
}
/* Font */
.messages {
font-size: 150%;
}
.inputMessage {
font-size: 100%;
}
.log {
color: gray;
font-size: 70%;
margin: 5px;
text-align: center;
}
/* Messages */
.chatArea {
height: 100%;
padding-bottom: 60px;
}
.messages {
height: 100%;
margin: 0;
overflow-y: scroll;
padding: 10px 20px 10px 20px;
}
.message.typing .messageBody {
color: gray;
}
.username {
font-weight: 700;
overflow: hidden;
padding-right: 15px;
text-align: right;
}
/* Input */
.inputMessage {
border: 10px solid #000;
bottom: 0;
height: 60px;
left: 0;
outline: none;
padding-left: 10px;
position: absolute;
right: 0;
width: 100%;
}

View File

@@ -0,0 +1,31 @@
# Socket.IO Chat with httpd & redis
A simple chat demo for socket.io
## How to use
Install [Docker Compose](https://docs.docker.com/compose/install/), then:
```
$ docker-compose up -d
```
And then point your browser to `http://localhost:3000`.
This will start four Socket.IO nodes, behind a httpd proxy which will loadbalance the requests (using a cookie for sticky sessions, see [cookie](http://httpd.apache.org/docs/2.4/fr/mod/mod_proxy_balancer.html)).
Each node connects to the redis backend, which will enable to broadcast to every client, no matter which node it is currently connected to.
```
# you can kill a given node, the client should reconnect to another node
$ docker-compose stop server-george
```
## Features
- Multiple users can join a chat room by each entering a unique username
on website load.
- Users can type chat messages to the chat room.
- A notification is sent to all users when a user joins or leaves
the chatroom.

View File

@@ -0,0 +1,51 @@
httpd:
build: ./httpd
links:
- server-john
- server-paul
- server-george
- server-ringo
ports:
- "3000:80"
server-john:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=John
server-paul:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Paul
server-george:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=George
server-ringo:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Ringo
redis:
image: redis:alpine
expose:
- "6379"

View File

@@ -0,0 +1,2 @@
FROM httpd:2.4-alpine
COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf

View File

@@ -0,0 +1,52 @@
Listen 80
ServerName localhost
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule headers_module modules/mod_headers.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule unixd_module modules/mod_unixd.so
User daemon
Group daemon
ErrorLog /proc/self/fd/2
Header add Set-Cookie "SERVERID=sticky.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<Proxy "balancer://nodes_polling">
BalancerMember "http://server-john:3000" route=john
BalancerMember "http://server-paul:3000" route=paul
BalancerMember "http://server-george:3000" route=george
BalancerMember "http://server-ringo:3000" route=ringo
ProxySet stickysession=SERVERID
</Proxy>
<Proxy "balancer://nodes_ws">
BalancerMember "ws://server-john:3000" route=john
BalancerMember "ws://server-paul:3000" route=paul
BalancerMember "ws://server-george:3000" route=george
BalancerMember "ws://server-ringo:3000" route=ringo
ProxySet stickysession=SERVERID
</Proxy>
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) balancer://nodes_ws/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) balancer://nodes_polling/$1 [P,L]
ProxyTimeout 3

View File

@@ -0,0 +1,15 @@
FROM mhart/alpine-node:6
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Bundle app source
COPY . /usr/src/app
EXPOSE 3000
CMD [ "npm", "start" ]

View File

@@ -0,0 +1,82 @@
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var port = process.env.PORT || 3000;
var serverName = process.env.NAME || 'Unknown';
io.adapter(redis({ host: 'redis', port: 6379 }));
server.listen(port, function () {
console.log('Server listening at port %d', port);
console.log('Hello, I\'m %s, how can I help?', serverName);
});
// Routing
app.use(express.static(__dirname + '/public'));
// Chatroom
var numUsers = 0;
io.on('connection', function (socket) {
socket.emit('my-name-is', serverName);
var addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function () {
if (addedUser) {
--numUsers;
// echo globally that this client has left
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: numUsers
});
}
});
});

View File

@@ -0,0 +1,17 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"description": "A simple chat client using socket.io",
"main": "index.js",
"author": "Grant Timmerman",
"private": true,
"license": "BSD",
"dependencies": {
"express": "4.13.4",
"socket.io": "^1.7.2",
"socket.io-redis": "^3.0.0"
},
"scripts": {
"start": "node index.js"
}
}

View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO Chat Example</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul class="pages">
<li class="chat page">
<div class="chatArea">
<ul class="messages"></ul>
</div>
<input class="inputMessage" placeholder="Type here..."/>
</li>
<li class="login page">
<div class="form">
<h3 class="title">What's your nickname?</h3>
<input class="usernameInput" type="text" maxlength="14" />
</div>
</li>
</ul>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,286 @@
$(function() {
var FADE_TIME = 150; // ms
var TYPING_TIMER_LENGTH = 400; // ms
var COLORS = [
'#e21400', '#91580f', '#f8a700', '#f78b00',
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
var $inputMessage = $('.inputMessage'); // Input message input box
var $loginPage = $('.login.page'); // The login page
var $chatPage = $('.chat.page'); // The chatroom page
// Prompt for setting a username
var username;
var connected = false;
var typing = false;
var lastTypingTime;
var $currentInput = $usernameInput.focus();
var socket = io();
function addParticipantsMessage (data) {
var message = '';
if (data.numUsers === 1) {
message += "there's 1 participant";
} else {
message += "there are " + data.numUsers + " participants";
}
log(message);
}
// Sets the client's username
function setUsername () {
username = cleanInput($usernameInput.val().trim());
// If the username is valid
if (username) {
$loginPage.fadeOut();
$chatPage.show();
$loginPage.off('click');
$currentInput = $inputMessage.focus();
// Tell the server your username
socket.emit('add user', username);
}
}
// Sends a chat message
function sendMessage () {
var message = $inputMessage.val();
// Prevent markup from being injected into the message
message = cleanInput(message);
// if there is a non-empty message and a socket connection
if (message && connected) {
$inputMessage.val('');
addChatMessage({
username: username,
message: message
});
// tell server to execute 'new message' and send along one parameter
socket.emit('new message', message);
}
}
// Log a message
function 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) {
// Don't fade the message in if there is an 'X was typing'
var $typingMessages = getTypingMessages(data);
options = options || {};
if ($typingMessages.length !== 0) {
options.fade = false;
$typingMessages.remove();
}
var $usernameDiv = $('<span class="username"/>')
.text(data.username)
.css('color', getUsernameColor(data.username));
var $messageBodyDiv = $('<span class="messageBody">')
.text(data.message);
var typingClass = data.typing ? 'typing' : '';
var $messageDiv = $('<li class="message"/>')
.data('username', data.username)
.addClass(typingClass)
.append($usernameDiv, $messageBodyDiv);
addMessageElement($messageDiv, options);
}
// Adds the visual chat typing message
function addChatTyping (data) {
data.typing = true;
data.message = 'is typing';
addChatMessage(data);
}
// Removes the visual chat typing message
function removeChatTyping (data) {
getTypingMessages(data).fadeOut(function () {
$(this).remove();
});
}
// Adds a message element to the messages and scrolls to the bottom
// el - The element to add as a message
// 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) {
var $el = $(el);
// Setup default options
if (!options) {
options = {};
}
if (typeof options.fade === 'undefined') {
options.fade = true;
}
if (typeof options.prepend === 'undefined') {
options.prepend = false;
}
// Apply options
if (options.fade) {
$el.hide().fadeIn(FADE_TIME);
}
if (options.prepend) {
$messages.prepend($el);
} else {
$messages.append($el);
}
$messages[0].scrollTop = $messages[0].scrollHeight;
}
// Prevents input from having injected markup
function cleanInput (input) {
return $('<div/>').text(input).text();
}
// Updates the typing event
function updateTyping () {
if (connected) {
if (!typing) {
typing = true;
socket.emit('typing');
}
lastTypingTime = (new Date()).getTime();
setTimeout(function () {
var typingTimer = (new Date()).getTime();
var timeDiff = typingTimer - lastTypingTime;
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
socket.emit('stop typing');
typing = false;
}
}, TYPING_TIMER_LENGTH);
}
}
// Gets the 'X is typing' messages of a user
function 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) {
// Compute hash code
var hash = 7;
for (var i = 0; i < username.length; i++) {
hash = username.charCodeAt(i) + (hash << 5) - hash;
}
// Calculate color
var index = Math.abs(hash % COLORS.length);
return COLORS[index];
}
// Keyboard events
$window.keydown(function (event) {
// Auto-focus the current input when a key is typed
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
$currentInput.focus();
}
// When the client hits ENTER on their keyboard
if (event.which === 13) {
if (username) {
sendMessage();
socket.emit('stop typing');
typing = false;
} else {
setUsername();
}
}
});
$inputMessage.on('input', function() {
updateTyping();
});
// Click events
// Focus input when clicking anywhere on login page
$loginPage.click(function () {
$currentInput.focus();
});
// Focus input when clicking on the message input's border
$inputMessage.click(function () {
$inputMessage.focus();
});
// Socket events
// Whenever the server emits 'login', log the login message
socket.on('login', function (data) {
connected = true;
// Display the welcome message
var message = "Welcome to Socket.IO Chat ";
log(message, {
prepend: true
});
addParticipantsMessage(data);
});
// Whenever the server emits 'new message', update the chat body
socket.on('new message', function (data) {
addChatMessage(data);
});
// Whenever the server emits 'user joined', log it in the chat body
socket.on('user joined', function (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) {
log(data.username + ' left');
addParticipantsMessage(data);
removeChatTyping(data);
});
// Whenever the server emits 'typing', show the typing message
socket.on('typing', function (data) {
addChatTyping(data);
});
// Whenever the server emits 'stop typing', kill the typing message
socket.on('stop typing', function (data) {
removeChatTyping(data);
});
socket.on('disconnect', function () {
log('you have been disconnected');
});
socket.on('reconnect', function () {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', function () {
log('attempt to reconnect has failed');
});
socket.on('my-name-is', function (serverName) {
log('host is now ' + serverName);
})
});

View File

@@ -0,0 +1,149 @@
/* Fix user-agent */
* {
box-sizing: border-box;
}
html {
font-weight: 300;
-webkit-font-smoothing: antialiased;
}
html, input {
font-family:
"HelveticaNeue-Light",
"Helvetica Neue Light",
"Helvetica Neue",
Helvetica,
Arial,
"Lucida Grande",
sans-serif;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
ul {
list-style: none;
word-wrap: break-word;
}
/* Pages */
.pages {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
.page {
height: 100%;
position: absolute;
width: 100%;
}
/* Login Page */
.login.page {
background-color: #000;
}
.login.page .form {
height: 100px;
margin-top: -100px;
position: absolute;
text-align: center;
top: 50%;
width: 100%;
}
.login.page .form .usernameInput {
background-color: transparent;
border: none;
border-bottom: 2px solid #fff;
outline: none;
padding-bottom: 15px;
text-align: center;
width: 400px;
}
.login.page .title {
font-size: 200%;
}
.login.page .usernameInput {
font-size: 200%;
letter-spacing: 3px;
}
.login.page .title, .login.page .usernameInput {
color: #fff;
font-weight: 100;
}
/* Chat page */
.chat.page {
display: none;
}
/* Font */
.messages {
font-size: 150%;
}
.inputMessage {
font-size: 100%;
}
.log {
color: gray;
font-size: 70%;
margin: 5px;
text-align: center;
}
/* Messages */
.chatArea {
height: 100%;
padding-bottom: 60px;
}
.messages {
height: 100%;
margin: 0;
overflow-y: scroll;
padding: 10px 20px 10px 20px;
}
.message.typing .messageBody {
color: gray;
}
.username {
font-weight: 700;
overflow: hidden;
padding-right: 15px;
text-align: right;
}
/* Input */
.inputMessage {
border: 10px solid #000;
bottom: 0;
height: 60px;
left: 0;
outline: none;
padding-left: 10px;
position: absolute;
right: 0;
width: 100%;
}

View File

@@ -0,0 +1,31 @@
# Socket.IO Chat with nginx & redis
A simple chat demo for socket.io
## How to use
Install [Docker Compose](https://docs.docker.com/compose/install/), then:
```
$ docker-compose up -d
```
And then point your browser to `http://localhost:3000`.
This will start four Socket.IO nodes, behind a nginx proxy which will loadbalance the requests (using the IP of the client, see [ip_hash](http://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash)).
Each node connects to the redis backend, which will enable to broadcast to every client, no matter which node it is currently connected to.
```
# you can kill a given node, the client should reconnect to another node
$ docker-compose stop server-george
```
## Features
- Multiple users can join a chat room by each entering a unique username
on website load.
- Users can type chat messages to the chat room.
- A notification is sent to all users when a user joins or leaves
the chatroom.

View File

@@ -0,0 +1,51 @@
nginx:
build: ./nginx
links:
- server-john
- server-paul
- server-george
- server-ringo
ports:
- "3000:80"
server-john:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=John
server-paul:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Paul
server-george:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=George
server-ringo:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Ringo
redis:
image: redis:alpine
expose:
- "6379"

View File

@@ -0,0 +1,3 @@
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

View File

@@ -0,0 +1,35 @@
# Reference: https://www.nginx.com/resources/wiki/start/topics/examples/full/
worker_processes 4;
events {
worker_connections 1024;
}
http {
server {
listen 80;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://nodes;
# enable WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
upstream nodes {
# enable sticky session
ip_hash;
server server-john:3000;
server server-paul:3000;
server server-george:3000;
server server-ringo:3000;
}
}

View File

@@ -0,0 +1,15 @@
FROM mhart/alpine-node:6
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Bundle app source
COPY . /usr/src/app
EXPOSE 3000
CMD [ "npm", "start" ]

View File

@@ -0,0 +1,82 @@
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var port = process.env.PORT || 3000;
var serverName = process.env.NAME || 'Unknown';
io.adapter(redis({ host: 'redis', port: 6379 }));
server.listen(port, function () {
console.log('Server listening at port %d', port);
console.log('Hello, I\'m %s, how can I help?', serverName);
});
// Routing
app.use(express.static(__dirname + '/public'));
// Chatroom
var numUsers = 0;
io.on('connection', function (socket) {
socket.emit('my-name-is', serverName);
var addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
if (addedUser) return;
// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function () {
if (addedUser) {
--numUsers;
// echo globally that this client has left
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: numUsers
});
}
});
});

View File

@@ -0,0 +1,17 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"description": "A simple chat client using socket.io",
"main": "index.js",
"author": "Grant Timmerman",
"private": true,
"license": "MIT",
"dependencies": {
"express": "4.13.4",
"socket.io": "^1.7.2",
"socket.io-redis": "^3.0.0"
},
"scripts": {
"start": "node index.js"
}
}

View File

@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO Chat Example</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul class="pages">
<li class="chat page">
<div class="chatArea">
<ul class="messages"></ul>
</div>
<input class="inputMessage" placeholder="Type here..."/>
</li>
<li class="login page">
<div class="form">
<h3 class="title">What's your nickname?</h3>
<input class="usernameInput" type="text" maxlength="14" />
</div>
</li>
</ul>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,286 @@
$(function() {
var FADE_TIME = 150; // ms
var TYPING_TIMER_LENGTH = 400; // ms
var COLORS = [
'#e21400', '#91580f', '#f8a700', '#f78b00',
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
var $inputMessage = $('.inputMessage'); // Input message input box
var $loginPage = $('.login.page'); // The login page
var $chatPage = $('.chat.page'); // The chatroom page
// Prompt for setting a username
var username;
var connected = false;
var typing = false;
var lastTypingTime;
var $currentInput = $usernameInput.focus();
var socket = io();
function addParticipantsMessage (data) {
var message = '';
if (data.numUsers === 1) {
message += "there's 1 participant";
} else {
message += "there are " + data.numUsers + " participants";
}
log(message);
}
// Sets the client's username
function setUsername () {
username = cleanInput($usernameInput.val().trim());
// If the username is valid
if (username) {
$loginPage.fadeOut();
$chatPage.show();
$loginPage.off('click');
$currentInput = $inputMessage.focus();
// Tell the server your username
socket.emit('add user', username);
}
}
// Sends a chat message
function sendMessage () {
var message = $inputMessage.val();
// Prevent markup from being injected into the message
message = cleanInput(message);
// if there is a non-empty message and a socket connection
if (message && connected) {
$inputMessage.val('');
addChatMessage({
username: username,
message: message
});
// tell server to execute 'new message' and send along one parameter
socket.emit('new message', message);
}
}
// Log a message
function 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) {
// Don't fade the message in if there is an 'X was typing'
var $typingMessages = getTypingMessages(data);
options = options || {};
if ($typingMessages.length !== 0) {
options.fade = false;
$typingMessages.remove();
}
var $usernameDiv = $('<span class="username"/>')
.text(data.username)
.css('color', getUsernameColor(data.username));
var $messageBodyDiv = $('<span class="messageBody">')
.text(data.message);
var typingClass = data.typing ? 'typing' : '';
var $messageDiv = $('<li class="message"/>')
.data('username', data.username)
.addClass(typingClass)
.append($usernameDiv, $messageBodyDiv);
addMessageElement($messageDiv, options);
}
// Adds the visual chat typing message
function addChatTyping (data) {
data.typing = true;
data.message = 'is typing';
addChatMessage(data);
}
// Removes the visual chat typing message
function removeChatTyping (data) {
getTypingMessages(data).fadeOut(function () {
$(this).remove();
});
}
// Adds a message element to the messages and scrolls to the bottom
// el - The element to add as a message
// 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) {
var $el = $(el);
// Setup default options
if (!options) {
options = {};
}
if (typeof options.fade === 'undefined') {
options.fade = true;
}
if (typeof options.prepend === 'undefined') {
options.prepend = false;
}
// Apply options
if (options.fade) {
$el.hide().fadeIn(FADE_TIME);
}
if (options.prepend) {
$messages.prepend($el);
} else {
$messages.append($el);
}
$messages[0].scrollTop = $messages[0].scrollHeight;
}
// Prevents input from having injected markup
function cleanInput (input) {
return $('<div/>').text(input).text();
}
// Updates the typing event
function updateTyping () {
if (connected) {
if (!typing) {
typing = true;
socket.emit('typing');
}
lastTypingTime = (new Date()).getTime();
setTimeout(function () {
var typingTimer = (new Date()).getTime();
var timeDiff = typingTimer - lastTypingTime;
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
socket.emit('stop typing');
typing = false;
}
}, TYPING_TIMER_LENGTH);
}
}
// Gets the 'X is typing' messages of a user
function 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) {
// Compute hash code
var hash = 7;
for (var i = 0; i < username.length; i++) {
hash = username.charCodeAt(i) + (hash << 5) - hash;
}
// Calculate color
var index = Math.abs(hash % COLORS.length);
return COLORS[index];
}
// Keyboard events
$window.keydown(function (event) {
// Auto-focus the current input when a key is typed
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
$currentInput.focus();
}
// When the client hits ENTER on their keyboard
if (event.which === 13) {
if (username) {
sendMessage();
socket.emit('stop typing');
typing = false;
} else {
setUsername();
}
}
});
$inputMessage.on('input', function() {
updateTyping();
});
// Click events
// Focus input when clicking anywhere on login page
$loginPage.click(function () {
$currentInput.focus();
});
// Focus input when clicking on the message input's border
$inputMessage.click(function () {
$inputMessage.focus();
});
// Socket events
// Whenever the server emits 'login', log the login message
socket.on('login', function (data) {
connected = true;
// Display the welcome message
var message = "Welcome to Socket.IO Chat ";
log(message, {
prepend: true
});
addParticipantsMessage(data);
});
// Whenever the server emits 'new message', update the chat body
socket.on('new message', function (data) {
addChatMessage(data);
});
// Whenever the server emits 'user joined', log it in the chat body
socket.on('user joined', function (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) {
log(data.username + ' left');
addParticipantsMessage(data);
removeChatTyping(data);
});
// Whenever the server emits 'typing', show the typing message
socket.on('typing', function (data) {
addChatTyping(data);
});
// Whenever the server emits 'stop typing', kill the typing message
socket.on('stop typing', function (data) {
removeChatTyping(data);
});
socket.on('disconnect', function () {
log('you have been disconnected');
});
socket.on('reconnect', function () {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', function () {
log('attempt to reconnect has failed');
});
socket.on('my-name-is', function (serverName) {
log('host is now ' + serverName);
})
});

View File

@@ -0,0 +1,149 @@
/* Fix user-agent */
* {
box-sizing: border-box;
}
html {
font-weight: 300;
-webkit-font-smoothing: antialiased;
}
html, input {
font-family:
"HelveticaNeue-Light",
"Helvetica Neue Light",
"Helvetica Neue",
Helvetica,
Arial,
"Lucida Grande",
sans-serif;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
ul {
list-style: none;
word-wrap: break-word;
}
/* Pages */
.pages {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
.page {
height: 100%;
position: absolute;
width: 100%;
}
/* Login Page */
.login.page {
background-color: #000;
}
.login.page .form {
height: 100px;
margin-top: -100px;
position: absolute;
text-align: center;
top: 50%;
width: 100%;
}
.login.page .form .usernameInput {
background-color: transparent;
border: none;
border-bottom: 2px solid #fff;
outline: none;
padding-bottom: 15px;
text-align: center;
width: 400px;
}
.login.page .title {
font-size: 200%;
}
.login.page .usernameInput {
font-size: 200%;
letter-spacing: 3px;
}
.login.page .title, .login.page .usernameInput {
color: #fff;
font-weight: 100;
}
/* Chat page */
.chat.page {
display: none;
}
/* Font */
.messages {
font-size: 150%;
}
.inputMessage {
font-size: 100%;
}
.log {
color: gray;
font-size: 70%;
margin: 5px;
text-align: center;
}
/* Messages */
.chatArea {
height: 100%;
padding-bottom: 60px;
}
.messages {
height: 100%;
margin: 0;
overflow-y: scroll;
padding: 10px 20px 10px 20px;
}
.message.typing .messageBody {
color: gray;
}
.username {
font-weight: 700;
overflow: hidden;
padding-right: 15px;
text-align: right;
}
/* Input */
.inputMessage {
border: 10px solid #000;
bottom: 0;
height: 60px;
left: 0;
outline: none;
padding-left: 10px;
position: absolute;
right: 0;
width: 100%;
}

View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,72 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
### `yarn start-server`
Starts the Socket.IO server.
### `yarn test`
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you cant go back!**
If you arent satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `yarn build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

View File

@@ -0,0 +1,37 @@
{
"name": "create-react-app-example",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"start-server": "node server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View File

@@ -0,0 +1,19 @@
const io = require('socket.io')();
io.on('connection', socket => {
console.log(`connect: ${socket.id}`);
socket.on('hello!', () => {
console.log(`hello from ${socket.id}`);
});
socket.on('disconnect', () => {
console.log(`disconnect: ${socket.id}`);
});
});
io.listen(3001);
setInterval(() => {
io.emit('message', new Date().toISOString());
}, 1000);

View File

@@ -0,0 +1,38 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,43 @@
import React, { useState, useEffect } from 'react';
import './App.css';
import io from 'socket.io-client';
const socket = io('localhost:3001');
function App() {
const [isConnected, setIsConnected] = useState(socket.connected);
const [lastMessage, setLastMessage] = useState(null);
useEffect(() => {
socket.on('connect', () => {
setIsConnected(true);
});
socket.on('disconnect', () => {
setIsConnected(false);
});
socket.on('message', data => {
setLastMessage(data);
});
return () => {
socket.off('connect');
socket.off('disconnect');
socket.off('message');
};
});
const sendMessage = () => {
socket.emit('hello!');
}
return (
<div className="App">
<header className="App-header">
<p>Connected: { '' + isConnected }</p>
<p>Last message: { lastMessage || '-' }</p>
<button onClick={ sendMessage }>Say hello!</button>
</header>
</div>
);
}
export default App;

View File

@@ -0,0 +1,9 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View File

@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,141 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then(registration => {
registration.unregister();
})
.catch(error => {
console.error(error.message);
});
}
}

View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
# Socket.IO custom parsers
Since Socket.IO version 2.0.0, you can provide your custom parser, according to the needs of your application.
Several parsers are showcased here:
- the default one: [socket.io-parser](https://github.com/socketio/socket.io-parser)
- one based on msgpack: [socket.io-msgpack-parser](https://github.com/darrachequesne/socket.io-msgpack-parser)
- one based on native JSON: [socket.io-json-parser](https://github.com/darrachequesne/socket.io-json-parser)
- a custom one based on [schemapack](https://github.com/phretaddin/schemapack)
They are tested with various payloads:
- string: `['1', '2', ... '1000']`
- numeric: `[1, 2, ... 1000]`
- binary: `Buffer.allocUnsafe(1000), where buf[i] = i`
## How to use
```
$ npm i && npm start
```
## Results
| bytes / packet | CONNECT packet | string | numeric | binary |
|----------------|----------------|--------|---------|-----------|
| default | 1 | 5903 | 3904 | 43 + 1000 |
| msgpack | 20 | 3919 | 2646 | 1029 |
| JSON | 20 | 5930 | 3931 | 3625 |
| schemapack | 20 | 3895 | 2005 | 1005 |
## Comparison
`default parser`
- supports any serializable datastructure, including Blob and File
- **but** binary payload is encoded as 2 packets
`msgpack`
- the size of payloads containing mostly numeric values will be greatly reduced
- **but** rely on [ArrayBuffer](https://caniuse.com/#feat=typedarrays) in the browser (IE > 9)
`JSON`
- optimized
- **but** does not support binary payloads
`schemapack`
- the most efficient in both speed and size
- **but** you have to provide a schema for each packet

View File

@@ -0,0 +1,21 @@
{
"name": "parsers",
"version": "1.0.0",
"description": "Various socket.io parsers",
"scripts": {
"build": "webpack --config ./support/webpack.config.js",
"start": "npm run build && node ./src/server.js"
},
"author": "Damien Arrachequesne",
"license": "MIT",
"dependencies": {
"component-emitter": "^1.2.1",
"express": "^4.15.2",
"schemapack": "^1.4.2",
"socket.io": "socketio/socket.io",
"socket.io-client": "socketio/socket.io-client",
"socket.io-json-parser": "^1.0.0",
"socket.io-msgpack-parser": "^1.0.0",
"webpack": "^2.4.1"
}
}

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO custom parsers</title>
</head>
<body>
<script src="client1.bundle.js"></script>
<script src="client2.bundle.js"></script>
<script src="client3.bundle.js"></script>
<script src="client4.bundle.js"></script>
</body>
</html>

View File

@@ -0,0 +1,8 @@
const socket = require('socket.io-client')('localhost:3001', {});
socket.io.engine.on('data', (data) => console.log('[default]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
socket.on('string', (data) => console.log('[default] [string]', data));
socket.on('numeric', (data) => console.log('[default] [numeric]', data));
socket.on('binary', (data) => console.log('[default] [binary]', data));

View File

@@ -0,0 +1,11 @@
const customParser = require('socket.io-msgpack-parser');
const socket = require('socket.io-client')('http://localhost:3002', {
parser: customParser
});
socket.io.engine.on('data', (data) => console.log('[msgpack]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
socket.on('string', (data) => console.log('[msgpack] [string]', data));
socket.on('numeric', (data) => console.log('[msgpack] [numeric]', data));
socket.on('binary', (data) => console.log('[msgpack] [binary]', data));

View File

@@ -0,0 +1,11 @@
const customParser = require('socket.io-json-parser');
const socket = require('socket.io-client')('localhost:3003', {
parser: customParser
});
socket.io.engine.on('data', (data) => console.log('[json]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
socket.on('string', (data) => console.log('[json] [string]', data));
socket.on('numeric', (data) => console.log('[json] [numeric]', data));
socket.on('binary', (data) => console.log('[json] [binary]', data));

View File

@@ -0,0 +1,11 @@
const customParser = require('./custom-parser');
const socket = require('socket.io-client')('localhost:3004', {
parser: customParser
});
socket.io.engine.on('data', (data) => console.log('[custom]' + ' size= ' + (typeof data === 'string' ? data.length : data.byteLength)));
socket.on('string', (data) => console.log('[custom] [string]', data));
socket.on('numeric', (data) => console.log('[custom] [numeric]', data));
socket.on('binary', (data) => console.log('[custom] [binary]', data));

View File

@@ -0,0 +1,125 @@
const Emitter = require('component-emitter');
const schemapack = require('schemapack');
/**
* Packet types (see https://github.com/socketio/socket.io-protocol)
*/
const TYPES = {
CONNECT: 0,
DISCONNECT: 1,
EVENT: 2,
ACK: 3,
ERROR: 4,
BINARY_EVENT: 5,
BINARY_ACK: 6
};
const stringSchema = schemapack.build({
_id: 'uint8',
data: [ 'string' ],
nsp: 'string'
});
const numericSchema = schemapack.build({
_id: 'uint8',
data: [ 'uint16' ],
nsp: 'string'
});
const binarySchema = schemapack.build({
_id: 'uint8',
data: 'buffer',
nsp: 'string'
});
const errorPacket = {
type: TYPES.ERROR,
data: 'parser error'
};
class Encoder {
encode (packet, callback) {
switch (packet.type) {
case TYPES.EVENT:
return callback([ this.pack(packet) ]);
default:
return callback([ JSON.stringify(packet) ]);
}
}
pack (packet) {
let eventName = packet.data[0];
let flatPacket = {
data: packet.data[1],
nsp: packet.nsp
};
switch (eventName) {
case 'string':
flatPacket._id = 1;
return stringSchema.encode(flatPacket);
case 'numeric':
flatPacket._id = 2;
return numericSchema.encode(flatPacket);
case 'binary':
flatPacket._id = 3;
return binarySchema.encode(flatPacket);
default:
throw new Error('unknown event name: ' + eventName);
}
}
}
class Decoder extends Emitter {
add (obj) {
if (typeof obj === 'string') {
this.parseJSON(obj);
} else {
this.parseBinary(obj);
}
}
parseJSON (obj) {
try {
let decoded = JSON.parse(obj);
this.emit('decoded', decoded);
} catch (e) {
this.emit('decoded', errorPacket);
}
}
parseBinary (obj) {
let view = new Uint8Array(obj);
let packetId = view[0];
try {
let packet = {
type: TYPES.EVENT
};
let decoded;
switch (packetId) {
case 1:
decoded = stringSchema.decode(obj);
packet.data = [ 'string', decoded.data ];
packet.nsp = decoded.nsp;
break;
case 2:
decoded = numericSchema.decode(obj);
packet.data = [ 'numeric', decoded.data ];
packet.nsp = decoded.nsp;
break;
case 3:
decoded = binarySchema.decode(obj);
packet.data = [ 'binary', decoded.data.buffer ];
packet.nsp = decoded.nsp;
break;
default:
throw new Error('unknown type');
}
this.emit('decoded', packet);
} catch (e) {
this.emit('decoded', errorPacket);
}
}
destroy () {}
}
exports.Encoder = Encoder;
exports.Decoder = Decoder;

View File

@@ -0,0 +1,55 @@
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const path = require('path');
const port = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, '../public')));
server.listen(port, () => console.log('>>> http://localhost:' + port));
const io = require('socket.io');
const msgpackParser = require('socket.io-msgpack-parser');
const jsonParser = require('socket.io-json-parser');
const customParser = require('./custom-parser');
let server1 = io(3001, {});
let server2 = io(3002, {
parser: msgpackParser
});
let server3 = io(3003, {
parser: jsonParser
});
let server4 = io(3004, {
parser: customParser
});
let string = [];
let numeric = [];
let binary = Buffer.allocUnsafe(1e3);
for (var i = 0; i < 1e3; i++) {
string.push('' + i);
numeric.push(i);
binary[i] = i;
}
server1.on('connect', onConnect(1000));
server2.on('connect', onConnect(2000));
server3.on('connect', onConnect(3000));
server4.on('connect', onConnect(4000));
function onConnect (delay) {
return function (socket) {
console.log('connect ' + socket.id);
setTimeout(() => {
socket.emit('string', string);
socket.emit('numeric', numeric);
socket.emit('binary', binary);
}, delay);
socket.on('disconnect', () => console.log('disconnect ' + socket.id));
};
}

View File

@@ -0,0 +1,15 @@
const path = require('path');
module.exports = {
entry: {
client1: './src/client1.js',
client2: './src/client2.js',
client3: './src/client3.js',
client4: './src/client4.js'
},
output: {
path: path.resolve(__dirname, '../public'),
filename: '[name].bundle.js'
}
};

View File

@@ -0,0 +1,17 @@
# Example with [ES modules](https://nodejs.org/api/esm.html)
## How to use
```
# install the dependencies
$ npm ci
# start the server
$ node server.js
# start the client
$ node client.js
```
You need Node.js `>=12.17.0`.

View File

@@ -0,0 +1,18 @@
import { Manager } from "socket.io-client";
const manager = new Manager("ws://localhost:8080");
const socket = manager.socket("/");
socket.on("connect", () => {
console.log(`connect ${socket.id}`);
});
socket.on("disconnect", () => {
console.log(`disconnect`);
});
setInterval(() => {
socket.emit("ping", () => {
console.log("pong");
});
}, 1000);

238
examples/es-modules/package-lock.json generated Normal file
View File

@@ -0,0 +1,238 @@
{
"name": "es-modules",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/component-emitter": {
"version": "1.2.10",
"resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz",
"integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg=="
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"engine.io": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-4.0.0.tgz",
"integrity": "sha512-WyTa1NJR8rRmPXGXNSSgA+XhzfYLVcRBjRoFx7gI3cARnEsyuMpg0PS/PMDnPMMQtkjmVZsi2/ETrpq4mhoYSw==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.0",
"ws": "^7.1.2"
}
},
"engine.io-client": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-4.0.0.tgz",
"integrity": "sha512-dmydBrbZW0AYX4+u7aRzslPw/MSZy3HwwqHx3Es5OPj9q0tnyPeFZZU/UE5aLjba6xIwZTXkB+OdqF5wmR21IA==",
"requires": {
"base64-arraybuffer": "0.1.4",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-parser": "~4.0.1",
"has-cors": "1.1.0",
"parseqs": "0.0.6",
"parseuri": "0.0.5",
"ws": "~7.2.1",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"ws": {
"version": "7.2.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
"integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
}
}
},
"engine.io-parser": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-4.0.1.tgz",
"integrity": "sha512-v5aZK1hlckcJDGmHz3W8xvI3NUHYc9t8QtTbqdR5OaH3S9iJZilPubauOm+vLWOMMWzpE3hiq92l9lTAHamRCg=="
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"socket.io": {
"version": "3.0.0-rc2",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-3.0.0-rc2.tgz",
"integrity": "sha512-viH824jjAF+tCN6zfFgZR9wsgXWZ6zuVZ46ZQDsThyXdkv6+RihAZ/xz6LqsP8vFK83MVtRztMAIc56eyeKX0g==",
"requires": {
"base64id": "~2.0.0",
"debug": "~4.1.0",
"engine.io": "~4.0.0",
"socket.io-adapter": "2.0.3-rc1",
"socket.io-client": "3.0.0-rc2",
"socket.io-parser": "4.0.1-rc2"
}
},
"socket.io-adapter": {
"version": "2.0.3-rc1",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.0.3-rc1.tgz",
"integrity": "sha512-/+irxVkiriDRMt2hcYYlDOl2LUU3wkjPQ1917t/hb617/W+bnlDaqEGqtQpFE/tWys5FI1+q2JnjdwNeRHxyNw=="
},
"socket.io-client": {
"version": "3.0.0-rc2",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-3.0.0-rc2.tgz",
"integrity": "sha512-ZIVzV1ANogp5ZqkIn504UhD4NwZkCWLoq9/rHiNSI0RIsvsfGxyOzhNvr0fWPppfr8+hNt5Qbij6ucV5ijIJDg==",
"requires": {
"@types/component-emitter": "^1.2.10",
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~4.1.0",
"engine.io-client": "~4.0.0",
"parseuri": "0.0.6",
"socket.io-parser": "4.0.1-rc2"
}
},
"socket.io-parser": {
"version": "4.0.1-rc2",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.1-rc2.tgz",
"integrity": "sha512-aI20UWITf5vKgwA1HjCanYlM8cr2VDmOBiM3+1MM1MautnHAngiceuN0gUOtQs4MmWatuD2Td8g00EwHf3qthQ==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~4.1.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "es-modules",
"version": "1.0.0",
"description": "An example with ES modules (https://nodejs.org/api/esm.html)",
"type": "module",
"author": "Damien Arrachequesne",
"license": "MIT",
"engines": {
"node": ">=12.17.0"
},
"dependencies": {
"socket.io": "beta",
"socket.io-client": "beta"
}
}

View File

@@ -0,0 +1,16 @@
import { Server } from "socket.io";
const io = new Server(8080);
io.on("connect", (socket) => {
console.log(`connect ${socket.id}`);
socket.on("ping", (cb) => {
console.log("ping");
cb();
});
socket.on("disconnect", () => {
console.log(`disconnect ${socket.id}`);
});
});

View File

@@ -0,0 +1,14 @@
# Example with [Passport](http://www.passportjs.org/)
This example shows how to retrieve the authentication context from a basic [Express](http://expressjs.com/) + [Passport](http://www.passportjs.org/) application.
![Passport example](assets/passport_example.gif)
## How to use
```
$ npm ci && npm start
```
And point your browser to `http://localhost:3000`. Optionally, specify a port by supplying the `PORT` env variable.

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Passport example</title>
</head>
<body>
<p>Authenticated!</p>
<p>Socket ID: <span id="socketId"></span></p>
<p>Username: <span id="username"></span></p>
<form action="/logout" method="post">
<div>
<input type="submit" value="Log out" />
</div>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const socketIdSpan = document.getElementById("socketId");
const usernameSpan = document.getElementById("username");
socket.on('connect', () => {
socketIdSpan.innerText = socket.id;
socket.emit('whoami', (username) => {
usernameSpan.innerText = username;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,104 @@
const app = require("express")();
const server = require("http").createServer(app);
const port = process.env.PORT || 3000;
const session = require("express-session");
const bodyParser = require("body-parser");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const sessionMiddleware = session({ secret: "changeit", resave: false, saveUninitialized: false });
app.use(sessionMiddleware);
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
const DUMMY_USER = {
id: 1,
username: "john",
};
passport.use(
new LocalStrategy((username, password, done) => {
if (username === "john" && password === "doe") {
console.log("authentication OK");
return done(null, DUMMY_USER);
} else {
console.log("wrong credentials");
return done(null, false);
}
})
);
app.get("/", (req, res) => {
const isAuthenticated = !!req.user;
if (isAuthenticated) {
console.log(`user is authenticated, session is ${req.session.id}`);
} else {
console.log("unknown user");
}
res.sendFile(isAuthenticated ? "index.html" : "login.html", { root: __dirname });
});
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/",
})
);
app.post("/logout", (req, res) => {
console.log(`logout ${req.session.id}`);
const socketId = req.session.socketId;
if (socketId && io.of("/").connected[socketId]) {
console.log(`forcefully closing socket ${socketId}`);
io.sockets.connected[socketId].disconnect(true);
}
req.logout();
res.cookie("connect.sid", "", { expires: new Date() });
res.redirect("/");
});
passport.serializeUser((user, cb) => {
console.log(`serializeUser ${user.id}`);
cb(null, user.id);
});
passport.deserializeUser((id, cb) => {
console.log(`deserializeUser ${id}`);
cb(null, DUMMY_USER);
});
const io = require('socket.io')(server);
// convert a connect middleware to a Socket.IO middleware
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);
io.use(wrap(sessionMiddleware));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));
io.use((socket, next) => {
if (socket.request.user) {
next();
} else {
next(new Error('unauthorized'))
}
});
io.on('connect', (socket) => {
console.log(`new connection ${socket.id}`);
socket.on('whoami', (cb) => {
cb(socket.request.user ? socket.request.user.username : '');
});
const session = socket.request.session;
console.log(`saving sid ${socket.id} in session ${session.id}`);
session.socketId = socket.id;
session.save();
});
server.listen(port, () => {
console.log(`application is running at: http://localhost:${port}`);
});

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Passport example</title>
</head>
<body>
<p>Not authenticated</p>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username" />
<br/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,783 @@
{
"name": "passport-example",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
"requires": {
"mime-types": "~2.1.24",
"negotiator": "0.6.2"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
"requires": {
"bytes": "3.1.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"on-finished": "~2.3.0",
"qs": "6.7.0",
"raw-body": "2.4.0",
"type-is": "~1.6.17"
}
},
"bytes": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
"requires": {
"safe-buffer": "5.1.2"
}
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
"integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
"requires": {
"accepts": "~1.3.4",
"base64id": "2.0.0",
"cookie": "0.3.1",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"ws": "^7.1.2"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"engine.io-client": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.2.tgz",
"integrity": "sha512-AWjc1Xg06a6UPFOBAzJf48W1UR/qKYmv/ubgSCumo9GXgvL/xGIvo05dXoBL+2NTLMipDI7in8xK61C17L25xg==",
"requires": {
"component-emitter": "~1.3.0",
"component-inherit": "0.0.3",
"debug": "~4.1.0",
"engine.io-parser": "~2.2.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ws": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
"engine.io-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"express-session": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz",
"integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.0",
"uid-safe": "~2.1.5"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"statuses": "~1.5.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.1",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"requires": {
"mime-db": "1.44.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"passport": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
}
},
"passport-local": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
"integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
"requires": {
"passport-strategy": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"qs": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
"requires": {
"bytes": "3.1.0",
"http-errors": "1.7.2",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.7.2",
"mime": "1.6.0",
"ms": "2.1.1",
"on-finished": "~2.3.0",
"range-parser": "~1.2.1",
"statuses": "~1.5.0"
},
"dependencies": {
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
}
}
},
"serve-static": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.17.1"
}
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
},
"socket.io": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
"integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
"requires": {
"debug": "~4.1.0",
"engine.io": "~3.4.0",
"has-binary2": "~1.0.2",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"socket.io-parser": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
}
}
},
"socket.io-parser": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
"integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
}
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz",
"integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "passport-example",
"version": "0.0.1",
"description": "Example with Passport (http://www.passportjs.org/)",
"dependencies": {
"body-parser": "~1.19.0",
"express": "~4.17.1",
"express-session": "~1.17.1",
"passport": "~0.4.1",
"passport-local": "~1.0.0",
"socket.io": "~2.3.0"
},
"scripts": {
"start": "node index.js"
}
}

View File

@@ -0,0 +1,4 @@
{
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
}

14
examples/react-native/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
node_modules/**/*
.expo/*
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
web-report/
# macOS
.DS_Store

71
examples/react-native/App.js vendored Normal file
View File

@@ -0,0 +1,71 @@
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import io from 'socket.io-client';
const socket = io("192.168.0.28:3000"); // replace with the IP of your server, when testing on real devices
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
connected: socket.connected,
currentTransport: socket.connected ? socket.io.engine.transport.name : '-',
lastMessage: ""
};
}
componentDidMount() {
socket.on('connect', () => this.onConnectionStateUpdate());
socket.on('disconnect', () => this.onConnectionStateUpdate());
socket.on('message', (content) => this.onMessage(content));
}
componentWillUnmount() {
socket.off('connect');
socket.off('disconnect');
socket.off('message');
}
onConnectionStateUpdate() {
this.setState({
connected: socket.connected,
currentTransport: socket.connected ? socket.io.engine.transport.name : '-'
});
if (socket.connected) {
socket.io.engine.on('upgrade', () => this.onUpgrade());
} else {
socket.io.engine.off('upgrade');
}
}
onMessage(content) {
this.setState({
lastMessage: content
});
}
onUpgrade() {
this.setState({
currentTransport: socket.io.engine.transport.name
});
}
render() {
return (
<View style={styles.container}>
<Text>State: { this.state.connected ? 'Connected' : 'Disconnected' }</Text>
<Text>Current transport: { this.state.currentTransport }</Text>
<Text>Last message: { this.state.lastMessage }</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

View File

@@ -0,0 +1,16 @@
# Example with [React Native](https://reactnative.dev/)
![react native logo](assets/react-native.png)
This example shows a basic example based on [Expo](https://expo.io/).
![demo](assets/react-native-demo.gif)
## How to use
```
$ npm ci
$ npm start # run expo
$ node server.js # run the server
```

View File

@@ -0,0 +1,28 @@
{
"expo": {
"name": "react-native",
"slug": "react-native",
"platforms": [
"ios",
"android",
"web"
],
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Some files were not shown because too many files have changed in this diff Show More