// package serv_http -- востроенный HTTP-сервер package serv_http import ( "fmt" "os" "time" "github.com/gofiber/fiber/v2" "git.p78su.freemyip.com/svi/gostore/pkg/types" ) // ServHttp -- встроенный HTTP-сервер type ServHttp struct { serv types.IService fiberApp *fiber.App port string login string // Логин для входа не сервер pass string // Пароль для входа на сервер mem types.IStoreMem disk types.IStoreDisk } // NewServHttp -- возвращает новый HTTP-сервер func NewServHttp(serv types.IService) (types.IServHttp, error) { if serv == nil { return nil, fmt.Errorf("NewServHttp(): IService==nil") } port := os.Getenv("STORE_HTTP_PORT") if port == "" { return nil, fmt.Errorf("NewServHttp(): env STORE_HTTP_PORT not set") } config := fiber.Config{ Prefork: false, ServerHeader: "GoStore", StrictRouting: false, CaseSensitive: false, Immutable: false, UnescapePath: false, ETag: false, BodyLimit: 10_000_000, Concurrency: 0, Views: nil, ViewsLayout: "", PassLocalsToViews: false, ReadTimeout: time.Second * 60, WriteTimeout: time.Second * 60, IdleTimeout: time.Second * 10, ReadBufferSize: 0, WriteBufferSize: 0, CompressedFileSuffix: "", ProxyHeader: "", GETOnly: false, // ErrorHandler: func(*fiber.Ctx, error) error { panic("not implemented") }, DisableKeepalive: false, DisableDefaultDate: false, DisableDefaultContentType: false, DisableHeaderNormalizing: false, DisableStartupMessage: false, AppName: "GoStore", StreamRequestBody: false, DisablePreParseMultipartForm: false, ReduceMemoryUsage: false, // JSONEncoder: func(interface{}) ([]byte, error) { panic("not implemented") }, // JSONDecoder: func([]byte, interface{}) error { panic("not implemented") }, // XMLEncoder: func(interface{}) ([]byte, error) { panic("not implemented") }, Network: "tcp4", EnableTrustedProxyCheck: false, TrustedProxies: []string{}, EnableIPValidation: false, EnablePrintRoutes: false, // ColorScheme: fiber.Colors{ // Black: "", // Red: "", // Green: "", // Yellow: "", // Blue: "", // Magenta: "", // Cyan: "", // White: "", // Reset: "", // }, // RequestMethods: []string{}, EnableSplittingOnParsers: false, } app := fiber.New(config) sf := &ServHttp{ serv: serv, fiberApp: app, port: port, login: serv.User().Login(), pass: serv.User().Pass(), mem: serv.StoreMem(), disk: serv.StoreDisk(), } sf.fiberApp.Get("/time", sf.getTime) sf.fiberApp.Post("/put/:login/:pass/:key", sf.postLogin, sf.postPut) sf.fiberApp.Post("/get/:login/:pass/:key", sf.postLogin, sf.postGet) sf.fiberApp.Post("/del/:login/:pass/:key", sf.postLogin, sf.postDel) sf.fiberApp.Post("/find/:login/:pass/:key", sf.postLogin, sf.postFind) return sf, nil } type ListValRequest struct { Val_ []string `json:"val" form:"val"` } // postFind -- возвращает список ключей по префиксу func (sf *ServHttp) postFind(ctx *fiber.Ctx) error { key := ctx.Params("key") lstKey, err := sf.disk.Find(key) if err != nil { return fiber.NewError(40, "ServHttp.postFind(): in get keys by prefix from db") } return ctx.JSON(&ListValRequest{ Val_: lstKey, }) } type ValRequest struct { Val_ []byte `json:"val" form:"val"` } // postDel -- удаляет содержимое ключа func (sf *ServHttp) postDel(ctx *fiber.Ctx) error { key := ctx.Params("key") sf.mem.Del(key) sf.disk.Del(key) return nil } // postGet -- возвращает содержимое ключа func (sf *ServHttp) postGet(ctx *fiber.Ctx) error { key := ctx.Params("key") val, err := sf.mem.Get(key) if err == nil { return ctx.JSON(&ValRequest{ Val_: val, }) } val, err = sf.disk.Get(key) if err != nil { return fiber.NewError(30, "ServHttp.postGet(): in get val from db") } _ = sf.mem.Put(key, val) return ctx.JSON(&ValRequest{ Val_: val, }) } // postPut -- сохраняет содержимое ключа func (sf *ServHttp) postPut(ctx *fiber.Ctx) error { key := ctx.Params("key") req := &ValRequest{} if err := ctx.BodyParser(req); err != nil { return fiber.NewError(20, "ServHttp.postPut(): bad FORM") } _ = sf.mem.Put(key, req.Val_) err := sf.disk.Put(key, req.Val_) if err != nil { return fiber.NewError(21, "ServHttp.postPut(): in put val on db") } return nil } // getTime -- возвращает ответ с меткой времени (для KeepAlive) func (sf *ServHttp) getTime(ctx *fiber.Ctx) error { ctx.Response().Header.Add("Cache-Control", "no-cache") // Cache-Control: no-cache return ctx.SendString(time.Now().Local().String()) } // Run -- запускает веб-сервер в работу func (sf *ServHttp) Run() error { defer sf.serv.CancelApp() err := sf.fiberApp.Listen(":" + sf.port) if err != nil { return fmt.Errorf("ServHttp.Run(): in listen port(%q), err=\n\t%w", sf.port, err) } return nil } // Проверяет логин/пароль в запросе func (sf *ServHttp) postLogin(ctx *fiber.Ctx) error { login := ctx.Params("login") if login != sf.login { return fiber.NewError(1, "ServHttp.postLogin(): bad login/pass") } pass := ctx.Params("pass") if pass != sf.pass { return fiber.NewError(2, "ServHttp.postLogin(): bad login/pass") } key := ctx.Params("key") if key == "" { return fiber.NewError(3, "ServHttp.postLogin(): key is empty") } ctx.Next() return nil }