Socket.io Client: Always invalid namespace message - node.js

Server code:
import http from 'http';
import Koa from 'koa';
import { Server } from 'socket.io';
(async () => {
const app = new Koa();
var server = http.createServer(app.callback());
var io = new Server(server, {
path: '/seacher',
transports: ['websocket'],
});
io.on('connection', (socket) => {
setTimeout(() => socket.emit('message', { say: 'hello' }), 1000);
socket.on('message', (msg) => {
console.log('[msg]', msg);
});
});
server.listen(3000)
})();
Client code:
var socket = io('http://localhost:3000/seacher', {
path: '/seacher',
autoConnect: false,
transports: ['websocket'],
});
socket.on('error', (err) => console.log('error', err));
socket.on('connect', () => console.log('connect'));
socket.connect();
No any messages in browser / nodejs console.
In the Network tab in browser a lot of connections with messages like

Change the client code to this:
var socket = io('http://localhost:3000', { // note changed URL here
path: '/seacher',
autoConnect: false,
transports: ['websocket'],
});
The path option specifies what URL socket.io is going to use internally. You put that in the path option as you have already done in both client and server.
If you put something in the URL you specify like you had 'http://localhost:3000/seacher', then that is the namespace /seacher that you are trying to connect, but your server does not support that namespace.
This is a confusing part of socket.io's design, but path and namespace are not the same thing in socket.io. Do not confuse or mix them.
FYI, there is rarely a reason to customize the path option the way you have done unless you are trying to run more than one socket.io server shared on the same http server (something it does not appear you are doing). By default, the path is /socket.io which makes a lot of sense in logs and debuggers and when accessing the client-side library. I would suggest you remove the path option from both client and server and let it default to /socket.io. And, don't use a path in your connection URL either as that specifies a namespace, not a path.

Related

Connecting an open client socket to rails endpoint

