Isolate http specific parts to minimal

This commit is contained in:
lash 2024-09-12 03:30:23 +01:00
parent 0f9b5551ec
commit dde9f552a6
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
4 changed files with 165 additions and 67 deletions

View File

@ -112,6 +112,7 @@ func getResource(resourceDir string, ctx context.Context) (resource.Resource, er
return rfs, nil
}
func main() {
var dbDir string
var resourceDir string

View File

@ -0,0 +1,47 @@
package handlers
import (
"context"
"errors"
"io"
"git.defalsify.org/vise.git/engine"
"git.defalsify.org/vise.git/resource"
"git.defalsify.org/vise.git/persist"
"git.grassecon.net/urdt/ussd/internal/storage"
)
var (
ErrInvalidRequest = errors.New("invalid request for context")
ErrSessionMissing = errors.New("missing session")
ErrInvalidInput = errors.New("invalid input")
ErrStorage = errors.New("storage retrieval fail")
ErrEngineType = errors.New("incompatible engine")
ErrEngineInit = errors.New("engine init fail")
ErrEngineExec = errors.New("engine exec fail")
)
type RequestSession struct {
Ctx context.Context
Config engine.Config
Engine engine.Engine
Input []byte
Storage storage.Storage
Writer io.Writer
}
type engineMaker func(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine
type RequestParser interface {
GetSessionId(rq any) (string, error)
GetInput(rq any) ([]byte, error)
}
type RequestHandler interface {
GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
Process(rs RequestSession) (RequestSession, error)
Output(rs RequestSession) (RequestSession, error)
Reset(rs RequestSession) (RequestSession, error)
ShutDown()
}

View File

@ -1,9 +1,9 @@
package http
import (
"fmt"
"io/ioutil"
"net/http"
"strconv"
"git.defalsify.org/vise.git/db"
"git.defalsify.org/vise.git/engine"
@ -11,32 +11,38 @@ import (
"git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
"git.grassecon.net/urdt/ussd/internal/storage"
)
var (
logg = logging.NewVanilla().WithDomain("httpserver")
)
type RequestParser interface {
GetSessionId(rq *http.Request) (string, error)
GetInput(rq *http.Request) ([]byte, error)
}
type DefaultRequestParser struct {
}
func(rp *DefaultRequestParser) GetSessionId(rq *http.Request) (string, error) {
v := rq.Header.Get("X-Vise-Session")
func(rp *DefaultRequestParser) GetSessionId(rq any) (string, error) {
rqv, ok := rq.(*http.Request)
if !ok {
return "", handlers.ErrInvalidRequest
}
v := rqv.Header.Get("X-Vise-Session")
if v == "" {
return "", fmt.Errorf("no session found")
return "", handlers.ErrSessionMissing
}
return v, nil
}
func(rp *DefaultRequestParser) GetInput(rq *http.Request) ([]byte, error) {
defer rq.Body.Close()
v, err := ioutil.ReadAll(rq.Body)
func(rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) {
rqv, ok := rq.(*http.Request)
if !ok {
return nil, handlers.ErrInvalidRequest
}
defer rqv.Body.Close()
v, err := ioutil.ReadAll(rqv.Body)
if err != nil {
return nil, err
}
@ -45,33 +51,30 @@ func(rp *DefaultRequestParser) GetInput(rq *http.Request) ([]byte, error) {
type SessionHandler struct {
cfgTemplate engine.Config
rp RequestParser
rp handlers.RequestParser
rs resource.Resource
//first resource.EntryFunc
hn *ussd.Handlers
provider StorageProvider
provider storage.StorageProvider
}
//func NewSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, first resource.EntryFunc) *SessionHandler {
func NewSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *ussd.Handlers) *SessionHandler {
func NewSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp handlers.RequestParser, hn *ussd.Handlers) *SessionHandler {
return &SessionHandler{
cfgTemplate: cfg,
rs: rs,
//first: first,
hn: hn,
rp: rp,
provider: NewSimpleStorageProvider(stateDb, userdataDb),
provider: storage.NewSimpleStorageProvider(stateDb, userdataDb),
}
}
func(f *SessionHandler) writeError(w http.ResponseWriter, code int, msg string, err error) {
w.Header().Set("X-Vise", msg + ": " + err.Error())
w.Header().Set("Content-Length", "0")
func(f *SessionHandler) writeError(w http.ResponseWriter, code int, err error) {
s := err.Error()
w.Header().Set("Content-Length", strconv.Itoa(len(s)))
w.WriteHeader(code)
_, err = w.Write([]byte{})
if err != nil {
logg.Errorf("error writing error!!", "err", err, "olderr", s)
w.WriteHeader(500)
w.Header().Set("X-Vise", err.Error())
}
return
}
@ -83,68 +86,115 @@ func(f* SessionHandler) Shutdown() {
}
}
func(f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func(f *SessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine {
en := engine.NewEngine(cfg, rs)
en = en.WithPersister(pr)
return en
}
func(f *SessionHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) {
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()
cfg := f.cfgTemplate
cfg.SessionId = sessionId
var err error
var ok bool
logg.InfoCtxf(rqs.Ctx, "new request", rqs)
logg.InfoCtxf(ctx, "new request", "session", cfg.SessionId, "input", input)
storage, err := f.provider.Get(cfg.SessionId)
rqs.Storage, err = f.provider.Get(rqs.Config.SessionId)
if err != nil {
f.writeError(w, 500, "Storage retrieval fail", err)
return
logg.ErrorCtxf(rqs.Ctx, "", "storage error", "err", err)
return rqs, handlers.ErrStorage
}
f.hn = f.hn.WithPersister(rqs.Storage.Persister)
eni := f.GetEngine(rqs.Config, f.rs, rqs.Storage.Persister)
en, ok := eni.(*engine.DefaultEngine)
if !ok {
return rqs, handlers.ErrEngineType
}
f.hn = f.hn.WithPersister(storage.Persister)
defer f.provider.Put(cfg.SessionId, storage)
en := getEngine(cfg, f.rs, storage.Persister)
en = en.WithFirst(f.hn.Init)
if cfg.EngineDebug {
if rqs.Config.EngineDebug {
en = en.WithDebug(nil)
}
rqs.Engine = en
r, err = en.Init(ctx)
r, err = rqs.Engine.Init(rqs.Ctx)
if err != nil {
f.writeError(w, 500, "Engine init fail", err)
return rqs, err
}
if r && len(rqs.Input) > 0 {
r, err = rqs.Engine.Exec(rqs.Ctx, rqs.Input)
}
if err != nil {
return rqs, err
}
_ = r
return rqs, nil
}
func(f *SessionHandler) Output(rqs handlers.RequestSession) error {
var err error
_, err = rqs.Engine.WriteResult(rqs.Ctx, rqs.Writer)
return err
}
func(f *SessionHandler) Reset(rqs handlers.RequestSession) error {
defer f.provider.Put(rqs.Config.SessionId, rqs.Storage)
return rqs.Engine.Finish()
}
func(f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var code int
var err error
rqs := handlers.RequestSession{
Ctx: req.Context(),
Writer: w,
}
cfg := f.cfgTemplate
cfg.SessionId, err = f.rp.GetSessionId(req)
if err != nil {
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
f.writeError(w, 400, err)
}
rqs.Config = cfg
rqs.Input, err = f.rp.GetInput(req)
if err != nil {
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
f.writeError(w, 400, err)
return
}
if r && len(input) > 0 {
r, err = en.Exec(ctx, input)
rqs, err = f.Process(rqs)
switch err {
case handlers.ErrStorage:
code = 500
case handlers.ErrEngineInit:
code = 500
case handlers.ErrEngineExec:
code = 500
default:
code = 200
}
if err != nil {
f.writeError(w, 500, "Engine exec fail", err)
if code != 200 {
f.writeError(w, 500, err)
return
}
w.WriteHeader(200)
w.Header().Set("Content-Type", "text/plain")
_, err = en.WriteResult(ctx, w)
err = f.Output(rqs)
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)
f.writeError(w, 500, err)
return
}
_ = r
}
func getEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) *engine.DefaultEngine {
en := engine.NewEngine(cfg, rs)
en = en.WithPersister(pr)
return en
err = f.Reset(rqs)
if err != nil {
f.writeError(w, 500, err)
return
}
}

View File

@ -1,4 +1,4 @@
package http
package storage
import (
"git.defalsify.org/vise.git/db"