123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- #!/usr/bin/env node
- /*
- * Simple web server. Only handles GET requests (for now at least). Takes the
- * site's root directory as the first argument.
- *
- * TODO:
- * - Write a help message
- * - Implement proper error handling
- * - Remove die
- * - Use an approach closer to JavaScript
- * - Fallback method for updating 'files' on systems that don't report filename
- * - Optionally print diagnostic information (verbose flag)
- */
- "use strict";
- import path from 'node:path';
- import { readFileSync, readdirSync, existsSync, watch} from 'node:fs';
- import { argv } from 'node:process';
- import { createServer } from 'node:https';
- import { die } from './die.js';
- import { getOpt } from './getOpt.js';
- function fetch(url) {
- let head = { statusCode: '', contentType: ''};
- let contents = '';
- const notFoundMsg = `
- <!doctype html>
- <head>
- <meta charset="utf-8">
- <title>404: not found</title>
- </head>
- <body>
- <h1>404: not found</h1>
- <p>
- The server couldn't find the requested page. Possible reasons
- include:
- <ol>
- <li>Poorly formatted URL
- <li>The requested page doesn't yet, or will never, exist.
- <li>A server error has ocurred. In this case, the fault lies on the
- braindead developer (me) who coded it.
- </ol>
- </body>
- </html>`
-
- let baseUrl = path.basename(url);
- if (!baseUrl) baseUrl = 'index';
-
- if (baseUrl.endsWith('.css'))
- head.contentType = 'text/css';
- else
- head.contentType = 'text/html';
-
- const file = files.find(f => f.includes(baseUrl));
- if (file) {
- head.statusCode = 200;
- contents = readFileSync(path.join(root, file));
- } else {
- head.statusCode = 404;
- contents = notFoundMsg;
- }
- return [head, contents];
- }
- const opts = getOpt({
- 'priv-key': { type: 'value', short: 'k'},
- cert: { type: 'value', short: 'c'},
- port:
- {
- type: 'value', short: 'p', default: 8000,
- convert: v => parseInt(v), validate: v => v ? true : false,
- }
- });
- const root = process.argv.at(-1);
- // Test if files exist
- if (!existsSync(opts['priv-key'])) die('Couldn\'t stat private-key');
- if (!existsSync(opts.cert)) die('Couldn\'t stat certificate');
- if (!existsSync(root)) die('Couldn\'t stat root dir');
- const files = readdirSync(root, { recursive: true });
- const options = {
- key: readFileSync(opts['priv-key']),
- cert: readFileSync(opts.cert)
- };
- // Set up a watch to update the 'files' array on directory change
- watch(root, { recursive: true }, (eventType, file) => {
- if (!file)
- die("System don't support reporting filename on directory change");
- if (eventType == 'rename') {
- if (files.includes(file))
- files.splice(files.indexOf(file), 1);
- else
- files.push(file);
- }
- });
- const server = createServer(options, (req, res) => {
- let [head, contents] = fetch(req.url);
- res.writeHead(head.statusCode, {'Content-Type': head.contentType});
- res.write(contents);
- res.end();
- });
- console.log(`Starting server at port ${opts.port}...`)
- server.listen(opts.port);
|