| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- // 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/defs"
- "gitp78su.ipnodns.ru/svi/kern/v4/lev0/helpers"
- mKs "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kspec"
- mL1 "gitp78su.ipnodns.ru/svi/kern/v4/lev1"
- "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kctx"
- )
- var (
- streamName = defs.NewStreamName("kernel_server_http") // Контрольная строка для ожидателя потока
- )
- // kServHttp -- встроенный HTTP-сервер.
- type kServHttp struct {
- kCtx mKs.IKernelCtx
- lCtx mKs.ILocalCtx
- log mKs.ILogBuf
- strUrl string // URL, на котором слушает HTTP-сервер
- fiberApp *fiber.App
- isWork mKs.ISafeBool
- isEnd mKs.ISafeBool
- }
- //go:embed static/*
- var embedDirStatic embed.FS
- var (
- kernServHttp *kServHttp
- block sync.Mutex
- assert = helpers.Assert
- hassert = helpers.Hassert
- )
- func getParams() (string, mKs.ILogBuf, mKs.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() mKs.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() mKs.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 != "<nil>" {
- 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")
- }
|