Currently working on a project which is Vue on top of rails 4. I am consuming webhooks from the square API, and I want to be able to get that data into vue so that my data can be updated in real time as square data changes. I've asked this question before, but I'm at a slightly different point in the problem; this is getting down to the nuts and bolts.
Currently, on the server side, I have webhooks setup to fire off to a rails controller, and that works well, i can see that data coming in.
On the client side, I have a socket open and listening to that same rails controller endpoint.
What I'm having trouble with is that even though I can see the webhook hit the controller, and the socket is active, I cant seem to get the socket to pick up on the controller endpoint emitting data. Doesnt seem like the controller endpoint is passing the data along as I expect, and I suspect there is both a gap in my knowledge of rails about how to emit data properly, and how to properly consume it with a socket.
What am I missing to be able to connect the dots here?
Caveats:
I realize this might be possible with ActionCable, and as a last resort I may go with that, but my client is very against upgrading from rails 4 (heavy lift). If this happens to be the only way to do it, so be it, but I want to explore all other options first.
Im no rails expert, and have very little experience with sockets. So this whole approach might, and probably IS foolish. I also may be misunderstanding some parts of how some of these technologies work.
I am unfortunately, bound to using rails and vue.
I am working on localhost, and using ngrok to create a proper URL for the webhooks to hit. I doubt its a problem, but maybe?
I have explored using a node server behind the scenes and sending webhooks directly to that and listening to that server with the socket. Couldnt get that to work either, but tips on how to achieve that if its a good idea are also welcome.
For reference:
Rails Controller:
class WebhooksController < ApplicationController
skip_forgery_protection
def order_update
p request
p params
if request.headers['Content-Type'] == 'application/json'
data = JSON.parse(request.body.read)
else
# application/x-www-form-urlencoded
data = params.as_json
end
render json: data
end
end
Client Code (Vue && Socket):
import { createApp } from 'vue';
import SquareOrders from '../views/SquareOrders.vue';
import VueSocketIO from 'vue-socket.io';
import { io } from "socket.io-client";
const socket = io("http://localhost:3000", {
transports: ["polling", "flashsocket"],
withCredentials: true,
path: '/webhooks/order_update'
});
export default function loadOrdersApp(el, pinia) {
const app = createApp(SquareOrders);
app.use(pinia)
.use(new VueSocketIO({
debug: true,
connection: socket
}))
.mount(el);
}
Suggestions on better approaches are appreciated, as are corrections to my basic knowledge if I am misunderstanding something.
So, after a lot of trial and error, i managed to answer this with a different approach.
as far as I can tell, actually listening to the rails endpoint without something like ActionCable seems impossible, so I booted up and express server to run in the background and ingest incoming webhooks.
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors');
// Create a new instance of express
const app = express()
// Tell express to use the body-parser middleware for JSON
app.use(bodyParser.json())
// ALLOW OUR CLIENT SIDE TO ACCESS BACKEND SERVER via CORS
app.use(cors({
origin: 'http://localhost:3000'
}));
// Tell our app to listen on port 3000
const server = app.listen(8080, function (err) {
if (err) {
throw err
}
console.log('Server started on port 8080')
})
const io = require('socket.io')(server);
app.set('io', io)
// Route that receives a POST request to /webhook
app.post('/webhook', function (req, res, next) {
const io = req.app.get('io');
console.log(req.body)
io.sockets.emit('orderUpdate', req.body)
//res.send(req.body)
res.sendStatus( 200 );
next();
})
io.on('connection', function(socket){
console.log('A connection is made');
});
With that in place, I pointed my webhook to localhost through ngrok, and then added logic in Vue through https://www.npmjs.com/package/vue-socket.io-extended
import { io } from "socket.io-client";
import SquareOrders from '../views/SquareOrders.vue';
import VueSocketIOExt from 'vue-socket.io-extended';
import socketStore from '../stores/sockets.js';
const socket = io("http://localhost:8080", {
transports: [ "websocket", "polling"],
});
export default function loadOrdersApp(el, pinia) {
const app = createApp(SquareOrders);
app.use(pinia)
//.use(socketStore)
.use(VueSocketIOExt, socket)
.mount(el);
}
To then listen for incoming socket emits on the socket.io channel.
With those both in place, i was able to ingest those socket emissions in my frontend app
export default {
name: "SquareOrders",
components: {
Filters,
GlobalLoader,
OrdersList,
FilterGroup,
Filter,
OrderActions
},
// LISTENING FOR THE SOCKETS IN VUE COMPONENT
sockets: {
connect() {
console.log('socket connected in vue')
},
orderUpdate(val) {
console.log(val)
debugger;
console.log('this method was fired by the socket server. eg: io.emit("customEmit", data)')
}
},
mounted() {
const os = orderStore();
os.getOrders();
os.getSourcesList();
os.getSandboxOrders();
console.log(this.$socket)
this.$socket.client.io.on('orderUpdate', (payload) => {
console.log(payload)
})
setInterval(function () {
os.getOrders();
}, 60000);
},
computed: {
...mapState(orderStore, {
orders: store => store.orders,
initalLoad: store => store.flags.loaded,
filterDefs: store => store.filters.definitions,
sandbox: store => store.sandbox
})
},
};

Connect to a third party server (twelvedata) from my own express server through web socket connection string

I want to connect to the twelevedata server through its provided socket connection to receive information.
import * as dotenv from 'dotenv'
import WebSocket from 'ws';
import express from 'express'
const app = express();
//setting up env
dotenv.config()
// setting up the websocket
const ws = new WebSocket(`wss://ws.twelvedata.com/v1/quotes/price?apikey=${process.env.API_KEY_TWELVEDATA}`);
const payload = {
"action": "subscribe",
"params": {
"symbols": "AAPL,INFY,TRP,QQQ,IXIC,EUR/USD,USD/JPY,BTC/USD,ETH/BTC"
},
}
ws.on('connection',function (steam) {
ws.on('open', (data) => {
console.log("data ==>",data);
ws.emit('subscribe',payload)
})
ws.on('subscribe', (data) => {
console.log("data ==>",data);
})
})
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`I am listening at ${port}`);
});
I created a websocket with my websocket connection on an express application but I am unable to receive any information from the twelvedata server regarding the subscribe event that I have emitted !
This is how the websocket should work as shown by the twelvedata website (look into the screen shots)
I am unable to connect and emit the subscribe event given by the twelvedata's documentation
You don't want to emit events from the websocket (that's for events you want to handle locally), but send, i.e., replace
ws.emit('subscribe',payload)
with
ws.send(payload)
// sending the parameters
ws.on('open', function open() {
ws.send(JSON.stringify(payload));
});
ws.on('message', function message(data) {
// receiving data
console.log('data ===>: ', JSON.parse(data));
});
ws.send did the charm for me

