node + socket.io: multiple server emits for single client emit? - javascript

I've just managed to connect to a server script with socket.io, so I'm happy about that. I'm less happy though about the weird behavior my script generates: I send one emit to the server script on a buttonclick, and the server test script sends back a message 6x to the console log. Googling this problem description gets ideas about spotty, repeating connections, but I don't think that's it.
Anyway, here's the client app.js:
var commentapp={
init: function(){
var commentapp=this;
commentapp.btn_api=$('#btn_api');
commentapp.btn_api.click(this.get_comment_data);
},
get_comment_data: function(btn_event){
var commentapp=this;
console.log('trying to connect');
commentapp.socket=io.connect('http://localhost:8080');
commentapp.socket.on('connect', function() {
commentapp.socket.emit('btn_api_call');
}); //commentapp.socket.on 'connect',
commentapp.socket.on('serverMessage', function(content){
console.log(content);
}
); //commentapp.socket.on('serverMessage'
}
};
$(function() {
commentapp.init();
});
The server script is as follows:
var httpd = require("http").createServer(handler);
var io=require('/Users/user/Virtualenvs/node_modules/socket.io/lib/socket.io').listen(httpd);
var fs = require('fs');
var url = require("url");
var path = require("path");
var port = process.argv[2] || 8080;
httpd.listen(parseInt(port, 10));
function handler (request, response) {
var uri = url.parse(request.url).pathname,
filename = path.join(process.cwd(), uri);
console.log(uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return; //these returns get you out of the function I think
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary"); //otherwise here's where the file gets finally served
response.end();
}); //fs.readFile
}); //path.exists
io.sockets.on('connection',function(socket) {
socket.on('btn_api_call', function() {
socket.emit('serverMessage', 'Server heard you.');
});
});
};
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");
Both of these are cannibalized from https://github.com/accbel/nodejs-socketio-example and Pedro Teixeira's book.
So if I click the button to generate the 'btn_api_call'emit, the console log will say "'Server heard you.'" 6x. Hopefully this is a rookie mistake easily set straight.
Thanks for your help!

This is likely due to having your registration for connections inside of a route handler.
Each time a request comes in that is handled by that route, the code is adding a new listener for connections.
You likely have a similar problem in your client - connecting each time the button is clicked.
Move your connection listener outside the route like this:
function handler (request, response) {
var uri = url.parse(request.url).pathname,
filename = path.join(process.cwd(), uri);
console.log(uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return; //these returns get you out of the function I think
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary"); //otherwise here's where the file gets finally served
response.end();
}); //fs.readFile
}); //path.exists
};
io.sockets.on('connection',function(socket) {
socket.on('btn_api_call', function() {
socket.emit('serverMessage', 'Server heard you.');
});
});
On the client move the connect logic to init - something like:
var commentapp={
init: function(){
var commentapp=this;
commentapp.btn_api=$('#btn_api');
commentapp.btn_api.click(this.get_comment_data);
console.log('trying to connect');
commentapp.socket=io.connect('http://localhost:8080');
commentapp.socket.on('connect', function() {
commentapp.socket.emit('btn_api_call');
}); //commentapp.socket.on 'connect',
commentapp.socket.on('serverMessage', function(content){
console.log(content);
}
); //commentapp.socket.on('serverMessage'
},
get_comment_data: function(btn_event){
var commentapp=this;
commentapp.socket.emit('btn_api_call');
}
};

Related

Router is not defined Node.js

I am new with Node.js, and I am trying to create an HTTP server, but for some reason, when I try to put the router for purchase URL request, it doesn't work.
My code :
Server.js
var url = require("url");
var http = require("http");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request received.");
route(pathname);
response.writeHead(200, {"Content-Type" : "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started;");
}
exports.start = start;
Index.js
var server = require("./server");
var router = require("./router");
server.start(router.route);
Router.js
function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
When trying to start the server through the Node.js, it says the follow error :
route is not defined
route(pathname);
How can I make this work ?
You're passing the route to the start function but a parameter isn't defined, you need to add one.
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request received.");
route(pathname);
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started;");
}

Display json file in Browser using Node.JS without file extension

