kserv_http.go 5.5 KB

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