FoalTS / foal

Full-featured Node.js framework, with no complexity. 🚀 Simple and easy to use, TypeScript-based and well-documented.
https://foalts.org/
MIT License
1.88k stars 137 forks source link

Websocket error in @foal/socket.io/lib/socketio-controller.service #1043

Closed pospile closed 2 years ago

pospile commented 2 years ago

Version of FoalTS: 2.8.0

I tried updating my backend but I am still failing at this error:

/node_modules/@foal/socket.io/lib/socketio-controller.service.js:59
                     return cb({
                                ^
 TypeError: cb is not a function
     at Socket.<anonymous> (/node_modules/@foal/socket.io/lib/socketio-controller.service.js:59:28)
     at processTicksAndRejections (node:internal/process/task_queues:94:5)
 Program node ./build/index.js exited with code 1

What can be my problem, did I missed some step in upgrading the project to 2.8?

Thanks

LoicPoullain commented 2 years ago

Hmm, this looks definitely like a bug. When does it occur? When the application is launched or when a WS controller receive an event? If it is when it receives an event, could you share the client code that emits the event?

pospile commented 2 years ago

Its when controller receives en event but everything inside that method is normally executed I think

@EventName('msg')
ping(ctx: WebsocketContext) {
      console.log("event msg");
      //return new WebsocketErrorResponse();
}

Because as you can see, if response get commented it still crashes on the same error right after it report problem with non existing response and console log is executed normally

event msg
[1] Error: The controller method "ping" should return a WebsocketResponse or a WebsocketErrorResponse.
[1]     at Object.getWebsocketResponse (/node_modules/@foal/socket.io/lib/routes/get-websocket-response.js:35:23)
[1]     at processTicksAndRejections (node:internal/process/task_queues:94:5)
[1]     at Socket.<anonymous> (/node_modules/@foal/socket.io/lib/socketio-controller.service.js:52:38)
[1] 
[1]/node_modules/@foal/socket.io/lib/socketio-controller.service.js:54
[1]                         return cb({
[1]                                ^
[1] TypeError: cb is not a function
[1]     at Socket.<anonymous> (/node_modules/@foal/socket.io/lib/socketio-controller.service.js:54:32)
[1]     at processTicksAndRejections (node:internal/process/task_queues:94:5)
[1] Program node ./build/index.js exited with code 1
pospile commented 2 years ago

And I dont have any client code yet, just testing it out with with Postman socket.io integration for now. But I will go and try to test it with actual js client.

pospile commented 2 years ago

Ok so the socket.io client library makes no difference ie:

<!doctype html>
<html>
<head>
    <script src='/socket.io/socket.io.js'></script>
    <script>
        var socket = io();

        socket.on('welcome', function(data) {
            addMessage(data.message);

            // Respond with a message including this clients' id sent from the server
            socket.emit('i am client', {data: 'foo!', id: data.id});
        });
        socket.on('time', function(data) {
            addMessage(data.time);
        });
        socket.on('error', console.error.bind(console));
        socket.on('message', console.log.bind(console));

        function addMessage(message) {
            var text = document.createTextNode(message),
                el = document.createElement('li'),
                messages = document.getElementById('messages');

            el.appendChild(text);
            messages.appendChild(el);
        }
    </script>
</head>
<body>
<ul id='messages'></ul>
</body>
</html>

and server code is:

import {
    EventName,
    SocketIOController,
    WebsocketContext
} from '@foal/socket.io';

export class WebsocketController extends SocketIOController {

    @EventName('i am client')
    ping(ctx: WebsocketContext, payload) {
        console.log("event msg");
        console.log(payload);
        //return new WebsocketErrorResponse();
    }

    onConnection(ctx: WebsocketContext): void | Promise<void> {
        ctx.socket.emit("welcome", {"message": "ahojda"});
        return super.onConnection(ctx);
    }
}

Welcome message goes through as expected, socket.emit also works fine and it all fails at client emitting "i am client" message.

Also there is index.ts as it might be relevant:

import 'source-map-support/register';
import * as admin from 'firebase-admin';

// std
import * as http from 'http';

// 3p
import {Config, createApp, displayServerURL, ServiceManager} from '@foal/core';

// App
import { AppController } from './app/app.controller';
import {WebsocketController} from "./app/services";

async function main() {
  const serviceManager = new ServiceManager();

  const app = await createApp(AppController, { serviceManager });
  const httpServer = http.createServer(app);
  const port = Config.get('port', 'number', 3001);
  const serviceAccount = require("../firebase.json");

  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
  });

  await serviceManager.get(WebsocketController).attachHttpServer(httpServer);
  httpServer.listen(port, () => displayServerURL(port));
}

main()
  .catch(err => { console.error(err.stack); process.exit(1); });
pospile commented 2 years ago

socket.io in yarn.lock

"@foal/socket.io@npm:^2.8.0":
  version: 2.8.0
  resolution: "@foal/socket.io@npm:2.8.0"
  dependencies:
    "@foal/core": ^2.8.0
    reflect-metadata: ~0.1.13
    socket.io: ~4.4.1
  checksum: da8ad244edea664e87e05bdc746b9ad2b2ce285a8e1ea6d5fd3d7c270a0f85c5e465a1e6df95dabfb8ee664a296fd9a5202cf42a2ed35b6c797664eb04acbc77
  languageName: node
  linkType: hard

It should be latest version as per npm.

LoicPoullain commented 2 years ago

Thank you for you investigation. 👍

I found the issue. Setting aside the error that is raised because no response is returned, the TypeError: cb is not a function error comes from the fact that the client does not provide a callback in its emit function:

// Does not work
socket.emit('i am client', {data: 'foo!', id: data.id});

// Works
socket.emit('i am client', {data: 'foo!', id: data.id}, (data) => {
  console.log(data);
});

It appears that, when no callback is provided in the client, the server sdk does not also pass a function to the on method.

LoicPoullain commented 2 years ago

I'll work on a fix.

LoicPoullain commented 2 years ago

@pospile which version of Node are you on?

pospile commented 2 years ago

@LoicPoullain tried multiple, currently node 16.

LoicPoullain commented 2 years ago

The issue has been fixed in version 2.8.1 which will be released this week-end. Thank you for reporting this problem @pospile 👍