You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

hub_io.go 7.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package http
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "time"
  8. "github.com/gin-gonic/gin"
  9. "github.com/inhies/go-bytesize"
  10. "gitlink.org.cn/cloudream/common/consts/errorcode"
  11. "gitlink.org.cn/cloudream/common/pkgs/future"
  12. "gitlink.org.cn/cloudream/common/pkgs/logger"
  13. "gitlink.org.cn/cloudream/common/utils/http2"
  14. "gitlink.org.cn/cloudream/common/utils/io2"
  15. "gitlink.org.cn/cloudream/common/utils/serder"
  16. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/ioswitch/exec"
  17. hubapi "gitlink.org.cn/cloudream/jcs-pub/hub/sdk/api"
  18. )
  19. type IOService struct {
  20. *Server
  21. }
  22. func (s *Server) IOSvc() *IOService {
  23. return &IOService{
  24. Server: s,
  25. }
  26. }
  27. func (s *IOService) GetStream(ctx *gin.Context) {
  28. log := logger.WithField("HTTP", "HubIO.GetStream")
  29. req, err := serder.JSONToObjectStreamEx[hubapi.GetStreamReq](ctx.Request.Body)
  30. if err != nil {
  31. log.Warnf("deserializing request: %v", err)
  32. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  33. return
  34. }
  35. log = log.WithField("PlanID", req.PlanID).WithField("VarID", req.VarID)
  36. // 设置超时
  37. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  38. defer cancel()
  39. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  40. if sw == nil {
  41. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "plan not found"))
  42. return
  43. }
  44. sw.PutVar(req.SignalID, req.Signal)
  45. strVal, err := exec.BindVar[*exec.StreamValue](sw, ctx.Request.Context(), req.VarID)
  46. if err != nil {
  47. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("bind var: %v", err)))
  48. return
  49. }
  50. defer strVal.Stream.Close()
  51. ctx.Header("Content-Type", http2.ContentTypeOctetStream)
  52. startTime := time.Now()
  53. cw := http2.NewChunkedWriter(io2.NopWriteCloser(ctx.Writer))
  54. n, err := cw.WriteStreamPart("stream", strVal.Stream)
  55. dt := time.Since(startTime)
  56. log.Debugf("size: %v, time: %v, speed: %v/s", n, dt, bytesize.New(float64(n)/dt.Seconds()))
  57. if err != nil {
  58. log.Warnf("writing stream part: %v", err)
  59. cw.Abort(err.Error())
  60. return
  61. }
  62. err = cw.Finish()
  63. if err != nil {
  64. log.Warnf("finishing chunked writer: %v", err)
  65. return
  66. }
  67. }
  68. func (s *IOService) SendStream(ctx *gin.Context) {
  69. cr := http2.NewChunkedReader(ctx.Request.Body)
  70. _, infoData, err := cr.NextDataPart()
  71. if err != nil {
  72. logger.Warnf("reading info data: %s", err.Error())
  73. ctx.JSON(http.StatusBadRequest, Failed(errorcode.OperationFailed, fmt.Sprintf("reading info data: %v", err)))
  74. return
  75. }
  76. info, err := serder.JSONToObjectEx[hubapi.SendStreamInfo](infoData)
  77. if err != nil {
  78. logger.Warnf("deserializing info data: %s", err.Error())
  79. ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadArgument, fmt.Sprintf("deserializing info data: %v", err)))
  80. return
  81. }
  82. _, stream, err := cr.NextPart()
  83. if err != nil {
  84. logger.Warnf("reading stream data: %s", err.Error())
  85. ctx.JSON(http.StatusBadRequest, Failed(errorcode.OperationFailed, fmt.Sprintf("reading stream data: %v", err)))
  86. return
  87. }
  88. defer cr.Close()
  89. // 超时设置
  90. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  91. defer cancel()
  92. sw := s.svc.swWorker.FindByIDContexted(c, info.PlanID)
  93. if sw == nil {
  94. ctx.JSON(http.StatusNotFound, gin.H{"error": "plan not found"})
  95. return
  96. }
  97. fut := future.NewSetVoid()
  98. sw.PutVar(info.VarID, &exec.StreamValue{
  99. Stream: io2.DelegateReadCloser(stream, func() error {
  100. fut.SetVoid()
  101. return nil
  102. }),
  103. })
  104. // 等待流发送完毕才能发送响应
  105. err = fut.Wait(ctx.Request.Context())
  106. if err != nil {
  107. logger.Warnf("sending stream: %s", err.Error())
  108. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("sending stream: %v", err)))
  109. return
  110. }
  111. ctx.JSON(http.StatusOK, OK(nil))
  112. }
  113. func (s *IOService) ExecuteIOPlan(ctx *gin.Context) {
  114. log := logger.WithField("HTTP", "HubIO.ExecuteIOPlan")
  115. data, err := io.ReadAll(ctx.Request.Body)
  116. if err != nil {
  117. log.Warnf("reading body: %s", err.Error())
  118. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "reading body failed"))
  119. return
  120. }
  121. req, err := serder.JSONToObjectEx[hubapi.ExecuteIOPlanReq](data)
  122. if err != nil {
  123. log.Warnf("deserializing request: %s", err.Error())
  124. ctx.JSON(http.StatusOK, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  125. return
  126. }
  127. log = log.WithField("PlanID", req.Plan.ID)
  128. log.Infof("begin execute io plan")
  129. sw := exec.NewExecutor(req.Plan, exec.Location{
  130. IsDriver: false,
  131. WorkerName: req.WorkerName,
  132. })
  133. s.svc.swWorker.Add(sw)
  134. defer s.svc.swWorker.Remove(sw)
  135. execCtx := exec.NewWithContext(ctx.Request.Context())
  136. exec.SetValueByType(execCtx, s.svc.stgPool)
  137. ret, err := sw.Run(execCtx)
  138. if err != nil {
  139. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("executing plan: %v", err)))
  140. return
  141. }
  142. ctx.JSON(http.StatusOK, OK(hubapi.ExecuteIOPlanResp{
  143. Result: ret,
  144. }))
  145. }
  146. func (s *IOService) SendVar(ctx *gin.Context) {
  147. log := logger.WithField("HTTP", "HubIO.SendVar")
  148. data, err := io.ReadAll(ctx.Request.Body)
  149. if err != nil {
  150. log.Warnf("reading body: %s", err.Error())
  151. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "reading body failed"))
  152. return
  153. }
  154. req, err := serder.JSONToObjectEx[hubapi.SendVarReq](data)
  155. if err != nil {
  156. log.Warnf("deserializing request: %s", err.Error())
  157. ctx.JSON(http.StatusOK, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  158. return
  159. }
  160. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  161. defer cancel()
  162. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  163. if sw == nil {
  164. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "plan not found"))
  165. return
  166. }
  167. sw.PutVar(req.VarID, req.VarValue)
  168. ctx.JSON(http.StatusOK, OK(nil))
  169. }
  170. func (s *IOService) GetVar(ctx *gin.Context) {
  171. log := logger.WithField("HTTP", "HubIO.GetVar")
  172. data, err := io.ReadAll(ctx.Request.Body)
  173. if err != nil {
  174. log.Warnf("reading body: %s", err.Error())
  175. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "reading body failed"))
  176. return
  177. }
  178. req, err := serder.JSONToObjectEx[hubapi.GetVarReq](data)
  179. if err != nil {
  180. log.Warnf("deserializing request: %s", err.Error())
  181. ctx.JSON(http.StatusOK, Failed(errorcode.BadArgument, "missing argument or invalid argument"))
  182. return
  183. }
  184. log = log.WithField("PlanID", req.PlanID).WithField("VarID", req.VarID)
  185. c, cancel := context.WithTimeout(ctx.Request.Context(), time.Second*30)
  186. defer cancel()
  187. sw := s.svc.swWorker.FindByIDContexted(c, req.PlanID)
  188. if sw == nil {
  189. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, "plan not found"))
  190. return
  191. }
  192. sw.PutVar(req.SignalID, req.Signal)
  193. v, err := sw.BindVar(ctx.Request.Context(), req.VarID)
  194. if err != nil {
  195. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("bind var: %v", err)))
  196. return
  197. }
  198. resp := Response{
  199. Code: errorcode.OK,
  200. Data: hubapi.GetVarResp{
  201. Value: v,
  202. },
  203. }
  204. respData, err := serder.ObjectToJSONEx(resp)
  205. if err != nil {
  206. ctx.JSON(http.StatusOK, Failed(errorcode.OperationFailed, fmt.Sprintf("serializing response: %v", err)))
  207. return
  208. }
  209. ctx.JSON(http.StatusOK, respData)
  210. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。