ifdef NoNanoHTTPD !include once #1028763 endifdef sclass HttpFromFileSystem { File baseDir; bool serveDirectoryListings; // default to false bool serveIndexHTML = true; bool maxCache; // treat files as immutable bool debug; SS headers; S baseLink = ""; *() {} *(File *baseDir) {} *(File *baseDir, bool *serveDirectoryListings) {} O serve(S uri, SS params, SS headers default null) { if (eq("1", params.get("maxCache"))) maxCache = true; this.headers = headers; ret serve(uri); } O serve(S uri) { S originalURI = uri; uri = urldecode(uri); LS parts = nempties(splitAtSlash(uri)); if (baseDir == null) ret serve404("No base directory set"); // process . and .. for i over parts: if (eq(parts.get(i), ".")) parts.remove(i); else if (eq(parts.get(i), "..")) if (i == 0) fail(".. leading nowhere: " + uri); else { parts.remove(i); parts.remove(--i); } if (!all validFileName(parts)) { print("Parts: " + quoteAll(parts)); fail("Bad path: " + uri); } File f = baseDir; if (!isDirectory(baseDir)) print("Base dir not found: " + f2s(f)); for (S part : parts) { f = newFile(f, part); if (!fileExists(f)) ret serve404("Path not found: " + uri); } print("Trying to serve: " + f2s(f)); if (isDirectory(f)) ret serveListing(uri, f, originalURI); else ret serveFile(f); } swappable O addHeaders(O response) { if (maxCache) ret subBot_maxCacheHeaders(response); ret response; } swappable O serveFile(File f) { O response; S range = mapGet(headers, "range"); S ct = contentTypeForFile(f); if (nempty(range)) reponse = subBot_serveFileRange(f, ct, range); else response = main serveFile(f, ct); ret addHeaders(response); } O serveListing(S uri, File dir, S originalURI) { if (serveIndexHTML) { File index = newFile(dir, "index.html"); if (fileExists(index)) { /*if (nempty(originalURI) && !endsWithSlash(originalURI)) ret subBot_serveRedirect(baseLink + originalURI + "/");*/ ret serveFile(index); } } if (!serveDirectoryListings) fail("Can't serve directory listings"); // take care of directory listing without trailing slash in URI S local = addSlashIfNempty(afterLast("/" + uri, '/')); ret "Directory listing of " + f2s(dir) + ul(map(listFileNames(dir), name -> { bool isDir = isDirectory(newFile(dir, name)); if (isDir) name += "/"; ret ahref(baseLink + local + name, htmlEncode2(name)) + (!debug ? "" : htmlEncode2(appendBracketed(renderVars(+baseLink, +uri, +local)))); })); } swappable S contentTypeForFile(File f) { ret or2(extensionToMimeType(fileExtension(f)), binaryMimeType()); } swappable bool validFileName(S name) { ret isValidFileName(name); } }