html5 rocks node js server-sent-events SSE example not working - javascript

link to article: http://www.html5rocks.com/en/tutorials/eventsource/basics/
The node.js SSE server is not working in that example. I end up with an open connection to /events, but no response is received by the browser.
sse-server.js
var http = require('http');
var sys = require('sys');
var fs = require('fs');
http.createServer(function(req, res) {
//debugHeaders(req);
if (req.headers.accept && req.headers.accept == 'text/event-stream') {
if (req.url == '/events') {
sendSSE(req, res);
} else {
res.writeHead(404);
res.end();
}
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync(__dirname + '/sse-node.html'));
res.end();
}
}).listen(8000);
function sendSSE(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
var id = (new Date()).toLocaleTimeString();
// Sends a SSE every 5 seconds on a single connection.
setInterval(function() {
constructSSE(res, id, (new Date()).toLocaleTimeString());
}, 5000);
constructSSE(res, id, (new Date()).toLocaleTimeString());
}
function constructSSE(res, id, data) {
res.write('id: ' + id + '\n');
res.write("data: " + data + '\n\n');
}
function debugHeaders(req) {
sys.puts('URL: ' + req.url);
for (var key in req.headers) {
sys.puts(key + ': ' + req.headers[key]);
}
sys.puts('\n\n');
}
sse-node.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<script>
var source = new EventSource('/events');
source.onmessage = function(e) {
document.body.innerHTML += e.data + '<br>';
};
</script>
</body>
</html>

The problem was with the server. In my case I was using node with IIS using the iisnode node package. To solve this, I needed to configure iisnode in the web.config like so:
<iisnode flushResponse="true" />
After this, everything worked fine. Others with a similar issue may start here, but apache and nginx may have similar configuration requirements.

Why did you comment out the res.end() at the end of the sendSSE function? That's the method that actually sends the response to the browser.

I already try that code and is working for me, as you are not using ngnix, or any other server as proxy for your node instances I would believe that the problem is with your machine, if you have firewall or anti virus running, stop it, and try again or any other software that could be intercepting yours req and res.

Make sure you have saved the HTML file with same name as described sse-node.html in same directory. Other thing might be make sure 8000 port is open in your local machine no one is using it. or change the port and re-run sse-server.js. its working for me as is.

I faced the same issue. Nothing wrong with your code just Provide Full Resource Address in your HTML File.
var source = new EventSource('http://localhost:8000/events'); //true way
instead of
var source = new EventSource('/events'); //improper way

Related

Loading two different types of data to a webpage with http.createServer's callback

I'm trying to load an HTML page along with two separate CSS files through http.createServer()'s callback. Here's the important part of the code so far:
var server = http.createServer(function (req, res) {
var htmlData = fs.readFileSync(__dirname + "/public/index.html");
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(htmlData);
res.end();
}).listen(port);
When I try to load this, it also tries to load the CSS files linked within the HTML file. I've tried both adding direct links within the header and adding a script within the HTML file to add the links to the header, but neither work. How can I do this without putting the content of the CSS files directly within tags in the HTML file?
You are ignoring the request path and giving it the same file each time.
You need to serve the right file based on the request.
For example: if you want to serve two files index.html and style.css:
var server = http.createServer(function (req, res) {
if (req.url === '/' || req.url === '/index.html') { // respond to both / and /index.html
var htmlData = fs.readFileSync(__dirname + "/public/index.html");
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(htmlData);
res.end();
}
else if (req.url === '/style.css') { // respond to /style.css
var cssData = fs.readFileSync(__dirname + "/public/style.css");
res.writeHead(200, { 'Content-Type': 'text/css' });
res.write(cssData);
res.end();
}
}).listen(port);

Ajax with node.js

