diff --git a/cmd/http/main.go b/cmd/http/main.go index 7b085a8..819abca 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -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 diff --git a/internal/handlers/single.go b/internal/handlers/single.go new file mode 100644 index 0000000..7b6c9db --- /dev/null +++ b/internal/handlers/single.go @@ -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() +} diff --git a/internal/http/server.go b/internal/http/server.go index 7d1d8fe..8425302 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -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 + } } diff --git a/internal/http/storage.go b/internal/storage/storage.go similarity index 98% rename from internal/http/storage.go rename to internal/storage/storage.go index 9b0cf44..d009fd0 100644 --- a/internal/http/storage.go +++ b/internal/storage/storage.go @@ -1,4 +1,4 @@ -package http +package storage import ( "git.defalsify.org/vise.git/db"