How to make a Server for Websocket secure with node.js - javascript

My websocket server, based on node.js, works for ws:// but not for wss://
The server runs on my Raspberry Pi B 3+. Now that I have changed ws:// to wss:// in my JavaScript file, it does not work anymore.
The node.js server:
const WebSocket = require('ws');
var wss = new WebSoket.Server({ port: 4445 });
wss.on('connection', function connection(ws) {
console.log("New client connected.");
ws.on('message', function incoming(data) {
console.log(data);
ws.close();
});
ws.on('close', function close() {
console.log("Client disconnected.");
});
});
The JavaScript client:
var connection = new Websocket('wss://myDomain:4445');
connection.onopen = function () {
connection.send("Hello");
connection.close();
}
connection.onerror = function (error) {
console.log(error);
connection.lose();
}
'myDomain' is a subdomain that refers to the IP of the Raspberry Pi via dns.
I get the following error:
WebSocket connection to 'wss://myDomain:4445/' failed: Error in
connection establishment: net::ERR_CONNECTION_CLOSED

Maybe it will help you
Example:
Node server.js
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const axios = require("axios");
const port = process.env.PORT || 4445;
const index = require("./routes/index");
const app = express();
app.use(index);
const server = http.createServer(app);
const io = socketIo(server);
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => getApiAndEmit(socket), 10000);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
const getApiAndEmit = async socket => {
try {
const res = await axios.get(
"https://b.application.com/api/v1/scores?expand=createdBy"
);
socket.emit("FromAPI", res.data); // Emitting a new message. It will be consumed by the client
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
server.listen(port, () => console.log(`Listening on port ${port}`));
Client in React
import socketIOClient from "socket.io-client";
class App extends Component {
constructor (props) {
super(props);
this.state = {
scores: []
endpoint: "http://127.0.0.1:4445"
}
}
componentDidMount() {
const { endpoint } = this.state;
const socket = socketIOClient(endpoint);
socket.on("FromAPI", data => this.setState({ scores: data }));
}
render () {
<div>
</div>
)
}
}
export default App;

Related

Integrating websocket at React client

I am trying to connect a backend layer running on localhost, below is the source code:
const { createServer } = require("http");
const cors = require("cors");
const photos = require("./photos");
const app = require("express")();
const WebSocket = require("ws");
app.use(cors());
app.get("/", (req, res) => {
res.status(200).json({});
});
app.get("/photos", (req, res) => {
res.status(200).json({ photos });
});
const clients = new Set();
app.post("/photos/:id", (req, res) => {
const photo = photos.find((p) => {
return p.id === req.params.id;
});
photo.status= "PENDING";
// Send back an approval
const timeout = (3 + Math.floor(Math.random() * 4)) * 1000;
setTimeout(() => {
photo.status = "APPROVED";
clients.forEach((ws) => {
ws.send(JSON.stringify({ event: "APPROVED", photo }));
});
}, timeout);
res.status(200).json({ photo });
});
const port = process.env.PORT || 3001;
const server = createServer(app);
server.listen(port, () => {
console.log(`Starting server on port ${port}`);
});
const wss = new WebSocket.Server({ path: "/ws", server });
wss.on("connection", (ws) => {
clients.add(ws);
console.log("WebSocket connection established");
ws.on("close", () => {
clients.delete(ws);
console.log("WebSocket connection closed");
});
});
As on react client we can't use "ws" so I tried using both "websocket" package but I am not able to connect to "http" as it is not supported. Below is the source code:
import React from "react";
import { w3cwebsocket as W3CWebSocket } from "websocket";
const client = new W3CWebSocket('http://localhost:3001/ws');
// const client = new W3CWebSocket('ws://localhost:3001');
function App() {
React.useEffect(
() => {
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage = (message) => {
console.log(message);
};
}, []
)
return null
}
Need help at client level to connect to 'http://localhost:3001/ws' to establish and listen connection.
You are connecting to the wrong url. In following line on the server, you specify a path as /ws .
const wss = new WebSocket.Server({ path: "/ws", server });
So you need to connect to the specified path.
const client = new W3CWebSocket('ws://localhost:3001/ws');
If you remove the path: "/ws" from your createServer, the url ws://localhost:3001 (notice, no /ws path.. )should also work as expected.
Here's an example which worked on my machine ( without react, it's for showing socket connection.)
Client
var W3CWebSocket = require('websocket').w3cwebsocket;
const client = new W3CWebSocket('ws://localhost:3001/ws');
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage = (message) => {
console.log(message);
};
client.onerror = function() {
console.log('Connection Error');
};
Server
const { createServer } = require("http");
const cors = require("cors");
const app = require("express")();
const WebSocket = require("ws");
app.use(cors());
const port = process.env.PORT || 3001;
const server = createServer(app);
server.listen(port, () => {
console.log(`Starting server on port ${port}`);
});
const wss = new WebSocket.Server({ path: "/ws", server });
wss.on("connection", (ws) => {
console.log("WebSocket connection established");
ws.on("close", () => {
console.log("WebSocket connection closed");
});
});

