kserv_http.go 5.1 KB

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