Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
darrachequesne committed Sep 12, 2022
0 parents commit e892ecf
Show file tree
Hide file tree
Showing 44 changed files with 7,384 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
push:
pull_request:
schedule:
- cron: "0 0 * * 0"

jobs:
test:
runs-on: ubuntu-latest

strategy:
matrix:
deno-version:
- 1.25.2

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Use Deno ${{ matrix.deno-version }}
uses: denolib/setup-deno@v2
with:
deno-version: ${{ matrix.deno-version }}

- name: Check format
run: deno fmt --check

- name: Run tests
run: deno test --allow-net
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ISC License

Copyright (c) 2022 Damien ARRACHEQUESNE

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
236 changes: 236 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Socket.IO server for Deno

An implementation of the Socket.IO protocol for Deno.

Table of content:

- [Usage](#usage)
- [Options](#options)
- [`path`](#path)
- [`connectTimeout`](#connecttimeout)
- [`pingTimeout`](#pingtimeout)
- [`pingInterval`](#pinginterval)
- [`upgradeTimeout`](#upgradetimeout)
- [`maxHttpBufferSize`](#maxhttpbuffersize)
- [`allowRequest`](#allowrequest)
- [`cors`](#cors)
- [`editHandshakeHeaders`](#edithandshakeheaders)
- [`editResponseHeaders`](#editresponseheaders)
- [Logs](#logs)

## Usage

```ts
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import { Server } from "https://deno.land/x/[email protected]/mod.ts";

const io = new Server();

io.on("connection", (socket) => {
console.log(`socket ${socket.id} connected`);

socket.emit("hello", "world");

socket.on("disconnect", (reason) => {
console.log(`socket ${socket.id} disconnected due to ${reason}`);
});
});

await serve(io.handler(), {
port: 3000,
});
```

And then run with:

```
$ deno run --allow-net index.ts
```

Like the [Node.js server](https://socket.io/docs/v4/typescript/), you can also
provide types for the events sent between the server and the clients:

```ts
interface ServerToClientEvents {
noArg: () => void;
basicEmit: (a: number, b: string, c: Buffer) => void;
withAck: (d: string, callback: (e: number) => void) => void;
}

interface ClientToServerEvents {
hello: () => void;
}

interface InterServerEvents {
ping: () => void;
}

interface SocketData {
user_id: string;
}

const io = new Server<
ClientToServerEvents,
ServerToClientEvents,
InterServerEvents,
SocketData
>();
```

## Options

### `path`

Default value: `/socket.io/`

It is the name of the path that is captured on the server side.

Caution! The server and the client values must match (unless you are using a
path-rewriting proxy in between).

Example:

```ts
const io = new Server(httpServer, {
path: "/my-custom-path/",
});
```

### `connectTimeout`

Default value: `45000`

The number of ms before disconnecting a client that has not successfully joined
a namespace.

### `pingTimeout`

Default value: `20000`

This value is used in the heartbeat mechanism, which periodically checks if the
connection is still alive between the server and the client.

The server sends a ping, and if the client does not answer with a pong within
`pingTimeout` ms, the server considers that the connection is closed.

Similarly, if the client does not receive a ping from the server within
`pingInterval + pingTimeout` ms, the client also considers that the connection
is closed.

### `pingInterval`

Default value: `25000`

See [`pingTimeout`](#pingtimeout) for more explanation.

### `upgradeTimeout`

Default value: `10000`

This is the delay in milliseconds before an uncompleted transport upgrade is
cancelled.

### `maxHttpBufferSize`

Default value: `1e6` (1 MB)

This defines how many bytes a single message can be, before closing the socket.
You may increase or decrease this value depending on your needs.

### `allowRequest`

Default value: `-`

A function that receives a given handshake or upgrade request as its first
parameter, and can decide whether to continue or not.

Example:

```ts
const io = new Server({
allowRequest: (req, connInfo) => {
return Promise.reject("thou shall not pass");
},
});
```

### `cors`

Default value: `-`

A set of options related to
[Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
(CORS).

Example:

```ts
const io = new Server({
cors: {
origin: ["https://example.com"],
allowedHeaders: ["my-header"],
credentials: true,
},
});
```

### `editHandshakeHeaders`

Default value: `-`

A function that allows to edit the response headers of the handshake request.

Example:

```ts
const io = new Server({
editHandshakeHeaders: (responseHeaders, req, connInfo) => {
responseHeaders.set("set-cookie", "sid=1234");
},
});
```

### `editResponseHeaders`

Default value: `-`

A function that allows to edit the response headers of all requests.

Example:

```ts
const io = new Server({
editResponseHeaders: (responseHeaders, req, connInfo) => {
responseHeaders.set("my-header", "abcd");
},
});
```

## Logs

The library relies on the standard `log` module, so you can display the internal
logs of the Socket.IO server with:

```ts
import * as log from "https://deno.land/[email protected]/log/mod.ts";

await log.setup({
handlers: {
console: new log.handlers.ConsoleHandler("DEBUG"),
},
loggers: {
"socket.io": {
level: "DEBUG",
handlers: ["console"],
},
"engine.io": {
level: "DEBUG",
handlers: ["console"],
},
},
});
```

## License

[ISC](/LICENSE)
6 changes: 6 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export {
type ConnInfo,
type Handler,
} from "https://deno.land/[email protected]/http/server.ts";

export { getLogger } from "https://deno.land/[email protected]/log/mod.ts";
1 change: 1 addition & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Server, type ServerOptions } from "./packages/socket.io/mod.ts";
63 changes: 63 additions & 0 deletions packages/engine.io-parser/base64-arraybuffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Use a lookup table to find the index.
const lookup = new Uint8Array(256);
for (let i = 0; i < chars.length; i++) {
lookup[chars.charCodeAt(i)] = i;
}

export function encodeToBase64(arraybuffer: ArrayBuffer): string {
const bytes = new Uint8Array(arraybuffer);
const len = bytes.length;
let base64 = "";

for (let i = 0; i < len; i += 3) {
base64 += chars[bytes[i] >> 2];
base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
base64 += chars[bytes[i + 2] & 63];
}

if (len % 3 === 2) {
base64 = base64.substring(0, base64.length - 1) + "=";
} else if (len % 3 === 1) {
base64 = base64.substring(0, base64.length - 2) + "==";
}

return base64;
}

export function decodeFromBase64(base64: string): ArrayBuffer {
const len = base64.length;
let bufferLength = base64.length * 0.75,
i,
p = 0,
encoded1,
encoded2,
encoded3,
encoded4;

if (base64[base64.length - 1] === "=") {
bufferLength--;
if (base64[base64.length - 2] === "=") {
bufferLength--;
}
}

const arraybuffer = new ArrayBuffer(bufferLength),
bytes = new Uint8Array(arraybuffer);

for (i = 0; i < len; i += 4) {
encoded1 = lookup[base64.charCodeAt(i)];
encoded2 = lookup[base64.charCodeAt(i + 1)];
encoded3 = lookup[base64.charCodeAt(i + 2)];
encoded4 = lookup[base64.charCodeAt(i + 3)];

bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
}

return arraybuffer;
}
Loading

0 comments on commit e892ecf

Please sign in to comment.