1 /+ dub.sdl:
2 name "static"
3 description "Serves all files in the given directory"
4 dependency "lighttp" path=".."
5 +/
6 module app;
7 
8 import std.algorithm : sort;
9 import std.array : Appender;
10 import std.file;
11 import std.getopt : getopt;
12 import std.path : buildNormalizedPath;
13 import std.regex : ctRegex;
14 import std.string : startsWith, endsWith, toLower;
15 
16 import lighttp;
17 
18 void main(string[] args) {
19 
20 	string path = ".";
21 	string ip = "0.0.0.0";
22 	ushort port = 80;
23 	
24 	getopt(args, "path", &path, "ip", &ip, "port", &port);
25 
26 	auto server = new Server(new StaticRouter(path));
27 	server.host(ip, port);
28 	
29 	while(true) server.eventLoop.loop();
30 
31 }
32 
33 class StaticRouter : Router {
34 
35 	private immutable string path;
36 	
37 	this(string path) {
38 		if(!path.endsWith("/")) path ~= "/";
39 		this.path = path;
40 	}
41 
42 	@Get(ctRegex!`\/(.*)`) get(Request req, Response res, string _file) {
43 		//TODO remove ../ for security reason
44 		immutable file = buildNormalizedPath(this.path, _file);
45 		if(exists(file)) {
46 			if(file.isFile) {
47 				// browsers should be able to get the mime type from the content
48 				res.body_ = read(file);
49 			} else if(file.isDir) {
50 				string[] dirs, files;
51 				foreach(f ; dirEntries(file, SpanMode.shallow)) {
52 					if(f.isDir) dirs ~= f[file.length+1..$];
53 					else if(f.isFile) files ~= f[file.length+1..$];
54 				}
55 				sort!((a, b) => a.toLower < b.toLower)(dirs);
56 				sort!((a, b) => a.toLower < b.toLower)(files);
57 				if(!_file.startsWith("/")) _file = "/" ~ _file;
58 				if(!_file.endsWith("/")) _file ~= "/";
59 				Appender!string ret;
60 				ret.put("<h1>Index of ");
61 				ret.put(_file);
62 				ret.put("</h1><hr>");
63 				foreach(dir ; dirs) ret.put("<a href='" ~ _file ~ dir ~ "'>" ~ dir ~ "/</a><br>");
64 				foreach(f ; files) ret.put("<a href='" ~ _file ~ f ~ "'>" ~ f ~ "</a><br>");
65 				res.body_ = ret.data;
66 				res.headers["Content-Type"] = "text/html";
67 			}
68 		} else {
69 			res.status = StatusCodes.notFound;
70 		}
71 	}
72 
73 }