Empty HTTP response - node.js

For some reason, this code keeps failing :
var http = require('http');
var url = require ('url');
var jade = require('jade');
var fs = require('fs');
http.createServer(function(req, res) {
// Request handlers
var path = url.parse(req.url, true).pathname;
var dot = path.lastIndexOf('.');
var extension = path.substr(dot + 1);
// Response handlers
switch(extension) {
case 'css' :
res.writeHeader(200, {"Content-Type": "text/css"});
fs.readFile(path.substr(1, dot - 1) + '.css', function (err, css) {
if(err) {
res.write(err, 'utf8')
};
res.write(css, 'utf8')
});
break;
case 'js' :
res.writeHeader(200, {"Content-Type": "text/javascript"});
fs.readFile(path.substr(1, dot - 1) + '.js', function (err, js) {
if(err) {
res.write(err, 'utf8')
};
res.write(js, 'utf8')
});
break;
default :
res.writeHeader(200, {"Content-Type": "text/html"});
jade.renderFile(path.substr(1, path.length) + '.jade', { title: 'test' }, function(err, html) {
if(err) {
res.write(err, 'utf8')
};
res.write(html, 'utf8');
});
break;
}
res.end();
}).listen(8080);
I don't get any errors, it's just that the responses seem to be empty.
There is a header but no css or javascript file.
The way I link them in Jade is the following :
link(rel='stylesheet', href='/css/bootstrap.min.css')
link(rel='stylesheet', href='/css/style.css')
Thank you in advance !

You've called res.end() too soon. Instead, put it after the last res.write() in each of your fs.readFile handlers.

Related

Nodejs error: 'cannot read property isFile() of undefined'

