NestJs Websocket, how to test and debug "Socket hang up"? - websocket

I have a fairly simple Websocket Server, all I am trying to achieve is to have a socket gateway that would emit events to listening/connected clients based on db status updates.
here is my implementation:
#WebSocketGateway(5010, { transports: ['websocket'], cors: true })
export class SocketGateway
implements OnGatewayConnection, OnGatewayDisconnect
{
private readonly logger: Logger = new Logger(SocketGateway.name);
#WebSocketServer() server: Server;
constructor(private readonly socketService: SocketService) {}
#SubscribeMessage('statusUpdate')
updateStatus(data: any) {
this.server.emit('statusUpdate', data);
}
handleConnection(client: any, ...args: any[]): any {
return this.logger.log(`Client disconnected: ${client.id}`);
}
handleDisconnect(client: any): any {
return this.logger.log(`Client connected: ${client.id}`);
}
}
Now I am trying to connect using postman by connecting to this URL
ws://localhost:5010
which results in this error socket hang up
not really sure why is it behaving this way and no sufficient information to debug it.
would really appreciate if someone could share a hint on where to look.
I am on macOS Monterey: 12.0.1 ( last update )
installed Websocket libs:
#nestjs/platform-socket.io: 8.2.4
#nestjs/websockets": 8.2.4
Thanks

Through e2e testing, you can try with this example:
import * as WebSocket from 'ws'
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [
SocketModule,
],
}).compile()
app = moduleFixture.createNestApplication()
app.useWebSocketAdapter(new WsAdapter(app))
await app.init()
})
it('should connect successfully', (done) => {
const address = app.getHttpServer().listen().address()
const baseAddress = `http://[${address.address}]:${address.port}`
const socket = new WebSocket(baseAddress)
socket.on('open', () => {
console.log('I am connected! YEAAAP')
done()
})
socket.on('close', (code, reason) => {
done({ code, reason })
})
socket.on ('error', (error) => {
done(error)
})
})
this example is based on this answer

Related

Express socket.io to netty socket.io in spring

I need to stream data from my backend (in spring) to my angular frontend.
I cant get the netty socket.io implementation working.
public ConnectListener onUserConnectWithSocket = new ConnectListener() {
#Override
public void onConnect(SocketIOClient socketIOClient) {
log.info("Client connected: " + socketIOClient.getSessionId());
socketIOClient.sendEvent("getAllDashboardData", generateRandomValues());
}
};
public DataListener<String> getAllDashboardData = new DataListener<String>() {
#Override
public void onData(SocketIOClient socketIOClient, String message, AckRequest ackRequest) throws Exception {
log.info("Message received: " + message);
socketIOClient.sendEvent("getAllDashboardData", generateRandomValues().toString());
}
};
when i have something like this, the EventListener never gets called (does not log User requested data). Hence, the onConnect logs that the frontend connected.
I tried out the frontend call using express!
This simple examples works perfect:
module.exports = (io) => {
io.on('connect', (socket) => {
console.log('user connected');
socket.on('getAllDashboardData', (data) => {
//send some data to client back
socket.emit('getAllDashboardData', {data: 'data'});
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
}
how could i write this in spring?
I also tested the backend with postman and it works fine!
The answer is:
import { Injectable } from '#angular/core';
const io = require('socket.io-client');
import {Observable} from "rxjs";
#Injectable({
providedIn: 'root'
})
export class SocketService {
socket: any;
readonly uri = 'ws://localhost:8085';
constructor() {
this.socket = io(this.uri);
}
listen(eventName: string) {
return new Observable((resolve) => {
this.socket.on(eventName, (data: any) => {
// this.socket.emit(eventName, data); maybe don't use this produces 1000 of calls
resolve.next(data);
});
});
}
emit(eventName: string, data: any) {
this.socket.emit(eventName, data);
}
}
and use socket.io-client version 2.3.0 to work with netty spring.

Setting up graphql yoga with websockets in nextjs api

In graphql yoga documentation, I found this example for using graphql yoga with websockets but it's in nodejs environment. How can I setup a server in nextjs api using this example? All advice is appreciated, thanks.
import { createServer } from '#graphql-yoga/node'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
async function main() {
const yogaApp = createServer({
graphiql: {
// Use WebSockets in GraphiQL
subscriptionsProtocol: 'WS'
}
})
// Get NodeJS Server from Yoga
const httpServer = await yogaApp.start()
// Create WebSocket server instance from our Node server
const wsServer = new WebSocketServer({
server: httpServer,
path: yogaApp.getAddressInfo().endpoint
})
// Integrate Yoga's Envelop instance and NodeJS server with graphql-ws
useServer(
{
execute: (args: any) => args.rootValue.execute(args),
subscribe: (args: any) => args.rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx)
const args = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe
}
}
const errors = validate(args.schema, args.document)
if (errors.length) return errors
return args
}
},
wsServer
)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})

Could anyone provide a fastapi websocket endpoint which could connect with the example given for RTK Query streaming updates

