| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- // package kserv_http -- встроенный HTTP-сервер
- package kserv_http
- import (
- "embed"
- "fmt"
- "net/http"
- "os"
- "strings"
- "sync"
- "time"
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/compress"
- "github.com/gofiber/fiber/v2/middleware/filesystem"
- "github.com/gofiber/fiber/v2/middleware/monitor"
- "gitp78su.ipnodns.ru/svi/kern/v4/kc/log_buf"
- "gitp78su.ipnodns.ru/svi/kern/v4/krn/kctx"
- . "gitp78su.ipnodns.ru/svi/kern/v4/lev0/ktypes"
- . "gitp78su.ipnodns.ru/svi/kern/v4/lev1/helpers"
- "gitp78su.ipnodns.ru/svi/kern/v4/lev1/local_ctx"
- . "gitp78su.ipnodns.ru/svi/kern/v4/lev1/result"
- "gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"
- )
- const (
- streamName = "kernel_server_http" // Контрольная строка для ожидателя потока
- )
- // kServHttp -- встроенный HTTP-сервер
- type kServHttp struct {
- kCtx IKernelCtx
- ctx ILocalCtx
- log ILogBuf
- strUrl string // URL, на котором слушает HTTP-сервер
- fiberApp *fiber.App
- isWork ISafeBool
- isEnd ISafeBool
- }
- //go:embed static/*
- var embedDirStatic embed.FS
- var (
- kernServHttp *kServHttp
- block sync.Mutex
- )
- // GetKernelServHttp -- возвращает встроенный HTTP-сервер
- func GetKernelServHttp() IResult[IKernelServerHttp] {
- block.Lock()
- defer block.Unlock()
- if kernServHttp != nil {
- kernServHttp.log.Debug("GetKernelServHttp()")
- return NewRes(IKernelServerHttp(kernServHttp))
- }
- log := log_buf.NewLogBuf(log_buf.OptIsTerm(true), log_buf.OptPrefix("kServHttp"))
- log.Debug("GetKernelServHttp(): first run")
- ctx := kctx.GetKernelCtx()
- strUrl := os.Getenv("LOCAL_HTTP_URL")
- Hassert(strUrl != "", "GetKernelServHttp(): env LOCAL_HTTP_URL not set")
- res := ctx.Get("monolitName")
- if res.IsErr() {
- err := fmt.Errorf("GetKernelServHttp(): in get from kCtx monolit name, err=\n\t%w", res.Err())
- return NewErr[IKernelServerHttp](err)
- }
- strMonolit := res.Val().Val().(string)
- confFiber := fiber.Config{
- ServerHeader: strMonolit,
- UnescapePath: true,
- ReadTimeout: time.Second * 15,
- WriteTimeout: time.Second * 15,
- AppName: strMonolit,
- Network: "tcp4",
- EnablePrintRoutes: true,
- }
- sf := &kServHttp{
- kCtx: ctx,
- log: log,
- ctx: local_ctx.NewLocalCtx(ctx.Ctx()),
- strUrl: strUrl,
- fiberApp: fiber.New(confFiber),
- isWork: safe_bool.NewSafeBool(),
- isEnd: safe_bool.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}))
- res1 := sf.kCtx.Wg().Add(streamName)
- res1.Hassert("GetKernelServHttp(): in add stream %v", streamName)
- resSet := ctx.Set("fiberApp", sf.fiberApp, "GetKernelServHttp() internal fiber app")
- if resSet.IsErr() {
- err := fmt.Errorf("GetKernelServHttp(): in set fiber app, err=\n\t%w", resSet.Err())
- return NewErr[IKernelServerHttp](err)
- }
- kernServHttp = sf
- resSet = ctx.Set("kServHttp", kernServHttp, "kServHttp")
- if resSet.IsErr() {
- err := fmt.Errorf("GetKernelServHttp(): in set kernServHttp, err=\n\t%w", resSet.Err())
- return NewErr[IKernelServerHttp](err)
- }
- return NewRes(IKernelServerHttp(kernServHttp))
- }
- // IsWork -- возвращает признак работы
- func (sf *kServHttp) IsWork() bool {
- return sf.isWork.Get()
- }
- // Log -- возвращает локальный лог
- func (sf *kServHttp) Log() ILogBuf {
- return sf.log
- }
- // Fiber -- возвращает объект веб-приложения fiber
- func (sf *kServHttp) Fiber() *fiber.App {
- return sf.fiberApp
- }
- // Run -- запускает сервер в работу (блокирующий вызов)
- func (sf *kServHttp) Run() {
- 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() (err error) {
- client := &http.Client{Timeout: 5 * time.Millisecond}
- url := sf.strUrl + "monitor"
- sf.log.Debug("url=%v", url)
- var resp *http.Response
- if resp, err = client.Get(url); err == nil {
- if resp.StatusCode == http.StatusOK {
- defer 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.Done()
- 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")
- }
|