package engine import ( "net/http" "path/filepath" "github.com/didip/tollbooth" "github.com/sirupsen/logrus" "github.com/xyproto/algernon/themes" lua "github.com/xyproto/gopher-lua" ) // handleRegistryPrefix keys a handler function in a Lua state's registry const handleRegistryPrefix = "algernon:handle:" // LoadLuaHandlerFunctions makes functions related to handling HTTP requests // available to Lua scripts. // // When registerRoutes is true, handle() registers the route on the mux. That // wrapped handler borrows a state from ac.handlerPool at request time, looks // up the handler function from the state's registry by path, and runs it. // When registerRoutes is false, handle() only stores the function in the // state's registry; this is the mode used while populating the pool. func (ac *Config) LoadLuaHandlerFunctions(L *lua.LState, filename string, mux *http.ServeMux, addDomain bool, httpStatus *FutureStatus, theme string, registerRoutes bool) { L.SetGlobal("handle", L.NewFunction(func(L *lua.LState) int { handlePath := L.ToString(1) handleFunc := L.ToFunction(2) // Store the function in this state's registry, keyed by path, // so the request-time wrapper can fetch it from whichever pool // state it happens to borrow. L.G.Registry.RawSetString(handleRegistryPrefix+handlePath, handleFunc) if !registerRoutes { return 0 // number of results } wrappedHandleFunc := func(w http.ResponseWriter, req *http.Request) { if ac.handlerPool == nil { logrus.Error("Handler for " + handlePath + " called before the handler pool was built") return } poolL := ac.handlerPool.Get() defer ac.handlerPool.Put(poolL) fn := poolL.G.Registry.RawGetString(handleRegistryPrefix + handlePath) handlerFn, ok := fn.(*lua.LFunction) if !ok { logrus.Error("Handler for " + handlePath + " is missing from the pool state") return } ac.LoadCommonFunctions(w, req, filename, poolL, nil, httpStatus) poolL.Push(handlerFn) if err := poolL.PCall(0, lua.MultRet, nil); err != nil { // Non-fatal error logrus.Error("Handler for "+handlePath+" failed:", err) } // Then exit after the first request, if specified if ac.quitAfterFirstRequest { go ac.quitSoon("Quit after first request", defaultSoonDuration) } } // Handle requests differently depending on if rate limiting is enabled or not if ac.disableRateLimiting { mux.HandleFunc(handlePath, wrappedHandleFunc) } else { limiter := tollbooth.NewLimiter(float64(ac.limitRequests), nil) limiter.SetMessage(themes.MessagePage("Rate-limit exceeded", "