i know some persons asked this question before but i don't understand answers :/
I'm using node.js, and i realy want to use Ajax in it.
My code is :
var $ = require('jquery');
var http = require("http");
var ws = require("nodejs-websocket");
var fs = require("fs");
var colors = require('colors');
http.createServer(function (req, res) {
fs.createReadStream("index.php").pipe(res)
}).listen(8080)
// ###################################################################################################################################
// ########################################################## CLASSE SERVER ##########################################################
// ###################################################################################################################################
var tableauDeJoueur = new Array();
var server = ws.createServer(function (connection){
connection.nickname = null
connection.on("text", function (str){
if (connection.nickname === null){
connection.nickname = str;
console.log((connection.nickname+" arrive sur PixelWorld !").green);
}
else{
var code = str.substring(0,2);
var reste = str.substring(2,str.length);
switch(code){
case "01":
var coupe = reste.split("4H[m~Ft7");
var mail = coupe[0];
var mdp = coupe[1];
$.ajax({
url: "fonctionPHP/connection.php",
type: "POST",
data: {'mail': mail,'mdp': mdp},
async:false,
success: function(html){
if(html == "OK"){
console.log("oui");
}
else{
console.log("non");
}
}
});
break;
case "02":
break;
}
}
})
connection.on("close", function (){
console.log((connection.nickname+" a quitté PixelWorld !").red);
})
})
server.listen(8081)
function broadcast(str) {
server.connections.forEach(function (connection) {
connection.sendText(str)
})
}
My problem is at the line "$.ajax({".
The server notice me when a user is coming, it's ok. But when he send a message with a 01 code, node crash and say me :
$.ajax({
^
TypeError: Object function ( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
} has no method 'ajax'
at Connection.<anonymous> (/var/www/dhkuhnuhbnkiuh/app.js:46:8)
at Connection.EventEmitter.emit (events.js:95:17)
at Connection.processFrame (/var/www/dhkuhnuhbnkiuh/node_modules/nodejs-websocket/Connection.js:516:9)
at Connection.extractFrame (/var/www/dhkuhnuhbnkiuh/node_modules/nodejs-websocket/Connection.js:458:14)
at Connection.doRead (/var/www/dhkuhnuhbnkiuh/node_modules/nodejs-websocket/Connection.js:209:23)
at Socket.<anonymous> (/var/www/dhkuhnuhbnkiuh/node_modules/nodejs-websocket/Connection.js:52:8)
at Socket.EventEmitter.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:408:10)
at emitReadable (_stream_readable.js:404:5)
at readableAddChunk (_stream_readable.js:165:9)
Sorry if my English isn't good, I'm French and bad at English. :/
Thank you for your help :D
Doing a request from nodejs is fairly easy, dont have to use $.ajax at all. You can use the npm request module. $.ajax is built for firing requests from the browser. But if you 'really' want to use $.ajax on node, I think you can read through this question
First,we begin with understanding AJAX and Node.Ajax is a client-side xml-based technology that automatically updates contents of a web page, without the page having to reload. Node.js is a server-side scripting language.
To illustrate this clearly, we will create a client client.html file and a server server.js
Aside from having npm installed, we will install express middleware and some of it's dependencies that we are going to use.
npm install --save express body-parser body-parser-xml
Let's begin by writing our server.js file. This file is going to parse xml requests sent AJAX. After processing request body, server should then send response back to client.
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
require('body-parser-xml')(bodyParser);
app.use(bodyParser.xml({
limit: '1MB',
XmlParseOptions: {
normalize: true,
normalizeTags: true,
explicitArray: false
}
}));
app.get('/', function(req, res) {
res.sendFile(__dirname + "/" + "client.html");
});
app.post('/library', bodyParser.urlencoded({ extended: false }), function(req, res) {
console.log(req.body);
var title = req.body.book.title;
var author = req.body.book.author;
var year = req.body.book.year;
console.log(title + " " + author + " " + year);
//optional operations like database can be performed here
// we are sending a response mimicking a successfull search query
res.end("Book Found in library");
});
var server = app.listen(8080, function() {
var host = '127.0.0.1';
var port = server.address().port;
console.log("Server running at http://%s:%s\n", host, port);
});
Next, create client.html file. This file will have simple form that when submitted call on an AJAX function that in turn sends xml data to server.js then waits and process response
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type="text/javascript">
function Search() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.getAllResponseHeaders();
xmlhttp.open('POST', 'http://127.0.0.1:8080/library', true);
console.log(document.getElementById('title').value);
console.log(document.getElementById('author').value);
var text = "<book>" +
"<title>" + document.getElementById('title').value + "</title>" +
"<author>" + document.getElementById('author').value + "</author>" +
"<year>" + document.getElementById('year').value + "</year>" +
"</book>";
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
alert(xmlhttp.responseText);
console.log("All ok. You hit the server");
}
}
};
xmlhttp.setRequestHeader("Content-Type", "text/xml");
xmlhttp.send(text);
}
</script>
</head>
<body>
<form name="" method="POST" action="">
Title:<input type="text" name="title" id="title">
Author:<input type="text" name="author" id="author">
Year:<input type="text" name="year" id="year"><br>
<br>
<input type="button" value="Search" onclick="Search()" />
</form>
</body>
</html>
Hope this guide helps in future. Thanks

