WebFund 2016W: Assignment 3: Difference between revisions
No edit summary |
No edit summary |
||
Line 29: | Line 29: | ||
</ol> | </ol> | ||
</ol> | </ol> | ||
==Solutions== | |||
<ol> | |||
<li> rawContents is a string, split is a method (function), lines is an array (of strings) | |||
<li> finished! will be printed first, before any of the matched lines. | |||
<li> Change line 42 to be "status=404;"; | |||
<li> Before line 92 add the following code: | |||
if (request.headers.user-agent && request.headers.user-agent.indexOf("iPhone")) { | |||
return respond(request, response, 403); | |||
} else // just before if on line 92 | |||
<li> | |||
<ol style="list-style-type:lower-alpha"> | |||
<li> The program would terminate at the first reference to app (line 16) because app is no longer defined. | |||
<li> It would function identically unless the PORT environment variable were set, in which case its value would be ignored rather than being used to set the listening port. | |||
<li> The app would now look for jade templates in the top-level directory rather than in views/. If the templates were not moved the app would terminate with an error saying it couldn't find a specified jade file the moment a page was requested that had a route that used a jade template. | |||
<li> No routes would be defined so none of the URLs specified in routes would work (/, /add, /list). | |||
<li> The pages /, /add, and /list would all have empty HTML <body> but the same <head>. | |||
<li> The Submit button's position on in / will change its position slightly and the button will do nothing - it will no longer submit the form. | |||
</ol> | |||
==Code== | ==Code== |
Revision as of 19:35, 9 February 2016
Please submit your answers by 2:30 PM on Tuesday, February 9th, 2016 via cuLearn.
The first lines of your assignment should be:
COMP 2406 2016W Assignment #3 <name> <student #>
Submit your answers as a text or PDF file only. MS Word .doc files, OpenOffice .odt, and other word processing files are not acceptable: convent to text or PDF!
If you used any outside resources or got help from any other students, please cite them with each answer or at the end of your assignment.
Questions
- [1] In simplegrep_async.js:14, what is the type of all of the symbols referenced in the function? Specifically, what is the type of lines, rawContents, and split?
- [1] If I create a line 24 in simplegrep_async.js that says 'console.log("finished!");', when will finished! be printed relative to the other output of the program?
- [1] What is a one-line change to tinywebserver.js that would make every HTTP response have a 404 response code?
- [1] How you could return a 403 error (access denied) to all requests from iPhones in tinywebserver.js? (Remember the User-Agent HTTP header.)
- [6] How would the behavior of form-demo changed when you make the following changes? Specifically, what changes would you see on the server and in the browser? Be sure to consider messages that are logged on the server and console output in the browser.
- Comment out form-demo/bin/www:7
- Change form-demo/bin/www:15 to be "var port=3000;".
- Change form-demo/app.js:13 to be "app.set('views', '.');"
- Comment out form-demo/routes/index.js:24
- Comment out form-demo/views/layout.jade:8
- Change the indentation of form-demo/views/index.jade:24 such that it is at the same indentation as form in line 8.
Solutions
- rawContents is a string, split is a method (function), lines is an array (of strings)
- finished! will be printed first, before any of the matched lines.
- Change line 42 to be "status=404;";
- Before line 92 add the following code: if (request.headers.user-agent && request.headers.user-agent.indexOf("iPhone")) { return respond(request, response, 403); } else // just before if on line 92
-
- The program would terminate at the first reference to app (line 16) because app is no longer defined.
- It would function identically unless the PORT environment variable were set, in which case its value would be ignored rather than being used to set the listening port.
- The app would now look for jade templates in the top-level directory rather than in views/. If the templates were not moved the app would terminate with an error saying it couldn't find a specified jade file the moment a page was requested that had a route that used a jade template.
- No routes would be defined so none of the URLs specified in routes would work (/, /add, /list).
- The pages /, /add, and /list would all have empty HTML <body> but the same <head>.
- The Submit button's position on in / will change its position slightly and the button will do nothing - it will no longer submit the form.
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}