Nuxt Socket.io is unresponsive without any error

I'm using nuxt-socket-io along with an Express.js server with socketio as well.
When I start up the client/server, the server-side socket.io connects and console for the server will print "connected").
When I try to connect with nuxt (the client-side part of socket.io), nothing happens. Mounted() is called correctly (the "hm" console log prints out), but the socket never seems to be made. I tried testing this.socket.on('connect-error') and this.socket.on('connect-timeout') for the CLIENT side (the server-side socket.io connects properly), but nothing was ever emitted after about 5 minutes of waiting. The persist: true isn't the issue either; I tried to remove it and had the same issue. I initially didn't have this.socket.open() and had the same problems, so I don't think that line does anything, either.
NuxtJS frontend
mounted() {
console.log("hm");
this.socket = this.$nuxtSocket({
channel: '/profile',
persist: true
})
this.socket.open();
this.socket.on('connection', (socket) => {
console.log("connected")
})
//Listens for the SERVER-EMITTED event 'send'
this.socket.on('send', (message) => {
console.log("client received event!")
console.log(message);
});
},
methods: {
sendMessage() {
// This method IS CALLED correctly with a button (I checked), but the emit is not transmitting
// sends a CLIENT-EMITTED event to the server
this.socket.emit('send-message', {
message: "hey!"
}, (res) => {console.log(res)})
},
nuxt.config.js
io: {
sockets: [{
name: 'main',
default: true,
url: 'http://localhost:3000'
}]
},
My Express Backend (port is 8080)
import express from "express";
import { db } from "./app/config/db.config";
import { authRouter } from "./app/routes/auth.router";
import * as dotenv from "dotenv";
const http = require('http');
const socketio = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketio(server, {
cors: {
origin: '*',
methods: ["GET", "POST"]
}
});
// run on connection to socket
io.on('connection', (socket: any) => {
console.log("connected")
})
// listens for the CLIENT-EMITTED event 'send-message'
io.on('send-message', (message: any) => {
console.log(message + "server received!");
// sends a SERVER-EMITTED event "send" to be received by nuxt client
io.emit('send', "message!")
})
server.listen(process.env.PORT, () => {
console.log(`Server is running on port ${process.env.PORT}`);
});
Axios is also running on port 8080, I don't know if that would cause any issues but I don't get any errors when I try to run my whole program (which includes login/registration with axios).
Anyone know why my events aren't transmitting? Thank you!
In your server code, you're adding your 'send-message' event listener to the io object, which is your main socket.io Server instance. However, event listeners should be added to the socket object you get from the connection event. Something like:
// A new connection comes in
io.on('connection', (socket: Socket) => {
// Now we listen for the event that comes from this particular socket
socket.on('send-message', (message: any) => {
// You also use the Socket instance to send events back to clients, not to the `io` Server.
socket.emit('send', "message!");
});
});
The Socket instance docs have more info on what you can do with these sockets.

Calls to Node.js Socket.io on localhost not working, logging, emitting, creating errors, or doing anything else, but non-socket.io calls work fine

I'm trying to make a real-time socket.io app in Angular and Node, exactly like what is explained here. I set up the server and the client connection, but the connection is never made.
Server Code:
const port = process.env.PORT || 8080;
const host = process.env.BASE_URL || 'http://127.0.0.1';
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('a user connected');
socket.broadcast.emit('message-broadcast','Started');
socket.on('message', (msg) => {
console.log(msg);
socket.broadcast.emit('message-broadcast',msg);
})
});
server.listen(port, () => console.log(`listening on port ${port} + host ${host}`));
I had some issues with setting up the client code as it's explained in the tutorial. When I used the line
import * as io from 'socket.io-client';
and then tried to call
this.socket = io(environment.SocketEndpoint);
I received an error "This expression is not callable."
After some fiddling around I replaced the import line with
import { io } from 'socket.io-client';
import { Socket } from 'socket.io-client';
and the connect function with
setupSocketConnection(){
this.socket = io(environment.SocketEndpoint).connect();
this.socket.on('message-broadcast', (data: string)=>{
console.log('Recieved message:',data);
})
console.log('Set up socket',this.socket);
}
This MIGHT be related to the issue, but I don't think it is.
When I do this, it APPEARS to work, in the sense that I can see periodic calls in the Networking tab, which look like
http://localhost:8080/socket.io/?EIO=4&transport=polling&t=NbRCamo
But none of these calls do anything. They show up red in the Networking tab. The timing tab says they are "stalled". On the server, there is no console log associated with these calls, nor does the client receive any kind of emitted message.
What's odd is that I CAN get a response by pasting this call in the browser, which looks like this:
0{"sid":"3KsDDAa1mA2OVxKIAADT","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000}
But there is still no console logging on the server. In fact any call that starts with "server.io" doesn't create any logging at all. But if I make any other call to the server that doesn't start with "socket.io", I DO get a console log recording that the call was made - even if it's only a 404 error. So I know that socket.io must be doing SOMETHING, it just isn't doing what it's supposed to.
EDIT: Okay, when I uploaded it to a different server it seems to work all right - it's only a problem with localhost, apparently. I guess I could manage, but being able to test on localhost would be convenient, so if anyone can figure this out it would be helpful.
EDIT2: It was caused by a CORS policy issue. I was able to fix it by changing the server code to this:
const port = process.env.PORT || 8080;
const host = process.env.BASE_URL || 'http://127.0.0.1';
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server, {
cors: {
origin: "http://localhost:4200",
methods: ["GET", "POST"]
}
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.broadcast.emit('message-broadcast','Started');
socket.on('message', (msg) => {
console.log(msg);
socket.broadcast.emit('message-broadcast',msg);
})
});
server.listen(port, () => console.log(`listening on port ${port} + host ${host}`));
This might be related to CORS problems. I had a similar issue where i had the socketio server running on port 5000, the angular app on 4200, which the CORS policy did not like at all. I solved this using a proxy configuration and starting the dev server with --proxy-config proxy.json.
proxy.json:
{
"/socket.io": {
"target": "http://localhost:8080",
"secure": false,
"ws": true,
"changeOrigin": true,
"logLevel": "debug",
"pathRewrite": { }
}
}
Not sure if it's the same issue for you though.

