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.

351 lines
10 KiB

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