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.

utils.go 7.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. package rpc
  2. import (
  3. "fmt"
  4. "io"
  5. "gitlink.org.cn/cloudream/common/consts/errorcode"
  6. "gitlink.org.cn/cloudream/common/utils/io2"
  7. "gitlink.org.cn/cloudream/common/utils/serder"
  8. "golang.org/x/net/context"
  9. "google.golang.org/grpc"
  10. "google.golang.org/grpc/codes"
  11. "google.golang.org/grpc/status"
  12. )
  13. func UnaryClient[Resp, Req any](apiFn func(context.Context, *Request, ...grpc.CallOption) (*Response, error), ctx context.Context, req Req) (Resp, *CodeError) {
  14. data, err := serder.ObjectToJSONEx(req)
  15. if err != nil {
  16. var resp Resp
  17. return resp, Failed(errorcode.OperationFailed, err.Error())
  18. }
  19. resp, err := apiFn(ctx, &Request{
  20. Payload: data,
  21. })
  22. if err != nil {
  23. var resp Resp
  24. return resp, getCodeError(err)
  25. }
  26. ret, err := serder.JSONToObjectEx[Resp](resp.Payload)
  27. if err != nil {
  28. return ret, Failed(errorcode.OperationFailed, err.Error())
  29. }
  30. return ret, nil
  31. }
  32. func UnaryServer[Resp, Req any](apiFn func(context.Context, Req) (Resp, *CodeError), ctx context.Context, req *Request) (*Response, error) {
  33. rreq, err := serder.JSONToObjectEx[Req](req.Payload)
  34. if err != nil {
  35. return nil, makeCodeError(errorcode.OperationFailed, err.Error())
  36. }
  37. ret, cerr := apiFn(ctx, rreq)
  38. if cerr != nil {
  39. return nil, wrapCodeError(cerr)
  40. }
  41. data, err := serder.ObjectToJSONEx(ret)
  42. if err != nil {
  43. return nil, makeCodeError(errorcode.OperationFailed, err.Error())
  44. }
  45. return &Response{
  46. Payload: data,
  47. }, nil
  48. }
  49. type UploadStreamAPIClient interface {
  50. GRPCChunkedWriter
  51. CloseAndRecv() (*Response, error)
  52. }
  53. type UploadStreamAPIServer interface {
  54. GRPCChunkedReader
  55. SendAndClose(*Response) error
  56. Context() context.Context
  57. }
  58. type UploadStreamReq interface {
  59. GetStream() io.Reader
  60. SetStream(io.Reader)
  61. }
  62. // 封装了上传流API的客户端逻辑。记得将Req里的Stream字段设置为不需要序列化(json:"-")
  63. func UploadStreamClient[Resp any, Req UploadStreamReq, APIRet UploadStreamAPIClient](apiFn func(context.Context, ...grpc.CallOption) (APIRet, error), ctx context.Context, req Req) (Resp, *CodeError) {
  64. stream := req.GetStream()
  65. var ret Resp
  66. data, err := serder.ObjectToJSONEx(req)
  67. if err != nil {
  68. return ret, Failed(errorcode.OperationFailed, err.Error())
  69. }
  70. ctx2, cancelFn := context.WithCancel(ctx)
  71. defer cancelFn()
  72. cli, err := apiFn(ctx2)
  73. if err != nil {
  74. return ret, getCodeError(err)
  75. }
  76. cw := NewChunkedWriter(cli)
  77. err = cw.WriteDataPart("", data)
  78. if err != nil {
  79. return ret, Failed(errorcode.OperationFailed, err.Error())
  80. }
  81. _, err = cw.WriteStreamPart("", stream)
  82. if err != nil {
  83. return ret, Failed(errorcode.OperationFailed, err.Error())
  84. }
  85. err = cw.Finish()
  86. if err != nil {
  87. return ret, Failed(errorcode.OperationFailed, err.Error())
  88. }
  89. resp, err := cli.CloseAndRecv()
  90. if err != nil {
  91. return ret, Failed(errorcode.OperationFailed, err.Error())
  92. }
  93. ret, err = serder.JSONToObjectEx[Resp](resp.Payload)
  94. if err != nil {
  95. return ret, Failed(errorcode.OperationFailed, err.Error())
  96. }
  97. return ret, nil
  98. }
  99. func UploadStreamServer[Resp any, Req UploadStreamReq, APIRet UploadStreamAPIServer](apiFn func(context.Context, Req) (Resp, *CodeError), req APIRet) error {
  100. cr := NewChunkedReader(req)
  101. _, data, err := cr.NextDataPart()
  102. if err != nil {
  103. return makeCodeError(errorcode.OperationFailed, err.Error())
  104. }
  105. _, pr, err := cr.NextPart()
  106. if err != nil {
  107. return makeCodeError(errorcode.OperationFailed, err.Error())
  108. }
  109. rreq, err := serder.JSONToObjectEx[Req](data)
  110. if err != nil {
  111. return makeCodeError(errorcode.OperationFailed, err.Error())
  112. }
  113. rreq.SetStream(pr)
  114. resp, cerr := apiFn(req.Context(), rreq)
  115. if cerr != nil {
  116. return wrapCodeError(cerr)
  117. }
  118. respData, err := serder.ObjectToJSONEx(resp)
  119. if err != nil {
  120. return makeCodeError(errorcode.OperationFailed, err.Error())
  121. }
  122. err = req.SendAndClose(&Response{Payload: respData})
  123. if err != nil {
  124. return makeCodeError(errorcode.OperationFailed, err.Error())
  125. }
  126. return nil
  127. }
  128. type DownloadStreamAPIClient interface {
  129. GRPCChunkedReader
  130. }
  131. type DownloadStreamAPIServer interface {
  132. GRPCChunkedWriter
  133. Context() context.Context
  134. }
  135. type DownloadStreamResp interface {
  136. GetStream() io.ReadCloser
  137. SetStream(io.ReadCloser)
  138. }
  139. // 封装了下载流API的客户端逻辑。记得将Resp里的Stream字段设置为不需要序列化(json:"-")
  140. func DownloadStreamClient[Resp DownloadStreamResp, Req any, APIRet DownloadStreamAPIClient](apiFn func(context.Context, *Request, ...grpc.CallOption) (APIRet, error), ctx context.Context, req Req) (Resp, *CodeError) {
  141. var ret Resp
  142. data, err := serder.ObjectToJSONEx(req)
  143. if err != nil {
  144. return ret, Failed(errorcode.OperationFailed, err.Error())
  145. }
  146. ctx2, cancelFn := context.WithCancel(ctx)
  147. cli, err := apiFn(ctx2, &Request{Payload: data})
  148. if err != nil {
  149. cancelFn()
  150. return ret, getCodeError(err)
  151. }
  152. cr := NewChunkedReader(cli)
  153. _, data, err = cr.NextDataPart()
  154. if err != nil {
  155. cancelFn()
  156. return ret, Failed(errorcode.OperationFailed, err.Error())
  157. }
  158. resp, err := serder.JSONToObjectEx[Resp](data)
  159. if err != nil {
  160. cancelFn()
  161. return ret, Failed(errorcode.OperationFailed, err.Error())
  162. }
  163. _, pr, err := cr.NextPart()
  164. if err != nil {
  165. cancelFn()
  166. return ret, Failed(errorcode.OperationFailed, err.Error())
  167. }
  168. resp.SetStream(io2.DelegateReadCloser(pr, func() error {
  169. cancelFn()
  170. return nil
  171. }))
  172. return resp, nil
  173. }
  174. func DownloadStreamServer[Resp DownloadStreamResp, Req any, APIRet DownloadStreamAPIServer](apiFn func(context.Context, Req) (Resp, *CodeError), req *Request, ret APIRet) error {
  175. rreq, err := serder.JSONToObjectEx[Req](req.Payload)
  176. if err != nil {
  177. return makeCodeError(errorcode.OperationFailed, err.Error())
  178. }
  179. resp, cerr := apiFn(ret.Context(), rreq)
  180. if cerr != nil {
  181. return wrapCodeError(cerr)
  182. }
  183. cw := NewChunkedWriter(ret)
  184. data, err := serder.ObjectToJSONEx(resp)
  185. if err != nil {
  186. return makeCodeError(errorcode.OperationFailed, err.Error())
  187. }
  188. err = cw.WriteDataPart("", data)
  189. if err != nil {
  190. return makeCodeError(errorcode.OperationFailed, err.Error())
  191. }
  192. _, err = cw.WriteStreamPart("", resp.GetStream())
  193. if err != nil {
  194. return makeCodeError(errorcode.OperationFailed, err.Error())
  195. }
  196. err = cw.Finish()
  197. if err != nil {
  198. return makeCodeError(errorcode.OperationFailed, err.Error())
  199. }
  200. return nil
  201. }
  202. func Failed(errCode string, format string, args ...any) *CodeError {
  203. return &CodeError{
  204. Code: errCode,
  205. Message: fmt.Sprintf(format, args...),
  206. }
  207. }
  208. // 定义一个额外的结构体,防止陷入 (*CodeError)(nil) != nil 的陷阱
  209. type ErrorCodeError struct {
  210. CE *CodeError
  211. }
  212. func (c *ErrorCodeError) Error() string {
  213. return fmt.Sprintf("code: %s, message: %s", c.CE.Code, c.CE.Message)
  214. }
  215. func (c *CodeError) ToError() error {
  216. if c == nil {
  217. return nil
  218. }
  219. return &ErrorCodeError{CE: c}
  220. }
  221. func getCodeError(err error) *CodeError {
  222. status, ok := status.FromError(err)
  223. if ok {
  224. dts := status.Details()
  225. if len(dts) > 0 {
  226. ce, ok := dts[0].(*CodeError)
  227. if ok {
  228. return ce
  229. }
  230. }
  231. }
  232. return Failed(errorcode.OperationFailed, err.Error())
  233. }
  234. func makeCodeError(code string, msg string) error {
  235. ce, _ := status.New(codes.Unknown, "custom error").WithDetails(Failed(code, msg))
  236. return ce.Err()
  237. }
  238. func wrapCodeError(ce *CodeError) error {
  239. e, _ := status.New(codes.Unknown, "custom error").WithDetails(ce)
  240. return e.Err()
  241. }

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