#!/usr/bin/env node // vim: set ts=2 sw=2 et tw=80: const http = require('http'); const url = require('url'); const fs = require('fs'); const formidable = require('formidable'); const routes = Object.create(null); function error(res, code, text = '') { res.writeHead(code, { 'Content-Type': 'text/html' }); res.end(` Error ${code}

Error ${code}

${text}


SA3 - HW4

`); } function fileData(reqUrl, prefix, wantExt = true) { const uri = decodeURIComponent(url.parse(reqUrl).pathname .substring(prefix.length)).replace(/\/+$/, ''); const file = __dirname + '/NodeStaticFiles' + uri; const name = file.substring(file.lastIndexOf('/') + 1); const ext = wantExt ? name.substring(name.indexOf('.') + 1) : null; return { uri: !uri ? '/' : uri, file: file, name: name, ext: ext }; } routes['explore'] = (req, res) => { if (req.method != 'GET') { error(res, 405, 'Use this URL with only GET requests'); return; } const { uri, file, name } = fileData(req.url, '/explore', false); fs.readdir(file, { withFileTypes: true }, (err, dir) => { if (err) { error(res, 404, 'Directory not found'); return; } const list = [{ name: '.', path: '/explore' + (uri == '/' ? '' : uri) + '/' }]; if (uri != '/') { const parentUri = uri.substring(0, uri.length - name.length - 1); list.push({ name: '..', path: '/explore' + parentUri }); } for (const e of dir) { list.push({ name: e.name, dir: !e.isFile(), path: (e.isFile() ? '/file' : '/explore') + uri + (uri == '/' ? '' : '/') + e.name }); } res.writeHead(200, { 'Content-Type': 'text/html' }); res.write(` ${uri} [DIR]

${uri} [DIR]

`); }); }; routes['file'] = (req, res) => { const FILE_TYPES = { html: 'text/html', css: 'text/css', txt: 'text/plain', mp4: 'video/mp4', ogv: 'video/ogg', gif: 'image/gif', jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', mp3: 'audio/mpeg', js: 'application/javascript', json: 'application/json', pdf: 'application/pdf', zip: 'application/zip' }; if (req.method != 'GET') { error(res, 405, 'Use this URL with only GET requests'); return; } const { file, name, ext } = fileData(req.url, '/file'); fs.readFile(file, (err, data) => { if (err) { error(res, 404, 'File not found: ' + JSON.stringify(err)); return; } res.setHeader('Content-Disposition', 'attachment; filename="' + name + '"'); res.writeHead(200, { 'Content-Type': ext in FILE_TYPES ? FILE_TYPES[ext] : 'application/octet-stream' }); res.end(data); }); } routes['upload'] = (req, res) => { if (req.method != 'GET' && req.method != 'POST') { error(res, 405, 'Use this URL with only GET or POST requests'); return; } if (req.method == 'POST') { const form = new formidable.IncomingForm(); form.uploadDir = __dirname + '/NodeStaticFiles'; form.keepExtensions = true; form.parse(req); form.on('fileBegin', (name, file) => { file.path = file.path.substring(0, file.path.lastIndexOf('/') + 1); file.path += file.name; }); form.on('end', () => { res.writeHead(302, { 'Location': '/explore' }); res.end(); }); return; } res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(` Upload

Upload


`); }; routes['stats'] = (req, res) => { res.end() }; // Main server handler function onRequest(req, res) { const pathname = url.parse(req.url).pathname; const uri = pathname.split('/', 3)[1]; if (typeof routes[uri] === 'function') { routes[uri](req, res); } else { error(res, 404, 'Path not found'); } } http.createServer(onRequest).listen(3000); console.log('Server started at localhost:3000');