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.
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;
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.
Related
So I searched for a week now, tried every solution in other Posts or Forums, still nothing so yeah I'm in need of help .. please.
Node is up to date, if thats important. FYI. v9.4.0
If there's something else you guys need to know let me know.
'use strict'
var EventEmitter = require('events').EventEmitter
var util = require('util')
var WebSocketServer = require('ws').Server
var CONNECTION_ERROR_LOG_RATE = 1000 * 60 * 60
var Browser = function () {
if (!(this instanceof Browser)) return new Browser()
EventEmitter.call(this)
this.wss = null
this.ws = null
this.lastConnectionErrorLog = null
}
util.inherits(Browser, EventEmitter)
Browser.prototype.listen = function listen (port) {
console.log('Listening on websocket port %d', port)
this.wss = new WebSocketServer({port, host: '127.0.0.1'})
var self = this
this.wss.on('connection', function (ws) {
self.ws = ws
ws.on('message', function (data) {
var res = JSON.parse(data)
self.emit('message', res)
})
self.lastConnectionErrorLog = null
self.emit('connected')
})
this.wss.on('close', function () {
self.emit('closed')
})
this.wss.on('error', function (err) {
self.emit('error', err)
})
}
Browser.prototype.isConnected = function isConnected () {
return !!this.ws
}
Browser.prototype.send = function send (req) {
if (!this.ws) {
var elapsed = this.lastConnectionErrorLog === null ||
Date.now() - this.lastConnectionErrorLog > CONNECTION_ERROR_LOG_RATE
if (elapsed) {
console.log('browser not connected')
this.lastConnectionErrorLog = Date.now()
}
return
}
var self = this
var message = JSON.stringify(req)
this.ws.send(message, function (err) {
if (err) {
var elapsed = self.lastConnectionErrorLog === null ||
Date.now() - self.lastConnectionErrorLog > CONNECTION_ERROR_LOG_RATE
if (elapsed) {
self.lastConnectionErrorLog = Date.now()
self.emit('messageError', err)
}
} else {
self.emit('messageSent')
}
})
}
module.exports = Browser
I am kind of new to Websockets/Node/Javascript so it may be that the answer is very simple..
I apologize in advance
Thank you kindly
The error you're seeing is because you are sending a non-WebSocket request (i.e, a normal HTTP request from a web browser) to a WebSockets server.
To connect to a WebSockets server in a browser, you'll need to use the WebSocket interface in Javascript.
The code you posted doesn't include the string "Upgrade required" so the problem must be coming from elsewhere. Since you're working with Node/NPM, it's usually pretty easy to figure out where this problem is coming from. Just use your IDE to search across all files in your project director (including the node_modules folder) to see where the "Upgrade required" string is found. This will at least point you to which component is triggering that error and point you towards where to look for further troubleshooting and/or upgrading.
I want to build a db of users and if they have a site check if the site is online save the url if not delete it and advise the user that the site is offline. The problem is when the program goes thru the request it seems to do nothing and just jumps out, I tried using axios and request but the problem still remains; I believe it might be due to asynchronous computations. Any help will be really appreciated =)
var r = require('rethinkdb');
var axios = require('axios');
var schema = function(data, callback){
new_schema = true;
var schema = {};
if(new_schema){
schema.user = user.id;
}
schema.name = data.title || '';
schema.email = data.email || '';
if(data.url){
axios.get(data.url).then(function(err, response){
if(err) schema.url = 'no site'
schema.url = 'data.url';
callback(schema);
}).catch(function(error){
console.log(error);
callback(schema);
});
}else{
callback(schema);
}
};
var datos = '';
command.stdout.on('data', (data) => {
datos = datos + data;
});
command.on('close', (code) => {
const objs = JSON.parse(datos);
for (var i in objs) {
let obj = schema(objs[i], function(sch){
console.log(sch);
});
}
}catch(e){
console.log(e);
}
process.exit();
});
});
The problem is due to the asynchronous. To solved it had to use queues and I used the library better-queue. This fix the problem and makes all the calls to the server side.
I am facing an issue with try/catch in Mocha script. Eventhough some of the tests are failing but Mocha is displaying as "All are passed". Below is my test script file help me in fixing this issue. In this code, I am posting a input string from an array to an URL and expecting XML response. I am using Xml2js module to parse Xml response to JSON string and comparing XML child elements with expect
var expect = require('chai').expect;
var request = require('supertest');
var xml2js = require('xml2js');
var PrettyError = require('pretty-error');
var pe = new PrettyError();
var async = require('async');
var fs = require('fs');
var parser = new xml2js.Parser();
var build = require('./config').Build;
var build_url = build.Environment[build.Type].HostUrl;
var inputArray1 = [];
describe('Verifying Incoming post output response', function() {
var index = 0;
validateFields(index);
function validateFields(index) {
var input = inputArray1[index];
var actualInputArray = input.split(",");
describe(actualInputArray[0], function() {
it(actualInputArray[1], function(done) {
request(build_url).post('/lead')
.send(actualInputArray[2])
.end(function(err, res) {
var resxml = res.text;
console.log("resxml", resxml);
parser.parseString(resxml, function(err, result) {
try {
expect(result['Header']['RESULT']).to.deep.equal([actualInputArray[3]]);
expect(result['Header']['STATUS_CODE']).to.deep.equal([actualInputArray[4]]);
expect(result['Header']['MESSAGE']).to.deep.equal( [actualInputArray[5]]);
} catch (err) {
console.log(pe.render(err.message));
}
index = index + 1;
if (index < inputArray1.length) {
validateFields(index);
}
done();
});
});
}).timeout(5000);
});
}
});
When you catch the error the done() method is still called and resolve the promise, so all your tests will pass anyway.
Try to add done(new Error("Your error message")); to the catch and let fail the test in case of error.
I've had no trouble testing my own route handlers but in this case I want to test express's static handler. I can't for the life of me figure out why it's hanging. Clearly there's some callback I'm missing or some event I need to emit.
I tried to make the smallest example I could.
var events = require('events');
var express = require('express');
var stream = require('stream');
var util = require('util');
function MockResponse(callback) {
stream.Writable.call(this);
this.headers = {};
this.statusCode = -1;
this.body = undefined;
this.setHeader = function(key, value) {
this.headers[key] = value;
}.bind(this);
this.on('finish', function() {
console.log("finished response");
callback();
});
};
util.inherits(MockResponse, stream.Writable);
MockResponse.prototype._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
function createRequest(req) {
var emitter = new events.EventEmitter();
req.on = emitter.on.bind(emitter);
req.once = emitter.once.bind(emitter);
req.addListener = emitter.addListener.bind(emitter);
req.emit = emitter.emit.bind(emitter);
return req;
};
describe('test', function() {
var app;
before(function() {
app = express();
app.use(express.static(__dirname));
});
it('gets test.js', function(done) {
var req = createRequest({
url: "http://foo.com/test.js",
method: 'GET',
headers: {
},
});
var res = new MockResponse(responseDone);
app(req, res);
function responseDone() {
console.log("done");
done();
}
});
});
Setup,
mkdir foo
cd foo
mkdir test
cat > test/test.js # copy and paste code above
^D
npm install express
npm install mocha
node node_modules/mocha/bin/mocha --recursive
it just times out.
What am I missing?
I also tried making the request a Readable stream. No change
var events = require('events');
var express = require('express');
var stream = require('stream');
var util = require('util');
function MockResponse(callback) {
stream.Writable.call(this);
this.headers = {};
this.statusCode = -1;
this.body = undefined;
this.setHeader = function(key, value) {
this.headers[key] = value;
}.bind(this);
this.on('finish', function() {
console.log("finished response");
callback();
});
};
util.inherits(MockResponse, stream.Writable);
MockResponse.prototype._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
function MockMessage(req) {
stream.Readable.call(this);
var self = this;
Object.keys(req).forEach(function(key) {
self[key] = req[key];
});
}
util.inherits(MockMessage, stream.Readable);
MockMessage.prototype._read = function() {
this.push(null);
};
describe('test', function() {
var app;
before(function() {
app = express();
app.use(express.static(__dirname));
});
it('gets test.js', function(done) {
var req = new MockMessage({
url: "http://foo.com/test.js",
method: 'GET',
headers: {
},
});
var res = new MockResponse(responseDone);
app(req, res);
function responseDone() {
console.log("done");
done();
}
});
});
I've still been digging. Look inside static-server I see it creates a Readable stream by calling fs.createReadStream. It does effectively
var s = fs.createReadStream(filename);
s.pipe(res);
So trying that myself works just fine
it('test stream', function(done) {
var s = fs.createReadStream(__dirname + "/test.js");
var res = new MockResponse(responseDone);
s.pipe(res);
function responseDone() {
console.log("done");
done();
}
});
I thought maybe it's something about express waiting for the input stream to finish but that doesn't seem to be it either. If I consume the mock input stream with the response it works just fine
it('test msg->res', function(done) {
var req = new MockMessage({});
var res = new MockResponse(responseDone);
req.pipe(res);
function responseDone() {
console.log("done");
done();
}
});
Any insight what I might be missing would be helpful
Note: while suggestions for 3rd party mocking libraries are appreciated I'm still really looking to understand what I'm missing to do it myself. Even if I eventually switch to some library I still want to know why this isn't working.
I found two issues that prevent the finish callback from being executed.
serve-static uses send module which is used to create file readstream from the path and pipe it to res object. But that module uses on-finished module which checks if finished attribute is set to false in response object, otherwise it destroys the file readstream. So filestream never gets a chance to emit data event.
express initialization overwrites the response object prototype. So the default stream methods like end() method is overwritten by http response prototype:
exports.init = function(app){
return function expressInit(req, res, next){
...
res.__proto__ = app.response;
..
};
};
To prevent this, I added another middleware right before static middleware to reset it back to MockResponse prototype:
app.use(function(req, res, next){
res.__proto__ = MockResponse.prototype; //change it back to MockResponse prototype
next();
});
Here are the changes made to make it work with MockResponse:
...
function MockResponse(callback) {
...
this.finished = false; // so `on-finished` module doesn't emit finish event prematurely
//required because of 'send' module
this.getHeader = function(key) {
return this.headers[key];
}.bind(this);
...
};
...
describe('test', function() {
var app;
before(function() {
app = express();
//another middleware to reset the res object
app.use(function(req, res, next){
res.__proto__ = MockResponse.prototype;
next();
});
app.use(express.static(__dirname));
});
...
});
EDIT:
As #gman pointed out, it is possible to use direct property instead of prototype method. In that case the extra middleware to overwrite prototype isn't necessary:
function MockResponse(callback) {
...
this.finished = false; // so `on-finished` module doesn't emit finish event prematurely
//required because of 'send' module
this.getHeader = function(key) {
return this.headers[key];
}.bind(this);
...
//using direct property for _write, write, end - since all these are changed when prototype is changed
this._write = function(chunk, encoding, done) {
if (this.body === undefined) {
this.body = "";
}
this.body += chunk.toString(encoding !== 'buffer' ? encoding : undefined);
done();
};
this.write = stream.Writable.prototype.write;
this.end = stream.Writable.prototype.end;
};
It appears my answer is not complete. For some reason the app works only if the file is not found. First thing to debug is do the following in your shell (or cmd):
export DEBUG=express:router,send
then run the test, you'll get more info.
Meanwhile I am still looking into this, for now, ignore my answer below.
----------- ignore this till I verify that it does work -----------
It seems like express static does not favor the absolute path you give it (__dirname).
Try:
app.use(express.static('.'));
and it will work. Note that your current dir for the mocha runner is 'test/'
I have to admit this is quite a mistery. I tried 'fulling' it by doing:
app.use(express.static(__dirname + '/../test')
but still it didn't work. Even specifying a full path did not solve this. Strange.
I am trying to write a newer watch module that uses the fs.watch method instead of the watchFile approach.
So far, it works beautifully, but only when I run it outside of mocha. I can't figure out why my unit test is throwing a tantrum, maybe someone here can?
Here is my class code:
/**
* requirements
*/
var fs, path, events;
fs = require('fs');
path = require('path');
events = require('events');
/**
* private
*/
var Monitor = function(directory, options) {
this.directory = directory;
this.options = options || {};
(this.options.lazy && this.empty()) || this.walk(this.directory);
this.watch(this.directory);
};
Monitor.prototype = new events.EventEmitter();
Monitor.prototype.watch = function(directory, stats) {
var stats = stats || {};
if (!this.directories[directory]) {
var w = fs.watch(directory, this.options, this.justlookatit.bind(this));
}
this.directories[directory] = { 'stats': stats, 'w': w };
};
Monitor.prototype.directories = function() {
if (!Object.keys(this.directories).length) {
this.walk(this.directory);
}
return this.directories;
};
Monitor.prototype.files = function() {
if (!Object.keys(this.files).length) {
this.walk(this.directory);
}
return this.files;
};
Monitor.prototype.unwatch = function() {
if (!Object.keys(this.directories).length) {
for (var dir in this.directories) {
dir.w.close();
}
}
};
Monitor.prototype.empty = function() {
this.unwatch();
this.files = {};
this.directories = {};
};
Monitor.prototype.walk = function(directory) {
var monitor = this;
this.empty();
fs.readdir(directory, function(err, files) {
if (err) return;
for (var file in files) {
var fullname = path.resolve(files[file]);
if (!monitor.options.filter || monitor.options.filter(fullname)) {
fs.stat(fullname, function(err, stats) {
if (err) return;
if (stats.isDirectory()) {
monitor.walk(fullname);
monitor.watch(fullname, stats);
} else {
monitor.files[fullname] = stats;
}
});
}
}
});
};
Monitor.prototype.justlookatit = function(action, file) {
var monitor = this;
var fullname = path.resolve(file);
if (this.options.filter && !this.options.filer(fullname)) return;
fs.exists(fullname, function(exists) {
if (exists) {
fs.stat(fullname, function(err, stats) {
if (stats.isDirectory()) {
monitor.watch(fullname, stats);
} else {
if (monitor.files[fullname]) {
if (stats.mtime.getTime() > monitor.files[fullname].mtime.getTime()) {
monitor.emit('modified', fullname, stats);
}
} else {
monitor.emit('added', fullname, stats);
}
monitor.files[fullname] = stats;
}
});
} else {
if (monitor.files[fullname]) {
delete monitor.files[fullname];
monitor.emit('deleted', fullname);
} else if (monitor.directories[fullname]) {
monitor.directories[fullname].w.close();
delete monitor.directories[fullname];
}
}
});
};
/**
* exports
*/
exports.start = function(directory, options) {
return new Monitor(directory, options);
};
Here is my Working external test code:
var watch = require("./watch.js");
var fs = require('fs');
monitor = watch.start(__dirname);
monitor.on('added', function(file, stats) {
console.log("Caught Added: " + file);
});
monitor.on('modified', function(file, stats) {
console.log("Caught Modified: " + file);
});
monitor.on('deleted', function(file) {
console.log("Caught deleted: " + file);
});
// try creating a file immediately
fs.openSync('v.md', 'w');
The first test file runs perfectly fine, and I've tried both openSync and open. Finally, here is a version of the same test code, wrapped in a mocha unit test which is timing out:
/**
* requirements
*/
var watch, Q, fs, path, mocha, chai, assert;
watch = require('../lib/watch.js');
Q = require('q');
fs = require('fs');
path = require('path');
mocha = require('mocha');
chai = require('chai');
assert = chai.assert;
/**
* variables
*/
var watch_directory = path.join(__dirname, './watch');
/**
* tests
*/
describe('test watch', function() {
it('should create a monitor and run callbacks after fs changes', function(done) {
// I had planned to implement promises that chained the three callbacks
// but couldn't get one of them working in general
var added = function(file, stats) {
console.log("added");
done();
};
var modified = function(file, stats) {
console.log("modified");
};
var deleted = function(file, stats) {
console.log("deleted");
};
// create our service
var monitor = watch.start(watch_directory);
// assert it is defined
assert.isDefined(monitor);
// establish a listener
monitor.on('added', added);
monitor.on('modified', modified);
monitor.on('deleted', deleted);
// here is a file name using the current date to prevent duplication during tests
var file = path.join(watch_directory, (new Date()).getTime() + '.md');
// let's create the file, then delete it
fs.open(file, 'w+', function(err, fileDescriptor) {
// this prints before console output from the watch.js's `justlookatit` method
console.log(err);
console.log("writing to file");
// we probably don't want to try closing the fileDescriptor if the open failed
if (err) return;
// close the file descriptor
fs.close(fileDescriptor, function() {
// delete the file we just created
// fs.unlink(file, function() { /* not a big deal */ });
});
});
// modify a known-existing test file
fs.open('test.md', 'w+', function() {/* we don't care about this */});
})
});
I checked with console.log(fullname) inside the justlookatit method on the watch code, and it spits out the correct file name, matching the one generated by the unit test.
However, it then proceeds to return false when I run fs.exists. As I undestand it, that means the file system is notifying me that a file exists before it exists, which doesn't make sense really. So I tried adding an additional delay by wrapping my fs.exists method in a setTimeout, and that didn't change the results. I have also tried using both openSync and existsSync, and that made no difference.
I'm stumped, does anyone have any ideas why the mocha code isn't working?
So, the solution was to go for a walk. I came back, looked at the code again and figured out the cause of the problem with mocha, and also identified many other bugs.
The problem was the lack of context. The justlookatit method does not have a context, and in the test.js scenario it is watching the current directory, while the mocha test is watching a sub-directory.
The path.resolve was receiving only the file name, not the directory, and therefore merged it with the default (executables) directory, so the level of test.js, or watch_test.js for mocha. It proceeded to fail to locate any of the files in the mocha test case because they were all one level below the executable.
I won't go into detail about all the other bugs, but I may come back and post the repository link when I get to a point that I want to push it online.
You're missing the callback return(done); at the end of your test. Unless you call that callback, Mocha will time out every time.