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.

349 lines
10 KiB

3 years ago
6 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
2 years ago
  1. package s3api
  2. import (
  3. "crypto/sha1"
  4. "encoding/xml"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. "github.com/google/uuid"
  12. "github.com/seaweedfs/seaweedfs/weed/glog"
  13. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  14. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  15. weed_server "github.com/seaweedfs/seaweedfs/weed/server"
  16. "github.com/aws/aws-sdk-go/aws"
  17. "github.com/aws/aws-sdk-go/service/s3"
  18. )
  19. const (
  20. maxObjectListSizeLimit = 10000 // Limit number of objects in a listObjectsResponse.
  21. maxUploadsList = 10000 // Limit number of uploads in a listUploadsResponse.
  22. maxPartsList = 10000 // Limit number of parts in a listPartsResponse.
  23. globalMaxPartID = 100000
  24. )
  25. // NewMultipartUploadHandler - New multipart upload.
  26. func (s3a *S3ApiServer) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
  27. bucket, object := s3_constants.GetBucketAndObject(r)
  28. createMultipartUploadInput := &s3.CreateMultipartUploadInput{
  29. Bucket: aws.String(bucket),
  30. Key: objectKey(aws.String(object)),
  31. Metadata: make(map[string]*string),
  32. }
  33. metadata := weed_server.SaveAmzMetaData(r, nil, false)
  34. for k, v := range metadata {
  35. createMultipartUploadInput.Metadata[k] = aws.String(string(v))
  36. }
  37. contentType := r.Header.Get("Content-Type")
  38. if contentType != "" {
  39. createMultipartUploadInput.ContentType = &contentType
  40. }
  41. response, errCode := s3a.createMultipartUpload(createMultipartUploadInput)
  42. glog.V(2).Info("NewMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)), errCode)
  43. if errCode != s3err.ErrNone {
  44. s3err.WriteErrorResponse(w, r, errCode)
  45. return
  46. }
  47. writeSuccessResponseXML(w, r, response)
  48. }
  49. // CompleteMultipartUploadHandler - Completes multipart upload.
  50. func (s3a *S3ApiServer) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
  51. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html
  52. bucket, object := s3_constants.GetBucketAndObject(r)
  53. parts := &CompleteMultipartUpload{}
  54. if err := xmlDecoder(r.Body, parts, r.ContentLength); err != nil {
  55. s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
  56. return
  57. }
  58. // Get upload id.
  59. uploadID, _, _, _ := getObjectResources(r.URL.Query())
  60. err := s3a.checkUploadId(object, uploadID)
  61. if err != nil {
  62. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchUpload)
  63. return
  64. }
  65. response, errCode := s3a.completeMultipartUpload(&s3.CompleteMultipartUploadInput{
  66. Bucket: aws.String(bucket),
  67. Key: objectKey(aws.String(object)),
  68. UploadId: aws.String(uploadID),
  69. }, parts)
  70. glog.V(2).Info("CompleteMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)), errCode)
  71. if errCode != s3err.ErrNone {
  72. s3err.WriteErrorResponse(w, r, errCode)
  73. return
  74. }
  75. writeSuccessResponseXML(w, r, response)
  76. }
  77. // AbortMultipartUploadHandler - Aborts multipart upload.
  78. func (s3a *S3ApiServer) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
  79. bucket, object := s3_constants.GetBucketAndObject(r)
  80. // Get upload id.
  81. uploadID, _, _, _ := getObjectResources(r.URL.Query())
  82. err := s3a.checkUploadId(object, uploadID)
  83. if err != nil {
  84. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchUpload)
  85. return
  86. }
  87. response, errCode := s3a.abortMultipartUpload(&s3.AbortMultipartUploadInput{
  88. Bucket: aws.String(bucket),
  89. Key: objectKey(aws.String(object)),
  90. UploadId: aws.String(uploadID),
  91. })
  92. if errCode != s3err.ErrNone {
  93. s3err.WriteErrorResponse(w, r, errCode)
  94. return
  95. }
  96. glog.V(2).Info("AbortMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)))
  97. //https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html
  98. s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
  99. s3err.PostLog(r, http.StatusNoContent, s3err.ErrNone)
  100. }
  101. // ListMultipartUploadsHandler - Lists multipart uploads.
  102. func (s3a *S3ApiServer) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
  103. bucket, _ := s3_constants.GetBucketAndObject(r)
  104. prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType := getBucketMultipartResources(r.URL.Query())
  105. if maxUploads < 0 {
  106. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxUploads)
  107. return
  108. }
  109. if keyMarker != "" {
  110. // Marker not common with prefix is not implemented.
  111. if !strings.HasPrefix(keyMarker, prefix) {
  112. s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
  113. return
  114. }
  115. }
  116. response, errCode := s3a.listMultipartUploads(&s3.ListMultipartUploadsInput{
  117. Bucket: aws.String(bucket),
  118. Delimiter: aws.String(delimiter),
  119. EncodingType: aws.String(encodingType),
  120. KeyMarker: aws.String(keyMarker),
  121. MaxUploads: aws.Int64(int64(maxUploads)),
  122. Prefix: aws.String(prefix),
  123. UploadIdMarker: aws.String(uploadIDMarker),
  124. })
  125. glog.V(2).Infof("ListMultipartUploadsHandler %s errCode=%d", string(s3err.EncodeXMLResponse(response)), errCode)
  126. if errCode != s3err.ErrNone {
  127. s3err.WriteErrorResponse(w, r, errCode)
  128. return
  129. }
  130. // TODO handle encodingType
  131. writeSuccessResponseXML(w, r, response)
  132. }
  133. // ListObjectPartsHandler - Lists object parts in a multipart upload.
  134. func (s3a *S3ApiServer) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
  135. bucket, object := s3_constants.GetBucketAndObject(r)
  136. uploadID, partNumberMarker, maxParts, _ := getObjectResources(r.URL.Query())
  137. if partNumberMarker < 0 {
  138. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidPartNumberMarker)
  139. return
  140. }
  141. if maxParts < 0 {
  142. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxParts)
  143. return
  144. }
  145. err := s3a.checkUploadId(object, uploadID)
  146. if err != nil {
  147. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchUpload)
  148. return
  149. }
  150. response, errCode := s3a.listObjectParts(&s3.ListPartsInput{
  151. Bucket: aws.String(bucket),
  152. Key: objectKey(aws.String(object)),
  153. MaxParts: aws.Int64(int64(maxParts)),
  154. PartNumberMarker: aws.Int64(int64(partNumberMarker)),
  155. UploadId: aws.String(uploadID),
  156. })
  157. if errCode != s3err.ErrNone {
  158. s3err.WriteErrorResponse(w, r, errCode)
  159. return
  160. }
  161. glog.V(2).Infof("ListObjectPartsHandler %s count=%d", string(s3err.EncodeXMLResponse(response)), len(response.Part))
  162. writeSuccessResponseXML(w, r, response)
  163. }
  164. // PutObjectPartHandler - Put an object part in a multipart upload.
  165. func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
  166. bucket, object := s3_constants.GetBucketAndObject(r)
  167. uploadID := r.URL.Query().Get("uploadId")
  168. err := s3a.checkUploadId(object, uploadID)
  169. if err != nil {
  170. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchUpload)
  171. return
  172. }
  173. partIDString := r.URL.Query().Get("partNumber")
  174. partID, err := strconv.Atoi(partIDString)
  175. if err != nil {
  176. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidPart)
  177. return
  178. }
  179. if partID > globalMaxPartID {
  180. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidMaxParts)
  181. return
  182. }
  183. dataReader := r.Body
  184. if s3a.iam.isEnabled() {
  185. rAuthType := getRequestAuthType(r)
  186. var s3ErrCode s3err.ErrorCode
  187. switch rAuthType {
  188. case authTypeStreamingSigned:
  189. dataReader, s3ErrCode = s3a.iam.newSignV4ChunkedReader(r)
  190. case authTypeSignedV2, authTypePresignedV2:
  191. _, s3ErrCode = s3a.iam.isReqAuthenticatedV2(r)
  192. case authTypePresigned, authTypeSigned:
  193. _, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
  194. }
  195. if s3ErrCode != s3err.ErrNone {
  196. s3err.WriteErrorResponse(w, r, s3ErrCode)
  197. return
  198. }
  199. }
  200. defer dataReader.Close()
  201. glog.V(2).Infof("PutObjectPartHandler %s %s %04d", bucket, uploadID, partID)
  202. uploadUrl := s3a.genPartUploadUrl(bucket, uploadID, partID)
  203. if partID == 1 && r.Header.Get("Content-Type") == "" {
  204. dataReader = mimeDetect(r, dataReader)
  205. }
  206. destination := fmt.Sprintf("%s/%s%s", s3a.option.BucketsPath, bucket, object)
  207. etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader, destination, bucket)
  208. if errCode != s3err.ErrNone {
  209. s3err.WriteErrorResponse(w, r, errCode)
  210. return
  211. }
  212. setEtag(w, etag)
  213. writeSuccessResponseEmpty(w, r)
  214. }
  215. func (s3a *S3ApiServer) genUploadsFolder(bucket string) string {
  216. return fmt.Sprintf("%s/%s/%s", s3a.option.BucketsPath, bucket, s3_constants.MultipartUploadsFolder)
  217. }
  218. func (s3a *S3ApiServer) genPartUploadUrl(bucket, uploadID string, partID int) string {
  219. return fmt.Sprintf("http://%s%s/%s/%04d_%s.part",
  220. s3a.option.Filer.ToHttpAddress(), s3a.genUploadsFolder(bucket), uploadID, partID, uuid.NewString())
  221. }
  222. // Generate uploadID hash string from object
  223. func (s3a *S3ApiServer) generateUploadID(object string) string {
  224. if strings.HasPrefix(object, "/") {
  225. object = object[1:]
  226. }
  227. h := sha1.New()
  228. h.Write([]byte(object))
  229. return fmt.Sprintf("%x", h.Sum(nil))
  230. }
  231. // Check object name and uploadID when processing multipart uploading
  232. func (s3a *S3ApiServer) checkUploadId(object string, id string) error {
  233. hash := s3a.generateUploadID(object)
  234. if !strings.HasPrefix(id, hash) {
  235. glog.Errorf("object %s and uploadID %s are not matched", object, id)
  236. return fmt.Errorf("object %s and uploadID %s are not matched", object, id)
  237. }
  238. return nil
  239. }
  240. // Parse bucket url queries for ?uploads
  241. func getBucketMultipartResources(values url.Values) (prefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int, encodingType string) {
  242. prefix = values.Get("prefix")
  243. keyMarker = values.Get("key-marker")
  244. uploadIDMarker = values.Get("upload-id-marker")
  245. delimiter = values.Get("delimiter")
  246. if values.Get("max-uploads") != "" {
  247. maxUploads, _ = strconv.Atoi(values.Get("max-uploads"))
  248. } else {
  249. maxUploads = maxUploadsList
  250. }
  251. encodingType = values.Get("encoding-type")
  252. return
  253. }
  254. // Parse object url queries
  255. func getObjectResources(values url.Values) (uploadID string, partNumberMarker, maxParts int, encodingType string) {
  256. uploadID = values.Get("uploadId")
  257. partNumberMarker, _ = strconv.Atoi(values.Get("part-number-marker"))
  258. if values.Get("max-parts") != "" {
  259. maxParts, _ = strconv.Atoi(values.Get("max-parts"))
  260. } else {
  261. maxParts = maxPartsList
  262. }
  263. encodingType = values.Get("encoding-type")
  264. return
  265. }
  266. func xmlDecoder(body io.Reader, v interface{}, size int64) error {
  267. var lbody io.Reader
  268. if size > 0 {
  269. lbody = io.LimitReader(body, size)
  270. } else {
  271. lbody = body
  272. }
  273. d := xml.NewDecoder(lbody)
  274. d.CharsetReader = func(label string, input io.Reader) (io.Reader, error) {
  275. return input, nil
  276. }
  277. return d.Decode(v)
  278. }
  279. type CompleteMultipartUpload struct {
  280. Parts []CompletedPart `xml:"Part"`
  281. }
  282. type CompletedPart struct {
  283. ETag string
  284. PartNumber int
  285. }