summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authorunwox <me@unwox.com>2024-06-05 23:40:42 +0600
committerunwox <me@unwox.com>2024-06-05 23:40:42 +0600
commit2012809dc4a4692456d811c9b3f0293c54f4320b (patch)
tree40b06ee0ea47ab4793df6f25a75cfc151d4da23a /main.go
init
Diffstat (limited to 'main.go')
-rw-r--r--main.go236
1 files changed, 236 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..01027e7
--- /dev/null
+++ b/main.go
@@ -0,0 +1,236 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+)
+/*
+#cgo LDFLAGS: -llua
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+int
+luna_load_file(lua_State *L, char *file) {
+ if (luaL_dofile(L, file) != LUA_OK) {
+ return -1;
+ }
+ if (!lua_istable(L, -1)) {
+
+ return -1;
+ }
+ return luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+int
+luna_isfunction(lua_State *L, int n) {
+ return lua_isfunction(L, n);
+}
+
+void
+luna_push_table_item(lua_State *L, int ref, char *key) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+ lua_pushstring(L, key);
+ lua_gettable(L, -2);
+}
+
+void
+luna_pop(lua_State *L, int n) {
+ lua_pop(L, n);
+}
+
+const char *
+luna_tostring(lua_State *L, int n) {
+ return lua_tostring(L, n);
+}
+
+int
+luna_tonumber(lua_State *L, int n) {
+ return lua_tonumber(L, n);
+}
+
+void
+luna_push_ref(lua_State *L, int ref) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+}
+
+int
+luna_pop_ref(lua_State *L) {
+ return luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+const char *
+luna_pcall(lua_State *L, int nargs, int nresults) {
+ if (lua_pcall(L, nargs, nresults, 0) != LUA_OK) {
+ // FIXME: check for errors
+ return NULL;
+ }
+}
+*/
+import "C"
+
+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
+ L *C.lua_State
+ routes map[string]C.int
+ api C.int
+ started bool
+}
+
+func NewWorker(read chan *WorkerRequest) *Worker {
+ return &Worker {
+ read: read,
+ routes: make(map[string]C.int),
+ }
+}
+
+func (w *Worker) Start (filename string) error {
+ if w.started {
+ return errors.New("already started")
+ }
+
+ w.L = C.luaL_newstate()
+ C.luaL_openlibs(w.L)
+ // FIXME: C.luna_load_file should return an error.
+ w.api = C.luna_load_file(w.L, C.CString(filename))
+ w.initRoutes()
+
+ return nil
+}
+
+func (w *Worker) Listen () {
+ for {
+ r := <- w.read
+ handlerRef := w.routes[r.Route]
+ C.luna_push_ref(w.L, handlerRef)
+ C.lua_pushstring(w.L, C.CString(r.Request.Method))
+ C.lua_pushstring(w.L, C.CString(r.Request.URL.Path))
+
+ // table for headers
+ C.lua_createtable(w.L, 0, C.int(len(r.Request.Header)))
+
+ for k := range r.Request.Header {
+ h := r.Request.Header.Get(k)
+ C.lua_pushstring(w.L, C.CString(h))
+ C.lua_setfield(w.L, -2, C.CString(k))
+ }
+
+ body, err := io.ReadAll(r.Request.Body)
+ if err != nil {
+ C.luna_pop(w.L, 4);
+ // TODO: 500
+ return
+ }
+
+ C.lua_pushstring(w.L, C.CString(string(body)))
+ C.luna_pcall(w.L, 4, 3)
+ code := C.luna_tonumber(w.L, -3)
+ rbody := C.GoString(C.luna_tostring(w.L, -1))
+
+ // parse headers
+ headers := make(map[string]string)
+ C.luna_pop(w.L, 1)
+ C.lua_pushnil(w.L)
+ for C.lua_next(w.L, -2) != 0 {
+ if C.lua_isstring(w.L, -2) == 0 || C.lua_isstring(w.L, -1) == 0 {
+ C.luna_pop(w.L, 1)
+ continue
+ }
+ v := C.GoString(C.luna_tostring(w.L, -1))
+ C.luna_pop(w.L, 1)
+ // we must not pop the item key from the stack because
+ // otherwise C.lua_next won't work properly
+ k := C.GoString(C.luna_tostring(w.L, -1))
+ headers[k] = v
+ }
+ C.luna_pop(w.L, 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 () {
+ C.lua_close(w.L)
+}
+
+func (w *Worker) initRoutes() {
+ C.luna_push_table_item(w.L, w.api, C.CString("routes"))
+ C.lua_pushnil(w.L)
+ for C.lua_next(w.L, -2) != 0 {
+ if C.lua_isstring(w.L, -2) == 0 || C.luna_isfunction(w.L, -1) == 0 {
+ C.luna_pop(w.L, 1)
+ continue
+ }
+ handlerRef := C.luna_pop_ref(w.L)
+ // we must not pop the item key from the stack because
+ // otherwise C.lua_next won't work properly
+ route := C.GoString(C.luna_tostring(w.L, -1))
+ w.routes[route] = handlerRef
+ }
+}
+
+func main() {
+ rch := make(chan *WorkerRequest, 4096)
+ for i := 0; i < 8; i++ {
+ log.Printf("worker %d started\n", i)
+ w := NewWorker(rch)
+ w.Start("init.lua")
+ if i == 0 {
+ for _, route := range w.ListRoutes() {
+ http.HandleFunc(
+ route,
+ func (wr http.ResponseWriter, r *http.Request) {
+ resCh := w.Request(route, r)
+ res := <- resCh
+ for k, h := range res.Headers {
+ wr.Header().Set(k, h)
+ }
+ wr.WriteHeader(int(res.Code))
+ fmt.Fprint(wr, string(res.Body))
+ },
+ )
+ }
+ }
+ go w.Listen()
+ defer w.Stop()
+ }
+ fmt.Println("running")
+ log.Fatal(http.ListenAndServe("127.0.0.1:3001", nil))
+}