jonny
jonny

Reputation: 3098

Static HTTP File Server in NodeJs: Sometimes external .js and .css files load properly, sometimes they don't?

Edit: I know using express or whatever would be easier, but this is all a learning exercise, so sorry if this is all massively convoluted, haha!

Edit 2: It appears (after adding a few console logs for debugging) that it seems the problem has something to do with the fact that when the browser makes one request to the server (say, for style.css), it makes another request (e.g. for login-fail.js) before completing the response for the first request. It seems these multiple requests from the browser cause some sort of problem, each subsequent request preventing the previous from completing. Yikes, I need help.

Edit 3: After some debugging, it appears that the pathname variable does not change its value upon each request. For some reason, pathname's value persists over each request and this makes every request's response the same - stranger still, the value for uri changes over each request (and the uri is what gives the pathname its value...) still trying to find out why this bizarre behaviour is happening.

So I've been having this problem when the server makes requests for external .js and .css files linked to specific .html files. The responses are always inconsistent. For instance, sometimes the code will run perfectly, other times the css will load and not the js, sometimes both, sometimes neither. I am not able to determine whether this is because my code is synchronous, or for some other reason. Here is my code:

Server.js

//Module requires
var http = require("http"),
fs = require("fs"),
path = require('path'),
url = require('url'),
invoke = require("./invoke");


//Object "MIMETYPES"
//Maps relationships between file extensions and their respective MIME Content-Types    
var MIMETYPES = {
".html": "text/html",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".js": "text/javascript",
".css": "text/css"
};

//Object "invokeOptions"
//Options passed to Invoke() function for POST requests 
var invokeOptions = {
postData : "",
uri : ""
}

var PORT = 8888;

//HTTP Server Begin
http.createServer(function(req, res) {
var uri = url.parse(req.url).pathname;
    pathname = path.resolve(__dirname, "..") + uri;

console.log("Recieved " + req.method + " request for : " + uri);

invokeOptions.uri = uri;

//GET requests wrapper
if (req.method == "GET"){
    //Invoke() function handler for GET requests
    if (path.extname(pathname) == ""){
        invoke.invoke(invokeOptions, req, res);
        return;
    }

    //Static file server for GET requests
    fs.exists(pathname, function(exists) {
        if(!exists) {
            console.log("Requested file \'" + pathname + "\' doesn't exist.");
            res.writeHead(404, {'Content-Type': 'text/plain'});
            res.write('404 Not Found\n');
            res.end();
            return;
        }
        var contentType = MIMETYPES[path.extname(pathname)];
        res.writeHead(200, {"Content-Type" : contentType});
        console.log("Current URI: " + uri + " has content type: " + contentType);

        fs.createReadStream(pathname).pipe(res);
        return;
    });
}

//POST requests wrapper
if (req.method == "POST"){
    var postData = "";

    req.on("data", function(postPacket) {
        postData += postPacket;
    });

    req.on("end", function() {
        invokeOptions.postData = postData;
        invoke.invoke(invokeOptions, req, res);
        return;
    }); 
}   
}).listen(PORT);

console.log ("Server listening on port: " + PORT);

Invoke.js - This handles requests for non-files, that is requests for functions on the server

var fs = require("fs"),
querystring = require("querystring"),
path = require("path");

function invoke (options, req, res){
process.stdout.write("Invoking function --> ");

if (options.uri == "/"){
    console.log("Index");
    res.writeHead(200, {"Content-Type" : "text/html"});
    fs.createReadStream("../index.html").pipe(res);
    return;
}
if (options.uri == "/login"){
    console.log("Login");

    fs.readFile(path.resolve("../users.json"), "UTF-8", function(err, data){
        if (err) throw err;
        var json = JSON.parse(data);

        var user = querystring.parse(options.postData).username,
            password = querystring.parse(options.postData).password;

        console.log("Submitted Username:  " + user + "\nSubmitted Password:  " + password);

        if (json.users[0].username == user && json.users[0].password == password){
            res.writeHead(200, {"Content-Type" : "text/html"});
            fs.createReadStream("../app.html").pipe(res);
            return; 
        }
        else {
            res.writeHead(300, {"Content-Type" : "text/html"});
            fs.createReadStream("../login-fail.html").pipe(res);
            return; 
        }
    });     
}
else {
    console.log("Error! Bad request.");
    res.writeHead(400, {"Content-Type" : "text/plain"});
    res.end("Error 400: Bad Request. \nThere is no function corresponding to that request.");
}
}

exports.invoke = invoke;

Login-fail.js - This is the code that hardly ever loads

$(document).ready(function() {
var current = 3;
var countdown = $(".countdown");

function down (){
    current--;
    if (current != 0){
         countdown.text(current);             
    }
    else {
    clearInterval(interval);
    window.location.replace("./");
    }

}
var interval = setInterval(down, 1000);
});

Basically, the index.html file is a form which accepts a username and password, compares the submitted POST data to a json file, and if it matches the hashes in the json file it requests app.html, otherwise it requests login-fail.html. When the login-html file is called, it has linked to it css and js which when requested hardly ever run!

Also, I thought it should be noted that the console.logs for "content-type" when requesting the css is 'text/javascript' when it doesn't work. Any help would be massively appreciated!

Upvotes: 0

Views: 399

Answers (2)

jonny
jonny

Reputation: 3098

Holy crap.

Pathname wasn't being declared as a variable each request, because I used a ; instead of a ,

I'll go die now ladies and gents.

Upvotes: 2

mscdex
mscdex

Reputation: 106696

The relative paths you're using in your login-fail.html are probably not resolving correctly because the URL path doesn't change (/login), so the browser is looking for /login/css/style.css and /login/js/login-fail.js. Try modifying your login-fail.html to use absolute paths instead of relative paths.

Upvotes: 0

Related Questions