WebFund 2016W: Assignment 3: Difference between revisions

From Soma-notes
No edit summary
Line 1: Line 1:
'''This assignment is not yet finalized (still in progress!).'''
'''This assignment is not yet finalized (needs proofreading).'''
 
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==
==Questions==

Revision as of 19:48, 1 February 2016

This assignment is not yet finalized (needs proofreading).

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. [1] In simplegrep_async.js:14, what is the type of all of the symbols referenced in the function? Specfiically, what is the type of lines, rawContents, and split?
  2. [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?
  3. [1] What is a one-line change to tinywebserver.js that would make every HTTP response have a 404 response code?
  4. [1] How you could return a 404 error to all requests from iPhones in tinywebserver.js?
  5. [6] How would the behavior of form-demo changed when you make the following changes?
    1. Delete form-demo/bin/www:7
    2. Change form-demo/bin/www:15 to be "var port=3000;".
    3. Change form-demo/app.js:13 to be "app.set('views', '.');"
    4. Delete form-demo/routes/index.js:24
    5. Delete form-demo/views/layout.jade:8
    6. Change the indentation of form-demo/views/index.jade:24 such that it is at the same indentation as form in line 8.

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}