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

202 lines
4.9 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. package s3api
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/gorilla/mux"
  13. "github.com/chrislusf/seaweedfs/weed/filer2"
  14. "github.com/chrislusf/seaweedfs/weed/glog"
  15. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  16. )
  17. func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
  18. // https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html
  19. // collect parameters
  20. vars := mux.Vars(r)
  21. bucket := vars["bucket"]
  22. glog.V(4).Infof("read v2: %v", vars)
  23. originalPrefix, marker, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())
  24. if maxKeys < 0 {
  25. writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
  26. return
  27. }
  28. if delimiter != "" && delimiter != "/" {
  29. writeErrorResponse(w, ErrNotImplemented, r.URL)
  30. return
  31. }
  32. if marker == "" {
  33. marker = startAfter
  34. }
  35. ctx := context.Background()
  36. response, err := s3a.listFilerEntries(ctx, bucket, originalPrefix, maxKeys, marker)
  37. if err != nil {
  38. writeErrorResponse(w, ErrInternalError, r.URL)
  39. return
  40. }
  41. writeSuccessResponseXML(w, encodeResponse(response))
  42. }
  43. func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
  44. // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html
  45. // collect parameters
  46. vars := mux.Vars(r)
  47. bucket := vars["bucket"]
  48. ctx := context.Background()
  49. originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())
  50. if maxKeys < 0 {
  51. writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
  52. return
  53. }
  54. if delimiter != "" && delimiter != "/" {
  55. writeErrorResponse(w, ErrNotImplemented, r.URL)
  56. return
  57. }
  58. response, err := s3a.listFilerEntries(ctx, bucket, originalPrefix, maxKeys, marker)
  59. if err != nil {
  60. writeErrorResponse(w, ErrInternalError, r.URL)
  61. return
  62. }
  63. writeSuccessResponseXML(w, encodeResponse(response))
  64. }
  65. func (s3a *S3ApiServer) listFilerEntries(ctx context.Context, bucket, originalPrefix string, maxKeys int, marker string) (response ListBucketResult, err error) {
  66. // convert full path prefix into directory name and prefix for entry name
  67. dir, prefix := filepath.Split(originalPrefix)
  68. if strings.HasPrefix(dir, "/") {
  69. dir = dir[1:]
  70. }
  71. // check filer
  72. err = s3a.withFilerClient(ctx, func(client filer_pb.SeaweedFilerClient) error {
  73. request := &filer_pb.ListEntriesRequest{
  74. Directory: fmt.Sprintf("%s/%s/%s", s3a.option.BucketsPath, bucket, dir),
  75. Prefix: prefix,
  76. Limit: uint32(maxKeys + 1),
  77. StartFromFileName: marker,
  78. InclusiveStartFrom: false,
  79. }
  80. stream, err := client.ListEntries(ctx, request)
  81. if err != nil {
  82. return fmt.Errorf("list buckets: %v", err)
  83. }
  84. var contents []ListEntry
  85. var commonPrefixes []PrefixEntry
  86. var counter int
  87. var lastEntryName string
  88. var isTruncated bool
  89. for {
  90. resp, recvErr := stream.Recv()
  91. if recvErr != nil {
  92. if recvErr == io.EOF {
  93. break
  94. } else {
  95. return recvErr
  96. }
  97. }
  98. entry := resp.Entry
  99. counter++
  100. if counter > maxKeys {
  101. isTruncated = true
  102. break
  103. }
  104. lastEntryName = entry.Name
  105. if entry.IsDirectory {
  106. if entry.Name != ".uploads" {
  107. commonPrefixes = append(commonPrefixes, PrefixEntry{
  108. Prefix: fmt.Sprintf("%s%s/", dir, entry.Name),
  109. })
  110. }
  111. } else {
  112. contents = append(contents, ListEntry{
  113. Key: fmt.Sprintf("%s%s", dir, entry.Name),
  114. LastModified: time.Unix(entry.Attributes.Mtime, 0),
  115. ETag: "\"" + filer2.ETag(entry.Chunks) + "\"",
  116. Size: int64(filer2.TotalSize(entry.Chunks)),
  117. Owner: CanonicalUser{
  118. ID: fmt.Sprintf("%x", entry.Attributes.Uid),
  119. DisplayName: entry.Attributes.UserName,
  120. },
  121. StorageClass: "STANDARD",
  122. })
  123. }
  124. }
  125. response = ListBucketResult{
  126. Name: bucket,
  127. Prefix: originalPrefix,
  128. Marker: marker,
  129. NextMarker: lastEntryName,
  130. MaxKeys: maxKeys,
  131. Delimiter: "/",
  132. IsTruncated: isTruncated,
  133. Contents: contents,
  134. CommonPrefixes: commonPrefixes,
  135. }
  136. glog.V(4).Infof("read directory: %v, found: %v, %+v", request, counter, response)
  137. return nil
  138. })
  139. return
  140. }
  141. func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, fetchOwner bool, maxkeys int) {
  142. prefix = values.Get("prefix")
  143. token = values.Get("continuation-token")
  144. startAfter = values.Get("start-after")
  145. delimiter = values.Get("delimiter")
  146. if values.Get("max-keys") != "" {
  147. maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
  148. } else {
  149. maxkeys = maxObjectListSizeLimit
  150. }
  151. fetchOwner = values.Get("fetch-owner") == "true"
  152. return
  153. }
  154. func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int) {
  155. prefix = values.Get("prefix")
  156. marker = values.Get("marker")
  157. delimiter = values.Get("delimiter")
  158. if values.Get("max-keys") != "" {
  159. maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
  160. } else {
  161. maxkeys = maxObjectListSizeLimit
  162. }
  163. return
  164. }