s3api_object_handlers_put.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. writeSuccessResponseEmpty(w, r)
  92. }
  93. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.Reader, destination string, bucket string) (etag string, code s3err.ErrorCode) {
  94. hash := md5.New()
  95. var body = io.TeeReader(dataReader, hash)
  96. proxyReq, err := http.NewRequest(http.MethodPut, uploadUrl, body)
  97. if err != nil {
  98. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  99. return "", s3err.ErrInternalError
  100. }
  101. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  102. if destination != "" {
  103. proxyReq.Header.Set(s3_constants.SeaweedStorageDestinationHeader, destination)
  104. }
  105. if s3a.option.FilerGroup != "" {
  106. query := proxyReq.URL.Query()
  107. query.Add("collection", s3a.getCollectionName(bucket))
  108. proxyReq.URL.RawQuery = query.Encode()
  109. }
  110. for header, values := range r.Header {
  111. for _, value := range values {
  112. proxyReq.Header.Add(header, value)
  113. }
  114. }
  115. // ensure that the Authorization header is overriding any previous
  116. // Authorization header which might be already present in proxyReq
  117. s3a.maybeAddFilerJwtAuthorization(proxyReq, true)
  118. resp, postErr := s3a.client.Do(proxyReq)
  119. if postErr != nil {
  120. glog.Errorf("post to filer: %v", postErr)
  121. return "", s3err.ErrInternalError
  122. }
  123. defer resp.Body.Close()
  124. etag = fmt.Sprintf("%x", hash.Sum(nil))
  125. resp_body, ra_err := io.ReadAll(resp.Body)
  126. if ra_err != nil {
  127. glog.Errorf("upload to filer response read %d: %v", resp.StatusCode, ra_err)
  128. return etag, s3err.ErrInternalError
  129. }
  130. var ret weed_server.FilerPostResult
  131. unmarshal_err := json.Unmarshal(resp_body, &ret)
  132. if unmarshal_err != nil {
  133. glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
  134. return "", s3err.ErrInternalError
  135. }
  136. if ret.Error != "" {
  137. glog.Errorf("upload to filer error: %v", ret.Error)
  138. return "", filerErrorToS3Error(ret.Error)
  139. }
  140. stats_collect.S3BucketTrafficReceivedBytesCounter.WithLabelValues(bucket).Add(float64(ret.Size))
  141. return etag, s3err.ErrNone
  142. }
  143. func setEtag(w http.ResponseWriter, etag string) {
  144. if etag != "" {
  145. if strings.HasPrefix(etag, "\"") {
  146. w.Header()["ETag"] = []string{etag}
  147. } else {
  148. w.Header()["ETag"] = []string{"\"" + etag + "\""}
  149. }
  150. }
  151. }
  152. func filerErrorToS3Error(errString string) s3err.ErrorCode {
  153. switch {
  154. case strings.HasPrefix(errString, "existing ") && strings.HasSuffix(errString, "is a directory"):
  155. return s3err.ErrExistingObjectIsDirectory
  156. case strings.HasSuffix(errString, "is a file"):
  157. return s3err.ErrExistingObjectIsFile
  158. default:
  159. return s3err.ErrInternalError
  160. }
  161. }
  162. func (s3a *S3ApiServer) maybeAddFilerJwtAuthorization(r *http.Request, isWrite bool) {
  163. encodedJwt := s3a.maybeGetFilerJwtAuthorizationToken(isWrite)
  164. if encodedJwt == "" {
  165. return
  166. }
  167. r.Header.Set("Authorization", "BEARER "+string(encodedJwt))
  168. }
  169. func (s3a *S3ApiServer) maybeGetFilerJwtAuthorizationToken(isWrite bool) string {
  170. var encodedJwt security.EncodedJwt
  171. if isWrite {
  172. encodedJwt = security.GenJwtForFilerServer(s3a.filerGuard.SigningKey, s3a.filerGuard.ExpiresAfterSec)
  173. } else {
  174. encodedJwt = security.GenJwtForFilerServer(s3a.filerGuard.ReadSigningKey, s3a.filerGuard.ReadExpiresAfterSec)
  175. }
  176. return string(encodedJwt)
  177. }