package main import ( "errors" "io" "net/http" ) type WorkerRequest struct { Request *http.Request Route string result chan *WorkerResponse } type WorkerResponse struct { Code int Headers map[string]string Body string } type Worker struct { read chan *WorkerRequest lua *Lua api LuaRef routes map[string]LuaRef started bool } func NewWorker(read chan *WorkerRequest) *Worker { return &Worker { read: read, routes: make(map[string]LuaRef), lua: &Lua{}, } } func (w *Worker) Start (filename string) error { if w.started { return errors.New("already started") } w.lua.Start() api, err := w.lua.Require(filename) if err != nil { return err } w.api = api w.initRoutes() return nil } func (w *Worker) Listen () { for { r := <- w.read handlerRef := w.routes[r.Route] w.lua.PushFromRef(handlerRef) w.lua.PushString(r.Request.Method) w.lua.PushString(r.Request.URL.Path) fh := make(map[string]string) for k := range r.Request.Header { fh[k] = r.Request.Header.Get(k) } w.lua.PushTable(fh) body, err := io.ReadAll(r.Request.Body) if err != nil { w.lua.Pop(4); // TODO: 500 return } w.lua.PushString(string(body)) w.lua.PCall(4, 3) code := w.lua.ToInt(-3) rbody := w.lua.ToString(-1) // parse headers headers := make(map[string]string) w.lua.Pop(1) w.lua.PushNil() for w.lua.Next() { if !w.lua.IsString(-2) || !w.lua.IsString(-2) { w.lua.Pop(1) continue } v := w.lua.ToString(-1) w.lua.Pop(1) // we must not pop the item key from the stack because // otherwise C.lua_next won't work properly k := w.lua.ToString(-1) headers[k] = v } w.lua.Pop(2) r.result <- &WorkerResponse { Code: int(code), Headers: headers, Body: rbody, } } } func (w *Worker) Request (route string, r *http.Request,) chan *WorkerResponse { res := make(chan *WorkerResponse) w.read <- &WorkerRequest{ Request: r, Route: route, result: res, } return res } func (w *Worker) ListRoutes() []string { res := []string{} for route, _ := range w.routes { res = append(res, route) } return res } func (w *Worker) Stop () { w.lua.Close() } func (w *Worker) initRoutes() { w.lua.PushFromRef(w.api) w.lua.PushTableItem("routes") // FIXME: istable? w.lua.PushNil() for w.lua.Next() { if !w.lua.IsString(-2) || !w.lua.IsFunction(-1) { w.lua.Pop(1) continue } handlerRef := w.lua.PopToRef() // we must not pop the item key from the stack because // otherwise C.lua_next won't work properly route := w.lua.ToString(-1) w.routes[route] = handlerRef } // FIXME: pop? }