123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- #!/usr/bin/env node
- var util = require("util"),
- http = require("http"),
- fs = require("fs"),
- url = require("url"),
- events = require("events");
- var DEFAULT_PORT = 3001;
- function main(argv) {
- new HttpServer({
- GET: createServlet(StaticServlet),
- HEAD: createServlet(StaticServlet)
- }).start(Number(argv[2]) || DEFAULT_PORT);
- }
- function escapeHtml(value) {
- return value
- .toString()
- .replace("<", "<")
- .replace(">", ">")
- .replace('"', """);
- }
- function createServlet(Class) {
- var servlet = new Class();
- return servlet.handleRequest.bind(servlet);
- }
- /**
- * An Http server implementation that uses a map of methods to decide
- * action routing.
- *
- * @param {Object} Map of method => Handler function
- */
- function HttpServer(handlers) {
- this.handlers = handlers;
- this.server = http.createServer(this.handleRequest_.bind(this));
- }
- HttpServer.prototype.start = function(port) {
- this.port = port;
- this.server.listen(port);
- util.puts("Http Server running at http://localhost:" + port + "/");
- };
- HttpServer.prototype.parseUrl_ = function(urlString) {
- var parsed = url.parse(urlString);
- parsed.pathname = url.resolve("/", parsed.pathname);
- return url.parse(url.format(parsed), true);
- };
- HttpServer.prototype.handleRequest_ = function(req, res) {
- var logEntry = req.method + " " + req.url;
- if (req.headers["user-agent"]) {
- logEntry += " " + req.headers["user-agent"];
- }
- util.puts(logEntry);
- req.url = this.parseUrl_(req.url);
- var handler = this.handlers[req.method];
- if (!handler) {
- res.writeHead(501);
- res.end();
- } else {
- handler.call(this, req, res);
- }
- };
- /**
- * Handles static content.
- */
- function StaticServlet() {}
- StaticServlet.MimeMap = {
- txt: "text/plain",
- html: "text/html",
- css: "text/css",
- xml: "application/xml",
- json: "application/json",
- js: "application/javascript",
- jpg: "image/jpeg",
- jpeg: "image/jpeg",
- gif: "image/gif",
- png: "image/png",
- svg: "image/svg+xml"
- };
- StaticServlet.prototype.handleRequest = function(req, res) {
- var that = this;
- var path = ("./" + req.url.pathname)
- .replace("//", "/")
- .replace(/%(..)/g, function(match, hex) {
- return String.fromCharCode(parseInt(hex, 16));
- });
- var parts = path.split("/");
- if (parts[parts.length - 1].charAt(0) === ".")
- return that.sendForbidden_(req, res, path);
- fs.stat(path, function(err, stat) {
- if (err) return that.sendMissing_(req, res, path);
- if (stat.isDirectory()) return that.sendDirectory_(req, res, path);
- return that.sendFile_(req, res, path);
- });
- };
- StaticServlet.prototype.sendError_ = function(req, res, error) {
- res.writeHead(500, {
- "Content-Type": "text/html"
- });
- res.write("<!doctype html>\n");
- res.write("<title>Internal Server Error</title>\n");
- res.write("<h1>Internal Server Error</h1>");
- res.write("<pre>" + escapeHtml(util.inspect(error)) + "</pre>");
- util.puts("500 Internal Server Error");
- util.puts(util.inspect(error));
- };
- StaticServlet.prototype.sendMissing_ = function(req, res, path) {
- path = path.substring(1);
- res.writeHead(404, {
- "Content-Type": "text/html"
- });
- res.write("<!doctype html>\n");
- res.write("<title>404 Not Found</title>\n");
- res.write("<h1>Not Found</h1>");
- res.write(
- "<p>The requested URL " +
- escapeHtml(path) +
- " was not found on this server.</p>"
- );
- res.end();
- util.puts("404 Not Found: " + path);
- };
- StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
- path = path.substring(1);
- res.writeHead(403, {
- "Content-Type": "text/html"
- });
- res.write("<!doctype html>\n");
- res.write("<title>403 Forbidden</title>\n");
- res.write("<h1>Forbidden</h1>");
- res.write(
- "<p>You do not have permission to access " +
- escapeHtml(path) +
- " on this server.</p>"
- );
- res.end();
- util.puts("403 Forbidden: " + path);
- };
- StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
- res.writeHead(301, {
- "Content-Type": "text/html",
- Location: redirectUrl
- });
- res.write("<!doctype html>\n");
- res.write("<title>301 Moved Permanently</title>\n");
- res.write("<h1>Moved Permanently</h1>");
- res.write(
- '<p>The document has moved <a href="' + redirectUrl + '">here</a>.</p>'
- );
- res.end();
- util.puts("301 Moved Permanently: " + redirectUrl);
- };
- StaticServlet.prototype.sendFile_ = function(req, res, path) {
- var that = this;
- var file = fs.createReadStream(path);
- res.writeHead(200, {
- "Content-Type": StaticServlet.MimeMap[path.split(".").pop()] || "text/plain"
- });
- if (req.method === "HEAD") {
- res.end();
- } else {
- file.on("data", res.write.bind(res));
- file.on("close", function() {
- res.end();
- });
- file.on("error", function(error) {
- that.sendError_(req, res, error);
- });
- }
- };
- StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
- var that = this;
- if (path.match(/[^\/]$/)) {
- req.url.pathname += "/";
- var redirectUrl = url.format(url.parse(url.format(req.url)));
- return that.sendRedirect_(req, res, redirectUrl);
- }
- fs.readdir(path, function(err, files) {
- if (err) return that.sendError_(req, res, error);
- if (!files.length) return that.writeDirectoryIndex_(req, res, path, []);
- var remaining = files.length;
- files.forEach(function(fileName, index) {
- fs.stat(path + "/" + fileName, function(err, stat) {
- if (err) return that.sendError_(req, res, err);
- if (stat.isDirectory()) {
- files[index] = fileName + "/";
- }
- if (!--remaining)
- return that.writeDirectoryIndex_(req, res, path, files);
- });
- });
- });
- };
- StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
- path = path.substring(1);
- res.writeHead(200, {
- "Content-Type": "text/html"
- });
- if (req.method === "HEAD") {
- res.end();
- return;
- }
- res.write("<!doctype html>\n");
- res.write("<title>" + escapeHtml(path) + "</title>\n");
- res.write("<style>\n");
- res.write(" ol { list-style-type: none; font-size: 1.2em; }\n");
- res.write("</style>\n");
- res.write("<h1>Directory: " + escapeHtml(path) + "</h1>");
- res.write("<ol>");
- files.forEach(function(fileName) {
- if (fileName.charAt(0) !== ".") {
- res.write(
- '<li><a href="' +
- escapeHtml(fileName) +
- '">' +
- escapeHtml(fileName) +
- "</a></li>"
- );
- }
- });
- res.write("</ol>");
- res.end();
- };
- // Must be last,
- main(process.argv);
|