client_bus_http.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // package client_bus_http -- клиент HTTP-шины.
  2. package client_bus_http
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "github.com/google/uuid"
  10. mL0 "gitp78su.ipnodns.ru/svi/kern/v4/lev0"
  11. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/defs/topic"
  12. "gitp78su.ipnodns.ru/svi/kern/v4/lev0/etypes/ebool"
  13. mKs "gitp78su.ipnodns.ru/svi/kern/v4/lev0/kspec"
  14. mL1 "gitp78su.ipnodns.ru/svi/kern/v4/lev1"
  15. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/kbus_msg/msg_pub"
  16. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/kbus_msg/msg_serve"
  17. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/kbus_msg/msg_sub"
  18. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/kbus_msg/msg_unsub"
  19. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/log_buf"
  20. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_bool"
  21. "gitp78su.ipnodns.ru/svi/kern/v4/lev1/safe_string"
  22. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kbus_http"
  23. "gitp78su.ipnodns.ru/svi/kern/v4/lev2/kctx"
  24. )
  25. // ClientBusHttp -- клиент HTTP-шины.
  26. type ClientBusHttp struct {
  27. bus mKs.IKernelBus
  28. lCtx mKs.ILocalCtx
  29. log mKs.ILogBuf
  30. isWork mKs.ISafeBool
  31. urlRemote string // URL дистанционной шины
  32. urlLocal string // URL локальной шины для веб-хуков
  33. }
  34. // NewClientBusHttp - -возвращает новый клиент HTTP-шины.
  35. func NewClientBusHttp(urlRemote string) mKs.IBusClient {
  36. param := &log_buf.LogBufParam{
  37. IsTerm_: safe_bool.NewSafeBool(true),
  38. Prefix_: "ClientBusHttp",
  39. }
  40. log := mL1.NewLogBuf(param)
  41. log.Debug("NewClientBusHttp()")
  42. mL0.Hassert(urlRemote != "", "NewClientBusHttp(): urlRemote is empty")
  43. kCtx := kctx.GetKernelCtx()
  44. resLocal := safe_string.NewSafeStringGetenv("LOCAL_HTTP_URL")
  45. resLocal.Hassert("NewClientBusHttp(): in get env LOCAL_HTTP_URL)")
  46. urlLocal := resLocal.Ok()
  47. kBusHttp := kbus_http.GetKernelBusHttp()
  48. sf := &ClientBusHttp{
  49. lCtx: mL1.NewLocalCtx(kCtx.Ctx()),
  50. log: log,
  51. bus: kBusHttp,
  52. isWork: mL1.NewSafeBool(true),
  53. urlRemote: strings.TrimSuffix(urlRemote, "/"),
  54. urlLocal: strings.TrimSuffix(urlLocal.Get(), "/"),
  55. }
  56. return sf
  57. }
  58. // Unsubscribe -- отписывается от топика в дистанционной шине.
  59. func (sf *ClientBusHttp) Unsubscribe(handler mKs.IBusHandlerSubscribe) {
  60. _uuid, err := uuid.NewV6()
  61. mL1.Hassert()(err == nil, "ClientBusHttp.Unsubscribe(): in generate UUID v6, err=\n\t%v", err)
  62. req := &msg_unsub.UnsubReq{
  63. Name_: handler.Name(),
  64. Uuid_: _uuid.String(),
  65. }
  66. req.SelfCheck()
  67. binReq, err := json.MarshalIndent(req, "", " ")
  68. mL1.Hassert()(err == nil, "ClientBusHttp.Unsubscribe(): in marshal to JSON unsubscribe request, err=\n\t%v", err)
  69. reader := strings.NewReader(string(binReq))
  70. hReq, err := http.NewRequest(http.MethodPost, sf.urlRemote+"/bus/unsub", reader)
  71. mL1.Hassert()(err == nil, "ClientBusHttp.Unsubscribe(): in new request, err=\n\t%v", err)
  72. resBody := sf.makePost(hReq)
  73. if resBody.IsErr() {
  74. return
  75. }
  76. resp := &msg_unsub.UnsubResp{}
  77. err = json.Unmarshal(resBody.Ok(), resp)
  78. if err != nil {
  79. sf.log.Err("Unsubscribe(): in unmarshal response, err=\n\t%v", err)
  80. return
  81. }
  82. if resp.Status_ != "ok" {
  83. sf.log.Err("Unsubscribe(): resp!='ok', err=\n\t%v", resp.Status_)
  84. }
  85. if resp.Uuid_ != req.Uuid_ {
  86. sf.log.Err("Unsubscribe(): resp uuid(%v) bad", resp.Uuid_)
  87. }
  88. }
  89. // Subscribe -- подписывается на топик в дистанционной шине.
  90. func (sf *ClientBusHttp) Subscribe(handler mKs.IBusHandlerSubscribe) mKs.IResult[mKs.EBool] {
  91. _uuid, err := uuid.NewV6()
  92. mL0.Hassert(err == nil, "ClientBusHttp.Subscribe(): in generate UUID v6, err=\n\t%v", err)
  93. req := &msg_sub.SubscribeReq{
  94. Topic_: handler.Topic(),
  95. Uuid_: _uuid.String(),
  96. WebHook_: sf.urlLocal + "/bus/pub", // Веб-хук всегда на своей стороне
  97. }
  98. req.SelfCheck()
  99. binReq, _ := json.MarshalIndent(req, "", " ")
  100. body := strings.NewReader(string(binReq))
  101. hReq, err := http.NewRequest(http.MethodPost, sf.urlRemote+"/bus/sub", body)
  102. mL0.Hassert(err != nil, "ClientBusHttp.Subscribe(): in new request, err=\n\t%v")
  103. resBody := sf.makePost(hReq)
  104. if resBody.IsErr() {
  105. err := fmt.Errorf("ClientBusHttp.Subscribe(): in make POST-request, err=\n\t%w", err)
  106. return mL0.NewErr[mKs.EBool](err)
  107. }
  108. resp := &msg_sub.SubscribeResp{}
  109. err = json.Unmarshal(resBody.Ok(), resp)
  110. if err != nil {
  111. err := fmt.Errorf("ClientBusHttp.Subscribe(): in unmarshal response, err=\n\t%v", err)
  112. return mL0.NewErr[mKs.EBool](err)
  113. }
  114. if resp.Status_ != "ok" {
  115. err := fmt.Errorf("ClientBusHttp.Subscribe(): resp!='ok', err=\n\t%v", resp.Status_)
  116. return mL0.NewErr[mKs.EBool](err)
  117. }
  118. if resp.Uuid_ != req.Uuid_ {
  119. err := fmt.Errorf("ClientBusHttp.Subscribe(): resp uuid(%v) bad", resp.Uuid_)
  120. return mL0.NewErr[mKs.EBool](err)
  121. }
  122. res := sf.bus.Subscribe(handler)
  123. return res
  124. }
  125. // SendRequest -- отправляет в дистанционную шину запрос.
  126. func (sf *ClientBusHttp) SendRequest(topic *topic.ATopic, binReq []byte) mL0.IResult[[]byte] {
  127. _uuid, err := uuid.NewV6()
  128. mL1.Hassert()(err == nil, "ClientBusHttp.SendRequest(): in generate UUID v6, err=\n\t%v", err)
  129. req := &msg_serve.ServeReq{
  130. Topic_: topic,
  131. Uuid_: _uuid.String(),
  132. BinReq_: binReq,
  133. }
  134. req.SelfCheck()
  135. _binReq, _ := json.MarshalIndent(req, "", " ")
  136. body := strings.NewReader(string(_binReq))
  137. hReq, err := http.NewRequest(http.MethodPost, sf.urlRemote+"/bus/request", body)
  138. mL1.Hassert()(err == nil, "ClientBusHttp.SendRequest(): in new request, err=\n\t%v")
  139. resBody := sf.makePost(hReq)
  140. if resBody.IsErr() {
  141. err := fmt.Errorf("ClientBusHttp.SendRequest(): in make request, err=\n\t%w", err)
  142. return mL0.WrapErr(resBody, err)
  143. }
  144. resp := &msg_serve.ServeResp{}
  145. err = json.Unmarshal(resBody.Ok(), resp)
  146. if err != nil {
  147. err := fmt.Errorf("ClientBusHttp.SendRequest(): in unmarshal response, err=\n\t%v", err)
  148. return mL0.NewErr[[]byte](err)
  149. }
  150. if resp.Status_ != "ok" {
  151. err := fmt.Errorf("ClientBusHttp.SendRequest(): resp!='ok', err=\n\t%v", resp.Status_)
  152. return mL0.NewErr[[]byte](err)
  153. }
  154. if resp.Uuid_ != req.Uuid_ {
  155. err := fmt.Errorf("ClientBusHttp.SendRequest(): resp uuid(%v) bad", resp.Uuid_)
  156. return mL0.NewErr[[]byte](err)
  157. }
  158. return mL0.NewOk(resp.BinResp_)
  159. }
  160. // RegisterServe -- регистрирует в локальной шине обработчик.
  161. func (sf *ClientBusHttp) RegisterServe(handler mKs.IBusHandlerServe) mL0.IResult[mKs.EBool] {
  162. if handler == nil {
  163. return mL0.NewErr[mKs.EBool](fmt.Errorf("ClientBusHttp.RegisterServe(): handler==nil"))
  164. }
  165. res := sf.bus.RegisterServe(handler)
  166. if res.IsErr() {
  167. err := fmt.Errorf("ClientBusHttp.RegisterServe(): in register serve to bus, err=\n\t%v", res.Err())
  168. return mL0.NewErr[mKs.EBool](err)
  169. }
  170. return mL0.NewOk(ebool.NewEBool(true))
  171. }
  172. // Publish -- публикует сообщение в дистанционной шину.
  173. func (sf *ClientBusHttp) Publish(topic *topic.ATopic, binMsg []byte) mL0.IResult[mKs.EBool] {
  174. _uuid, err := uuid.NewV6()
  175. mL1.Hassert()(err == nil, "ClientBusHttp.Publish(): in generate UUID v6, err=\n\t%v", err)
  176. req := &msg_pub.PublishReq{
  177. Topic_: topic,
  178. Uuid_: _uuid.String(),
  179. BinMsg_: binMsg,
  180. }
  181. req.SelfCheck()
  182. binReq, _ := json.MarshalIndent(req, "", " ")
  183. body := strings.NewReader(string(binReq))
  184. hReq, err := http.NewRequest(http.MethodPost, sf.urlRemote+"/bus/pub", body)
  185. mL1.Hassert()(err == nil, "ClientBusHttp.Publish(): in new request, err=\n\t%v")
  186. resBody := sf.makePost(hReq)
  187. if resBody.IsErr() {
  188. err := fmt.Errorf("ClientBusHttp.Publish(): in make request, err=\n\t%w", err)
  189. return mL0.NewErr[mKs.EBool](err)
  190. }
  191. resp := &msg_pub.PublishResp{}
  192. err = json.Unmarshal(resBody.Ok(), resp)
  193. if err != nil {
  194. err := fmt.Errorf("ClientBusHttp.Publish(): in unmarshal response, err=\n\t%v", err)
  195. return mL0.NewErr[mKs.EBool](err)
  196. }
  197. if resp.Status_ != "ok" {
  198. err := fmt.Errorf("ClientBusHttp.Publish(): resp!='ok', err=\n\t%v", resp.Status_)
  199. return mL0.NewErr[mKs.EBool](err)
  200. }
  201. if resp.Uuid_ != req.Uuid_ {
  202. err := fmt.Errorf("ClientBusHttp.Publish(): resp uuid(%v) bad", resp.Uuid_)
  203. return mL0.NewErr[mKs.EBool](err)
  204. }
  205. return mL0.NewOk(ebool.NewEBool(true))
  206. }
  207. // Единый обработчик запросов.
  208. func (sf *ClientBusHttp) makePost(hReq *http.Request) mKs.IResult[[]byte] {
  209. hReq.Header.Add("Content-Type", "application/json")
  210. _resp, err := http.DefaultClient.Do(hReq)
  211. if err != nil {
  212. err := fmt.Errorf("ClientBusHttp.makePost(): after request, err=\n\t%w", err)
  213. return mL0.NewErr[[]byte](err)
  214. }
  215. defer func() {
  216. _ = _resp.Body.Close()
  217. }()
  218. if _resp.StatusCode != http.StatusOK {
  219. err := fmt.Errorf("ClientBusHttp.makePost(): url=%q, status=%q",
  220. hReq.URL, _resp.Status)
  221. return mL0.NewErr[[]byte](err)
  222. }
  223. binBody, err := io.ReadAll(_resp.Body)
  224. if err != nil {
  225. err := fmt.Errorf("ClientBusHttp.makePost(): after read body, err=\n\t%v",
  226. err)
  227. return mL0.NewErr[[]byte](err)
  228. }
  229. return mL0.NewOk(binBody)
  230. }
  231. // Log -- возвращает локальный лог клиента.
  232. func (sf *ClientBusHttp) Log() mKs.ILogBuf {
  233. return sf.log
  234. }
  235. // IsWork -- возвращает признак работы.
  236. func (sf *ClientBusHttp) IsWork() mKs.EBool {
  237. return sf.bus.IsWork()
  238. }