Ctrl K
ComunidadGuías…

engine/luahandler.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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", "<div style='color:red'>You have reached the maximum request limit.</div>", theme))
			limiter.SetMessageContentType(htmlUTF8)
			mux.Handle(handlePath, tollbooth.LimitFuncHandler(limiter, wrappedHandleFunc))
		}

		return 0 // number of results
	}))

	L.SetGlobal("servedir", L.NewFunction(func(L *lua.LState) int {
		// servedir only has an effect during the first pass; subsequent
		// passes (pool build) must not re-register mux routes.
		if !registerRoutes {
			return 0 // number of results
		}
		handlePath := L.ToString(1) // serve as (ie. "/")
		rootdir := L.ToString(2)    // filesystem directory (ie. "./public")
		if handlePath == "" || rootdir == "" {
			logrus.Errorf("servedir needs an URL path to serve, ie. %q and a directory releative to %q, ie. %q", "/", filepath.Dir(filename), "./public")
			return 0
		}
		rootdir = filepath.Join(filepath.Dir(filename), rootdir)
		ac.RegisterHandlers(mux, handlePath, rootdir, addDomain)
		return 0 // number of results
	}))
}