how to use http server and socket connection together - javascript

I am using http server with socket connection response of http request goes when client responds to socket connection i am currently doing it by storing res in a global object which i think is not a correct method, what is the correct method to do it.
const app = require('express')()
bodyParser = require('body-parser')
app.use(bodyParser.json())
const net = require('net');
var client;
var res1,currentReq;
//----------------------------------------------------------------------------
// http requests listener
//----------------------------------------------------------------------------
app.listen(8001, () => console.log('Http server listening on port 8001'));
//----------------------------------------------------------------------------
// http requests handling
//----------------------------------------------------------------------------
app.post('/makeCall', (req, res) => {
console.log('sd' + req.body)
res1 = res;
currentReq='makeCall';
client.write("something");
});
//----------------------------------------------------------------------------
// Establishing tcp connection for incoming requests
//----------------------------------------------------------------------------
var server = net.createServer(function(connection) {
console.log ('client has connected successfully!');
client = connection;
client.on('data',function(data){
switch(currentReq)
{
case 'makeCall' :
res1.end(data);
break;
}
});
});
//----------------------------------------------------------------------------
// listener for tcp connections
//----------------------------------------------------------------------------
server.listen(8000, function() {
console.log('server for localhost is listening on port 8000');
});

Based on our discussion in comments, here is an example of how one might do this using socket.io.
Using a socket.io set-up, the browser will connect to the HTTP server to make HTTP GET AND POST requests, e.g. to /makeCall. The browser will also connect to the same HTTP server to open the socket. The browser will connect by HTTP initially, then send a special handshake which will convert the connection into a WebSocket.
I still am not sure to understand your use case so perhaps this solution is not appropriate, but here is the general setup of what it might look like.
const app = require('express')()
const bodyParser = require('body-parser')
const server = require('http').Server(app);
const io = require('socket.io')(server);
const cookieSession = require('cookie-session')
app.use(bodyParser.json())
app.use(cookieSession({
keys: ['secret key', 'another one', 'a third one'],
maxAge: 24 * 60 * 60 * 1000 // cookie max-age : 24 hours
}))
//----------------------------------------------------------------------------
// http requests listener
//----------------------------------------------------------------------------
server.listen(8001, () => console.log('Http server listening on port 8001'));
// WARNING: app.listen() will NOT work here! per socket.io docks
const idToSocketMap = new Map();
//----------------------------------------------------------------------------
// http requests handling
//----------------------------------------------------------------------------
app.post('/makeCall', (req, res) => {
console.log('sd' + req.body)
if (!req.session.id) {
req.session.id = generateId();
}
//this may not be necessary, see below
req.session.currentReq = 'makeCall';
const socket = idToSocketMap.get(req.session.id)
if (socket) { //if a socket connected for that id
socket.emit("makeCall", "an argument/parameter", "something");
}
});
//----------------------------------------------------------------------------
// Establishing websocket connection for incoming requests
//----------------------------------------------------------------------------
io.on('connection', (socket) => {
//"socket" is now what would have been called "client" in old code
//get the id from the session cookie of the http request
const id = socket.request.session.id;
//put the socket in the map
idToSocketMap.set(id, socket);
//remove the socket from the map on disconnect
socket.on('disconnect', () => idToSocketMap.delete(id));
console.log('client has connected successfully!');
socket.on('makeCall', (firstParameter, something) => {
// It's still not possible to send anything back to an HTTP connection
// but we may do some things and reply here
const someData = getSomeData();
socket.emit('makeCallResponse', 'someData');
});
});

Related

Firefox can’t establish a connection to the server at wss://localhost:8000/

I am using nodejs to run the server, there is no log file
This is my server.js
const https = require('https');
const fs = require('fs');
const ws = require('ws');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
const wss = new ws.Server({noServer: true});
function accept(req, res) {
// all incoming requests must be websockets
if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {
res.end();
return;
}
// can be Connection: keep-alive, Upgrade
if (!req.headers.connection.match(/\bupgrade\b/i)) {
res.end();
return;
}
wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect);
}
function onConnect(ws) {
ws.on('message', function (message) {
let name = message.match(/([\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]+)$/gu) || "Guest";
ws.send(`${name}!`);
//setTimeout(() => ws.close(1000, "Bye!"), 5000);
});
}
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
This is my code in react
componentDidMount() {
var connection = new WebSocket('wss://localhost:8000/');
connection.onopen = function(e) {
connection.send("add people");
};
connection.onmessage = function(event) {
// alert(`[message] Data received from server: ${event.data}`);
console.log("output ", event.data);
};
}
While I am trying to connect with web-socket with my jsx file its give me an error which is Firefox can’t establish a connection to the server at wss://localhost:8000/.
Your implementaion needs some changes. In the backend server, you forgot to call the onConnect function. So your ws.on method will never call.
Also, you imported the ws and create a WebSocket server wss, but you add some event listener on ws wrongly, you should add listener on your Websocket instance (wss):
// rest of the codes ...
const was = new ws.Server({noServer: true})
wss.on('connection`) {
// do something here ...
}
// rest of the codes ...
https.createServer(options, () => {
// do something here ...
})
There are some examples of how to create the WebSocket server along with the HTTP server on ws npm page.

