kserv_http.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // package kserv_http -- встроенный HTTP-сервер.
  2. package kserv_http
  3. import (
  4. "embed"
  5. "fmt"
  6. "io/fs"
  7. "net/http"
  8. "os"
  9. "strings"
  10. "sync"
  11. "time"
  12. "github.com/gofiber/contrib/v3/monitor"
  13. "github.com/gofiber/fiber/v3"
  14. "github.com/gofiber/fiber/v3/middleware/compress"
  15. "github.com/gofiber/fiber/v3/middleware/static"
  16. "gitp78su.ipnodns.ru/svi/kern/v4/lev0"
  17. "gitp78su.ipnodns.ru/svi/kern/v4/lev1"
  18. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kern_ctx"
  19. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kern_ent/stream_name"
  20. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kspec"
  21. )
  22. var (
  23. msg0 = lev0.Txt("kernel_server_http")
  24. streamName = stream_name.NewAStreamName(msg0) // Контрольная строка для ожидателя потока
  25. )
  26. // kServHttp -- встроенный HTTP-сервер.
  27. type kServHttp struct {
  28. kCtx *kern_ctx.KernCtx
  29. lCtx *lev1.LocalCtx
  30. log *lev1.LogBuf
  31. strUrl *lev0.LTxt // URL, на котором слушает HTTP-сервер
  32. fiberApp *fiber.App
  33. isWork *lev0.MSafeBool
  34. isEnd *lev0.MSafeBool
  35. }
  36. //go:embed static/*
  37. var embedDirStatic embed.FS
  38. var (
  39. kernServHttp *kServHttp
  40. block sync.Mutex
  41. )
  42. func getParams() (*lev0.LTxt, *lev1.LogBuf, *kern_ctx.KernCtx) {
  43. param := &lev1.LogBufParam{
  44. IsTerm_: lev0.MutSafeBool(true),
  45. Prefix_: lev0.LetTxt("kServHttp"),
  46. }
  47. log := lev1.NewLogBuf(param)
  48. log.Debug("GetKernelServHttp(): first run")
  49. strLocalUrl := os.Getenv("LOCAL_HTTP_URL")
  50. lev0.If(strLocalUrl == "").Hassert("getParams(): env LOCAL_HTTP_URL not set")
  51. kCtx := kern_ctx.GetKernCtx()
  52. return lev0.LetTxt(strLocalUrl), log, kCtx
  53. }
  54. // GetKernelServHttp -- возвращает встроенный HTTP-сервер.
  55. func GetKernelServHttp() kspec.IKernelServerHttp {
  56. block.Lock()
  57. defer block.Unlock()
  58. if kernServHttp != nil {
  59. kernServHttp.log.Debug("GetKernelServHttp()")
  60. return kernServHttp
  61. }
  62. strLocalUrl, log, kCtx := getParams()
  63. optMonolit := kCtx.Get(lev0.LetTxt("monolitName"))
  64. optMonolit.Hassert("GetKernelServHttp(): not have monolit name from kCtx")
  65. strMonolit := optMonolit.Some().Val().(*lev0.LTime)
  66. confFiber := fiber.Config{
  67. ServerHeader: strMonolit.String(),
  68. UnescapePath: true,
  69. ReadTimeout: time.Second * 15,
  70. WriteTimeout: time.Second * 15,
  71. AppName: strMonolit.String(),
  72. }
  73. sf := &kServHttp{
  74. kCtx: kCtx,
  75. log: log,
  76. lCtx: lev1.NewLocalCtx(kCtx.SelfCtx()),
  77. strUrl: strLocalUrl,
  78. fiberApp: fiber.New(confFiber),
  79. isWork: lev0.MutSafeBool(false),
  80. isEnd: lev0.MutSafeBool(false),
  81. }
  82. sf.fiberApp.Use(compress.New(compress.Config{
  83. Level: compress.LevelBestCompression, // 2
  84. }))
  85. sf.fiberApp.Get("/static", static.New("/static", static.Config{
  86. FS: fs.FS(embedDirStatic),
  87. Browse: true,
  88. MaxAge: 3600 * 24,
  89. }))
  90. // Или расширьте конфигурацию для индивидуальной настройки
  91. // Назначьте промежуточное программное обеспечение для /metrics
  92. // и измените заголовок на `Страница метрик MyService`
  93. sf.fiberApp.Get("/metrics", monitor.New(monitor.Config{
  94. Title: fmt.Sprintf("Страница метрик %q", strMonolit),
  95. }))
  96. sf.kCtx.Wg().Add(streamName)
  97. kCtx.Set(lev0.LetTxt("fiberApp"), sf.fiberApp, "GetKernelServHttp() internal fiber app")
  98. kernServHttp = sf
  99. kCtx.Set(lev0.LetTxt("kServHttp"), kernServHttp, "kServHttp")
  100. return kernServHttp
  101. }
  102. // IsWork -- возвращает признак работы.
  103. func (sf *kServHttp) IsWork() lev0.Bool {
  104. return sf.isWork.Get()
  105. }
  106. // Log -- возвращает локальный лог.
  107. func (sf *kServHttp) Log() *lev1.LogBuf {
  108. return sf.log
  109. }
  110. // Fiber -- возвращает объект веб-приложения fiber.
  111. func (sf *kServHttp) Fiber() *fiber.App {
  112. return sf.fiberApp
  113. }
  114. // Run -- запускает сервер в работу (не блокирующий вызов).
  115. func (sf *kServHttp) Run() {
  116. block.Lock()
  117. defer block.Unlock()
  118. if sf.isEnd.Get() {
  119. return
  120. }
  121. if sf.isWork.Get() {
  122. return
  123. }
  124. sf.log.Debug("Run(): url='%v'", sf.strUrl)
  125. lstPort := strings.Split(sf.strUrl.String(), ":")
  126. strPort := lstPort[len(lstPort)-1]
  127. strPort = strings.ReplaceAll(strPort, "/", "")
  128. strPort = strings.ReplaceAll(strPort, `"`, "")
  129. chErr := make(chan *lev0.LErr, 2)
  130. fnListen := func() {
  131. defer close(chErr)
  132. err := sf.fiberApp.Listen(":" + strPort)
  133. chErr <- lev0.LetErr("kServHttp.Run().fnListen(): in listen server on port=%q, err=\n\t%v", strPort, err)
  134. }
  135. go fnListen()
  136. go sf.fnChErr(chErr)
  137. fnCheckServer := func() error {
  138. client := &http.Client{Timeout: 5 * time.Millisecond}
  139. url := lev0.LetTxt(sf.strUrl.Get() + "monitor")
  140. sf.log.Debug("url=%v", url.Get())
  141. var (
  142. resp *http.Response
  143. err error
  144. )
  145. if resp, err = client.Get(url.String()); err == nil {
  146. _ = resp.Body.Close()
  147. }
  148. return err
  149. }
  150. for {
  151. time.Sleep(time.Millisecond * 10)
  152. err := fnCheckServer()
  153. if err == nil {
  154. break
  155. }
  156. }
  157. sf.isWork.Set()
  158. go sf.close()
  159. }
  160. // В отдельном потоке ждёт закрытия канала.
  161. func (sf *kServHttp) fnChErr(chErr <-chan *lev0.LErr) {
  162. err := <-chErr
  163. if err.Get() != "<nil>" {
  164. err := lev0.LetErr("kServHttp.fnChErr(): in listen, err=\n\t%w", err)
  165. sf.log.Err(err)
  166. sf.kCtx.Cancel()
  167. }
  168. }
  169. // Ожидает окончания работы.
  170. func (sf *kServHttp) close() {
  171. sf.kCtx.Wait()
  172. if !sf.isWork.Get() {
  173. return
  174. }
  175. sf.isWork.Reset()
  176. sf.isEnd.Set()
  177. err := sf.fiberApp.Server().Shutdown()
  178. lev0.If(err != nil).Assert("kServHttp.close(): in close server, err=\n\t%v", err)
  179. sf.kCtx.Wg().Done(streamName)
  180. sf.log.Debug("close(): end")
  181. }