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.

209 lines
6.0 KiB

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