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.

182 lines
4.6 KiB

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