kserv_http.go 4.8 KB

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