go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit f2205dfa78b60b77ba677398dbb1aa2c5929d199
parent 7bb678ca54afa29cd8a7073ef968384828c1614f
Author: lash <dev@holbrook.no>
Date:   Tue, 27 Aug 2024 20:40:35 +0100

Add working http server concept for persistence and dynamic session load

Diffstat:
Aengine/config.go | 11+++++++++++
Mengine/engine.go | 10----------
Aexamples/http/Makefile | 10++++++++++
Aexamples/http/main.go | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/http/root | 1+
Aexamples/http/root.vis | 2++
Aexamples/http/second | 1+
Aexamples/http/second.vis | 2++
Aexamples/http/third.vis | 1+
9 files changed, 207 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/main.go b/examples/http/main.go @@ -0,0 +1,179 @@ +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 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 + peBase string +} + +func NewDefaultSessionHandler(persistBase string, resourceBase string, rp RequestParser, outputSize uint32, cacheSize uint32, flagCount uint32) *DefaultSessionHandler { + return &DefaultSessionHandler{ + cfgTemplate: engine.Config{ + OutputSize: outputSize, + Root: "root", + FlagCount: flagCount, + CacheSize: cacheSize, + }, + rs: resource.NewFsResource(resourceBase), + 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 + } + + 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 @@ +please enter something else diff --git a/examples/http/second.vis b/examples/http/second.vis @@ -0,0 +1,2 @@ +HALT +INCMP third * diff --git a/examples/http/third.vis b/examples/http/third.vis @@ -0,0 +1 @@ +HALT