I am trying to display a html file in browser using Nodejs. But when I run the code I got the following error:
cannot read property isFile() of undefined
This is the code that I am using:
var http = require('http');
var url = require('url');
var path = require('path');
var fs = require('fs');
var mimeTypes = {
"html" : "text/html",
"jpeg" : "image/jpeg",
"jpg" : "image/jpg",
"png" : "image/png",
"js" : "text/javascript",
"css" : "text/css"
};
var stats;
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname;
var fileName = path.join(process.cwd(),unescape(uri));
console.log('Loading ' + uri);
try {
stats = fs.lstat(fileName);
} catch(e) {
res.writeHead(404, {'Content-type':'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
// Check if file/directory
if (stats.isFile()) {
var mimeType = mimeTypes[path.extname(fileName).split(".").reverse()[0]];
res.writeHead(200, {'Content-type' : mimeType});
var fileStream = fs.createReadStream(fileName);
fileStream.pipe(res);
return;
} else if (stats.isDirectory()) {
res.writeHead(302, {
'Location' : 'index.html'
});
res.end();
} else {
res.writeHead(500, {
'Content-type' : 'text/plain'
});
res.write('500 Internal Error\n');
res.end();
}
}).listen(3000);
The error I am getting is near stats.isFile(). I tried to resolve the error. But it is not working for me. I need some suggestions on resolving this error.
The variable stats gets set to undefined, without throwing an error. This happens because fs.lstat(fileName) returns undefined.
Before the if statements, or perhaps instead of the try catch block, you may want to do something like:
if (!stats) {
res.writeHead(404, {'Content-type':'text/plain'});
res.write('404 Not Found\n');
res.end();
return;
}
You are using wrong function. You should use:
stat=fs.lstatSync("your file")
Then your code should work.
fs.lstat("your file",function (err,stats){})
is an async function which expects callback. Take a look at the documentation here.

Why isn't node serving my image file?

I have a vanilla node.js http server. Everything except my image file works. I just get the broken image icon on the page.
Here is my server code:
"use strict";
class app {
constructor() {
app.loadServer();
}
static loadServer() {
const HTTP = require('http'),
PORT = 1337,
SERVER = HTTP.createServer(function(req, res) {
let httpHandler = function(err, str, contentType) {
console.log('\n\n' + 'Content type: ' + contentType + '\n\n');
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('An error has occurred: ' + err.message);
} else if (contentType.indexOf('image') >= 0) {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str, 'binary');
} else {
res.writeHead(200, { 'Content-Type': contentType });
res.end(str);
}
};
if (req.headers['x-requested-with'] === 'XMLHttpRequest') {
if (req.method == 'POST') {
app.getFormData(req, res);
} else {
console.log("[405] " + req.method + " to " + req.url);
res.writeHead(405, "Method not supported", { 'Content-Type': 'text/html' });
res.end('<html><head><title>405 - Method not supported</title></head><body><h1>Method not supported.</h1></body></html>');
}
} else if (req.url.indexOf('/javascripts/') >= 0) {
app.render(req.url.slice(1), 'application/ecmascript', httpHandler);
} else if (req.url.indexOf('/css/') >= 0) {
app.render(req.url.slice(1), 'text/css', httpHandler);
} else if (req.url.indexOf('/images/') >= 0) {
app.render(req.url.slice(1), 'image/jpg', httpHandler);
} else {
app.render('public/views/index.html', 'text/html', httpHandler);
}
}).listen(PORT, function() {
console.log('-= Francis Server Listening at http://127.0.0.1:' + PORT + ' =-');
});
}
static render(path, contentType, callback) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
static getFormData(req, res) {
const FORMIDABLE = require('formidable'),
DO_NAMES = require('./node/NameClass');
let formData = {};
new FORMIDABLE.IncomingForm().parse(req)
.on('field', function(field, name) {
formData[field] = name;
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
let finalName = new DO_NAMES(formData);
res.writeHead(200, {'content-type': 'text/plain'});
res.write('-= Received form: ');
res.end(finalName.getFirstName() + ' ' + finalName.getLastName());
});
}
}
module.exports = app;
It feels like it's trying to serve the image as text instead of picture. I verified that the image is there and readable.
I found the problem.
it happens here:
FS.readFile(__dirname + '/' + path, 'utf-8', function(err, str) {
callback(err, str, contentType);
});
You read the image file as UTF-8 but it is a binary file. That is why the image data is corrupt. Instead you have to use binary as encoding.
You could change your code like this:
static render(path, contentType, callback, encoding) {
const FS = require('fs');
FS.readFile(__dirname + '/' + path, encoding ? encoding : 'utf-8', function(err, str) {
callback(err, str, contentType);
});
}
and then call render like this:
app.render(req.url.slice(1), 'image/jpeg', httpHandler, 'binary');
There are obviously better ways to do it but this requires a minimum amount of change to your code. Just make sure the readFile() encoding is binary for binary files.
Also the correct mime type for jpg is image/jpeg not image/jpg. Most, if not all, browsers won't care but it is more clean.
It looks like your NODE server is setting the wrong MIME type. You can set the MIME type yourself, as you are doing, but this gets awfully painful. I would recommend using a MIME type node module that is made for this exact purpose.
https://www.npmjs.com/package/mime
This npm package does exactly this with very little effort.

How to handle several parallel request in NodeJs

