summaryrefslogtreecommitdiff
path: root/worker.go
diff options
context:
space:
mode:
Diffstat (limited to 'worker.go')
-rw-r--r--worker.go183
1 files changed, 91 insertions, 92 deletions
diff --git a/worker.go b/worker.go
index 646efea..b79f7e8 100644
--- a/worker.go
+++ b/worker.go
@@ -5,6 +5,7 @@ import (
"io"
"log"
"net/http"
+ "sync"
)
type HTTPRequest struct {
@@ -14,11 +15,6 @@ type HTTPRequest struct {
result chan *HTTPResponse
}
-type EvalRequest struct {
- code string
- result chan error
-}
-
type HTTPResponse struct {
Code int
Headers map[string]string
@@ -26,58 +22,70 @@ type HTTPResponse struct {
}
type Worker struct {
- read chan interface{}
lua *Lua
- api LuaRef
routes map[string]LuaRef
started bool
+ mu sync.Mutex
+}
+
+func HandleHTTPRequest(
+ queue chan any,
+ route string,
+ req *http.Request,
+) chan *HTTPResponse {
+ res := make(chan *HTTPResponse)
+ queue <- &HTTPRequest{
+ request: req,
+ route: route,
+ result: res,
+ }
+ return res
}
-func NewWorker(read chan interface{}) *Worker {
+func NewWorker() *Worker {
return &Worker {
- read: read,
routes: make(map[string]LuaRef),
lua: &Lua{},
}
}
-func (w *Worker) Start(filename string) error {
+func (w *Worker) Start(filename string, module map[string]any) error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
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
- err = w.initRoutes()
+ defer w.lua.RestoreStackFunc()()
+ w.initLunaModule(module)
+ err := w.lua.Require(filename)
if err != nil {
return err
}
-
+ w.started = true
return nil
}
-func (w *Worker) Listen() {
- for {
- resStack := w.lua.RestoreStackFunc()
- r := <- w.read
+func (w *Worker) Listen(queue chan any) {
+ handle := func() {
+ defer w.lua.RestoreStackFunc()()
+ r := <- queue
switch r.(type) {
case *HTTPRequest:
r := r.(*HTTPRequest)
- // If in debug mode always use handlers from api table
- // instead of cached references. Makes it much easier
- // to hot-replace route handlers.
- if r.debug {
- w.lua.PushFromRef(w.api)
- w.lua.PushTableItem("routes")
- w.lua.PushTableItem(r.route)
- } else {
- w.lua.PushFromRef(w.routes[r.route])
+ if _, ok := w.routes[r.route]; !ok {
+ r.result <- &HTTPResponse {
+ Code: 404,
+ Headers: make(map[string]string),
+ Body: "not found",
+ }
+ log.Println("no corresponding route")
+ return
}
+
+ w.lua.PushFromRef(w.routes[r.route])
w.lua.PushString(r.request.Method)
w.lua.PushString(r.request.URL.Path)
@@ -95,12 +103,22 @@ func (w *Worker) Listen() {
Body: "server error",
}
log.Println("could not read a request body")
- resStack()
- continue
+ return
}
w.lua.PushString(string(body))
- w.lua.PCall(4, 3)
+ err = w.lua.PCall(4, 3)
+
+ if err != nil {
+ r.result <- &HTTPResponse {
+ Code: 500,
+ Headers: make(map[string]string),
+ Body: "server error",
+ }
+ log.Println("could not read a request body")
+ return
+ }
+
code := w.lua.ToInt(-3)
rbody := w.lua.ToString(-1)
@@ -127,81 +145,62 @@ func (w *Worker) Listen() {
Body: rbody,
}
- case *EvalRequest:
- r := r.(*EvalRequest)
- err := w.lua.LoadString(r.code)
- r.result <- err
-
default:
log.Fatal("unknown request")
}
-
- resStack()
}
-}
-func (w *Worker) Request(
- route string,
- r *http.Request,
- debug bool,
-) chan *HTTPResponse {
- res := make(chan *HTTPResponse)
- w.read <- &HTTPRequest{
- request: r,
- route: route,
- debug: debug,
- result: res,
+ for {
+ handle()
}
- return res
}
-func (w *Worker) Eval(code string) chan error {
- res := make(chan error)
- w.read <- &EvalRequest{code: code, result: res}
- return res
+func (w *Worker) Run(code string) error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ defer w.lua.RestoreStackFunc()()
+ return w.lua.LoadString(code)
}
-func (w *Worker) ListRoutes() []string {
- res := []string{}
- for route, _ := range w.routes {
- res = append(res, route)
- }
- return res
+func (w *Worker) SetRoute(route string, handler LuaRef) {
+ w.routes[route] = handler
}
func (w *Worker) Stop() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
w.lua.Close()
}
-func (w *Worker) initLunaModule() {
- w.lua.CreateTable(1)
- w.lua.PushGoFunction(func (l *Lua) int {
- l.PushString("Hello, " + l.ToString(-1))
- return 1
- })
- w.lua.SetTableItem("helloFromGo")
+func (w *Worker) initLunaModule(module map[string]any) {
+ var pushTable func(t map[string]any)
+ pushTable = func (t map[string]any) {
+ w.lua.CreateTable(len(t))
+ for k, v := range t {
+ switch v.(type) {
+ case string:
+ v, _ := v.(string)
+ w.lua.PushString(v)
+ case func (l *Lua) int:
+ v, _ := v.(func (l *Lua) int)
+ w.lua.PushGoFunction(v)
+ case int:
+ v, _ := v.(int)
+ w.lua.PushNumber(v)
+ case map[string]any:
+ v, _ := v.(map[string]any)
+ pushTable(v)
+ default:
+ // FIXME: more details
+ log.Fatal("unsupported module value type")
+ }
+ w.lua.SetTableItem(k)
+ }
+ }
+ pushTable(module)
w.lua.SetGlobal("luna")
}
-func (w *Worker) initRoutes() error {
- defer w.lua.RestoreStackFunc()()
- w.lua.PushFromRef(w.api)
- w.lua.PushTableItem("routes")
- if !w.lua.IsTable(-1) {
- return errors.New("\"routes\" must be a table")
- }
- 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
- }
- w.lua.Pop(2)
- return nil
+func (w *Worker) HasSameLua(l *Lua) bool {
+ return w.lua == l
}