Giter Club home page Giter Club logo

socket.io-deno's Introduction

Socket.IO server for Deno

An implementation of the Socket.IO protocol for Deno.

Table of content:

Usage

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, you can also provide types for the events sent between the server and the clients:

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
>();

With oak

You need to use the .handle() method:

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

const app = new Application();

app.use((ctx) => {
  ctx.response.body = "Hello World!";
});

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}`);
  });
});

const handler = io.handler(async (req) => {
  return await app.handle(req) || new Response(null, { status: 404 });
});

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

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:

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 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:

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 (CORS).

Example:

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:

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:

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:

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"],
    },
  },
});

Adapters

Custom adapters can be used to broadcast packets between several Socket.IO servers.

Redis adapter

Documentation: https://socket.io/docs/v4/redis-adapter/

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

const [pubClient, subClient] = await Promise.all([
  createRedisClient({
    hostname: "localhost",
  }),
  createRedisClient({
    hostname: "localhost",
  }),
]);

const io = new Server({
  adapter: createRedisAdapter(pubClient, subClient),
});

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

License

ISC

socket.io-deno's People

Contributors

darrachequesne avatar novqigarrix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

socket.io-deno's Issues

TypeError: Headers are immutable

Trying to setup Websockets with Deno & IO.
Upon establishing connection I'm getting this pesky error - any idea how to fix this?

TypeError: Headers are immutable.
    at Headers.set (deno:ext/fetch/20_headers.js:384:15)
    at https://deno.land/x/[email protected]/packages/engine.io/lib/transports/websocket.ts:60:24
    at Headers.forEach (deno:ext/webidl/00_webidl.js:1001:13)
    at WS.onRequest (https://deno.land/x/[email protected]/packages/engine.io/lib/transports/websocket.ts:59:21)
    at Server.handleRequest (https://deno.land/x/[email protected]/packages/engine.io/lib/server.ts:217:35)
    at async Server.#respond (https://deno.land/[email protected]/http/server.ts:298:18)

Server:

const io = new Server({ cors: { origin: 'http://localhost:3000' } });

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

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

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

Client:

const socket = io('ws://localhost:8080');

  socket.on('connection', () => {
    console.log('Connection established.');
  });

Does this project also support the socket.io client?

Recently, I was trying to use import { io } from "https://cdn.socket.io/4.6.1/socket.io.esm.min.js"; in a deno project, but it didn't seem to work. Nothing was emitted. ๐Ÿค” Maybe this project implements a fixed version of the socket.io client? I couldn't find it in the repo, but I hope it's somewhere in here. Thanks much!

Client connected but it doesn't receive anything

The client is connected fine and can send and receive events, but some times the server doesn't recieve any event and the client is still connected.

The way to reproduce it:
Start and stop the client a few times, until it stops receiveing the pong message.

client:

const socket = io("http://localhost:8080");

socket.on("pong", ({ datetime }) => {
    console.log(`Response time (${(Date.now() - datetime)}ms)`)
});

socket.on("connect", () => {
    console.log(`Connected! ${socket.id}`);
    
    setInterval(() => {
        socket.emit('ping', { datetime: Date.now() });
    }, 1000);
});

socket.connect();

server:

const io = new Server();

io.on("connection", (socket) => {
    console.log(`socket ${socket.id} connected`);
    
    socket.on('ping', (data) => {
        console.log(`[${socket.id}]: ping!`)
        socket.emit('pong', data)
    });
});

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

Cannot access private nsp property on sockets

According to the documentation, you should be able
to access the namespace of a socket with socket.nsp

io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
  const namespace = socket.nsp;
});

However, in the Deno implementation of the Socket.IO server, this fails with Property nsp is private and only accessible within class 'Socket'..

This is because it is defined as readonly private in socket.io-deno while in socket.io it is just readonly.

std/http/server.ts>serve is deprecated, io.handler() is incompatible with Deno.serve

Describe the bug

Deno's std/http/server.ts>serve is deprecated and will be removed in version 1.0.0. Instead they suggest to use Deno.serve.

This library currently does not seem to be compatible with Deno.serve. It throws the following error:

Deno: No overload matches this call. Overload 1 of 3, '(handler: ServeHandler): Server', gave the following error. Argument of type '(req: Request, connInfo: ConnInfo) => Response | Promise' is not assignable to parameter of type 'ServeHandler'. Overload 2 of 3, '(options: ServeInit & (ServeOptions | ServeTlsOptions)): Server', gave the following error. Argument of type '(req: Request, connInfo: ConnInfo) => Response | Promise' is not assignable to parameter of type 'ServeInit & (ServeOptions | ServeTlsOptions)'.

To Reproduce

deps.ts

export * as socketio from "https://deno.land/x/[email protected]/mod.ts";

test.ts

import { socketio } from "./deps.ts";
const io = new socketio.Server();
Deno.serve(io.handler());

Expected behavior

Ideally I would expect the socketio handler to be compatible with Deno.serve.

Platform:

MacOS 13.5 (Intel-based)
deno 1.35.3 (release, x86_64-apple-darwin)
v8 11.6.189.12
typescript 5.1.6

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.