I'm using PhantomJS for taking screenshot inside NodeJS but it can't handle multiple request from users. The problem is when several users send request concurrently they get same result.
This is the code I'm using:
var http = require('http');
var phantom = require('phantom');
var url, img;
http.createServer(function(req, res) {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.writeHeader(200, { "Content-Type": "text/html" });
url = req.url;
url = url.replace('/', '');
url = url.trim();
if (!(url == 'favicon.ico')) {
console.log(url);
phantom.create().then(function(ph) {
ph.createPage().then(function(page) {
page.property('viewportSize', { width: 1024, height: 768 }).then(function() {
page.open('http://' + url + '/').then(function(status) {
console.log(status);
page.property('onLoadFinished').then(function() {
if (!(status == 'success')) {
res.write('<html><body><h2>' + status + ' : ' + url + ' is not correct url!</h2></body></html>');
res.end();
page.close();
} else {
setTimeout(function() {
page.renderBase64('jpeg').then(function(img) {
res.write('<html><body><img src="data:image/jpeg;base64,' + img + '"/></body></html>');
res.end();
page.close();
});
}, 4000);
}
});
});
});
});
});
}
}).listen(80, '127.0.0.1');
console.log('Server running at http://127.0.0.1:80/');
You've defined var url, img; outside the scope of http request, meaning they're being shared by different requests (one request might change it while a previous one was still processing it), which is probably what's causing the issue. Move those declarations inside the request handler:
// var url, img; // << move this
http.createServer(function(req, res) {
var url, img; // << here
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.writeHeader(200, { "Content-Type": "text/html" });

Serving css, js files in Node.js without frameworks like Express

This question has probably been asked earlier but the answers are not very convincing.
I have the following code with me -
var http = require('http'),
fs = require('fs');
connect = require('connect');
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
fs.readFile(__dirname + path, function(err,data) {
if(err) {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end('500 - Internal Error');
} else {
res.writeHeader(responseCode,
{ 'Content-Type': contentType });
res.write(data)
res.end();
}
});
}
http.createServer(function(req,res){
// normalize url by removing querystring, optional
// trailing slash, and making lowercase
var path = req.url.replace(/\/?(?:\?.*)?$/, '')
.toLowerCase();
switch(path) {
case '': serveStaticFile(res, '/public/index.html','texthtml');
break;
default:
serveStaticFile(res, '/public/404.html', 'text/html',
404);
break;
}
}).listen(3000);
console.log('Server started on localhost:3000; press Ctrl-C to terminate....');
I am just not able to load the css and js files mentioned in the index.js. What am I doing wrong? If I open index.js in the browser directly, the css and js load fine.
Express uses serve-static for serving static directories which is uses send for streaming files. You can use that instead of express, or take at a look their implementation.
What are you think about send ? Use that like this:
function serveStaticFile(res, path, contentType, responseCode) {
if(!responseCode) responseCode = 200;
res.status = responseCode;
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
function headers(res, path, stat) {
res.setHeader('Content-type', contentType);
}
send(req, path, {root: __dirname})
.on('error', error)
.on('headers', headers)
.pipe(res);
}

NodeJS - Stream video that is being downloaded

I am writing a program that will stream a video file that is currently being downloaded onto the drive. The problem I am having seems to be getting the browser to actually play the video. the script listens for file changes, and then streams the rest, but the browser doesnt do anything besides display a blank Video page.
var fs = require('fs'),
http = require('http'),
filename = '/home/qrpike/Videos/topgears.mp4';
http.createServer(function (req, res) {
console.log(req.url);
if( req.url == '/video.mp4'){
res.writeHead(200,{
'Content-Type' : 'video/mp4',
'Cache-Control' : 'public',
'Connection' : 'keep-alive',
'Content-Disposition' : 'inline; filename=topgears.mp4;',
'Content-Transfer-Encoding' : 'binary',
'Transfer-Encoding' : 'chunked'
});
fs.open(filename, 'r', function(err, fd) {
if (err) throw new Error('Could not open file');
var position = 0;
fs.stat(filename, read);
fs.watchFile(filename, read.bind(null, null));
function read(err, stat) {
var delta = stat.size - position;
if (delta <= 0) return;
fs.read(fd, new Buffer(delta), 0, delta, position, function(err, bytes, buffer) {
console.log("err", err, "bytes", bytes, "position",position,"delta",delta);
res.write(buffer.toString('binary'));
});
position = stat.size;
}
});
}
}).listen(1337);
console.log('Server running at http://127.0.0.1:1337/');
So this answer depends on growing-file, which in theory does what you want. My concern is that the project hasn't had a commit in two years so I have no idea if it still works. That being said, this worked for me locally (though I didn't test piping to the video file):
var fs = require('fs');
var http = require('http');
var GrowingFile = require('growing-file');
var FILENAME = '/home/dave/Desktop/video/video.ogg';
var server = http.createServer(function(req, res) {
var file;
if (req.url === '/video.ogg') {
res.writeHead(200, {
'Content-Type': 'video/ogg'
});
file = GrowingFile.open(FILENAME);
file.pipe(res);
} else {
res.statusCode = 404;
res.end('Not Found');
}
});
server.listen(1337);

Resources