// Serve all files in the current directory, or only a few select filetypes (html, css, js, png and txt) func registerHandlers(mux *http.ServeMux, handlePath, servedir string, perm pinterface.IPermissions, luapool *lStatePool, cache *FileCache, addDomain bool) { // Handle all requests with this function allRequests := func(w http.ResponseWriter, req *http.Request) { if perm.Rejected(w, req) { // Get and call the Permission Denied function perm.DenyFunction()(w, req) // Reject the request by returning return } // Local to this function servedir := servedir // Look for the directory that is named the same as the host if addDomain { servedir = filepath.Join(servedir, getDomain(req)) } urlpath := req.URL.Path filename := url2filename(servedir, urlpath) // Remove the trailing slash from the filename, if any noslash := filename if strings.HasSuffix(filename, pathsep) { noslash = filename[:len(filename)-1] } hasdir := fs.exists(filename) && fs.isDir(filename) dirname := filename hasfile := fs.exists(noslash) // Set the server header. serverHeaders(w) // Share the directory or file if hasdir { dirPage(w, req, servedir, dirname, perm, luapool, cache) return } else if !hasdir && hasfile { // Share a single file instead of a directory filePage(w, req, noslash, perm, luapool, cache) return } // Not found w.WriteHeader(http.StatusNotFound) fmt.Fprint(w, noPage(filename)) } // Handle requests differently depending on if rate limiting is enabled or not if disableRateLimiting { mux.HandleFunc(handlePath, allRequests) } else { limiter := tollbooth.NewLimiter(limitRequests, time.Second) limiter.MessageContentType = "text/html; charset=utf-8" limiter.Message = easyPage("Rate-limit exceeded", "<div style='color:red'>You have reached the maximum request limit.</div>") mux.Handle(handlePath, tollbooth.LimitFuncHandler(limiter, allRequests)) } }
// Make functions related to server configuration and permissions available func exportServerConfigFunctions(L *lua.LState, perm pinterface.IPermissions, filename string, luapool *lStatePool) { // Set a default host and port. Maybe useful for alg applications. L.SetGlobal("SetAddr", L.NewFunction(func(L *lua.LState) int { serverAddrLua = L.ToString(1) return 0 // number of results })) // Clear the default path prefixes. This makes everything public. L.SetGlobal("ClearPermissions", L.NewFunction(func(L *lua.LState) int { perm.Clear() return 0 // number of results })) // Registers a path prefix, for instance "/secret", // as having *user* rights. L.SetGlobal("AddUserPrefix", L.NewFunction(func(L *lua.LState) int { path := L.ToString(1) perm.AddUserPath(path) return 0 // number of results })) // Registers a path prefix, for instance "/secret", // as having *admin* rights. L.SetGlobal("AddAdminPrefix", L.NewFunction(func(L *lua.LState) int { path := L.ToString(1) perm.AddAdminPath(path) return 0 // number of results })) // Sets a Lua function as a custom "permissions denied" page handler. L.SetGlobal("DenyHandler", L.NewFunction(func(L *lua.LState) int { luaDenyFunc := L.ToFunction(1) // Custom handler for when permissions are denied perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) { // Set up a new Lua state with the current http.ResponseWriter and *http.Request, without caching exportCommonFunctions(w, req, filename, perm, L, luapool, nil, nil) // Then run the given Lua function L.Push(luaDenyFunc) if err := L.PCall(0, lua.MultRet, nil); err != nil { // Non-fatal error log.Error("Permission denied handler failed:", err) // Use the default permission handler from now on if the lua function fails perm.SetDenyFunction(permissions.PermissionDenied) perm.DenyFunction()(w, req) } }) return 0 // number of results })) // Sets a Lua function to be run once the server is done parsing configuration and arguments. L.SetGlobal("OnReady", L.NewFunction(func(L *lua.LState) int { luaReadyFunc := L.ToFunction(1) // Custom handler for when permissions are denied. // Put the *lua.LState in a closure. serverReadyFunctionLua = func() { // Run the given Lua function L.Push(luaReadyFunc) if err := L.PCall(0, lua.MultRet, nil); err != nil { // Non-fatal error log.Error("The OnReady function failed:", err) } } return 0 // number of results })) // Set a access log filename. If blank, the log will go to the console (or browser, if debug mode is set). L.SetGlobal("LogTo", L.NewFunction(func(L *lua.LState) int { filename := L.ToString(1) serverLogFile = filename // Log as JSON by default log.SetFormatter(&log.JSONFormatter{}) // Log to stderr if an empty filename is given if filename == "" { log.SetOutput(os.Stderr) L.Push(lua.LBool(true)) return 1 // number of results } // Try opening/creating the given filename, for appending f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, defaultPermissions) if err != nil { log.Error(err) L.Push(lua.LBool(false)) return 1 // number of results } // Set the file to log to and return log.SetOutput(f) L.Push(lua.LBool(true)) return 1 // number of results })) // Use a single Lua file as the server, instead of directory structure L.SetGlobal("ServerFile", L.NewFunction(func(L *lua.LState) int { givenFilename := L.ToString(1) serverfilename := filepath.Join(filepath.Dir(filename), givenFilename) if !fs.exists(filename) { log.Error("Could not find", serverfilename) L.Push(lua.LBool(false)) return 1 // number of results } luaServerFilename = serverfilename L.Push(lua.LBool(true)) return 1 // number of results })) L.SetGlobal("ServerInfo", L.NewFunction(func(L *lua.LState) int { // Return the string, but drop the final newline L.Push(lua.LString(serverInfo())) return 1 // number of results })) }