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.

207 lines
5.7 KiB

8 months ago
  1. package s3api
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/pquerna/cachecontrol/cacheobject"
  11. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  12. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  13. "github.com/seaweedfs/seaweedfs/weed/security"
  14. "github.com/seaweedfs/seaweedfs/weed/glog"
  15. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  16. weed_server "github.com/seaweedfs/seaweedfs/weed/server"
  17. )
  18. func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
  19. // http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html
  20. bucket, object := s3_constants.GetBucketAndObject(r)
  21. glog.V(3).Infof("PutObjectHandler %s %s", bucket, object)
  22. _, err := validateContentMd5(r.Header)
  23. if err != nil {
  24. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidDigest)
  25. return
  26. }
  27. if r.Header.Get("Cache-Control") != "" {
  28. if _, err = cacheobject.ParseRequestCacheControl(r.Header.Get("Cache-Control")); err != nil {
  29. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidDigest)
  30. return
  31. }
  32. }
  33. if r.Header.Get("Expires") != "" {
  34. if _, err = time.Parse(http.TimeFormat, r.Header.Get("Expires")); err != nil {
  35. s3err.WriteErrorResponse(w, r, s3err.ErrMalformedDate)
  36. return
  37. }
  38. }
  39. dataReader := r.Body
  40. rAuthType := getRequestAuthType(r)
  41. if s3a.iam.isEnabled() {
  42. var s3ErrCode s3err.ErrorCode
  43. switch rAuthType {
  44. case authTypeStreamingSigned:
  45. dataReader, s3ErrCode = s3a.iam.newSignV4ChunkedReader(r)
  46. case authTypeSignedV2, authTypePresignedV2:
  47. _, s3ErrCode = s3a.iam.isReqAuthenticatedV2(r)
  48. case authTypePresigned, authTypeSigned:
  49. _, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
  50. }
  51. if s3ErrCode != s3err.ErrNone {
  52. s3err.WriteErrorResponse(w, r, s3ErrCode)
  53. return
  54. }
  55. } else {
  56. if authTypeStreamingSigned == rAuthType {
  57. s3err.WriteErrorResponse(w, r, s3err.ErrAuthNotSetup)
  58. return
  59. }
  60. }
  61. defer dataReader.Close()
  62. objectContentType := r.Header.Get("Content-Type")
  63. if strings.HasSuffix(object, "/") && r.ContentLength <= 1024 {
  64. if err := s3a.mkdir(
  65. s3a.option.BucketsPath, bucket+strings.TrimSuffix(object, "/"),
  66. func(entry *filer_pb.Entry) {
  67. if objectContentType == "" {
  68. objectContentType = s3_constants.FolderMimeType
  69. }
  70. if r.ContentLength > 0 {
  71. entry.Content, _ = io.ReadAll(r.Body)
  72. }
  73. entry.Attributes.Mime = objectContentType
  74. }); err != nil {
  75. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  76. return
  77. }
  78. } else {
  79. uploadUrl := s3a.toFilerUrl(bucket, object)
  80. if objectContentType == "" {
  81. dataReader = mimeDetect(r, dataReader)
  82. }
  83. etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader, "", bucket)
  84. if errCode != s3err.ErrNone {
  85. s3err.WriteErrorResponse(w, r, errCode)
  86. return
  87. }
  88. setEtag(w, etag)
  89. }
  90. writeSuccessResponseEmpty(w, r)
  91. }
  92. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.Reader, destination string, bucket string) (etag string, code s3err.ErrorCode) {
  93. hash := md5.New()
  94. var body = io.TeeReader(dataReader, hash)
  95. proxyReq, err := http.NewRequest("PUT", uploadUrl, body)
  96. if err != nil {
  97. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  98. return "", s3err.ErrInternalError
  99. }
  100. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  101. if destination != "" {
  102. proxyReq.Header.Set(s3_constants.SeaweedStorageDestinationHeader, destination)
  103. }
  104. if s3a.option.FilerGroup != "" {
  105. query := proxyReq.URL.Query()
  106. query.Add("collection", s3a.getCollectionName(bucket))
  107. proxyReq.URL.RawQuery = query.Encode()
  108. }
  109. for header, values := range r.Header {
  110. for _, value := range values {
  111. proxyReq.Header.Add(header, value)
  112. }
  113. }
  114. // ensure that the Authorization header is overriding any previous
  115. // Authorization header which might be already present in proxyReq
  116. s3a.maybeAddFilerJwtAuthorization(proxyReq, true)
  117. resp, postErr := s3a.client.Do(proxyReq)
  118. if postErr != nil {
  119. glog.Errorf("post to filer: %v", postErr)
  120. return "", s3err.ErrInternalError
  121. }
  122. defer resp.Body.Close()
  123. etag = fmt.Sprintf("%x", hash.Sum(nil))
  124. resp_body, ra_err := io.ReadAll(resp.Body)
  125. if ra_err != nil {
  126. glog.Errorf("upload to filer response read %d: %v", resp.StatusCode, ra_err)
  127. return etag, s3err.ErrInternalError
  128. }
  129. var ret weed_server.FilerPostResult
  130. unmarshal_err := json.Unmarshal(resp_body, &ret)
  131. if unmarshal_err != nil {
  132. glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
  133. return "", s3err.ErrInternalError
  134. }
  135. if ret.Error != "" {
  136. glog.Errorf("upload to filer error: %v", ret.Error)
  137. return "", filerErrorToS3Error(ret.Error)
  138. }
  139. return etag, s3err.ErrNone
  140. }
  141. func setEtag(w http.ResponseWriter, etag string) {
  142. if etag != "" {
  143. if strings.HasPrefix(etag, "\"") {
  144. w.Header()["ETag"] = []string{etag}
  145. } else {
  146. w.Header()["ETag"] = []string{"\"" + etag + "\""}
  147. }
  148. }
  149. }
  150. func filerErrorToS3Error(errString string) s3err.ErrorCode {
  151. switch {
  152. case strings.HasPrefix(errString, "existing ") && strings.HasSuffix(errString, "is a directory"):
  153. return s3err.ErrExistingObjectIsDirectory
  154. case strings.HasSuffix(errString, "is a file"):
  155. return s3err.ErrExistingObjectIsFile
  156. default:
  157. return s3err.ErrInternalError
  158. }
  159. }
  160. func (s3a *S3ApiServer) maybeAddFilerJwtAuthorization(r *http.Request, isWrite bool) {
  161. encodedJwt := s3a.maybeGetFilerJwtAuthorizationToken(isWrite)
  162. if encodedJwt == "" {
  163. return
  164. }
  165. r.Header.Set("Authorization", "BEARER "+string(encodedJwt))
  166. }
  167. func (s3a *S3ApiServer) maybeGetFilerJwtAuthorizationToken(isWrite bool) string {
  168. var encodedJwt security.EncodedJwt
  169. if isWrite {
  170. encodedJwt = security.GenJwtForFilerServer(s3a.filerGuard.SigningKey, s3a.filerGuard.ExpiresAfterSec)
  171. } else {
  172. encodedJwt = security.GenJwtForFilerServer(s3a.filerGuard.ReadSigningKey, s3a.filerGuard.ReadExpiresAfterSec)
  173. }
  174. return string(encodedJwt)
  175. }