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.

dfstore.go 7.8 kB

2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package dfstore
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "github.com/go-http-utils/headers"
  7. "io"
  8. "net/http"
  9. "net/url"
  10. "path"
  11. "strconv"
  12. "code.gitea.io/gitea/modules/urfs_client/config"
  13. pkgobjectstorage "code.gitea.io/gitea/modules/urfs_client/objectstorage"
  14. )
  15. // Dfstore is the interface used for object storage.
  16. type Dfstore interface {
  17. // GetUrfsMetadataRequestWithContext returns *http.Request of getting Urfs metadata.
  18. GetUrfsMetadataRequestWithContext(ctx context.Context, input *GetUrfsMetadataInput) (*http.Request, error)
  19. // GetUrfsMetadataWithContext returns matedata of Urfs.
  20. GetUrfsMetadataWithContext(ctx context.Context, input *GetUrfsMetadataInput) (*pkgobjectstorage.ObjectMetadata, error)
  21. // GetUrfsRequestWithContext returns *http.Request of getting Urfs.
  22. GetUrfsRequestWithContext(ctx context.Context, input *GetUrfsInput) (*http.Request, error)
  23. // GetUrfsWithContext returns data of Urfs.
  24. GetUrfsWithContext(ctx context.Context, input *GetUrfsInput) (io.ReadCloser, error)
  25. // GetUrfsStatusRequestWithContext returns *http.Request of getting Urfs status.
  26. GetUrfsStatusRequestWithContext(ctx context.Context, input *GetUrfsInput) (*http.Request, error)
  27. // GetUrfsStatusWithContext returns schedule status of Urfs.
  28. GetUrfsStatusWithContext(ctx context.Context, input *GetUrfsInput) (io.ReadCloser, error)
  29. }
  30. // dfstore provides object storage function.
  31. type dfstore struct {
  32. endpoint string
  33. httpClient *http.Client
  34. }
  35. // Option is a functional option for configuring the dfstore.
  36. type Option func(dfs *dfstore)
  37. // New dfstore instance.
  38. func New(endpoint string, options ...Option) Dfstore {
  39. dfs := &dfstore{
  40. endpoint: endpoint,
  41. httpClient: http.DefaultClient,
  42. }
  43. for _, opt := range options {
  44. opt(dfs)
  45. }
  46. return dfs
  47. }
  48. // GetUrfsMetadataInput is used to construct request of getting object metadata.
  49. type GetUrfsMetadataInput struct {
  50. // Endpoint is endpoint name.
  51. Endpoint string
  52. // BucketName is bucket name.
  53. BucketName string
  54. // ObjectKey is object key.
  55. ObjectKey string
  56. // DstPeer is target peerHost.
  57. DstPeer string
  58. }
  59. // Validate validates GetUrfsMetadataInput fields.
  60. func (i *GetUrfsMetadataInput) Validate() error {
  61. if i.Endpoint == "" {
  62. return errors.New("invalid Endpoint")
  63. }
  64. if i.BucketName == "" {
  65. return errors.New("invalid BucketName")
  66. }
  67. if i.ObjectKey == "" {
  68. return errors.New("invalid ObjectKey")
  69. }
  70. return nil
  71. }
  72. // GetObjectMetadataRequestWithContext returns *http.Request of getting object metadata.
  73. func (dfs *dfstore) GetUrfsMetadataRequestWithContext(ctx context.Context, input *GetUrfsMetadataInput) (*http.Request, error) {
  74. if err := input.Validate(); err != nil {
  75. return nil, err
  76. }
  77. dstUrl := url.URL{
  78. Scheme: "http",
  79. Host: fmt.Sprintf("%s:%d", input.DstPeer, config.DefaultObjectStorageStartPort),
  80. }
  81. u, err := url.Parse(dstUrl.String())
  82. if err != nil {
  83. return nil, err
  84. }
  85. u.Path = path.Join("buckets", input.BucketName+"."+input.Endpoint, "objects", input.ObjectKey)
  86. req, err := http.NewRequestWithContext(ctx, http.MethodHead, u.String(), nil)
  87. if err != nil {
  88. return nil, err
  89. }
  90. return req, nil
  91. }
  92. // GetObjectMetadataWithContext returns metadata of object.
  93. func (dfs *dfstore) GetUrfsMetadataWithContext(ctx context.Context, input *GetUrfsMetadataInput) (*pkgobjectstorage.ObjectMetadata, error) {
  94. req, err := dfs.GetUrfsMetadataRequestWithContext(ctx, input)
  95. if err != nil {
  96. return nil, err
  97. }
  98. resp, err := dfs.httpClient.Do(req)
  99. if err != nil {
  100. return nil, err
  101. }
  102. defer resp.Body.Close()
  103. if resp.StatusCode/100 != 2 {
  104. return nil, fmt.Errorf("bad response status %s", resp.Status)
  105. }
  106. contentLength, err := strconv.ParseInt(resp.Header.Get(headers.ContentLength), 10, 64)
  107. if err != nil {
  108. return nil, err
  109. }
  110. return &pkgobjectstorage.ObjectMetadata{
  111. ContentDisposition: resp.Header.Get(headers.ContentDisposition),
  112. ContentEncoding: resp.Header.Get(headers.ContentEncoding),
  113. ContentLanguage: resp.Header.Get(headers.ContentLanguage),
  114. ContentLength: int64(contentLength),
  115. ContentType: resp.Header.Get(headers.ContentType),
  116. ETag: resp.Header.Get(headers.ContentType),
  117. Digest: resp.Header.Get(config.HeaderDragonflyObjectMetaDigest),
  118. }, nil
  119. }
  120. // GetUrfsInput is used to construct request of getting object.
  121. type GetUrfsInput struct {
  122. // Endpoint is endpoint name.
  123. Endpoint string
  124. // BucketName is bucket name.
  125. BucketName string
  126. // ObjectKey is object key.
  127. ObjectKey string
  128. // Filter is used to generate a unique Task ID by
  129. // filtering unnecessary query params in the URL,
  130. // it is separated by & character.
  131. Filter string
  132. // Range is the HTTP range header.
  133. Range string
  134. // DstPeer is target peerHost.
  135. DstPeer string
  136. }
  137. // GetObjectWithContext returns data of object.
  138. func (dfs *dfstore) GetUrfsWithContext(ctx context.Context, input *GetUrfsInput) (io.ReadCloser, error) {
  139. req, err := dfs.GetUrfsRequestWithContext(ctx, input)
  140. if err != nil {
  141. return nil, err
  142. }
  143. resp, err := dfs.httpClient.Do(req)
  144. if err != nil {
  145. return nil, err
  146. }
  147. if resp.StatusCode/100 != 2 {
  148. return nil, fmt.Errorf("bad response status %s", resp.Status)
  149. }
  150. return resp.Body, nil
  151. }
  152. // GetObjectRequestWithContext returns *http.Request of getting object.
  153. func (dfs *dfstore) GetUrfsRequestWithContext(ctx context.Context, input *GetUrfsInput) (*http.Request, error) {
  154. if err := input.Validate(); err != nil {
  155. return nil, err
  156. }
  157. dstUrl := url.URL{
  158. Scheme: "http",
  159. Host: fmt.Sprintf("%s:%d", input.DstPeer, config.DefaultObjectStorageStartPort),
  160. }
  161. u, err := url.Parse(dstUrl.String())
  162. if err != nil {
  163. return nil, err
  164. }
  165. u.Path = path.Join("buckets", input.BucketName+"."+input.Endpoint, "cache_object", input.ObjectKey)
  166. query := u.Query()
  167. if input.Filter != "" {
  168. query.Set("filter", input.Filter)
  169. }
  170. u.RawQuery = query.Encode()
  171. req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), nil)
  172. if err != nil {
  173. return nil, err
  174. }
  175. if input.Range != "" {
  176. req.Header.Set(headers.Range, input.Range)
  177. }
  178. return req, nil
  179. }
  180. // Validate validates GetUrfsInput fields.
  181. func (i *GetUrfsInput) Validate() error {
  182. if i.Endpoint == "" {
  183. return errors.New("invalid Endpoint")
  184. }
  185. if i.BucketName == "" {
  186. return errors.New("invalid BucketName")
  187. }
  188. if i.ObjectKey == "" {
  189. return errors.New("invalid ObjectKey")
  190. }
  191. return nil
  192. }
  193. // GetUrfsStatusWithContext returns schedule task status.
  194. func (dfs *dfstore) GetUrfsStatusWithContext(ctx context.Context, input *GetUrfsInput) (io.ReadCloser, error) {
  195. req, err := dfs.GetUrfsStatusRequestWithContext(ctx, input)
  196. if err != nil {
  197. return nil, err
  198. }
  199. resp, err := dfs.httpClient.Do(req)
  200. if err != nil {
  201. return nil, err
  202. }
  203. if resp.StatusCode/100 != 2 {
  204. return nil, fmt.Errorf("bad response status %s", resp.Status)
  205. }
  206. return resp.Body, nil
  207. }
  208. // GetObjectStatusRequestWithContext returns *http.Request of check schedule task status.
  209. func (dfs *dfstore) GetUrfsStatusRequestWithContext(ctx context.Context, input *GetUrfsInput) (*http.Request, error) {
  210. if err := input.Validate(); err != nil {
  211. return nil, err
  212. }
  213. dstUrl := url.URL{
  214. Scheme: "http",
  215. Host: fmt.Sprintf("%s:%d", input.DstPeer, config.DefaultObjectStorageStartPort),
  216. }
  217. u, err := url.Parse(dstUrl.String())
  218. if err != nil {
  219. return nil, err
  220. }
  221. u.Path = path.Join("buckets", input.BucketName+"."+input.Endpoint, "check_object", input.ObjectKey)
  222. query := u.Query()
  223. if input.Filter != "" {
  224. query.Set("filter", input.Filter)
  225. }
  226. u.RawQuery = query.Encode()
  227. req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
  228. if err != nil {
  229. return nil, err
  230. }
  231. if input.Range != "" {
  232. req.Header.Set(headers.Range, input.Range)
  233. }
  234. return req, nil
  235. }