kserv_http.go 5.4 KB

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