I'm trying to get my head around RTK Query as it applies to websockets. The example given is
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
import { createEntityAdapter, EntityState } from '#reduxjs/toolkit'
import { isMessage } from './schemaValidators'
export type Channel = 'redux' | 'general'
export interface Message {
id: number
channel: Channel
userName: string
text: string
}
const messagesAdapter = createEntityAdapter<Message>()
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getMessages: build.query<EntityState<Message>, Channel>({
query: (channel) => `messages/${channel}`,
transformResponse(response: Message[]) {
return messagesAdapter.addMany(
messagesAdapter.getInitialState(),
response
)
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
const ws = new WebSocket('ws://localhost:8080')
try {
await cacheDataLoaded
const listener = (event: MessageEvent) => {
const data = JSON.parse(event.data)
if (!isMessage(data) || data.channel !== arg) return
updateCachedData((draft) => {
messagesAdapter.upsertOne(draft, data)
})
}
ws.addEventListener('message', listener)
} catch {}
await cacheEntryRemoved
ws.close()
},
}),
}),
})
export const { useGetMessagesQuery } = api
for the frontend. It looks as though the idea is to make a request to /messages/{channel} and on successful receipt and caching of these messages to connect to a websocket api. I'm struggling to create a fastapi app that connects with this example so I can figure out the workings. Does anyone have an example they might be willing to please share?

Deno: How to use WebSocket with oak?

As Deno was released last Wednesday, I tried to play with it and redo the little example Chat App, I tried this:
import { Application, Router, send } from 'https://deno.land/x/oak/mod.ts';
import { listenAndServe } from 'https://deno.land/std/http/server.ts'
const app = new Application();
const router = new Router();
router
.get('/ws', handleSocket);
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: HTTP_PORT });
app.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std/ws/mod.ts'
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
const users = new Map<string, WebSocket>()
export const handleSocket = async (ctx: any) => {
if (acceptable(ctx.request.serverRequest)) {
const { conn, r: bufReader, w: bufWriter, headers } = ctx.request.serverRequest;
const socket = await acceptWebSocket({
conn,
bufReader,
bufWriter,
headers,
});
await socketEventHandlers(socket);
} else {
throw new Error('Error when connecting websocket');
}
}
...
export const socketEventHandlers = async (ws: WebSocket): Promise<void> => {
// Register user connection
const userId = v4.generate()
users.set(userId, ws)
await broadcast(`> User with the id ${userId} is connected`)
// Wait for new messages
for await (const event of ws) {
const message = typeof event === 'string' ? event : ''
await broadcast(message, userId)
// Unregister user conection
if (!message && isWebSocketCloseEvent(event)) {
users.delete(userId)
await broadcast(`> User with the id ${userId} is disconnected`)
}
}
}
socket.ts
The websocket connection works perfectly with the import { listenAndServe } from 'https://deno.land/std/http/server.ts'
, but with the code above I got errors like WebSocket connection to 'ws://localhost:3000/ws' failed: Invalid frame header.
Does anybody have any tips to solve it? Thx ;)
TL;DR - This has been updated since answer was accepted and is much simpler now.
router.get('/ws', async ctx => {
const sock = await ctx.upgrade();
handleSocket(sock);
});
Credit https://github.com/oakserver/oak/pull/137
The issue happens because you're using the wrong version of the libraries. Always use versioned URLs in Deno.
For Deno 1.0.0, you'll need to use oak v4.0.0 & std v0.51.0
app.ts
import { Application, Router, send } from 'https://deno.land/x/oak#v4.0.0/mod.ts';
socket.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std#0.51.0/ws/mod.ts'
import { v4 } from 'https://deno.land/std#0.51.0/uuid/mod.ts'
Once you make those changes, you'll be able to connect correctly to the WebSocket Server.
const ws = new WebSocket("ws://127.0.0.1:8080/ws")
ws.onopen = function () {
ws.send('OAK is working!')
}

How to get the socket-id in Angular using Socket-io.client

In my angular app I am using socket.io-client npm package to make a socket-io communication to another node-server.
I have the following code forthe same.
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import * as io from 'socket.io-client';
#Injectable({
providedIn: 'root'
})
export class DataService {
constructor() { }
private url = 'http://localhost:3000';
private socket;
getLiveData1() {
let observable = new Observable(observer => {
this.socket = io(this.url);
console.log("THIS SOCKET IS:getLiveData1");
this.socket.on('connect', function() {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
console.log(this.socket.socket.id);
});
this.socket.on('message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
}
})
return observable;
}
I am trying to access the client id only on the connect event.
this.socket.on('connect', function() {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
console.log(this.socket.socket.id);
});
however both the log-statements where i am trying to log the socket-id using : this.socket.id or this.socket.socket.id errors out saying that this.socket is undefined
How can i get the client-side socket-id in this case?
From docs
https://socket.io/docs/client-api/#socket-id
socket.id
(String)
An unique identifier for the socket session. Set after the connect event is triggered, and updated after the reconnect event.
const socket = io('http://localhost');
console.log(socket.id); // undefined
socket.on('connect', () => {
console.log(socket.id); // 'G5p5...'
});
You doing it right, your problem that you are using es5 function, that doesn't keep this context. Replace it with arrow functions. Or bind context.
this.socket.on('connect', /* arrow function */() => {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
});
this worked for me (Angular 6)
ngOnInit(): void {
this.socket = io.connect('http://localhost:5000');
this.socket.on('connect', this.socketOnConnect)}
above is the initialization code for socket and binding an angular method to "ON CONNECT" method
socketOnConnect() {
console.log('connected');
var socket = this;
console.log(socket['id']); } // prints socket id
the socket_on_connect method has a scope of Socket itself, so if we use this inside the method, it displays socket object. hence the above method works
npms used
"#types/socket.io-client": "^1.4.32"
"socket.io-client": "^2.3.0"

Resources