Using a single port for bittorrent-tracker server in Heroku

I am trying to build/deploy a tracker server for use with P2P applications using the below code. It works fine locally, but when I deploy it to Heroku, the port bindings fail because only one port is allowed.
// Create a web sockets signaling server
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
//Allow all requests from all domains & localhost
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept");
res.header("Access-Control-Allow-Methods", "POST, GET");
next();
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
let lookup = {}
const hostname = '0.0.0.0';
const port = process.env.PORT;
var Server = require('bittorrent-tracker').Server
var server = new Server({
udp: false, // enable udp server? [default=true]
http: true, // enable http server? [default=true]
ws: true, // enable websocket server? [default=true]
stats: true, // enable web-based statistics? [default=true]
})
server.on('error', function (err) {
// fatal server error!
console.log(err.message)
})
server.on('warning', function (err) {
// client sent bad data. probably not a problem, just a buggy client.
console.log(err.message)
})
server.on('listening', function () {
// fired when all requested servers are listening
console.log('Signal server http port:' + server.http.address().port)
console.log('Signal server ws port:' + server.ws.address().port)
})
// start tracker server listening! Use 0 to listen on a random free port.
server.listen(port, hostname, 'listening')
// listen for individual tracker messages from peers:
server.on('start', function (addr) {
console.log('got start message from ' + addr)
Object.keys(server.torrents).forEach(hash => {
lookup[server.torrents[hash].infoHash] = server.torrents[hash].peers.length
console.log("peers: " + server.torrents[hash].peers.length)
})
})
server.on('complete', function (addr) {})
server.on('stop', function (addr) {})
app.get('/peers', function(req, res) {
res.send(lookup);
})
app.listen(process.env.PORT, function() {
console.log('Express server port: ' + this.address().port); //Listening on port #
})
If I use process.env.PORT for both server and app, I get the following, which is expected since Heroku only allows 1 listen port:
2021-02-13T05:35:31.016101+00:00 heroku[web.1]: State changed from starting to up
2021-02-13T05:35:30.885170+00:00 app[web.1]: Express server port: 9898
2021-02-13T05:35:30.885204+00:00 app[web.1]: listen EADDRINUSE: address already in use 0.0.0.0:9898
2021-02-13T05:35:30.885205+00:00 app[web.1]: listen EADDRINUSE: address already in use 0.0.0.0:9898
If I hard code the port for either server or app, the application launches fine, but the signaling server doesn't work. No substantial logging is generated.
2021-02-13T05:38:21.141806+00:00 heroku[web.1]: State changed from starting to up
2021-02-13T05:38:20.998054+00:00 app[web.1]: Express server port: 25702
2021-02-13T05:38:20.998550+00:00 app[web.1]: Signal server http port:31415
2021-02-13T05:38:20.998683+00:00 app[web.1]: Signal server ws port:31415
Is it possible that the bittorrent-tracker server and express server can use the same port? For instance, could I get and return the list of peers within this block of code without the need for express at all?
server.on('start', function (addr) {
console.log('got start message from ' + addr)
// Could I do something here to eliminate the need for Express?
Object.keys(server.torrents).forEach(hash => {
lookup[server.torrents[hash].infoHash] = server.torrents[hash].peers.length
console.log("peers: " + server.torrents[hash].peers.length)
})
})
The documentation states:
The http server will handle requests for the following paths:
/announce, /scrape. Requests for other paths will not be handled.
But perhaps there is some way I can shim in the requests that express is handling?
Not long after asking this question, it occurred to me that I might not need express at all. It turns out that was correct.
For anyone wanting a Heroku-ready bittorrent-tracker, here is the updated code:
// Create a web sockets signaling server
let lookup = {}
const hostname = '0.0.0.0';
const port = process.env.PORT;
var Server = require('bittorrent-tracker').Server
var server = new Server({
udp: false, // enable udp server? [default=true]
http: true, // enable http server? [default=true]
ws: true, // enable websocket server? [default=true]
stats: true, // enable web-based statistics? [default=true]
})
server.on('error', function (err) {
// fatal server error!
console.log(err.message)
})
server.on('warning', function (err) {
// client sent bad data. probably not a problem, just a buggy client.
console.log(err.message)
})
server.on('listening', function () {
// fired when all requested servers are listening
console.log('Signal server http port:' + server.http.address().port)
console.log('Signal server ws port:' + server.ws.address().port)
})
// start tracker server listening! Use 0 to listen on a random free port.
server.listen(port, hostname, 'listening')
// listen for individual tracker messages from peers:
server.on('start', function (addr) {
console.log('got start message from ' + addr)
Object.keys(server.torrents).forEach(hash => {
lookup[server.torrents[hash].infoHash] = server.torrents[hash].peers.length
console.log("peers: " + server.torrents[hash].peers.length)
})
})
server.on('complete', function (addr) {})
server.on('stop', function (addr) {})

Socket.io is not connecting Node.js server to React.js client