I have made a server using javascript and Node.js that shows a JSON file in my browser.
However, I would like to call the site http://localhost:8888/Test.json without an extension.
For example just: http://localhost:8888/Test
Here is my server code:
var http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs")
port = process.argv[2] || 8888;
file = (__dirname + '/Test.json');
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname, filename = path.join(process.cwd(), uri);
var contentTypesByExtension = {
'.html': "text/html",
'.css': "text/css",
'.js': "text/javascript",
'.json': "application/json" //Edited due to answer - Still no success :(
};
path.exists(filename, function(exists) {
if(!exists) {
res.writeHead(404, {"Content-Type": "text/plain"});
res.write("404 Not Found\n");
res.end();
return;
}
fs.readFile(file, 'utf8', function (err, file) {
if (err) {
console.log('Error: ' + err);
return;
}
file = JSON.parse(file);
console.dir(file);
var headers = {};
var contentType = contentTypesByExtension[path.extname(file)];
if (contentType) headers["Content-Type"] = contentType;
res.writeHead(200, headers);
res.write(JSON.stringify(file, 0 ,3));
res.write
res.end();
});
});
}).listen(parseInt(port, 10));
console.log("JSON parsing rest server running at\n => http://localhost:" +
port + "/\nPress CTRL + C to exit and leave");
How can I do that?
Should I use routes/express?
Does someone have any suggestions?
Thank you in advance!
Cheers, Vlad
Your problem is probably due to the content type. Having the extension .json is probably triggering your browser to consume it as application/json. So if you remove the extension you need to add the proper Content-Type.
Given that you are already playing around with content types, can't you just add it here, and make sure you write the type for jsons as well?
var contentTypesByExtension = {
'.html': "text/html",
'.css': "text/css",
'.js': "text/javascript",
'.json': "application/json" // <---
};
I've just used the sledgehammer method now with commenting this code fragment out:
if(!exists) {
res.writeHead(404, {"Content-Type": "text/plain"});
res.write("404 Not Found\n");
res.end();
return;
}
Now it works to call just: http://localhost:8888/Test
Cheers, Vlad

Saving image to node.js server without express.js

So I have been wondering how to save an image to node.js server without the use of express.(just learning node and want to make everything myself to learn node better, without express).
So far I have a form with the image as only input which I send with a post request. This is what I have so far on my server, which does not log anything.
if(req.method === 'POST') {
if (req.url === '/upload') {
req.on('error', function(e) {
console.log('Problem with request: ' + e.message);
});
res.writeHead(200, {'Content-Type': 'image/jpg; charset=utf8'});
req.on('data', function (chunk) {
fs.writeFile(__dirname + "/uploads/dada.jpg", chunk, function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved!");
}
});
});
}
}
This is my form:
<form method="post" action="/upload" enctype="multipart/form-data">
EDIT: I fixed most of the problems, but the file is only saved as an image, but cannot be viewed like one. (Something is wrong with the content-type I guess, but don't know how to fix it)
Here is fiddle of my whole app. I know I need to separate it in different modules, but I will do that later
I completely forgot about this old question but now that I see it has quite some views, here is the solution I found:
var port = 1357;
var http = require('http'),
path = require('path'),
mime = require('mime'),
fs = require('fs'),
GUID = require('GUID'),
formidable = require('formidable'),
util = require('util');
var app = http.createServer(function (req, res) {
if (req.method === 'POST') {
if (req.url === '/upload') {
req.on('error', function (e) {
console.log('Problem with request: ' + e.message);
});
var fileDirectory = __dirname + '/db/',
form = new formidable.IncomingForm();
form.keepExtensions = true;
form.uploadDir =fileDirectory;
form.parse(req, function (err, fields, files) {
if (err) throw (err);
var pic = JSON.stringify(util.inspect(files)),
upIndx = pic.indexOf('db'),
path = pic.slice(upIndx + 6, upIndx + 42);
res.writeHead(200, {
'Content-Type': 'text/html'
});
fs.readFile('views/index.html', function (err, page) {
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.write(page);
res.write('<div>Download Link: </div><div>' + fileDirectory + path + '</div>');
res.end();
});
});
}
} else {
//not important for question, handle other request
}
});
app.listen(port);
console.log('Server running on port: ' + port)

Basic attempt at using NodeJS

I tried to follow an example on the web for creating a NodeJS server and router, but have ran into problems. What follows is primarily the example with a bit of other code.
Index.js
var server = require("./server");
var router = require("./router");
server.start(router.route);'
Router.js
var fs = require("fs");
function route(filename) {
fs.readFile(filename, 'utf8', function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
console.log("About to route a request for " + filename);
data = JSON.parse(data);
})
}
exports.route = route;
Server.js
var http = require('http');
var url = require("url");
function start(route) {
function onRequest(request, response) {
var filename = request.url.substr(1);
// call for specific json file here
if (filename == "favicon.ico") {
response.writeHead(200, {'Content-Type': 'image/x-icon'} );
response.end();
console.log('favicon requested');
return;
}
else {
filename += ".json";
console.log("Request for " + filename + " received.");
response.writeHead(200, { 'Content-Type': 'application/json', "Access-Control-Allow-Origin":"*" });
response.write("" + route(filename),0,4);
response.end();
}
}
http.createServer(onRequest).listen(8124);
}
exports.start = start;
console.log('Server running at http://127.0.0.1:8124/');
The question I have is, among others, how does the route command work?
What I get when I browse to the server is 'undefined'. How do I get the actual json from the Router?
I tried: var result = route(filename); but that failed badly.
In essence, how to get the data from the router and write it to the response. If you haven't guessed, javascript and NodeJS is rather new to me.
Any pointers would be greatly appreciated.
Thank you.
Your route function has an async method, fs.readFile. You'll need to pass in a callback to the route function, so that it can return the data properly. Otherwise, route will return immediately with no data (since it's async) regardless if you have a return statement inside readFile.
router.js
function route(filename, callback) {
fs.readFile(filename, 'utf8', function (err, data) {
if (err) {
console.log('Error: ' + err);
return;
}
console.log("About to route a request for " + filename);
data = JSON.parse(data);
callback(data); // Call this function after the data is grabbed.
})
}
server.js portion:
response.writeHead(200, { 'Content-Type': 'application/json', "Access-Control-Allow-Origin":"*" });
route(filename, function (data) {
response.write("" + JSON.stringify(data),0,4);
response.end();
});

