I have an assignment to write an http server in node.js, which is why I can't use the http module, as you'll soon see. Anyway, I need to be able to listen to both GET and POST requests. The way I understand it GET only fires data event, and post fires data and end. Here's my code:
function Server(resourceMap, rootFolder) {
this.resourceMap = resourceMap;
this.rootFolder = rootFolder;
function connectionHandler(socket) {
console.log('server connected');
console.log('CONNECTED: ' + socket.remoteAddress +':'+ socket.remotePort);
socket.setEncoding('utf8');
socket.on('data',function(data) {
var re = new RegExp("^( *)(GET)", "i");
if (data.match(re) != null) {
console.log("Handling GET request");
router.route(data,socket,handle,resourceMap,rootFolder);
}
else {
(function() {
var postData = data;
socket.on('data',function(data) {
postData += data;
});
console.log(postData);
socket.on('end',function(postData) {
console.log("END");
console.log("Handling POST request");
router.route(postData,socket,handle,resourceMap,rootFolder);
});
});
}
});
}
this.server = net.createServer(connectionHandler);
this.port = undefined;
this.startServer = function(port) { //Maybe change backlog for security reasons
this.port = port;
this.server.listen(port, function() { //'listening' listener add handle object here
console.log('server bound');});
}
}
GET requests works just fine. With POST It doesn't even enter the anonymous function.
Any ideas why and how I can solve it?
EDIT: solved - I defined the function but didn't call it :)
If you have any comments about my design I'd love to hear, as I am new to node.js and JavaSCript
Thanks
Common mistake: defined a function but did not call.
You can either:
Get rid of the function keyword after else for regex
Or add () after function (which is more recomended)
Related
I know this question was asked already, but the solutions provided didn't work for me.
Here is the websocket server code
const https = require('https');
const fs = require('fs');
const path = require('path');
const WebSocket = require('ws');
let connectionsList = [];
/*
*/
var server = https.createServer({
cert: fs.readFileSync(path.resolve(__dirname, '../cert/certificate.crt')),
key: fs.readFileSync(path.resolve(__dirname, '../cert/private.key'))
}, function (request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
wsServer = new WebSocket.Server({ server });
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
return true;
}
wsServer.on('connection', function (connection) {
//save new connections here
connectionsList.push(connection);
connection.on('message', function (message) {
const data = JSON.parse(message) || null;
if (data !== null && data.type === 'push') {
connectionsList.forEach((connection, index) => {
//Must Skip First Item This One Pumps Data To The Others
if (index > 0) {
if (connection.state === 'closed') {
ConnectionsList.splice(index);
}
connection.send(JSON.stringify(data.message));
}
})
}
});
});
wsServer.on("error", function(err){
console.log(err);
});
module.exports = server;
Here is the runner or starter
// A simple pid lookup
var exec = require('child_process').execFile;
const wss = require('./ws_server/wss');
const config = require('./config');
var fun = function () {
const process = exec(`${config.EXE.PATH}/${config.EXE.NAME}`, function () {
wss.close();
fun();
});
//if process is created, then makea websocket server
if (process.pid != null && process.pid !== undefined) {
try{
wss.on('error', function(error){
console.log(error);
});
wss.listen({port: config.PORT,host: config.HOST}, function () {
console.log((new Date()) + ` Server is listening on port ${config.PORT}`);
});
}
catch(err){
}
}
}
fun();
I keep having this error below even after I have checked and can't find anything using that port. I have tried all the approached mentioned from the here
How to fix Error: listen EADDRINUSE while using nodejs?
but nothing seems to work for me, please can anyone explain to me what's really the problem here. I am using windows server to run this nodejs script. thanks
How to fix Error: listen EADDRINUSE while using nodejs?
The issue is that the close is not awaited since:
wss.close is called
fun is executed in sync and the wss.listen execute before the closing has been completed
It should be necessary to run fun in the close callback
const process = exec(`${config.EXE.PATH}/${config.EXE.NAME}`, function () {
wss.close(function(){
// now the server is closed
fun();
});
});
I found the problem. It was a guess.
I updated the Node.js version in the computer and after that Node connected to the port and to the db successfully
Hey uh I am having an issue and I think this is probably related to net.createserver.
The issue is that whenever the first client joins after that another second client joins, the first client can control the second client and then the first client disconnects because the socket(end) event gets triggered. Is it related to sockets or something or the server can't handle two connections? Please help
The code:
handleGame: function() {
console.log(log.success('The source is starting!'));
var server = net.createServer(function(socket) {
console.log('A client has connected' + "\r\n");
socket.setEncoding('utf8')
global.clientObjz = new client(socket);
game.addClient(clientObjz);
socket.on('data', function(data) {
data = data.toString().split('\0')[0];
console.log('Incoming data: ' + data + "\r\n")
if (data == "<msg t='sys'><body action='verChk' r='0'><ver v='153' /></body></msg>" | data == "<msg t='sys'><body action='rndK' r='-1'></body></msg>" | data.startsWith("<msg t='sys'><body action='login' r='0'>")) {
parseXml(data, function(err, result) {
var type = result.msg['$'].t,
action = result.msg.body[0]['$'].action;
var method = Game.xmlHandlers[type][action];
if (typeof Game[method] == 'function') {
Game[method](data, client);
}
});
}
var dataType = data.charAt(0);
if (dataType == '%') {
game.handleraw(data, clientObjz);
}
});
socket.on('end', function() {
game.removeClient(socket);
console.log('A client has disconnected');
});
socket.on('error', function(err) {
console.log(err);
});
});
server.listen(Game1, "localhost", function() {
console.log('Server is listening on port 6113');
});
}
//this code is another file as i am calling it by game.clients.push
addClient: function(client) {
if (client) {
self.clients.push(client);
}
}
Yes, it's a clubpenguin emulator
full code: https://github.com/dev3211/bingojs
You need to implement socket channels if you are doing this only with NodeJS net.createServer. If you want it simple, you could look for something using express.js, something like this: https://medium.com/factory-mind/websocket-node-js-express-step-by-step-using-typescript-725114ad5fe4
I'm totally new to the whole nodeJS asynchronous-y callback-y programming so I need more like a guidance to understanding what I'm even doing. With that said, I have two files main.js and server.js
My main file looks like this:
var server=require('./server.js');
server();
function WhenUserClicksButton(){
server();
}
and my server file looks like this:
var net = require('net');
function server(){
net.createServer(function (socket) {
socket.write('\x16'); //SYN character
socket.on('data', function (data) {
//handle data from client
});
}).listen(33333);
}
First call of server(); starts the TCP server. Then function WhenUserClicksButton is called when user clicks button (duhh) in a GUI. But it attempts to start the server again so I get
Error: listen EADDRINUSE :::33333
I got why this is happening but I can't think of a solution for it. What I really need is:
Start the server and listen on 33333
When nothing is happening server and client just exchanges SYN and ACK characters every few seconds (I already have this part done, I just removed it from this example for clarity because it's not really topic of this question)
When user click button change socket.write('\x16'); to socket.write('something');
Then wait for server and client to exchange data and after everything is done return results back to main.js
As I said, I'm new to this and I believe my problem lies in not understanding fully of what I'm doing. Any help and explanations are welcome!
I think you're very near where you need to be. I would do something like this:
server.js
var net = require('net');
var netServer = null;
var netSocket = null;
function sendData(data) {
if (netServer && netSocket) {
console.log('Send data: sending: ', data);
netSocket.write(data);
}
}
function startServer(){
netServer = net.createServer(function (socket) {
netSocket = socket;
socket.write('\x16'); //SYN character
socket.on('data', function (data) {
console.log('Server: data from client: ', data);
if (data.length === 1 && data[0] === 0x16) {
// log and ignore SYN chars..
console.log('SYN received from client');
} else if (newDataCallback) {
newDataCallback(data);
};
});
});
console.log('Server listening on 33333..');
netServer.listen(33333);
}
var newDataCallback = null;
function setNewDataCallback(callback) {
newDataCallback = callback;
}
module.exports = {
sendData: sendData,
startServer: startServer,
setNewDataCallback: setNewDataCallback
};
main.js
var server = require('./server');
function newDataCallback(data) {
console.log('newDataCallback: New data from server: ', data);
}
server.setNewDataCallback(newDataCallback);
server.startServer();
function wheneverUserClicksButton() {
server.sendData('something');
}
testClient.js
var clientSocket = net.createConnection(33333, "127.0.0.1");
clientSocket.on('data', (someData) => {
console.log('Data received', someData);
});
clientSocket.on('connect', () => {
console.log('Client Socket connected ');
clientSocket.write('Hello from client');
});
I have a server
var connect = require('connect');
var serveStatic = require('serve-static');
var HTMLServer = function(path){
this.path = path;
this.server = connect().use(serveStatic(this.path));
this.startServer = function(callback){
this.server = this.server.listen(8080, callback);
};
this.stopServer = function(callback){
this.server.close(callback);
}
}
And I use it as follows:
var thisServer = new HTMLServer(__dirname);
thisServer.startServer(function(){
console.log('Server running on 8080...');
setTimeout(function(){
thisServer.stopServer(function(){
console.log('Server closed');
});
}, 3000);
});
As expected, server starts and after 3000 milliseconds it stops.
But, if within these 3000 milliseconds I make a request to this server, the stopServer is called, however the server is not closed.
I'm sure this line this.server.close(callback); gets executed, but doesn't close the server as I expect.
How can I fix that?
Is a request to the server changing the server instance in a way that needs a special handling?
Later edit:
I would like to add some precision now that I left the code running. It seems the server does get closed, however not instantly, but after an amount of time that I don't understand, no longer than 5 minutes.
So the close operation seems to be delayed. Can I make it instant somehow?
While #jfriend00 was correct that node.js keeps running until all exiting sockets are finished, the process.exit solution was a bit too radical for my use case and I needed a cleaner solution to close the server gracefully.
Looking into getConnections only added more confusion since it didn't function as expected. (for example it returned 2 connections even if I didn't make any request).
I also looked into server.listening but it returned false even if the server accepted more requests. Perhaps accepts connection from a client that made requests before closing the server.
Anyway, the solution for me was to use the http-shutdown lib which essentially adds the following .shutdown method to your server object.
function addShutdown(server) {
var connections = {};
var isShuttingDown = false;
var connectionCounter = 0;
function destroy(socket, force) {
if (force || (socket._isIdle && isShuttingDown)) {
socket.destroy();
delete connections[socket._connectionId];
}
};
function onConnection(socket) {
var id = connectionCounter++;
socket._isIdle = true;
socket._connectionId = id;
connections[id] = socket;
socket.on('close', function() {
delete connections[id];
});
};
server.on('request', function(req, res) {
req.socket._isIdle = false;
res.on('finish', function() {
req.socket._isIdle = true;
destroy(req.socket);
});
});
server.on('connection', onConnection);
server.on('secureConnection', onConnection);
function shutdown(force, cb) {
isShuttingDown = true;
server.close(function(err) {
if (cb) {
process.nextTick(function() { cb(err) });
}
});
Object.keys(connections).forEach(function(key) {
destroy(connections[key], force);
});
};
server.shutdown = function(cb) {
shutdown(false, cb);
};
server.forceShutdown = function(cb) {
shutdown(true, cb);
};
return server;
};
With this function, I can update my server as follows, and now stopServer works as expected:
var HTMLServer = function(path){
this.path = path;
this.server = connect().use(serveStatic(this.path));
this.startServer = function(callback){
this.server = addShutdown(this.server.listen(8080, callback));
};
this.stopServer = function(callback){
console.log("I was called");
this.server.shutdown(callback);
}
}
I'm using sockjs with standard configuration.
var ws = sockjs.createServer();
ws.on('connection', function(conn) {
conn.on('data', function(message) {
wsParser.parse(conn, message)
});
conn.on('close', function() {
});
});
var server = http.createServer(app);
ws.installHandlers(server, {prefix:'/ws'});
server.listen(config.server.port, config.server.host);
wsParser.parse function works like this:
function(conn, message) {
(...)
switch(message.action) {
case "titleAutocomplete":
titleAutocomplete(conn, message.data);
break;
(...) // a lot more of these
}
Each method called in switch sends back a message to client.
var titleAutocomplete = function(conn, data) {
redis.hgetall("titles:"+data.query, function(err, titles){
if(err) ERR(err);
if(titles) {
var response = JSON.stringify({"action": "titleAutocomplete", "data": {"titles": titles}});
conn.write(response);
}
})
};
Now my problem is that I'd like to make tests for my code (better late than never I guess) and I have no idea how to do it. I started writing normal http tests in with mocha + supertest but I just don't know how to handle websockets.
I'd like to have only one websocket connection to reuse through all tests, I'm binding the websocket connection with user session after first message and I want to test that persistence as well.
How do I make use of ws client's onmessage event and utilize it in my tests? How the tests can tell apart received messages and know which one they are supposed to wait for?
Collegue at work asked if it really needs to be a client connection or would it be possible to just mock it up. It turned out it was the way to go. I wrote a little helper class wsMockjs
var wsParser = require("../wsParser.js");
exports.createConnectionMock = function(id) {
return {
id: id,
cb: null,
write: function(message) {
this.cb(message);
},
send: function(action, data, cb) {
this.cb = cb;
var obj = {
action: action,
data: data
}
var message = JSON.stringify(obj);
wsParser.parse(this, message);
},
sendRaw: function(message, cb) {
this.cb = cb;
wsParser.parse(this, message);
}
}
}
Now in my mocha test I just do
var wsMock = require("./wsMock.js");
ws = wsMock.createConnectionMock("12345-67890-abcde-fghi-jklmn-opqrs-tuvwxyz");
(...)
describe('Websocket server', function () {
it('should set sessionId variable after handshake', function (done) {
ws.send('handshake', {token: data.token}, function (res) {
var msg = JSON.parse(res);
msg.action.should.equal('handshake');
msg.data.should.be.empty;
ws.should.have.property('sessionId');
ws.should.not.have.property('session');
done();
})
})
it('should not return error when making request after handshake', function (done) {
ws.send('titleAutocomplete', {query: "ter"}, function (res) {
var msg = JSON.parse(res);
msg.action.should.equal('titleAutocomplete');
msg.data.should.be.an.Object;
ws.should.have.property('session');
done();
})
})
})
It works like a charm and persist connection state and variables between requests.