// package kserv_http -- встроенный HTTP-сервер. package kserv_http import ( "embed" "fmt" "net/http" "os" "strings" "sync" "time" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/fiber/v2/middleware/monitor" "github.com/gofiber/fiber/v3" "gitp78su.ipnodns.ru/svi/kern/v4/lev0/alias" "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers" mKt "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes" mL1 "gitp78su.ipnodns.ru/svi/kern/v4/lev1" "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kctx" ) var ( streamName = alias.NewAStreamName("kernel_server_http") // Контрольная строка для ожидателя потока ) // kServHttp -- встроенный HTTP-сервер. type kServHttp struct { kCtx mKt.IKernelCtx lCtx mKt.ILocalCtx log mKt.ILogBuf strUrl string // URL, на котором слушает HTTP-сервер fiberApp *fiber.App isWork mKt.ISafeBool isEnd mKt.ISafeBool } //go:embed static/* var embedDirStatic embed.FS var ( kernServHttp *kServHttp block sync.Mutex assert = helpers.Assert hassert = helpers.Hassert ) func getParams() (string, mKt.ILogBuf, mKt.IKernelCtx) { log := mL1.NewLogBuf(mL1.OptIsTerm(true), mL1.OptPrefix("kServHttp")) log.Debug("GetKernelServHttp(): first run") strLocalUrl := os.Getenv("LOCAL_HTTP_URL") hassert(strLocalUrl != "", "getParams(): env LOCAL_HTTP_URL not set") kCtx := kctx.GetKernelCtx() return strLocalUrl, log, kCtx } // GetKernelServHttp -- возвращает встроенный HTTP-сервер. func GetKernelServHttp() mKt.IKernelServerHttp { block.Lock() defer block.Unlock() if kernServHttp != nil { kernServHttp.log.Debug("GetKernelServHttp()") return kernServHttp } strLocalUrl, log, kCtx := getParams() optMonolit := kCtx.Get("monolitName") optMonolit.Hassert("GetKernelServHttp(): not have monolit name from kCtx") strMonolit := optMonolit.Some().Val().(string) confFiber := fiber.Config{ ServerHeader: strMonolit, UnescapePath: true, ReadTimeout: time.Second * 15, WriteTimeout: time.Second * 15, AppName: strMonolit, } sf := &kServHttp{ kCtx: kCtx, log: log, lCtx: mL1.NewLocalCtx(kCtx.Ctx()), strUrl: strLocalUrl, fiberApp: fiber.New(confFiber), isWork: mL1.NewSafeBool(), isEnd: mL1.NewSafeBool(), } sf.fiberApp.Use(compress.New(compress.Config{ Level: compress.LevelBestCompression, // 2 })) sf.fiberApp.Use("/static", filesystem.New(filesystem.Config{ Root: http.FS(embedDirStatic), PathPrefix: "static", Browse: true, MaxAge: 3600 * 24, })) sf.fiberApp.Get("/monitor", monitor.New(monitor.Config{Title: strMonolit})) sf.kCtx.Wg().Add(streamName) kCtx.Set("fiberApp", sf.fiberApp, "GetKernelServHttp() internal fiber app") kernServHttp = sf kCtx.Set("kServHttp", kernServHttp, "kServHttp") return kernServHttp } // IsWork -- возвращает признак работы. func (sf *kServHttp) IsWork() bool { return sf.isWork.Get() } // Log -- возвращает локальный лог. func (sf *kServHttp) Log() mKt.ILogBuf { return sf.log } // Fiber -- возвращает объект веб-приложения fiber. func (sf *kServHttp) Fiber() *fiber.App { return sf.fiberApp } // Run -- запускает сервер в работу (не блокирующий вызов). func (sf *kServHttp) Run() { block.Lock() defer block.Unlock() if sf.isEnd.Get() { return } if sf.isWork.Get() { return } sf.log.Debug("Run(): url='%v'", sf.strUrl) lstPort := strings.Split(sf.strUrl, ":") strPort := lstPort[len(lstPort)-1] strPort = strings.ReplaceAll(strPort, "/", "") strPort = strings.ReplaceAll(strPort, `"`, "") chErr := make(chan string, 2) fnListen := func() { defer close(chErr) err := sf.fiberApp.Listen(":" + strPort) chErr <- fmt.Sprint(err) } go fnListen() go sf.fnChErr(chErr) fnCheckServer := func() error { client := &http.Client{Timeout: 5 * time.Millisecond} url := sf.strUrl + "monitor" sf.log.Debug("url=%v", url) var ( resp *http.Response err error ) if resp, err = client.Get(url); err == nil { _ = resp.Body.Close() } return err } for { time.Sleep(time.Millisecond * 10) err := fnCheckServer() if err == nil { break } } sf.isWork.Set() go sf.close() } // В отдельном потоке ждёт закрытия канала. func (sf *kServHttp) fnChErr(chErr <-chan string) { strErr := <-chErr if strErr != "" { err := fmt.Errorf("kServHttp.fnChErr(): in listen, err=\n\t%v", strErr) sf.log.Err("Run(): err=\n\t%v", err.Error()) sf.kCtx.Cancel() } } // Ожидает окончания работы. func (sf *kServHttp) close() { sf.kCtx.Wait() if !sf.isWork.Get() { return } sf.isWork.Reset() sf.isEnd.Set() err := sf.fiberApp.Server().Shutdown() assert(err == nil, "kServHttp.close(): in close server, err=\n\t%v", err) sf.kCtx.Wg().Done(streamName) sf.log.Debug("close(): end") }