how to serve a file (exe or rar ) to a client for download from node.js server?

I have a a node.js server that serves an index.html with a text input for a password.
After a serverside password check the download should start for the client.
The client shouldn't be able to see the location path where the file lies on the server.
here is my server.js:
var
http = require('http'),
qs = require('querystring'),
fs = require('fs') ;
console.log('server started');
var host = process.env.VCAP_APP_HOST || "127.0.0.1";
var port = process.env.VCAP_APP_PORT || 1337;
http.createServer(function (req, res) {
if(req.method=='GET') {
console.log ( ' login request from ' + req.connection.remoteAddress );
fs.readFile(__dirname +'/index.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
} // method GET end
else{ // method POST start
console.log('POST request from ' + req.connection.remoteAddress);
var body = '';
req.on('data', function (data) {
body += data;
if (body.length > 500) {
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
req.connection.destroy(); console.log('too much data')}
});
req.on('end', function () {
var postdata = qs.parse(body);
var password = postdata.passwordpost ;
if (password == '7777777') {
console.log('the password is right, download starting');
// ??????????????????????????????????? here I need help from stackoverflow
}
else{
console.log ('password wrong');
fs.readFile(__dirname +'/wrongpassword.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
}
}); // req on end function end
}
}).listen(port, host);
the part where I need help is marked with ????????
here is my index.html:
<html>
<body>
<br> <br>
please enter your password to start your download
<br> <br>
<form method="post" action="http://localhost:1337">
<input type="text" name="passwordpost" size="50"><br><br>
<input type="submit" value="download" />
</form>
</body>
</html>
Do you know how to do this?
Sure, you can use this in your code :
res.setHeader('Content-disposition', 'attachment; filename='+filename);
//filename is the name which client will see. Don't put full path here.
res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
var file = fs.createReadStream(filepath);
//replace filepath with path of file to send
file.pipe(res);
//send file
You needs to declare and require the path: path = require("path")
then can do:
var uri = url.parse(request.url).pathname
, filename = path.join(process.cwd(), uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary");
response.end();
}
check these complete example.
If you are willing to use express web framework, then it can be done in a much easier way.
app.get('/download', function(req, res){
var file = __dirname + 'learn_express.mp4';
res.download(file); // Sets disposition, content-type etc. and sends it
});
Express download API
I found some additional information about fs.createReadStream() ( especially error handling ) here and combined it with the answer of user568109. Here is my working downloadserver:
var
http = require('http'),
qs = require('querystring'),
fs = require('fs') ;
console.log('server started');
var host = process.env.VCAP_APP_HOST || "127.0.0.1";
var port = process.env.VCAP_APP_PORT || 1337;
http.createServer(function (req, res) {
if(req.method=='GET') {
console.log ( ' login request from ' + req.connection.remoteAddress );
fs.readFile(__dirname +'/index.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
} // method GET end
else{ // method POST start
console.log('POST request from ' + req.connection.remoteAddress);
var body = '';
req.on('data', function (data) {
body += data;
if (body.length > 500) {
// FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST
req.connection.destroy(); console.log('too much data')}
});
req.on('end', function () {
var postdata = qs.parse(body);
var password = postdata.passwordpost ;
if (password == '7777777') {
console.log('the password is right, download starting');
res.setHeader('Content-disposition', 'attachment; filename='+'test1.exe');
//filename is the name which client will see. Don't put full path here.
res.setHeader('Content-type', 'application/x-msdownload'); //for exe file
res.setHeader('Content-type', 'application/x-rar-compressed'); //for rar file
var readStream = fs.createReadStream('/test1.exe');
//replace filepath with path of file to send
readStream.on('open', function () {
// This just pipes the read stream to the response object (which goes to the client)
readStream.pipe(res);
});
// This catches any errors that happen while creating the readable stream (usually invalid names)
readStream.on('error', function(err) {
console.log (err) ;
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('an error occured', 'utf-8');
});
//send file
}
else{
console.log ('password wrong');
fs.readFile(__dirname +'/wrongpassword.html', function(error, content) {
if (error) {
res.writeHead(500);
res.end();
}
else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
}
}); // req on end function end
}
}).listen(port, host);

Categories

Resources