How do you send data to socket with different emit?

I want to send data to 2 different components using socket.io, now the first emit works but the second isn't even activated, I tried to invert the order of them inside io.on function and only the first written one works. Is the second waiting for the first emit to end? How can I make them work in parallel?
This is my code:
import {createServer} from "http";
import socketIo from "socket.io";
import express from "express";
import cors from "cors";
import mongoose from "mongoose";
import { MONGODB_URL } from "./config/config";
import jwt from "jsonwebtoken";
import { watchLeaves } from "./database/controllers/LeaveControllers";
import { watchCalendars } from "./database/controllers/calendarControllers";
const port = process.env.PORT || 3333;
mongoose.connect(MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => {
console.log("Connected to database");
});
const app = express();
app.use(cors());
app.get("/", (req, res) => {
res.send({ response: "I am a websocket!" }).status(200);
});
const verifySocketAuth = (header) => new Promise((resolve, reject) => {
if (typeof header !== "undefined") {
const headerArray = header.split(" ")
const token = headerArray[1]
return jwt.verify(token, "Bearer", (err, auth) => {
if (err) reject(new Error(err))
else resolve(auth)
})
}
})
const server = createServer(app);
const io = socketIo(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
}
});
io.use(async (socket, next) => {
const header = socket.handshake.headers["authorization"];
await verifySocketAuth(header)
console.log("token", await verifySocketAuth(header))
next()
});
const getLeavesAndEmit = async (socket) => {
const lastRecord = await watchLeaves();
socket.emit("FromLeaves", lastRecord);
};
const getCalendarsAndEmit = async (socket) => {
const lastRecord = await watchCalendars();
console.log("last record calendars==>", lastRecord);
socket.emit("FromCalendars", lastRecord);
};
io.on("connection", async (socket) => {
console.log("Client connected");
await getLeavesAndEmit(socket);
await getCalendarsAndEmit(socket);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
server.listen(port, () => console.log(`Soket Server listening on ${port}`));
I guess socket.emit doesn't return a Promise, so when you do await getLeavesAndEmit(socket) you are awaiting something that will never resolve. After socket.emit try to return something static from your async function, like return "done"

SOCKET.IO LISTEN HTTPS

i had my app with http, but i want add https, but i don't know how enable https on socket.io. The problem when i replaced http in 'require('socket.io)(https ....)' socket.io launch 404 error.
const http = require('http').Server(app);
const io = require('socket.io')(http, { pingInterval: 2000, pingTimeout: 7000,});
var https = require('https');
var fs = require('fs');
io.on('connect', socket => {
socket.on('ask', function(msg) {
search
.query(msg, socket.user, socket.token)
.then(result => {
socket.emit('response', result);
})
.catch(err => {
console.log(err);
socket.emit('response', {
msg: { text: "Lo siento, no puedo entender eso " },
type: 'error',
});
});
});
skills.registerClient(socket, socket.user).catch(err => {
console.warn('Failed to register client', err);
});
});
const port = await global.db.getGlobalValue('port');
http.listen(port, () => {
console.log(`Server started on http://localhost:${port}`);
});
const sslPort = '1443';
const server = https.createServer(httpsOptions, app)
.listen(sslPort, () => {
console.log(`Https server on https://localhost:${sslPort}`);
});
I am Currently using this:
const fs = require('fs');
const options = {
rejectUnauthorized: true,
key: fs.readFileSync('<SSL KEY PATH>/privkey.pem'),
cert: fs.readFileSync('<SSL CERT PATH>/fullchain.pem')};
const app = require('https').createServer(options);
const io = require('socket.io')(app);
io.on('connection', socket => {
console.log("New Connection");
socket.on('ask', function(msg) {
//your code here
});
});
app.listen(<PORT>); //e. g. 8080, Integer
First, you need to create cert and key for ssl.
After key and cert are generated, you can code like the following:
const express = require('express')
const https = require('https')
const fs = require('fs')
const port = 8888
const cert = fs.readFileSync('/path/to/your_cert.cert')
const key = fs.readFileSync('/path/to/your_key.key')
const SSLOption = {
key, cert
}
app = express()
let server = https.createServer(SSLOption, app)
server.listen(port, () => {
console.log('server started on port: ' + port)
})
app.get('/', (req, res) => {
res.send('Working good...')
})
const io = require('socket.io')(server)
io.on('connection', (socket) => {
console.log(socket)
})
enter image description here
This is the error.
var httpsOptions = {
key: fs.readFileSync(path.join(__dirname + '/ssl/localhost-key.pem')),
cert: fs.readFileSync(path.join(__dirname + '/ssl/localhost.pem'))
};
This is my httpsOptions.

The socket.io connection returns an empty array. Ways to receive data in React without reloading the page?

I create a chrome extension in React. I am trying to download data via socket.io. I didn't get any errors, but the array is empty.
I should set the header and token, but I do not know where to do it in the app.js server. Where can I set:
const token = '12345'
headers: {
  'Authorization': `Bearer $ {token}`
}
How can I set port in server app.js and endpoint in client app.jsx if I create chrome extension?
Is there any other way (other than socket.io) to receive data in the method componentDidMount() in React without reloading the page?
When I go to the address http://127.0.0.1:4001, the console.log displays to me New client connected and Error: undefined
Server
//app.js
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const axios = require("axios");
const port = process.env.PORT || 4001;/How port in `chrome extension?`
const index = require("./routes/index");
const app = express();
app.use(index);
const server = http.createServer(app);
const io = socketIo(server); // < Interesting!
const token = "12345";
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => getApiAndEmit(socket), 10000);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
const getApiAndEmit = async socket => {
try {
const res = await axios.get(
"https://b.application.com/api/v1/scores?expand=createdBy"
); // Getting the data from DarkSky
socket.emit("FromAPI", res.data); // Emitting a new message. It will be consumed by the client
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
server.listen(port, () => console.log(`Listening on port ${port}`));
//index.js
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
res.send({ response: "I am alive" }).status(200);
});
module.exports = router;
Client
//app.jsx
import socketIOClient from "socket.io-client";
class App extends Component {
constructor (props) {
super(props);
this.state = {
scores: []
endpoint: "http://127.0.0.1:4001" /How endpoint in `chrome extension`?
}
}
componentDidMount() {
const { endpoint } = this.state;
const token = "12345";
const socket = socketIOClient(endpoint);
socket.on("FromAPI", data => this.setState({ todos: data }));
}
render () {
<div>
</div>
)
}
}
export default App;
I solved it:
const getApiAndEmit = async socket => {
try {
const res = await axios.get(
"https://b.application.com/api/v1/scores?expand=createdBy",
headers: {
Authorization: `Bearer ${token}`
}
);
socket.emit("FromAPI", res.data); // Emitting a new message. It will be consumed by the client
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
-----------Try Replacing app.js:- -----------------
const express = require("express");
const http = require("http");
const socketIo = require("socket.io");
const axios = require("axios");
const port = process.env.PORT || 4001;/How port in `chrome extension?`
const index = require("./routes/index");
const app = express();
app.use(index);
const server = http.createServer(app);
const io = socketIo(server); // < Interesting!
const token = "12345";
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => getApiAndEmit(socket), 10000);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
const getApiAndEmit = async socket => {
try {
const res = await axios("https://b.application.com/api/v1/scores?expand=createdBy", {
method: 'GET',
headers: {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*",
'token': token
}
}); // Getting the data from DarkSky
console.log("res.data:- "+JSON.stringify(res.data,null,4));
socket.emit("FromAPI", res.data); // Emitting a new message. It wil be consumed by the client
} catch (error) {
console.error(`Error: ${error.code}`);
}
};
server.listen(port, () => console.log(`Listening on port ${port}`));
Try modifying app.jsx as:
import socketIOClient from "socket.io-client";
class App extends Component {
constructor(props) {
super(props);
this.state = {
scores: [],
receivedData:[],
endpoint: "https://b.application.com/api/v1/scores?expand=createdBy",
token: "12345"
}
this.onDataReceived = this.onDataReceived.bind(this);
this.socket = socketIOClient(this.state.endpoint, {
extraHeaders: {
Authorization: `Bearer ${this.state.token}`
}
});
this.socket.on("FromAPI", this.onDataReceived);
}
onDataReceived(receivedData) {
console.log("receivedData ", receivedData);
this.setState({
receivedData: receivedData
});
}
render() {
return(
<div></div>
)
}
}
export default App;
Try modifying app.js: function getApiAndEmit
replace the line: socket.emit("FromAPI", res.data);
io.emit("FromAPI", res.data);

Socket.io - listen events in separate files in node.js

For example my idea is:
File1.js
io.sockets.on('connection', function (socket) {
socket.on('file1Event', function () {
//logic
});
});
File2.js
io.sockets.on('connection', function (socket) {
socket.on('file2Event', function () {
//logic
});
});
This code is for a node server, will I have problems with this code?
Nope, just use the same "io" object.
File1.js
exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {
socket.on('file1Event', function () {
console.log('file1Event triggered');
});
});
}
File2.js
exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {
socket.on('file2Event', function () {
console.log('file2Event triggered');
});
});
}
app.js
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
, file1 = require('./File1')(io)
, file2 = require('./File2')(io)
app.listen(3000);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
index.html
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.emit('file1Event'); // 'file1Event triggered' will be shown
socket.emit('file2Event'); // 'file2Event triggered' will be shown
</script>
Be careful not to generate a new connection event for each file. You should use the same on('connection') event, otherwise after 10 files imported, you will get this error from node: MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 connection listeners added. Use emitter.setMaxListeners() to increase limit.
The better way is to do like this in your main file:
io.on('connection', function (socket) {
require('pathToSocketRoutesFile1')(socket);
require('pathToSocketRoutesFile2')(socket);
require('pathToSocketRoutesFileN')(socket);
return io;
};
and in each separate file:
module.exports = function(socket) {
socket.on('eventName1', function() {
//...
});
socket.on('eventName2', function() {
//...
});
};
Another option is to create a rootSocket which handles the initial connection and then passes the socket to other handlers.
const rootSocket = (io) => {
io.sockets.on('connection', (socket) => {
authorization(socket);
chat(socket);
});
};
exports.default = rootSocket;
You Can use IO module in any route just create global middleware.
socketiomw.js
module.exports = (io)=>{
return (req,res,next)=>{
req.io = io;
next();
}
}
middlewares.js
module.exports.global = {
socketIo:require('./socketiomw'),
// add other global middleware
};
index.js
const express = require('express');
const app = express();
const port = 3000;
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
//global middleware initialization
app.use(require('./middlewares').global.socketIo(io));
app.get('/notify',(req,res)=>{
req.io.emit("hello");
req.io.to("someRoom").emit("some event");
req.io.to("room1").to("room2").to("room3").emit("some event");
req.io.of("/").adapter.on("create-room", (room) => {
console.log(`room ${room} was created`);
});
req.io.of("/").adapter.on("join-room", (room, id) => {
console.log(`socket ${id} has joined room ${room}`);
});
req.json({ success:true })
);
server.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
I used this in global middleware so i can use this io module in any routes
rootSocket.js :
const rootSocket = (io) => {
io.on('connection', (socket) => {
console.log('New connection');
// possibility to outsource events
socket.on('myEvent', () => {
console.log('myEvent triggered');
});
});
}
module.exports = rootSocket;
index.js :
const express = require('express');
const app = express();
//app.use(express.json());
//const cors = require('cors');
//app.use(cors());
const http = require('http');
const server = http.createServer(app);
const socketIo = require('socket.io');
const io = socketIo(server);
const rootSocket = require('./rootSocket')(io);
const port = 8000;
// process.env.PORT for production
server.listen(port, () => console.log('server started on ' + port));

Categories

Resources