nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
66.9k stars 7.55k forks source link

Binary Messages over Websocket from default nestjs ws adapter doesnt work, doesnt connect. #13779

Closed Eyalm321 closed 2 months ago

Eyalm321 commented 2 months ago

Is there an existing issue for this?

Current behavior

In main.ts I define adapter from @nestjs/platform-socket.io and pass it httpsOptions(pem files) Upon connection from postman it seems to work and make a connection but if connection is made for binary streaming like audio, client will disconnect. Using WsAdapter from @nestjs/platform-ws solves it and messages come through securely using the same httpsServer BUT it doesnt support namespace so I can see how server dev becomes complex as I add more gateways , having all clients route to the same path.

Minimum reproduction code

https://github.com

Steps to reproduce

I'm not able to reproduce in a simple app, im using freeSwitch for my sip server, when I get a phone call it route RTP to WSS using mod_audio_stream. I can paste my main.ts and gateway

main.ts

import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import as cookieParser from 'cookie-parser'; import as dotenv from 'dotenv'; import as fs from 'fs'; import as https from 'https'; import { AllExceptionsFilter } from './shared/filters/http-exception.filter'; import { LoggingMiddleware } from './shared/middleware/logging.middleware'; import { WebSocketAdapter } from '@nestjs/common'; import { WsAdapter } from '@nestjs/platform-ws';

async function bootstrap() { dotenv.config(); // Load environment variables from .env file

const port = process.env.MAIN_PORT || 3000; const certPath = process.env.MILVUS_CERT_CHAIN_PATH; const keyPath = process.env.MILVUS_PRIVATE_KEY_PATH; const caPath = process.env.MILVUS_ROOT_CERT_PATH; const wssCertPath = process.env.WSS_CERT_PATH; const wssKeyPath = process.env.WSS_KEY_PATH;

// Check if the certificate files exist if (!fs.existsSync(certPath)) { throw new Error(Certificate file not found: ${certPath}); }

if (!fs.existsSync(keyPath)) { throw new Error(Private key file not found: ${keyPath}); }

if (!fs.existsSync(caPath)) { throw new Error(CA file not found: ${caPath}); }

const cert = fs.readFileSync(certPath, 'utf-8'); const key = fs.readFileSync(keyPath, 'utf-8'); const ca = fs.readFileSync(caPath, 'utf-8'); const wssCert = fs.readFileSync(wssCertPath, 'utf-8'); const wssKey = fs.readFileSync(wssKeyPath, 'utf-8');

const httpsOptions = { key: Buffer.from(key, 'utf-8'), cert: Buffer.from(cert, 'utf-8'), ca: Buffer.from(ca, 'utf-8'), };

const wssOptions = { key: Buffer.from(wssKey, 'utf-8'), cert: Buffer.from(wssCert, 'utf-8'), ca: Buffer.from(ca, 'utf-8'), };

// Create an Express server instance const app = await NestFactory.create(AppModule, { httpsOptions, cors: { origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', credentials: true, } });

// Apply global exception filter app.useGlobalFilters(new AllExceptionsFilter());

// Apply global logging middleware app.use(new LoggingMiddleware().use);

// Use middleware app.use(cookieParser()); // Parse cookies

const wsServer = https.createServer(httpsOptions, app.getHttpAdapter().getInstance()); wsServer.listen(443); app.useWebSocketAdapter(new WsAdapter(wsServer)); app.useWebSocketAdapter(new )

// Start the HTTP application await app.listen(port, () => { console.log(HTTP Application is running on: https://localhost:${port}); });

app.getHttpAdapter().getInstance().use((req, res, next) => { console.log(Incoming Request: ${req.method} ${req.url}); next(); }); }

bootstrap();

gateway:

import { WebSocketGateway, WebSocketServer, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { stringify } from 'flatted'; import { Logger } from '@nestjs/common'; import as fs from 'fs'; import as minimist from 'minimist';

@WebSocketGateway({ transports: ['websocket'], allowEIO3: true, allowUpgrades: true, allowRequests: true, }) export class AudioGateway implements OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; private logger: Logger = new Logger('AudioGateway'); private wstream: fs.WriteStream; private recordingPath: string;

constructor() {
    const argv = minimist(process.argv.slice(2));
    this.recordingPath = argv._.length ? argv._[0] : '/tmp/audio.raw';
    this.logger.log(`Writing incoming raw audio to file ${this.recordingPath}`);
}

afterInit(server: Server) {
    this.logger.log('WebSocket server initialized');
}

handleConnection(client: Socket, ...args: any[]) {
    this.logger.log(`Client connected: ${client.request}`);
    this.wstream = fs.createWriteStream(this.recordingPath, { flags: 'a' });

    client.on('message', (message) => {
        if (typeof message === 'string') {
            this.logger.log(`Received message: ${message}`);
        } else if (Buffer.isBuffer(message)) {
            this.logger.log(`Received binary message of length ${message.length}`);
            this.wstream.write(message);
        }
    });

    client.on('disconnect', (reason) => {
        this.logger.log(`Client disconnected: ${reason}`);
        this.wstream.end();
    });

    client.on('error', (error) => {
        this.logger.error(`Socket error: ${error.message}`);
        this.wstream.end();
    });
}

handleDisconnect(client: Socket) {
    this.logger.log(`Client disconnected: ${client.request}`);
}

}

Expected behavior

I expected to use adapter from @nestjs/platform-socket.io and have it working. that way I can define namespace to audio gateway.

Package

Other package

No response

NestJS version

10.4.1

Packages versions


| \ | | | | |_ |/ _|/ _ | | | | | | | | | | |\ --. | / \/| | | | | . | / \/ || | | | `--. | | | | | | | |\ || /\ | | /_/ //_/ /| __/| |___| | _| _/ _||/ _|__/ __/ __/__/___/

[System Information] OS Version : Linux 5.14.0-467.el9.x86_64 NodeJS Version : v22.3.0 NPM Version : 10.8.1

[Nest CLI] Nest CLI Version : 10.4.1

[Nest Platform Information] platform-socket.io version : 10.3.10 platform-express version : 10.3.10 cache-manager version : 2.2.2 microservices version : 10.3.10 serve-static version : 4.0.2 platform-ws version : 10.3.10 websockets version : 10.3.10 mongoose version : 10.0.6 testing version : 10.3.10 common version : 10.3.10 config version : 3.2.3 axios version : 3.0.2 core version : 10.3.10 cli version : 10.4.1

Node.js version

22.3.0

In which operating systems have you tested?

Other

No response

kamilmysliwiec commented 2 months ago

Thank you for taking the time to submit your report! From the looks of it, this could be better discussed on our Discord. If you haven't already, please join here and send a new post in the #⁠ 🐈 nestjs-help forum. Make sure to include a link to this issue, so you don't need to write it all again. We have a large community of helpful members, who will assist you in getting this to work.