kserv_http.go 5.6 KB

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