WebFund 2016W: Assignment 3: Difference between revisions
Created page with "'''This assignment is not yet finalized (still in progress!).''' ==Code== ===tinywebserver.js=== <source lang="javascript" lines> </source> ===form-demo/bin/www=== <sou..." |
No edit summary |
||
Line 3: | Line 3: | ||
==Code== | ==Code== | ||
Full source: [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/code/simplegrep_async.js simplegrep_async.js], [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/code/tinywebserver.js tinywebserver.js], [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/code/form-demo.zip form-demo.zip] | |||
===simplegrep_async.js=== | |||
<source lang="javascript" lines> | |||
var fs = require('fs'); | |||
if (process.argv.length < 4) { | |||
console.error('Not enough parameters given. Try this: ' + | |||
'"node simplegrep_async term filename.txt"'); | |||
process.exit(1); | |||
} | |||
var searchterm = process.argv[2]; | |||
var filename = process.argv[3]; | |||
var returnMatches = function(err, rawContents) { | |||
var lines = rawContents.split('\n'); | |||
lines.forEach(function(theLine) { | |||
if (theLine.indexOf(searchterm) > -1) { | |||
console.log(theLine); | |||
} | |||
}); | |||
} | |||
fs.readFile(filename, 'utf-8', returnMatches); | |||
</source> | |||
===tinywebserver.js=== | ===tinywebserver.js=== | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
var path = require('path'); | |||
var http = require('http'); | |||
var fs = require('fs'); | |||
var MIME_TYPES = { | |||
'css': 'text/css', | |||
'gif': 'image/gif', | |||
'htm': 'text/html', | |||
'html': 'text/html', | |||
'ico': 'image/x-icon', | |||
'jpeg': 'image/jpeg', | |||
'jpg': 'image/jpeg', | |||
'js': 'text/javascript', | |||
'json': 'application/json', | |||
'png': 'image/png', | |||
'txt': 'text/text' | |||
}; | |||
var options = { | |||
host: 'localhost', | |||
port: 8080, | |||
index: 'index.html', | |||
docroot: '.' | |||
}; | |||
var get_mime = function(filename) { | |||
var ext, type; | |||
for (ext in MIME_TYPES) { | |||
type = MIME_TYPES[ext]; | |||
if (filename.indexOf(ext, filename.length - ext.length) !== -1) { | |||
return type; | |||
} | |||
} | |||
return null; | |||
}; | |||
var respond = function(request, response, status, content, content_type) { | |||
if (!status) { | |||
status = 200; | |||
} | |||
if (!content_type) { | |||
content_type = 'text/plain'; | |||
} | |||
console.log("" + status + "\t" + | |||
request.method + "\t" + request.url); | |||
response.writeHead(status, { | |||
"Content-Type": content_type | |||
}); | |||
if (content) { | |||
response.write(content); | |||
} | |||
return response.end(); | |||
}; | |||
var serve_file = function(request, response, requestpath) { | |||
return fs.readFile(requestpath, function(error, content) { | |||
if (error != null) { | |||
console.error("ERROR: Encountered error while processing " + | |||
request.method + " of \"" + request.url + | |||
"\".", error); | |||
return respond(request, response, 500); | |||
} else { | |||
return respond(request, response, 200, | |||
content, get_mime(requestpath)); | |||
} | |||
}); | |||
}; | |||
var return_index = function(request, response, requestpath) { | |||
var exists_callback = function(file_exists) { | |||
if (file_exists) { | |||
return serve_file(request, response, requestpath); | |||
} else { | |||
return respond(request, response, 404); | |||
} | |||
} | |||
if (requestpath.substr(-1) !== '/') { | |||
requestpath += "/"; | |||
} | |||
requestpath += options.index; | |||
return fs.exists(requestpath, exists_callback); | |||
} | |||
var request_handler = function(request, response) { | |||
var requestpath; | |||
if (request.url.match(/((\.|%2E|%2e)(\.|%2E|%2e))|(~|%7E|%7e)/) != null) { | |||
console.warn("WARNING: " + request.method + | |||
" of \"" + request.url + | |||
"\" rejected as insecure."); | |||
return respond(request, response, 403); | |||
} else { | |||
requestpath = path.normalize(path.join(options.docroot, request.url)); | |||
return fs.exists(requestpath, function(file_exists) { | |||
if (file_exists) { | |||
return fs.stat(requestpath, function(err, stat) { | |||
if (err != null) { | |||
console.error("ERROR: Encountered error calling" + | |||
"fs.stat on \"" + requestpath + | |||
"\" while processing " + | |||
request.method + " of \"" + | |||
request.url + "\".", err); | |||
return respond(request, response, 500); | |||
} else { | |||
if ((stat != null) && stat.isDirectory()) { | |||
return return_index(request, response, requestpath); | |||
} else { | |||
return serve_file(request, response, requestpath); | |||
} | |||
} | |||
}); | |||
} else { | |||
return respond(request, response, 404); | |||
} | |||
}); | |||
} | |||
}; | |||
var server = http.createServer(request_handler); | |||
server.listen(options.port, options.host, function() { | |||
return console.log("Server listening at http://" + | |||
options.host + ":" + options.port + "/"); | |||
}); | |||
</source> | </source> | ||
Line 13: | Line 171: | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
#!/usr/bin/env node | |||
/** | |||
* Module dependencies. | |||
*/ | |||
var app = require('../app'); | |||
var debug = require('debug')('form-demo:server'); | |||
var http = require('http'); | |||
/** | |||
* Get port from environment and store in Express. | |||
*/ | |||
var port = normalizePort(process.env.PORT || '3000'); | |||
app.set('port', port); | |||
/** | |||
* Create HTTP server. | |||
*/ | |||
var server = http.createServer(app); | |||
/** | |||
* Listen on provided port, on all network interfaces. | |||
*/ | |||
server.listen(port); | |||
server.on('error', onError); | |||
server.on('listening', onListening); | |||
/** | |||
* Normalize a port into a number, string, or false. | |||
*/ | |||
function normalizePort(val) { | |||
var port = parseInt(val, 10); | |||
if (isNaN(port)) { | |||
// named pipe | |||
return val; | |||
} | |||
if (port >= 0) { | |||
// port number | |||
return port; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Event listener for HTTP server "error" event. | |||
*/ | |||
function onError(error) { | |||
if (error.syscall !== 'listen') { | |||
throw error; | |||
} | |||
var bind = typeof port === 'string' | |||
? 'Pipe ' + port | |||
: 'Port ' + port | |||
// handle specific listen errors with friendly messages | |||
switch (error.code) { | |||
case 'EACCES': | |||
console.error(bind + ' requires elevated privileges'); | |||
process.exit(1); | |||
break; | |||
case 'EADDRINUSE': | |||
console.error(bind + ' is already in use'); | |||
process.exit(1); | |||
break; | |||
default: | |||
throw error; | |||
} | |||
} | |||
/** | |||
* Event listener for HTTP server "listening" event. | |||
*/ | |||
function onListening() { | |||
var addr = server.address(); | |||
var bind = typeof addr === 'string' | |||
? 'pipe ' + addr | |||
: 'port ' + addr.port; | |||
debug('Listening on ' + bind); | |||
} | |||
</source> | </source> | ||
Line 21: | Line 268: | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
var express = require('express'); | |||
var path = require('path'); | |||
var favicon = require('serve-favicon'); | |||
var logger = require('morgan'); | |||
var cookieParser = require('cookie-parser'); | |||
var bodyParser = require('body-parser'); | |||
var routes = require('./routes/index'); | |||
var app = express(); | |||
// view engine setup | |||
app.set('views', path.join(__dirname, 'views')); | |||
app.set('view engine', 'jade'); | |||
// uncomment after placing your favicon in /public | |||
//app.use(favicon(__dirname + '/public/favicon.ico')); | |||
app.use(logger('dev')); | |||
app.use(bodyParser.json()); | |||
app.use(bodyParser.urlencoded({ extended: false })); | |||
app.use(cookieParser()); | |||
app.use(express.static(path.join(__dirname, 'public'))); | |||
app.use('/', routes); | |||
// catch 404 and forward to error handler | |||
app.use(function(req, res, next) { | |||
var err = new Error('Not Found'); | |||
err.status = 404; | |||
next(err); | |||
}); | |||
// error handlers | |||
if (app.get('env') === 'development') { | |||
// development error handler | |||
// will print stacktrace | |||
app.use(function(err, req, res, next) { | |||
res.status(err.status || 500); | |||
res.render('error', { | |||
message: err.message, | |||
error: err | |||
}); | |||
}); | |||
app.locals.pretty = true; | |||
} else { | |||
// production error handler | |||
// no stacktraces leaked to user | |||
app.use(function(err, req, res, next) { | |||
res.status(err.status || 500); | |||
res.render('error', { | |||
message: err.message, | |||
error: {} | |||
}); | |||
}); | |||
} | |||
module.exports = app; | |||
</source> | </source> | ||
Line 28: | Line 332: | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
var express = require('express'); | |||
var router = express.Router(); | |||
var state = []; | |||
router.get('/', function(req, res, next) { | |||
res.render('index', { title: 'COMP 2406 Simple form demo' }); | |||
}); | |||
router.post('/add', function(req, res) { | |||
var obj = { name: req.body.name, | |||
city: req.body.city, | |||
country: req.body.country, | |||
birthday: req.body.birthday, | |||
email: req.body.email }; | |||
state.push(obj); | |||
res.render('add', { title: 'Person just added', item: obj }); | |||
}); | |||
router.get('/list', function(req, res) { | |||
res.render('list', { title: 'People Listing', items: state}); | |||
}); | |||
module.exports = router; | |||
</source> | </source> | ||
Line 34: | Line 361: | ||
===form-demo/views/layout.jade=== | ===form-demo/views/layout.jade=== | ||
<source lang=" | <source lang="html4strict" lines> | ||
doctype html | |||
html | |||
head | |||
title= title | |||
link(rel='stylesheet', href='/stylesheets/style.css') | |||
block header | |||
body | |||
block content | |||
</source> | </source> | ||
Line 41: | Line 375: | ||
===form-demo/views/index.jade=== | ===form-demo/views/index.jade=== | ||
<source lang=" | <source lang="html4strict" lines> | ||
extends layout | |||
block content | |||
h1= title | |||
div | |||
p Fill out your info | |||
form(method="post", action="/add") | |||
div | |||
input#name(type="text", name="name") | |||
label Name | |||
div | |||
input#country(type="text", name="city") | |||
label City | |||
div | |||
input#country(type="text", name="country") | |||
label Country | |||
div | |||
input#birthday(type="text", name="birthday") | |||
label Birthday | |||
div | |||
input#email(type="text", name="email") | |||
label Email | |||
button(type="submit") Submit | |||
</source> | </source> | ||
Line 49: | Line 406: | ||
===form-demo/views/add.jade=== | ===form-demo/views/add.jade=== | ||
<source lang=" | <source lang="html4strict" lines> | ||
extends layout | |||
block content | |||
h1= title | |||
p Name: #{item.name} | |||
p City: #{item.city} | |||
p Country: #{item.country} | |||
p Birthday: #{item.birthday} | |||
p Email: #{item.email} | |||
</source> | </source> | ||
Line 57: | Line 423: | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
extends layout | |||
block content | |||
h1= title | |||
div | |||
div | |||
table | |||
thead | |||
th Name | |||
th City | |||
th Country | |||
th Birthday | |||
th Email | |||
tbody | |||
each item in items | |||
tr | |||
td #{item.name} | |||
td #{item.city} | |||
td #{item.country} | |||
td #{item.birthday} | |||
td #{item.email} | |||
form(method="get", action="/") | |||
button(type="submit") Home | |||
</source> | </source> | ||
Line 64: | Line 454: | ||
<source lang="javascript" lines> | <source lang="javascript" lines> | ||
extends layout | |||
block content | |||
h1= message | |||
h2= error.status | |||
pre #{error.stack} | |||
</source> | </source> |
Revision as of 20:50, 29 January 2016
This assignment is not yet finalized (still in progress!).
Code
Full source: simplegrep_async.js, tinywebserver.js, form-demo.zip
simplegrep_async.js
var fs = require('fs');
if (process.argv.length < 4) {
console.error('Not enough parameters given. Try this: ' +
'"node simplegrep_async term filename.txt"');
process.exit(1);
}
var searchterm = process.argv[2];
var filename = process.argv[3];
var returnMatches = function(err, rawContents) {
var lines = rawContents.split('\n');
lines.forEach(function(theLine) {
if (theLine.indexOf(searchterm) > -1) {
console.log(theLine);
}
});
}
fs.readFile(filename, 'utf-8', returnMatches);
tinywebserver.js
var path = require('path');
var http = require('http');
var fs = require('fs');
var MIME_TYPES = {
'css': 'text/css',
'gif': 'image/gif',
'htm': 'text/html',
'html': 'text/html',
'ico': 'image/x-icon',
'jpeg': 'image/jpeg',
'jpg': 'image/jpeg',
'js': 'text/javascript',
'json': 'application/json',
'png': 'image/png',
'txt': 'text/text'
};
var options = {
host: 'localhost',
port: 8080,
index: 'index.html',
docroot: '.'
};
var get_mime = function(filename) {
var ext, type;
for (ext in MIME_TYPES) {
type = MIME_TYPES[ext];
if (filename.indexOf(ext, filename.length - ext.length) !== -1) {
return type;
}
}
return null;
};
var respond = function(request, response, status, content, content_type) {
if (!status) {
status = 200;
}
if (!content_type) {
content_type = 'text/plain';
}
console.log("" + status + "\t" +
request.method + "\t" + request.url);
response.writeHead(status, {
"Content-Type": content_type
});
if (content) {
response.write(content);
}
return response.end();
};
var serve_file = function(request, response, requestpath) {
return fs.readFile(requestpath, function(error, content) {
if (error != null) {
console.error("ERROR: Encountered error while processing " +
request.method + " of \"" + request.url +
"\".", error);
return respond(request, response, 500);
} else {
return respond(request, response, 200,
content, get_mime(requestpath));
}
});
};
var return_index = function(request, response, requestpath) {
var exists_callback = function(file_exists) {
if (file_exists) {
return serve_file(request, response, requestpath);
} else {
return respond(request, response, 404);
}
}
if (requestpath.substr(-1) !== '/') {
requestpath += "/";
}
requestpath += options.index;
return fs.exists(requestpath, exists_callback);
}
var request_handler = function(request, response) {
var requestpath;
if (request.url.match(/((\.|%2E|%2e)(\.|%2E|%2e))|(~|%7E|%7e)/) != null) {
console.warn("WARNING: " + request.method +
" of \"" + request.url +
"\" rejected as insecure.");
return respond(request, response, 403);
} else {
requestpath = path.normalize(path.join(options.docroot, request.url));
return fs.exists(requestpath, function(file_exists) {
if (file_exists) {
return fs.stat(requestpath, function(err, stat) {
if (err != null) {
console.error("ERROR: Encountered error calling" +
"fs.stat on \"" + requestpath +
"\" while processing " +
request.method + " of \"" +
request.url + "\".", err);
return respond(request, response, 500);
} else {
if ((stat != null) && stat.isDirectory()) {
return return_index(request, response, requestpath);
} else {
return serve_file(request, response, requestpath);
}
}
});
} else {
return respond(request, response, 404);
}
});
}
};
var server = http.createServer(request_handler);
server.listen(options.port, options.host, function() {
return console.log("Server listening at http://" +
options.host + ":" + options.port + "/");
});
form-demo/bin/www
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('form-demo:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
form-demo/app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
if (app.get('env') === 'development') {
// development error handler
// will print stacktrace
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
app.locals.pretty = true;
} else {
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
}
module.exports = app;
form-demo/routes/index.js
var express = require('express');
var router = express.Router();
var state = [];
router.get('/', function(req, res, next) {
res.render('index', { title: 'COMP 2406 Simple form demo' });
});
router.post('/add', function(req, res) {
var obj = { name: req.body.name,
city: req.body.city,
country: req.body.country,
birthday: req.body.birthday,
email: req.body.email };
state.push(obj);
res.render('add', { title: 'Person just added', item: obj });
});
router.get('/list', function(req, res) {
res.render('list', { title: 'People Listing', items: state});
});
module.exports = router;
form-demo/views/layout.jade
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
block header
body
block content
form-demo/views/index.jade
extends layout
block content
h1= title
div
p Fill out your info
form(method="post", action="/add")
div
input#name(type="text", name="name")
label Name
div
input#country(type="text", name="city")
label City
div
input#country(type="text", name="country")
label Country
div
input#birthday(type="text", name="birthday")
label Birthday
div
input#email(type="text", name="email")
label Email
button(type="submit") Submit
form-demo/views/add.jade
extends layout
block content
h1= title
p Name: #{item.name}
p City: #{item.city}
p Country: #{item.country}
p Birthday: #{item.birthday}
p Email: #{item.email}
form-demo/views/list.jade
extends layout
block content
h1= title
div
div
table
thead
th Name
th City
th Country
th Birthday
th Email
tbody
each item in items
tr
td #{item.name}
td #{item.city}
td #{item.country}
td #{item.birthday}
td #{item.email}
form(method="get", action="/")
button(type="submit") Home
form-demo/views/error.jade
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}