kserv_http.go 5.6 KB

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