commit 66c71be317bd578f223af0e808880417632926d9
parent cdd0f97d046e60946e0688294831493aebdc0f50
Author: lash <dev@holbrook.no>
Date: Sat, 31 Aug 2024 01:20:45 +0100
Merge branch 'lash/http-example' into dev-0.1.0
Diffstat:
14 files changed, 245 insertions(+), 10 deletions(-)
diff --git a/engine/config.go b/engine/config.go
@@ -0,0 +1,11 @@
+package engine
+
+// Config globally defines behavior of all components driven by the engine.
+type Config struct {
+ OutputSize uint32 // Maximum size of output from a single rendered page
+ SessionId string
+ Root string
+ FlagCount uint32
+ CacheSize uint32
+ Language string
+}
diff --git a/engine/engine.go b/engine/engine.go
@@ -20,16 +20,6 @@ type EngineIsh interface {
Finish() error
}
-// Config globally defines behavior of all components driven by the engine.
-type Config struct {
- OutputSize uint32 // Maximum size of output from a single rendered page
- SessionId string
- Root string
- FlagCount uint32
- CacheSize uint32
- Language string
-}
-
// Engine is an execution engine that handles top-level errors when running client inputs against code in the bytecode buffer.
type Engine struct {
st *state.State
diff --git a/examples/http/Makefile b/examples/http/Makefile
@@ -0,0 +1,10 @@
+INPUTS = $(wildcard ./*.vis)
+TXTS = $(wildcard ./*.txt.orig)
+
+%.vis:
+ go run ../../dev/asm $(basename $@).vis > $(basename $@).bin
+
+all: $(INPUTS) $(TXTS)
+
+%.txt.orig:
+ cp -v $(basename $@).orig $(basename $@)
diff --git a/examples/http/_catch b/examples/http/_catch
@@ -0,0 +1 @@
+an error occurred, please try again
diff --git a/examples/http/_catch.vis b/examples/http/_catch.vis
@@ -0,0 +1,2 @@
+HALT
+MOVE ^
diff --git a/examples/http/end b/examples/http/end
@@ -0,0 +1 @@
+whoops
diff --git a/examples/http/end.vis b/examples/http/end.vis
@@ -0,0 +1,2 @@
+HALT
+MOVE ^
diff --git a/examples/http/main.go b/examples/http/main.go
@@ -0,0 +1,205 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path"
+
+ "git.defalsify.org/vise.git/engine"
+ "git.defalsify.org/vise.git/state"
+ "git.defalsify.org/vise.git/cache"
+ "git.defalsify.org/vise.git/persist"
+ "git.defalsify.org/vise.git/resource"
+ "git.defalsify.org/vise.git/logging"
+)
+
+var (
+ Logg logging.Logger = logging.NewVanilla().WithDomain("http")
+)
+
+type LocalHandler struct {
+ sessionId string
+}
+
+func NewLocalHandler() *LocalHandler {
+ return &LocalHandler{
+ sessionId: "",
+ }
+}
+
+func(h* LocalHandler) SetSession(sessionId string) {
+ h.sessionId = sessionId
+}
+
+func(h* LocalHandler) AddSession(ctx context.Context, sym string, input []byte) (resource.Result, error) {
+ return resource.Result{
+ Content: h.sessionId + ":" + string(input),
+ }, nil
+}
+
+type RequestParser interface {
+ GetSessionId(*http.Request) (string, error)
+ GetInput(*http.Request) ([]byte, error)
+}
+
+type DefaultRequestParser struct {
+}
+
+func(rp *DefaultRequestParser) GetSessionId(rq *http.Request) (string, error) {
+ v := rq.Header.Get("X-Vise-Session")
+ if v == "" {
+ return "", fmt.Errorf("no session found")
+ }
+ return v, nil
+}
+
+func(rp *DefaultRequestParser) GetInput(rq *http.Request) ([]byte, error) {
+ defer rq.Body.Close()
+ v, err := ioutil.ReadAll(rq.Body)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+}
+
+type DefaultSessionHandler struct {
+ cfgTemplate engine.Config
+ rp RequestParser
+ rs resource.Resource
+ rh *LocalHandler
+ peBase string
+}
+
+func NewDefaultSessionHandler(persistBase string, resourceBase string, rp RequestParser, outputSize uint32, cacheSize uint32, flagCount uint32) *DefaultSessionHandler {
+ rs := resource.NewFsResource(resourceBase)
+ rh := NewLocalHandler()
+ rs.AddLocalFunc("echo", rh.AddSession)
+ return &DefaultSessionHandler{
+ cfgTemplate: engine.Config{
+ OutputSize: outputSize,
+ Root: "root",
+ FlagCount: flagCount,
+ CacheSize: cacheSize,
+ },
+ rs: rs,
+ rh: rh,
+ rp: rp,
+ }
+}
+
+func(f *DefaultSessionHandler) GetEngine(ctx context.Context, sessionId string) (engine.EngineIsh, error) {
+ cfg := f.cfgTemplate
+ cfg.SessionId = sessionId
+
+ persistPath := path.Join(f.peBase, sessionId)
+ err := os.MkdirAll(persistPath, 0700)
+ if err != nil {
+ return nil, err
+ }
+ f.rh.SetSession(sessionId)
+
+ pe := persist.NewFsPersister(persistPath)
+ en, err := engine.NewPersistedEngine(ctx, cfg, pe, f.rs)
+ if err != nil {
+ st := state.NewState(cfg.FlagCount)
+ ca := cache.NewCache().WithCacheSize(cfg.CacheSize)
+ Logg.Infof("persisted engine create error. trying again with persisting empty state first...")
+ pe = pe.WithContent(&st, ca)
+ err = pe.Save(cfg.SessionId)
+ if err != nil {
+ return nil, err
+ }
+ en, err = engine.NewPersistedEngine(ctx, cfg, pe, f.rs)
+ }
+ return en, err
+}
+
+func(f *DefaultSessionHandler) writeError(w http.ResponseWriter, code int, msg string, err error) {
+ w.Header().Set("X-Vise", msg + ": " + err.Error())
+ w.Header().Set("Content-Length", "0")
+ w.WriteHeader(code)
+ _, err = w.Write([]byte{})
+ if err != nil {
+ w.WriteHeader(500)
+ w.Header().Set("X-Vise", err.Error())
+ }
+ return
+}
+
+func(f *DefaultSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ var r bool
+ sessionId, err := f.rp.GetSessionId(req)
+ if err != nil {
+ f.writeError(w, 400, "Session missing", err)
+ return
+ }
+ input, err := f.rp.GetInput(req)
+ if err != nil {
+ f.writeError(w, 400, "Input read fail", err)
+ return
+ }
+ ctx := req.Context()
+ en, err := f.GetEngine(ctx, sessionId)
+ if err != nil {
+ f.writeError(w, 400, "Engine start fail", err)
+ return
+ }
+
+ if len(input) == 0 {
+ r, err = en.Init(ctx)
+ } else {
+ r, err = en.Exec(ctx, input)
+ }
+ if err != nil {
+ f.writeError(w, 500, "Engine exec fail", err)
+ return
+ }
+ w.WriteHeader(200)
+ w.Header().Set("Content-Type", "text/plain")
+ _, err = en.WriteResult(ctx, w)
+ if err != nil {
+ f.writeError(w, 500, "Write result fail", err)
+ return
+ }
+ err = en.Finish()
+ if err != nil {
+ f.writeError(w, 500, "Engine finish fail", err)
+ return
+ }
+ _ = r
+}
+
+func main() {
+ var host string
+ var port string
+ var peDir string
+ var rsDir string
+ var outSize uint
+ var flagCount uint
+ var cacheSize uint
+ flag.StringVar(&rsDir, "r", ".", "resource dir")
+ flag.StringVar(&host, "h", "127.0.0.1", "http host")
+ flag.StringVar(&port, "p", "7123", "http port")
+ flag.StringVar(&peDir, "d", ".state", "persistance dir")
+ flag.UintVar(&flagCount, "f", 0, "flag count")
+ flag.UintVar(&cacheSize, "c", 1024 * 1024, "cache size")
+ flag.UintVar(&outSize, "s", 160, "max size of output")
+ flag.Parse()
+ fmt.Fprintf(os.Stderr, "starting server:\n\tpersistence dir: %s\n\tresource dir: %s\n", rsDir, peDir)
+
+ rp := &DefaultRequestParser{}
+ h := NewDefaultSessionHandler(peDir, rsDir, rp, uint32(outSize), uint32(cacheSize), uint32(flagCount))
+ s := &http.Server{
+ Addr: fmt.Sprintf("%s:%s", host, port),
+ Handler: h,
+ }
+ err := s.ListenAndServe()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Server error: %s", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/http/root b/examples/http/root
@@ -0,0 +1 @@
+Please enter something
diff --git a/examples/http/root.vis b/examples/http/root.vis
@@ -0,0 +1,2 @@
+HALT
+INCMP second *
diff --git a/examples/http/second b/examples/http/second
@@ -0,0 +1,2 @@
+you wrote: {{.echo}}
+now choose next step
diff --git a/examples/http/second.vis b/examples/http/second.vis
@@ -0,0 +1,5 @@
+LOAD echo 0
+MAP echo
+MOUT quit 0
+HALT
+INCMP third 0
diff --git a/examples/http/third b/examples/http/third
@@ -0,0 +1 @@
+bye
diff --git a/examples/http/third.vis b/examples/http/third.vis
@@ -0,0 +1,2 @@
+HALT
+MOVE ^