0

I am new to JS and trying to write a simple webserver. Here is my prj browser. When I go to browser -http://localhost:4000/home.html

I get the error - Cannot call method 'toString' of null.

The problem is that UrlResLoader.httpcode and UrlResLoader.fileType isn't defined

  var UrlResLoader = new UrlLoader();
           UrlResLoader.requestUrl(fileResEngine);
           res.writeHead(UrlResLoader.httpcode, UrlResLoader.fileType);
           res.write(UrlResLoader.data);
           res.end()

I am not sure what is the problem here, I have hooked a debugger and have found that the problem happens on the fs.readFile(fileResEngine.fullpath, function (err, data).

I am still unclear on why is it happening. After researching a bit, I found that inorder to invoke closure functions, I should save the "this" pointer to refer the member varaibles. Other wise, the instance will be different. But, this hasn't fixed the problem. Also, any design flaws or comments will be welcomed here.

enter image description here

Here is my code -

The main file -

const http = require('http');
const parseUrl = require('parseurl');


const fileEngine = require('./fileErrHandler');
const UrlLoader = require('./urlController');

http.createServer( function (req, res)
{
    try
    {

        // this is a library function
        var pathName = decodeURIComponent(parseUrl(req).pathname);

        var fileResEngine=  new fileEngine(pathName);

        // create a literal validateFile to validate the path
        fileResEngine.pathCheck();
        if (fileResEngine.error === true )
        {
            res.statusCode = fileResEngine.statusCode;
            res.end(fileResEngine.ErrorMsg);

            return;
        }

        else
        {
           var UrlResLoader = new UrlLoader();
           UrlResLoader.requestUrl(fileResEngine);
           res.writeHead(UrlResLoader.httpcode, UrlResLoader.fileType);
           res.write(UrlResLoader.data);
           res.end();
        }

    }
    catch(err)
    {
        res.statusCode = err.status || 500;
        res.end(err.message);
    }

}).listen(4000);

The file error handler

var resolvePath = require('resolve-path');
const path = require('path');
var pagesDir = path.join(__dirname, 'Pages');



    function pathCheckerEngine(path)
    {
        this.error = true;
        this.path = path;
        this.statusCode = 500;
        this.ErrorMsg = "Internal Server Error";
        this.PageRequest = "home.html";
        this.extname = "html";
        this.fullpath = './';
        var pcEngine = this;
        this.pathCheck = function()
        {

           try {
               if (!path) {
                   pcEngine.statusCode = 400;
                   pcEngine.ErrorMsg = 'path required';
                   pcEngine.error = true;
               }

               else {

                   //removes first '/' of the path
                   pcEngine.PageRequest = path.substr(1);
                   pcEngine.fullpath = resolvePath(pagesDir, this.PageRequest);

                   pcEngine.statusCode = 200;
                   pcEngine.ErrorMsg = null;
                   pcEngine.error = false;
                   pcEngine.extname =  this.PageRequest.split('.').pop();

               }
           }
           catch(err)
           {
               pcEngine.statusCode = err.status || 500;
               pcEngine.ErrorMsg = 'Malicious Page Request';
               pcEngine.error = true;
           }
        }
    }
    module.exports = pathCheckerEngine;

And the final file

const fileEngine = require('./fileErrHandler');

const fs = require('fs');
const mime = require('mime');

    function UrlController(fileResEngine) {
        this.httpcode = null;
        this.fileType = null;
        this.data = null;
        var urctrl = this;
        this.requestUrl = function (fileResEngine) {


            switch (fileResEngine.extname) {

                case 'html':
                    fs.readFile(fileResEngine.fullpath, function (err, data) {
                        if (err)
                        {
                            console.log(err);
                            urctrl.httpcode = 404;
                            urctrl.data = "Page not found";
                            return;
                        }
                        urctrl.httpcode = 200;
                        urctrl.fileType = "'Content-Type': 'text/html'";
                        urctrl.data = data;

                    });

                    break;

                case 'png':
                case 'jpeg':
                case 'jpg':
                case 'bmp':
                    fs.readFile(fileResEngine.fullpath, function (err, data) {
                        if (err) {
                            console.log(err);
                            urctrl.httpcode = 404;
                            urctrl.data = "File not found";
                            return;
                        }
                        urctrl.httpcode = 200;
                        urctrl.fileType = mime.lookup(mime.lookup('./images' + req.url));
                        urctrl.data = data;

                    });

                    break;

                default:
                    urctrl.httpcode = 404;
                    urctrl.data = "Page not Found"
                    break;

            }
        }

        return;
    }

    module.exports = UrlController;
Bali Vinayak
  • 289
  • 1
  • 4
  • 11
  • 1
    And it doesn't say what line the error is on ? – adeneo Sep 11 '17 at 00:20
  • @adeneo he said around `fs.readFile(fileResEngine.fullpath, function (err, data)` – Isaac Sep 11 '17 at 00:22
  • 1
    @Isaac - my point being, the error should say exactly what line the error was thrown on, not "around" something that occurs twice in the posted code – adeneo Sep 11 '17 at 00:25
  • @adeneo I know, and looking at his code it isn't immediately obvious what is null and where `toString` would be called. The error is probably elsewhere, and I agree this question should be cleaned up – Isaac Sep 11 '17 at 00:27
  • res.writeHead(UrlResLoader.httpcode, UrlResLoader.fileType); Here - UrlResLoader.httpcode is null and also filetype is null. – Bali Vinayak Sep 11 '17 at 02:17

1 Answers1

1

For starters, this is wrong:

var pathName = decodeURIComponent(parseUrl(req).pathname);

which is probably why your pathName later on is wrong. req is a request object, it's not a URL you can parse. The URL path is in that request object. req.url will contain the request path (without domain. port and protocol - just the path).

So, change that above line to this:

var pathName = decodeURIComponent(req.url);

I can't promise there aren't other errors in that block of code (I don't know that fileEngine module at all), but this is at least one thing to clear up that affects your pathName being wrong which can lead to the error you get later on when trying to use the pathName.


Now that you've fixed that, it appears you also have asynchronous problems. You've coded UrlController.requestUrl, but not coded any way to know when it is done with its asynchronous work. It returns right away and then sometime later, the fs.readFile() inside of it finishes. So you end up trying to use the properties of your object long before they have been set.

You will need to use either promises or a callback to let your caller know when your asynchronous operations are actually done. I'd suggest you read this canonical answer on the topic of returning async data.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • res.writeHead(UrlResLoader.httpcode, UrlResLoader.fileType); Here - UrlResLoader.httpcode is null and also filetype is null. – Bali Vinayak Sep 11 '17 at 02:43
  • @BaliVinayak - See what I added to the end of my answer. You also have asynchronous issues where you are trying to use the result before it has been set. – jfriend00 Sep 11 '17 at 03:00