Skip to content

Commit e892ecf

Browse files
initial commit
0 parents  commit e892ecf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+7384
-0
lines changed

.github/workflows/ci.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
schedule:
7+
- cron: "0 0 * * 0"
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
strategy:
14+
matrix:
15+
deno-version:
16+
- 1.25.2
17+
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v2
21+
22+
- name: Use Deno ${{ matrix.deno-version }}
23+
uses: denolib/setup-deno@v2
24+
with:
25+
deno-version: ${{ matrix.deno-version }}
26+
27+
- name: Check format
28+
run: deno fmt --check
29+
30+
- name: Run tests
31+
run: deno test --allow-net

LICENSE

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
ISC License
2+
3+
Copyright (c) 2022 Damien ARRACHEQUESNE
4+
5+
Permission to use, copy, modify, and/or distribute this software for any
6+
purpose with or without fee is hereby granted, provided that the above
7+
copyright notice and this permission notice appear in all copies.
8+
9+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15+
PERFORMANCE OF THIS SOFTWARE.

README.md

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
# Socket.IO server for Deno
2+
3+
An implementation of the Socket.IO protocol for Deno.
4+
5+
Table of content:
6+
7+
- [Usage](#usage)
8+
- [Options](#options)
9+
- [`path`](#path)
10+
- [`connectTimeout`](#connecttimeout)
11+
- [`pingTimeout`](#pingtimeout)
12+
- [`pingInterval`](#pinginterval)
13+
- [`upgradeTimeout`](#upgradetimeout)
14+
- [`maxHttpBufferSize`](#maxhttpbuffersize)
15+
- [`allowRequest`](#allowrequest)
16+
- [`cors`](#cors)
17+
- [`editHandshakeHeaders`](#edithandshakeheaders)
18+
- [`editResponseHeaders`](#editresponseheaders)
19+
- [Logs](#logs)
20+
21+
## Usage
22+
23+
```ts
24+
import { serve } from "https://deno.land/[email protected]/http/server.ts";
25+
import { Server } from "https://deno.land/x/[email protected]/mod.ts";
26+
27+
const io = new Server();
28+
29+
io.on("connection", (socket) => {
30+
console.log(`socket ${socket.id} connected`);
31+
32+
socket.emit("hello", "world");
33+
34+
socket.on("disconnect", (reason) => {
35+
console.log(`socket ${socket.id} disconnected due to ${reason}`);
36+
});
37+
});
38+
39+
await serve(io.handler(), {
40+
port: 3000,
41+
});
42+
```
43+
44+
And then run with:
45+
46+
```
47+
$ deno run --allow-net index.ts
48+
```
49+
50+
Like the [Node.js server](https://socket.io/docs/v4/typescript/), you can also
51+
provide types for the events sent between the server and the clients:
52+
53+
```ts
54+
interface ServerToClientEvents {
55+
noArg: () => void;
56+
basicEmit: (a: number, b: string, c: Buffer) => void;
57+
withAck: (d: string, callback: (e: number) => void) => void;
58+
}
59+
60+
interface ClientToServerEvents {
61+
hello: () => void;
62+
}
63+
64+
interface InterServerEvents {
65+
ping: () => void;
66+
}
67+
68+
interface SocketData {
69+
user_id: string;
70+
}
71+
72+
const io = new Server<
73+
ClientToServerEvents,
74+
ServerToClientEvents,
75+
InterServerEvents,
76+
SocketData
77+
>();
78+
```
79+
80+
## Options
81+
82+
### `path`
83+
84+
Default value: `/socket.io/`
85+
86+
It is the name of the path that is captured on the server side.
87+
88+
Caution! The server and the client values must match (unless you are using a
89+
path-rewriting proxy in between).
90+
91+
Example:
92+
93+
```ts
94+
const io = new Server(httpServer, {
95+
path: "/my-custom-path/",
96+
});
97+
```
98+
99+
### `connectTimeout`
100+
101+
Default value: `45000`
102+
103+
The number of ms before disconnecting a client that has not successfully joined
104+
a namespace.
105+
106+
### `pingTimeout`
107+
108+
Default value: `20000`
109+
110+
This value is used in the heartbeat mechanism, which periodically checks if the
111+
connection is still alive between the server and the client.
112+
113+
The server sends a ping, and if the client does not answer with a pong within
114+
`pingTimeout` ms, the server considers that the connection is closed.
115+
116+
Similarly, if the client does not receive a ping from the server within
117+
`pingInterval + pingTimeout` ms, the client also considers that the connection
118+
is closed.
119+
120+
### `pingInterval`
121+
122+
Default value: `25000`
123+
124+
See [`pingTimeout`](#pingtimeout) for more explanation.
125+
126+
### `upgradeTimeout`
127+
128+
Default value: `10000`
129+
130+
This is the delay in milliseconds before an uncompleted transport upgrade is
131+
cancelled.
132+
133+
### `maxHttpBufferSize`
134+
135+
Default value: `1e6` (1 MB)
136+
137+
This defines how many bytes a single message can be, before closing the socket.
138+
You may increase or decrease this value depending on your needs.
139+
140+
### `allowRequest`
141+
142+
Default value: `-`
143+
144+
A function that receives a given handshake or upgrade request as its first
145+
parameter, and can decide whether to continue or not.
146+
147+
Example:
148+
149+
```ts
150+
const io = new Server({
151+
allowRequest: (req, connInfo) => {
152+
return Promise.reject("thou shall not pass");
153+
},
154+
});
155+
```
156+
157+
### `cors`
158+
159+
Default value: `-`
160+
161+
A set of options related to
162+
[Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
163+
(CORS).
164+
165+
Example:
166+
167+
```ts
168+
const io = new Server({
169+
cors: {
170+
origin: ["https://example.com"],
171+
allowedHeaders: ["my-header"],
172+
credentials: true,
173+
},
174+
});
175+
```
176+
177+
### `editHandshakeHeaders`
178+
179+
Default value: `-`
180+
181+
A function that allows to edit the response headers of the handshake request.
182+
183+
Example:
184+
185+
```ts
186+
const io = new Server({
187+
editHandshakeHeaders: (responseHeaders, req, connInfo) => {
188+
responseHeaders.set("set-cookie", "sid=1234");
189+
},
190+
});
191+
```
192+
193+
### `editResponseHeaders`
194+
195+
Default value: `-`
196+
197+
A function that allows to edit the response headers of all requests.
198+
199+
Example:
200+
201+
```ts
202+
const io = new Server({
203+
editResponseHeaders: (responseHeaders, req, connInfo) => {
204+
responseHeaders.set("my-header", "abcd");
205+
},
206+
});
207+
```
208+
209+
## Logs
210+
211+
The library relies on the standard `log` module, so you can display the internal
212+
logs of the Socket.IO server with:
213+
214+
```ts
215+
import * as log from "https://deno.land/[email protected]/log/mod.ts";
216+
217+
await log.setup({
218+
handlers: {
219+
console: new log.handlers.ConsoleHandler("DEBUG"),
220+
},
221+
loggers: {
222+
"socket.io": {
223+
level: "DEBUG",
224+
handlers: ["console"],
225+
},
226+
"engine.io": {
227+
level: "DEBUG",
228+
handlers: ["console"],
229+
},
230+
},
231+
});
232+
```
233+
234+
## License
235+
236+
[ISC](/LICENSE)

deps.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export {
2+
type ConnInfo,
3+
type Handler,
4+
} from "https://deno.land/[email protected]/http/server.ts";
5+
6+
export { getLogger } from "https://deno.land/[email protected]/log/mod.ts";

mod.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Server, type ServerOptions } from "./packages/socket.io/mod.ts";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const chars =
2+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3+
4+
// Use a lookup table to find the index.
5+
const lookup = new Uint8Array(256);
6+
for (let i = 0; i < chars.length; i++) {
7+
lookup[chars.charCodeAt(i)] = i;
8+
}
9+
10+
export function encodeToBase64(arraybuffer: ArrayBuffer): string {
11+
const bytes = new Uint8Array(arraybuffer);
12+
const len = bytes.length;
13+
let base64 = "";
14+
15+
for (let i = 0; i < len; i += 3) {
16+
base64 += chars[bytes[i] >> 2];
17+
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
18+
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
19+
base64 += chars[bytes[i + 2] & 63];
20+
}
21+
22+
if (len % 3 === 2) {
23+
base64 = base64.substring(0, base64.length - 1) + "=";
24+
} else if (len % 3 === 1) {
25+
base64 = base64.substring(0, base64.length - 2) + "==";
26+
}
27+
28+
return base64;
29+
}
30+
31+
export function decodeFromBase64(base64: string): ArrayBuffer {
32+
const len = base64.length;
33+
let bufferLength = base64.length * 0.75,
34+
i,
35+
p = 0,
36+
encoded1,
37+
encoded2,
38+
encoded3,
39+
encoded4;
40+
41+
if (base64[base64.length - 1] === "=") {
42+
bufferLength--;
43+
if (base64[base64.length - 2] === "=") {
44+
bufferLength--;
45+
}
46+
}
47+
48+
const arraybuffer = new ArrayBuffer(bufferLength),
49+
bytes = new Uint8Array(arraybuffer);
50+
51+
for (i = 0; i < len; i += 4) {
52+
encoded1 = lookup[base64.charCodeAt(i)];
53+
encoded2 = lookup[base64.charCodeAt(i + 1)];
54+
encoded3 = lookup[base64.charCodeAt(i + 2)];
55+
encoded4 = lookup[base64.charCodeAt(i + 3)];
56+
57+
bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
58+
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
59+
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
60+
}
61+
62+
return arraybuffer;
63+
}

0 commit comments

Comments
 (0)