Using eval in Node.js to send requests to the server using submit button and input text

So here is what I'm trying to achieve!
I want to create a very simple Node application where I can make use of the eval function by creating a input text box and a submit button. I want to be able to write things in the text box and when hitting submit to use the eval function to send this parameter to the server.
So if I write while(true) in the text box this should cause a DoS attack to the server, due to the eval vulnerability.
Here is my code so far (fix doesn't work properly)
var http = require("http");
var server = http.createServer(function(request, res) {
res.writeHead(200, {"Content-Type": "text/html"});
res.write("<html>");
res.write("<head>");
res.write("<title>Hello World Page</title>");
res.write("</head>");
res.write("<body>");
res.write("Enter Some text");
res.write('<input type="text" name="fname">');
res.write('<input type="submit" value="Submit")');
var parameter = eval(req.body.fname);
res.send(parameter);
res.write("</body>");
res.write("</html>");
res.end();
});
server.listen(1337);
console.log("Server is listening");
Any ideas on how to make this work?
First - here is a good post on the topic: Accessing the HTTP message body (e.g. POST data) in node.js
A couple of minor changes to your html:
I've added a form element with an action and method set to POST. You could use GET as well to avoid some of the req.on(...) logic below.
added closing bracket for the submit button - you had a typo with a paren
When reading posted form data, you will need to read all of the data coming in until it is complete. The req parameter is an event emitter so you would use:
req.on('data', function(chunk) {...}
req.on('end', function() {...}
To buffer up the incoming data then act on it when the end of the request is reached.
The form data is encoded, so you will need to parse it using the querystring module.
I've also added just enough error checking (and fixed a few naming bugs) to get the code to function at a basic level.
Code:
var http = require("http");
var querystring = require('querystring');
var util = require('util');
var server = http.createServer(function(req, res) {
var body = "";
req.on('data', function (chunk) {
body += chunk;
});
req.on('end', function () {
console.log('POSTed: ' + body);
body = querystring.parse(body);
console.log('decoded: ' + util.inspect(body));
res.writeHead(200, {"Content-Type": "text/html"});
res.write("<html>");
res.write("<head>");
res.write("<title>Hello World Page</title>");
res.write("</head>");
res.write("<body>");
res.write("Enter Some text");
res.write('<form action="/" method="post">');
res.write('<input type="text" name="fname">');
res.write('<input type="submit" value="Submit">');
res.write('</form>');
if(body.fname) {
var parameter = eval(body.fname);
console.log(util.inspect(parameter));
if(parameter) {
res.write(parameter);
}
}
res.write("</body>");
res.write("</html>");
res.end();
});
});
server.listen(1337);
console.log("Server is listening");

How do I handle CSS with Node.js?

I'm trying to use just straight vanilla Node.js with no frameworks other than what is necessary for educational purposes. I currently have this setup:
var fs = require('fs'),
jade = require('jade');
function compile(dirPath, res) {
var filePath = __dirname + dirPath;
fs.readFile(filePath, 'utf8', function(err, fd) {
var fn = jade.compile(fd, { filename: filePath, pretty: true});
res.write(fn());
res.end();
});
}
exports.compile = compile;
Which is called when the requested page is asked for, like this:
var jadec = require('./jadecompile');
function start(res, postData) {
res.writeHead(200, {"Content-Type": "text/html"});
var cpage = '/staticpages/index.jade',
page = jadec.compile(cpage, res);
}
The page loads great and I get this as my view source:
<!DOCTYPE html>
<html>
<head>
<title>Hello!</title>
<link rel="text/css" href="assets/css/bootstrap.css">
</head>
<body>
<div class="hero-unit">
<p>Hello This Is A Test</p>
</div>
</body>
</html>
But the css doesn't work at all, I've been trying to google it but every answer seems to use express or something else, but I want to learn how to do this as vanilla as possible, so my question is, how can I handle the *.css so that the css is loaded properly, when I click on the href link in the view source, it loads my 404 page, rather than just the plain text of the css file. Thanks!
Feel free to view the repo at: https://github.com/Gacnt/Node-Tests
I managed to fix my problem by creating a function to handle static pages, such as .css or .js, like so:
var http = require('http'),
url = require('url'),
path = require('path');
fs = require('fs');
// ADDED THIS FUNCTION HERE -----------------------------------------------------
function handleStaticPages(pathName, res) {
var ext = path.extname(pathName);
switch(ext) {
case '.css':
res.writeHead(200, {"Content-Type": "text/css"});
fs.readFile('./' + pathName, 'utf8', function(err, fd) {
res.end(fd);
});
console.log('Routed for Cascading Style Sheet '+ pathName +' Successfully\n');
break;
case '.js':
res.writeHead(200, {"Content-Type": "text/javascript"});
fs.readFile('./' + pathName, 'utf8', function(err, fd) {
res.end(fd);
});
console.log('Routed for Javascript '+ pathName +' Successfully\n');
break;
}
}
// ADDED THIS FUNCTION HERE -----------------------------------------------------
function start(route, handle) {
function onRequest(req, res) {
var postData = "",
pathName = url.parse(req.url).pathname;
console.log('Request for ' + pathName + ' received.');
req.addListener('data', function(data) {
postData += data;
console.log('Received POST data chunk ' + postData + '.');
});
req.addListener('end', function() {
var pathext = path.extname(pathName);
if (pathext === '.js' || pathext === '.css') {
handleStaticPages(pathName, res);
} else {
console.log('--> Routing only the view page');
route(handle, pathName, res, postData);
}
});
}
http.createServer(onRequest).listen(4000);
console.log('Server is now listening at: http://127.0.0.1:4000 .');
}
exports.start = start;
In order to handle .css and other static files, there needs to be a file stream in place. Rather than using a switch statement you can just use the following.
var stream = fs.createReadStream(path); //create the file stream
stream.pipe(res); //transfer the file

JavaScript EventSource SSE not firing in browser

I have been developing a nodejs server to provide server-side-events for a new website I am developing in HTML5.
When I telnet to the server it works correctly, sending me the required HTTP response headers followed by a stream of events that i am presently generating every 2 or 3 seconds just to prove it works.
I have tried the latest version of FireFox, Chrome and Opera and they create the EventSource object and connect to the nodejs server OK but none of the browsers generate any of the events, including the onopen, onmessage and onerror.
However, if I stop my nodejs server, terminating the connection from the browsers, they all suddenly dispatch all the messages and all my events are shown. The browsers then all try to reconnect to the server as per spec.
I am hosting everything on a webserver. nothing is running in local files.
I have read everything I can find online, including books I've purchased and nothing indicates any such problem. Is there something Im missing?
A sample server implementation
var http = require('http');
var requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Access-Control-Allow-Origin': '*',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'});
requests.push({ip:clientIP, port:clientPort, res:res});
res.write(": connected.\n\n");
});
server.listen(8080);
setInterval(function test() {
broadcast('poll', "test message");
}, 2000);
function broadcast(rtype, msg) {
var lines = msg.split("\n");
for(var i=requests.length -1; i>=0; i--) {
requests[i].res.write("event: " + rtype + "\n");
for(var j=0; j<lines.length; j++) {
if (lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("\n");
}
}
A sample html page
<!DOCTYPE html>
<html>
<head>
<title>SSE Test</title>
<meta charset="utf-8" />
<script language="JavaScript">
function init() {
if(typeof(EventSource)!=="undefined") {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() testing begins..<br>";
}
var svrEvents = new EventSource('/sse');
svrEvents.onopen = function() {
connectionOpen(true);
}
svrEvents.onerror = function() {
connectionOpen(false);
}
svrEvents.addEventListener('poll', displayPoll, false); // display multi choice and send back answer
svrEvents.onmessage = function(event) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'message: ' + event.data + "<br>";
}
// absorb any other messages
}
} else {
var log = document.getElementById('log');
if (log) {
log.innerHTML = "EventSource() not supported<br>";
}
}
}
function connectionOpen(status) {
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'connected: ' + status + "<br>";
}
}
function displayPoll(event) {
var html = event.data;
var log = document.getElementById('log');
if (log) {
log.innerHTML += 'poll: ' + html + "<br>";
}
}
</script>
</head>
<body onLoad="init()">
<div id="log">testing...</div>
</body>
</html>
These examples are basic but of the same variety as every other demo i've seen in books and online. The eventSource only seems to be working if I end a client connection or terminate the server but this would be polling instead of SSE and I particularly want to use SSE.
Interestingly, demos, such as thouse from html5rock also seem to not quite work as expected when I use them online..
cracked it! :)
Thanks to some help from Tom Kersten who helped me with testing. Turns out the code isnt the problem.
Be warned.. if your client uses any kind of anti-virus software which intercepts web requests, it may cause problems here. In this case, Sophos Endpoint Security, which provides enterprise grade anti-virus and firewall protection has a feature called web protection. Within this features is an option to scan downloads; it seems that the SSE connection is treated as a download and thus not released to the browser until the connection is closed and the stream received to scan. Disabling this option cures the problem. I have submitted a bug report but other anti-virus systems may do the same.
thanks for your suggestions and help everyone :)
http://www.w3.org/TR/eventsource/#parsing-an-event-stream
Since connections established to remote servers for such resources are
expected to be long-lived, UAs should ensure that appropriate
buffering is used. In particular, while line buffering with lines are
defined to end with a single U+000A LINE FEED (LF) character is safe,
block buffering or line buffering with different expected line endings
can cause delays in event dispatch.
Try to play with line endings ("\r\n" instead of "\n").
http://www.w3.org/TR/eventsource/#notes
Authors are also cautioned that HTTP chunking can have unexpected
negative effects on the reliability of this protocol. Where possible,
chunking should be disabled for serving event streams unless the rate
of messages is high enough for this not to matter.
I modified your server-side script, which 'seems' partly works for Chrome.
But the connection break for every 2 broadcast & only 1 can be shown on client.
Firefox works for 1st broadcast and stop by this error:
Error: The connection to /sse was interrupted while the page was loading.
And Chrome will try to reconnect and received 3rd broadcast.
I think it's related to firewall setting too but can't explain why sometime will works.
Note: For event listener of response (line 10), 'close' & 'end' have different result,
You can try it and my result is [close: 1 success/2 broadcast] & [end: 1 success/8 broadcast]
var http = require('http'), fs = require('fs'), requests = [];
var server = http.Server(function(req, res) {
var clientIP = req.socket.remoteAddress;
var clientPort = req.socket.remotePort;
if (req.url == '/sse') {
var allClient="";for(var i=0;i<requests.length;i++){allClient+=requests[i].ip+":"+requests[i].port+";";}
if(allClient.indexOf(clientIP+":"+clientPort)<0){
requests.push({ip:clientIP, port:clientPort, res:res});
res.on('close', function() {
console.log("client " + clientIP + ":" + clientPort + " died");
for(var i=requests.length -1; i>=0; i--) {
if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
requests.splice(i, 1);
}
}
});
}
}else{
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync('./test.html'));
res.end();
}
});
server.listen(80);
setInterval(function test() {
broadcast('poll', "test message");
}, 500);
var broadcastCount=0;
function broadcast(rtype, msg) {
if(!requests.length)return;
broadcastCount++;
var lines = msg.split("\n");
for(var i = requests.length - 1; i >= 0; i--) {
requests[i].res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
requests[i].res.write("event: " + rtype + "\n");
for(var j = 0; j < lines.length; j++) {
if(lines[j]) {
requests[i].res.write("data: " + lines[j] + "\n");
}
}
requests[i].res.write("data: Count\: " + broadcastCount + "\n");
requests[i].res.write("\n");
}
console.log("Broadcasted " + broadcastCount + " times to " + requests.length + " user(s).");
}

Categories

Resources