WebFund 2016W: Assignment 2: Difference between revisions
No edit summary |
|||
(19 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
''' | '''The solutions [http://homeostasis.scs.carleton.ca/~soma/webfund-2016w/code/tinyweb.js are now available].''' | ||
In this assignment you will be be extending the functionality of tinywebserver.js from [[WebFund 2016W: Tutorial 2|Tutorial 2]]. There are | In this assignment you will be be extending the functionality of tinywebserver.js from [[WebFund 2016W: Tutorial 2|Tutorial 2]]. There are 10 points. This assignment is due on January 31, 2016 (extended from January 28th). | ||
Please submit your answers as a single text (javascript) file called "tinyweb.js". Please cite any sources you used in creating your answer as comments in your code. | |||
Your tinyweb.js (a modified version of tinywebserver.js) should have the following functionality: | |||
# [1] Add a new 777 status code if there is a request for any document with the word "lucky" in its URL (path or document name). | |||
# [1] Add a command line argument which is the name of a configuration file. This file should contain the contents of the options object. Note that tinyweb.js should start up correctly without this option file being specified; in this case it should behave as tinywebserver.js does (with respect to the options, not the lucky return code). | |||
# Add a new 777 status code if there is a request for any document with the word "lucky" in its URL (path or document name). | |||
# Add a command line argument which is the name of a configuration file. This file should contain the contents of the options object. Note that | |||
# In the configuration file add the following properties to the option object that change the behavior of the app as follows (note it should also contain all the options that were already part of the options object): | # In the configuration file add the following properties to the option object that change the behavior of the app as follows (note it should also contain all the options that were already part of the options object): | ||
#* logged-headers: an array of headers (as strings) that should be logged for each incoming request. If no logged headers are specified, no headers should be logged. | #* [2] logged-headers: an array of headers (as strings) that should be logged for each incoming request. If no logged headers are specified, no headers should be logged. Header names should be in all lowercase with words separated by dashes. Thus "UserAgent" becomes "user-agent". (This change is because this is the format of header names in request.headers.) The format should be three spaces, the header name, a colon, a space, then the contents of the header. | ||
#* logfile: the filename where to write log messages. They should be written to standard out (as console.log does) if no logfile is specified. | #* [2] logfile: the filename where to write log messages. They should be written to standard out (as console.log does) if no logfile is specified. | ||
#* errorpage: html file to be returned when an error page is returned (not 200). The 777 status code should NOT return an error page, it should return the regular document (777 is an alias for 200). | #* [2] errorpage: html file to be returned when an error page is returned (not 200). The 777 status code should NOT return an error page, it should return the regular document (777 is an alias for 200). | ||
#* aliases: object where the key is the alias and the value is what should be substituted. The alias should be for the entire doc path (the URL without the hostname). | #* [2] aliases: object where the key is the alias and the value is what should be substituted. The alias should be for the entire doc path (the URL without the hostname). Aliases should be handled before lucky URL processing (and really, just about everything else). | ||
==Hints== | ==Hints== | ||
===log files=== | |||
To log everything to a file you'll want to use a custom Console object with a special write stream. To get append-only behavior (as logs shouldn't normally be erased) do something like this: | |||
<source lang="javascript" line> | |||
// logdemo.js | |||
// invoke as: | |||
// node logdemo.js <logfile> | |||
var fs = require("fs"); | |||
var Console = require('console').Console; | |||
var logFilename = process.argv[2]; | |||
var logStream; | |||
// Note: this doesn't catch all errors, see | |||
// https://www.joyent.com/developers/node/design/errors | |||
try { | |||
logStream = fs.createWriteStream(logFilename, {'flags': 'a'}); | |||
} catch(e) { | |||
logStream = process.stdout; | |||
} | |||
var myConsole = new Console(logStream); | |||
myConsole.log("This will be written to a file, unless there was a problem!"); | |||
</source> | </source> | ||
===JSON parsing=== | |||
JSON.parse() is very strict with its parsing, much stricter than regular object declarations in JavaScript. In particular properties need to be quoted (only with double quotes). You can use try/catch to allow this to be handled gracefully: | |||
<source lang="javascript" line> | |||
try { | |||
console. | options = JSON.parse(fs.readFileSync(optionsFilename, "utf-8")); | ||
} catch (e) { | |||
if (optionsFilename) { | |||
console.error("Error reading/parsing options file " + optionsFilename + | |||
", using defaults."); | ", using defaults."); | ||
} else { | |||
console.log("No options file specified, using defaults."); | console.log("No options file specified, using defaults."); | ||
} | |||
options = default_options; | |||
} | |||
</source> | |||
===example config=== | |||
Here's as example config file: | |||
<source lang="javascript" line> | |||
{ | |||
"host": "localhost", | |||
"port": 8000, | |||
"index": "index.html", | |||
"docroot": ".", | |||
"logged-headers": ["user-agent", "referer"], | |||
"logfile": "tinyweb.log", | |||
"errorpage": "error.html", | |||
"aliases": { | |||
"/index.html": "/hello.html", | |||
"/": "/hello.html", | |||
"/really/cool.html": "/notcool.html" | |||
} | } | ||
} | |||
</source> | |||
===example header log=== | |||
Here's an example of the header log messages: | |||
<pre> | |||
404 POST /useName | |||
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 | |||
referer: http://localhost:8000/formtest.html | |||
</pre> |
Latest revision as of 19:38, 2 February 2016
The solutions are now available.
In this assignment you will be be extending the functionality of tinywebserver.js from Tutorial 2. There are 10 points. This assignment is due on January 31, 2016 (extended from January 28th).
Please submit your answers as a single text (javascript) file called "tinyweb.js". Please cite any sources you used in creating your answer as comments in your code.
Your tinyweb.js (a modified version of tinywebserver.js) should have the following functionality:
- [1] Add a new 777 status code if there is a request for any document with the word "lucky" in its URL (path or document name).
- [1] Add a command line argument which is the name of a configuration file. This file should contain the contents of the options object. Note that tinyweb.js should start up correctly without this option file being specified; in this case it should behave as tinywebserver.js does (with respect to the options, not the lucky return code).
- In the configuration file add the following properties to the option object that change the behavior of the app as follows (note it should also contain all the options that were already part of the options object):
- [2] logged-headers: an array of headers (as strings) that should be logged for each incoming request. If no logged headers are specified, no headers should be logged. Header names should be in all lowercase with words separated by dashes. Thus "UserAgent" becomes "user-agent". (This change is because this is the format of header names in request.headers.) The format should be three spaces, the header name, a colon, a space, then the contents of the header.
- [2] logfile: the filename where to write log messages. They should be written to standard out (as console.log does) if no logfile is specified.
- [2] errorpage: html file to be returned when an error page is returned (not 200). The 777 status code should NOT return an error page, it should return the regular document (777 is an alias for 200).
- [2] aliases: object where the key is the alias and the value is what should be substituted. The alias should be for the entire doc path (the URL without the hostname). Aliases should be handled before lucky URL processing (and really, just about everything else).
Hints
log files
To log everything to a file you'll want to use a custom Console object with a special write stream. To get append-only behavior (as logs shouldn't normally be erased) do something like this:
// logdemo.js
// invoke as:
// node logdemo.js <logfile>
var fs = require("fs");
var Console = require('console').Console;
var logFilename = process.argv[2];
var logStream;
// Note: this doesn't catch all errors, see
// https://www.joyent.com/developers/node/design/errors
try {
logStream = fs.createWriteStream(logFilename, {'flags': 'a'});
} catch(e) {
logStream = process.stdout;
}
var myConsole = new Console(logStream);
myConsole.log("This will be written to a file, unless there was a problem!");
JSON parsing
JSON.parse() is very strict with its parsing, much stricter than regular object declarations in JavaScript. In particular properties need to be quoted (only with double quotes). You can use try/catch to allow this to be handled gracefully:
try {
options = JSON.parse(fs.readFileSync(optionsFilename, "utf-8"));
} catch (e) {
if (optionsFilename) {
console.error("Error reading/parsing options file " + optionsFilename +
", using defaults.");
} else {
console.log("No options file specified, using defaults.");
}
options = default_options;
}
example config
Here's as example config file:
{
"host": "localhost",
"port": 8000,
"index": "index.html",
"docroot": ".",
"logged-headers": ["user-agent", "referer"],
"logfile": "tinyweb.log",
"errorpage": "error.html",
"aliases": {
"/index.html": "/hello.html",
"/": "/hello.html",
"/really/cool.html": "/notcool.html"
}
}
example header log
Here's an example of the header log messages:
404 POST /useName user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 referer: http://localhost:8000/formtest.html