kserv_http.go 6.0 KB

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