| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // package kserv_http -- встроенный HTTP-сервер.
- package kserv_http
- import (
- "embed"
- "fmt"
- "io/fs"
- "net/http"
- "os"
- "strings"
- "time"
- "github.com/gofiber/contrib/v3/monitor"
- "github.com/gofiber/fiber/v3"
- "github.com/gofiber/fiber/v3/middleware/compress"
- "github.com/gofiber/fiber/v3/middleware/static"
- "gitp78su.ipnodns.ru/svi/kern/v4/d0"
- "gitp78su.ipnodns.ru/svi/kern/v4/d1"
- "gitp78su.ipnodns.ru/svi/kern/v4/d2/kern_ctx"
- "gitp78su.ipnodns.ru/svi/kern/v4/d2/kern_ent/stream_name"
- "gitp78su.ipnodns.ru/svi/kern/v4/d2/kspec"
- )
- var (
- msg0 = d0.Txt("kernel_server_http")
- streamName = stream_name.NewAStreamName(msg0) // Контрольная строка для ожидателя потока
- )
- // kServHttp -- встроенный HTTP-сервер.
- type kServHttp struct {
- owner *d0.LOwner
- kCtx *kern_ctx.KernCtx
- lCtx *d1.LocalCtx
- log *d1.LogBuf
- strUrl *d0.LTxt // URL, на котором слушает HTTP-сервер
- fiberApp *fiber.App
- isWork *d0.MSafeBool
- isEnd *d0.MSafeBool
- }
- //go:embed static/*
- var embedDirStatic embed.FS
- var (
- kernServHttp *kServHttp
- block = d0.MutSafeMutex()
- )
- func getParams(owner *d0.LOwner) (*d0.LTxt, *d1.LogBuf, *kern_ctx.KernCtx) {
- param := &d1.LogBufParam{
- IsTerm_: d0.MutSafeBool(owner, true),
- Prefix_: d0.LetTxt("kServHttp"),
- }
- log := d1.NewLogBuf(param)
- log.Debug("GetKernelServHttp(): first run")
- strLocalUrl := os.Getenv("LOCAL_HTTP_URL")
- d0.If(strLocalUrl == "").Hassert("getParams(): env LOCAL_HTTP_URL not set")
- kCtx := kern_ctx.GetKernCtx()
- return d0.LetTxt(strLocalUrl), log, kCtx
- }
- // GetKernelServHttp -- возвращает встроенный HTTP-сервер.
- func GetKernelServHttp() kspec.IKernelServerHttp {
- block.Lock()
- defer block.Unlock()
- if kernServHttp != nil {
- kernServHttp.log.Debug("GetKernelServHttp()")
- return kernServHttp
- }
- owner := d0.LetOwner(1, "isol_kern_serv_http")
- strLocalUrl, log, kCtx := getParams(owner)
- optMonolit := kCtx.Get(d0.LetTxt("monolitName"))
- optMonolit.Hassert("GetKernelServHttp(): not have monolit name from kCtx")
- strMonolit := optMonolit.Some().Val().(*d0.LTime)
- confFiber := fiber.Config{
- ServerHeader: strMonolit.String(),
- UnescapePath: true,
- ReadTimeout: time.Second * 15,
- WriteTimeout: time.Second * 15,
- AppName: strMonolit.String(),
- }
- sf := &kServHttp{
- owner: owner,
- kCtx: kCtx,
- log: log,
- lCtx: d1.NewLocalCtx(owner, kCtx.SelfCtx()),
- strUrl: strLocalUrl,
- fiberApp: fiber.New(confFiber),
- isWork: d0.MutSafeBool(owner, false),
- isEnd: d0.MutSafeBool(owner, false),
- }
- sf.fiberApp.Use(compress.New(compress.Config{
- Level: compress.LevelBestCompression, // 2
- }))
- sf.fiberApp.Get("/static", static.New("/static", static.Config{
- FS: fs.FS(embedDirStatic),
- Browse: true,
- MaxAge: 3600 * 24,
- }))
- // Или расширьте конфигурацию для индивидуальной настройки
- // Назначьте промежуточное программное обеспечение для /metrics
- // и измените заголовок на `Страница метрик MyService`
- sf.fiberApp.Get("/metrics", monitor.New(monitor.Config{
- Title: fmt.Sprintf("Страница метрик %q", strMonolit),
- }))
- sf.kCtx.Wg().Add(streamName)
- kCtx.Set(d0.LetTxt("fiberApp"), sf.fiberApp, "GetKernelServHttp() internal fiber app")
- kernServHttp = sf
- kCtx.Set(d0.LetTxt("kServHttp"), kernServHttp, "kServHttp")
- return kernServHttp
- }
- // IsWork -- возвращает признак работы.
- func (sf *kServHttp) IsWork() d0.Bool {
- return sf.isWork.IsOk()
- }
- // Log -- возвращает локальный лог.
- func (sf *kServHttp) Log() *d1.LogBuf {
- 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.IsOk() {
- return
- }
- if sf.isWork.IsOk() {
- return
- }
- sf.log.Debug("Run(): url='%v'", sf.strUrl)
- lstPort := strings.Split(sf.strUrl.String(), ":")
- strPort := lstPort[len(lstPort)-1]
- strPort = strings.ReplaceAll(strPort, "/", "")
- strPort = strings.ReplaceAll(strPort, `"`, "")
- chErr := make(chan *d0.LErr, 2)
- fnListen := func() {
- defer close(chErr)
- err := sf.fiberApp.Listen(":" + strPort)
- chErr <- d0.LetErr("kServHttp.Run().fnListen(): in listen server on port=%q, err=\n\t%v", strPort, err)
- }
- go fnListen()
- go sf.fnChErr(chErr)
- fnCheckServer := func() error {
- client := &http.Client{Timeout: 5 * time.Millisecond}
- url := d0.LetTxt(sf.strUrl.Get() + "monitor")
- sf.log.Debug("url=%v", url.Get())
- var (
- resp *http.Response
- err error
- )
- if resp, err = client.Get(url.String()); 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 *d0.LErr) {
- err := <-chErr
- if err.Get() != "<nil>" {
- err := d0.LetErr("kServHttp.fnChErr(): in listen, err=\n\t%w", err)
- sf.log.Err(err)
- sf.kCtx.Cancel()
- }
- }
- // Ожидает окончания работы.
- func (sf *kServHttp) close() {
- sf.kCtx.Wait()
- if sf.isWork.IsNot() {
- return
- }
- sf.isWork.Reset()
- sf.isEnd.Set()
- err := sf.fiberApp.Server().Shutdown()
- d0.If(err != nil).Assert("kServHttp.close(): in close server, err=\n\t%v", err)
- sf.kCtx.Wg().Done(streamName)
- sf.log.Debug("close(): end")
- }
|