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.

226 lines
5.3 KiB

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