How to deal with server.handleUpgrade() was called more than once in Nodejs?

I am running a nodejs application which runs great the first time I use it but when I refresh and enter the new data Node crashes and gives me this. The error looks like this :
/www/wwwroot/domain.to/node_modules/ws/lib/websocket-server.js:267
throw new Error(
^
Error: server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration
at WebSocketServer.completeUpgrade (/www/wwwroot/domain.to/node_modules/ws/lib/websocket-server.js:267:13)
at WebSocketServer.handleUpgrade (/www/wwwroot/domain.to/node_modules/ws/lib/websocket-server.js:245:10)
at Server.upgrade (/www/wwwroot/tehran.us.to/node_modules/ws/lib/websocket-server.js:89:16)
at Server.emit (events.js:327:22)
at onParserExecuteCommon (_http_server.js:646:14)
at onParserExecute (_http_server.js:587:3)
The code on the server looks like this for express :
const app = express();
const options = {
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
};
const server = https.createServer(options, app)
server.listen(3000, function () {
console.log("Express server listening on port " + 3000);
});
I also use Websocket on the same end point and the code looks like this :
const wss = new Websocket.Server({ server })
console.log(url)
wss.on('connection', ws => {
console.log('user is connected to the server #mainpage')
I don't know how to go around it as the application seems to not create a new instance of the application for a new refresh.
How can I deal with this and make my app running continuously?
This is because you have already created the server using express and the handleUpgrade method is called inside express, so you need to inform your WebSocket server to ignore the server initiating part and continue with the existing resource as giving the options as { noServer: true }
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', function (request, socket, head) {
wss.handleUpgrade(request, socket, head, function (ws) {
wss.emit('connection', ws, request);
})
})
Well, In My project I was using express-monitor which was also using socket.io hence leading the server to handle the upgrade for ws connection request as well as socket.io initialization.
Removing express-monitor solved the issue

Resources