So what I am trying is to establish a socket.io connection between an express server and a react client. Maybe also mentionable the client is hosted on netlify and the server on heroku.
The actual problem is the client seems to connect and receives the upgrade protocol to websocket messages with a HTTP 101. But nothing else happens afterwards. No "connection" or "connected" event is emitted. Maybe some one spots a mistake in the code or can guide me to the solution.
Server
/**
* App Configuration
*/
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server, {
cors: {
origin: process.env.CLIENT_URL,
methods: ["GET", "POST"],
credentials: true
}
});
/**
* Socket IO
*/
io.on("connection", (socket: any) => {
console.log("Client Connected via Socket");
// Join a conversation
const { deviceId } = socket.handshake.query;
socket.join(deviceId);
// Leave the room if the user closes the socket
socket.on("disconnect", () => {
socket.leave(deviceId);
});
});
/**
* Server Activation
*/
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}`);
});
Client
const socketRef = useRef();
useEffect(() => {
// Creates a WebSocket connection
socketRef.current = io(process.env.REACT_APP_API, {
transports: ['websocket'],
query: { deviceId: 'test-id' }
});
socketRef.current.on('connect', () => {
console.log("connected");
});
// Listens for incoming messages
socketRef.current.on("message", (message) => {
console.log(message);
});
// Destroys the socket reference
// when the connection is closed
return () => {
socketRef.current.disconnect();
};
}, []);
Edit:
Adding an image of the recurring http message about the websocket upgrade.
HTTP Message
Change Server activation line to below
server.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}`);
})

Websocket connection error: returns 101, but does not upgrade

I am setting up some websockets using ws library. I am struggling to set up authorisation using a handshake. I have added a route to our server to upgrade to a websocket connection like so:
.get(
'/chat',
authorisationFunction,
upgradeConnection,
),
The websocket server:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3030 });
This is the upgradeConnection function, which will run if authorisation is successful:
const upgradeConnection = (request, socket, head) => {
return wss.handleUpgrade(request, request.socket, head, function done(ws) {
return wss.emit('connection', ws, request);
});
}
I also have a function that listens to messages:
function webSocketsServer() {
wss.on('connection', (ws, request, client) => {
ws.on('message', message => {
ws.send(message);
});
});
}
A connection gets emitted, and from my server I get this response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: QyVvqadEcI1+ALka6j2pLKBkfNQ=
but then immediately on my client I get the error “WebSocket connection to 'ws://localhost:3000/chat’ failed: Invalid frame header”.
But when I bypass the handshake and connect directly to my websocket server, I can send messages successfully. The error is only on the client and not on the backend. What am I missing?
I am not 100% sure it is the only way but might help so I post it.
Based on this answer I would go for a server that uses the same port for http and websocket connections.
You can achieve it like this:
const { createServer } = require('http')
const ws = require('ws')
const express = require('express')
const app = express()
const server = createServer(app)
app.get('/', (req, res) => {
res.send('I am a normal http server response')
})
const wsServer = new ws.Server({
server,
path: '/websocket-path',
})
wsServer.on('connection', (connection) => {
connection.send('I am a websocket response')
})
server.listen(3030, () => {
console.log(`Server is now running on http://localhost:3030`)
console.log(`Websocket is now running on ws://localhost:3030/<websocket-path>`)
})
So your server listens on port 3030 for normal http requests. If it gets a websocket connection request on path '/websocket-path' it is passed to the the ws connection handler and from there you are good to go.

Using Websocket and http server together

I am using websocket and http server together, assigning each of them a different port, when a post request hits it sends a message to client using websocket and response of http request is send when client responds to the message.
My problem is I want to create multiple websockets but a single https server, I have tried some things but unable to do it.
Current code for creating websocket and httpserver is
const app = require('express')()
bodyParser = require('body-parser')
app.use(bodyParser.json())
const net = require('net');
var client;
var res1,currentReq;
//----------------------------------------------------------------------------
// http requests listener
//----------------------------------------------------------------------------
app.listen(8001, () => console.log('Http server listening on port 8001'));
//----------------------------------------------------------------------------
// http requests handling
//----------------------------------------------------------------------------
app.post('/getUser', (req, res) => {
console.log(req.body)
res1 = res;
currentReq='getUser';
client.write('{"route":"/","data":{"username":"' + req.body.username +'"}, "res": "' + res + '"}');
//res.end('{"Extension":"' +' data '+ '"}');
console.log("/getUser finished");
// res.end('{"Extension":"' +' data '+ '"}');
});
//----------------------------------------------------------------------------
// Establishing tcp connection for incoming requests
//----------------------------------------------------------------------------
var server = net.createServer(function(connection) {
console.log ('client has connected successfully!');
client = connection;
client.on('data',function(data){
switch(currentReq)
{
case 'getUser' :
console.log('send get user response');
res1.end(data);
break;
}
console.log(data.toString());
//res1.end(data);
});
connection.pipe(connection);
});
//----------------------------------------------------------------------------
// listener for tcp connections
//----------------------------------------------------------------------------
server.listen(8000, function() {
console.log('server for localhost is listening on port 8000');
console.log('server bound address is: ' + server.address ());
});

Categories

Resources