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 }