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.

199 lines
4.7 KiB

7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
  1. package s3api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/glog"
  6. "github.com/gorilla/mux"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "strings"
  11. )
  12. var (
  13. client *http.Client
  14. )
  15. func init() {
  16. client = &http.Client{Transport: &http.Transport{
  17. MaxIdleConnsPerHost: 1024,
  18. }}
  19. }
  20. type UploadResult struct {
  21. Name string `json:"name,omitempty"`
  22. Size uint32 `json:"size,omitempty"`
  23. Error string `json:"error,omitempty"`
  24. }
  25. func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
  26. // http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html
  27. vars := mux.Vars(r)
  28. bucket := vars["bucket"]
  29. object := vars["object"]
  30. _, err := validateContentMd5(r.Header)
  31. if err != nil {
  32. writeErrorResponse(w, ErrInvalidDigest, r.URL)
  33. return
  34. }
  35. rAuthType := getRequestAuthType(r)
  36. dataReader := r.Body
  37. if rAuthType == authTypeStreamingSigned {
  38. dataReader = newSignV4ChunkedReader(r)
  39. }
  40. uploadUrl := fmt.Sprintf("http://%s%s/%s/%s?collection=%s",
  41. s3a.option.Filer, s3a.option.BucketsPath, bucket, object, bucket)
  42. etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)
  43. if errCode != ErrNone {
  44. writeErrorResponse(w, errCode, r.URL)
  45. return
  46. }
  47. setEtag(w, etag)
  48. writeSuccessResponseEmpty(w)
  49. }
  50. func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
  51. if strings.HasSuffix(r.URL.Path, "/") {
  52. writeErrorResponse(w, ErrNotImplemented, r.URL)
  53. return
  54. }
  55. destUrl := fmt.Sprintf("http://%s%s%s",
  56. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  57. s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
  58. }
  59. func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
  60. destUrl := fmt.Sprintf("http://%s%s%s",
  61. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  62. s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
  63. }
  64. func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
  65. destUrl := fmt.Sprintf("http://%s%s%s",
  66. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  67. s3a.proxyToFiler(w, r, destUrl, func(proxyResonse *http.Response, w http.ResponseWriter) {
  68. for k, v := range proxyResonse.Header {
  69. w.Header()[k] = v
  70. }
  71. w.WriteHeader(http.StatusNoContent)
  72. })
  73. }
  74. // DeleteMultipleObjectsHandler - Delete multiple objects
  75. func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
  76. // TODO
  77. writeErrorResponse(w, ErrNotImplemented, r.URL)
  78. }
  79. func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResonse *http.Response, w http.ResponseWriter)) {
  80. glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)
  81. proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)
  82. if err != nil {
  83. glog.Errorf("NewRequest %s: %v", destUrl, err)
  84. writeErrorResponse(w, ErrInternalError, r.URL)
  85. return
  86. }
  87. proxyReq.Header.Set("Host", s3a.option.Filer)
  88. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  89. for header, values := range r.Header {
  90. for _, value := range values {
  91. proxyReq.Header.Add(header, value)
  92. }
  93. }
  94. resp, postErr := client.Do(proxyReq)
  95. if postErr != nil {
  96. glog.Errorf("post to filer: %v", postErr)
  97. writeErrorResponse(w, ErrInternalError, r.URL)
  98. return
  99. }
  100. defer resp.Body.Close()
  101. responseFn(resp, w)
  102. }
  103. func passThroghResponse(proxyResonse *http.Response, w http.ResponseWriter) {
  104. for k, v := range proxyResonse.Header {
  105. w.Header()[k] = v
  106. }
  107. w.WriteHeader(proxyResonse.StatusCode)
  108. io.Copy(w, proxyResonse.Body)
  109. }
  110. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.ReadCloser) (etag string, code ErrorCode) {
  111. proxyReq, err := http.NewRequest("PUT", uploadUrl, dataReader)
  112. if err != nil {
  113. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  114. return "", ErrInternalError
  115. }
  116. proxyReq.Header.Set("Host", s3a.option.Filer)
  117. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  118. for header, values := range r.Header {
  119. for _, value := range values {
  120. proxyReq.Header.Add(header, value)
  121. }
  122. }
  123. resp, postErr := client.Do(proxyReq)
  124. if postErr != nil {
  125. glog.Errorf("post to filer: %v", postErr)
  126. return "", ErrInternalError
  127. }
  128. defer resp.Body.Close()
  129. etag = resp.Header.Get("ETag")
  130. resp_body, ra_err := ioutil.ReadAll(resp.Body)
  131. if ra_err != nil {
  132. glog.Errorf("upload to filer response read: %v", ra_err)
  133. return etag, ErrInternalError
  134. }
  135. var ret UploadResult
  136. unmarshal_err := json.Unmarshal(resp_body, &ret)
  137. if unmarshal_err != nil {
  138. glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
  139. return etag, ErrInternalError
  140. }
  141. if ret.Error != "" {
  142. glog.Errorf("upload to filer error: %v", ret.Error)
  143. return etag, ErrInternalError
  144. }
  145. return etag, ErrNone
  146. }
  147. func setEtag(w http.ResponseWriter, etag string) {
  148. if etag != "" {
  149. w.Header().Set("ETag", "\""+etag+"\"")
  150. }
  151. }