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.

221 lines
5.2 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. if strings.HasSuffix(r.URL.Path, "/") {
  49. writeErrorResponse(w, ErrNotImplemented, r.URL)
  50. return
  51. }
  52. destUrl := fmt.Sprintf("http://%s%s%s",
  53. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  54. s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
  55. }
  56. func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
  57. destUrl := fmt.Sprintf("http://%s%s%s",
  58. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  59. s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
  60. }
  61. func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
  62. destUrl := fmt.Sprintf("http://%s%s%s",
  63. s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
  64. s3a.proxyToFiler(w, r, destUrl, func(proxyResonse *http.Response, w http.ResponseWriter) {
  65. for k, v := range proxyResonse.Header {
  66. w.Header()[k] = v
  67. }
  68. w.WriteHeader(http.StatusNoContent)
  69. })
  70. }
  71. // DeleteMultipleObjectsHandler - Delete multiple objects
  72. func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
  73. // TODO
  74. writeErrorResponse(w, ErrNotImplemented, r.URL)
  75. }
  76. func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResonse *http.Response, w http.ResponseWriter)) {
  77. glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)
  78. proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)
  79. if err != nil {
  80. glog.Errorf("NewRequest %s: %v", destUrl, err)
  81. writeErrorResponse(w, ErrInternalError, r.URL)
  82. return
  83. }
  84. proxyReq.Header.Set("Host", s3a.option.Filer)
  85. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  86. for header, values := range r.Header {
  87. for _, value := range values {
  88. proxyReq.Header.Add(header, value)
  89. }
  90. }
  91. resp, postErr := client.Do(proxyReq)
  92. if postErr != nil {
  93. glog.Errorf("post to filer: %v", postErr)
  94. writeErrorResponse(w, ErrInternalError, r.URL)
  95. return
  96. }
  97. defer resp.Body.Close()
  98. responseFn(resp, w)
  99. }
  100. func passThroghResponse(proxyResonse *http.Response, w http.ResponseWriter) {
  101. for k, v := range proxyResonse.Header {
  102. w.Header()[k] = v
  103. }
  104. w.WriteHeader(proxyResonse.StatusCode)
  105. io.Copy(w, proxyResonse.Body)
  106. }
  107. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.ReadCloser) (etag string, code ErrorCode) {
  108. hash := md5.New()
  109. var body io.Reader = io.TeeReader(dataReader, hash)
  110. proxyReq, err := http.NewRequest("PUT", uploadUrl, body)
  111. if err != nil {
  112. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  113. return "", ErrInternalError
  114. }
  115. proxyReq.Header.Set("Host", s3a.option.Filer)
  116. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  117. for header, values := range r.Header {
  118. for _, value := range values {
  119. proxyReq.Header.Add(header, value)
  120. }
  121. }
  122. resp, postErr := client.Do(proxyReq)
  123. dataReader.Close()
  124. if postErr != nil {
  125. glog.Errorf("post to filer: %v", postErr)
  126. return "", ErrInternalError
  127. }
  128. defer resp.Body.Close()
  129. etag = fmt.Sprintf("%x", hash.Sum(nil))
  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 weed_server.FilerPostResult
  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 "", ErrInternalError
  140. }
  141. if ret.Error != "" {
  142. glog.Errorf("upload to filer error: %v", ret.Error)
  143. return "", ErrInternalError
  144. }
  145. return etag, ErrNone
  146. }
  147. func setEtag(w http.ResponseWriter, etag string) {
  148. if etag != "" {
  149. if strings.HasPrefix(etag, "\"") {
  150. w.Header().Set("ETag", etag)
  151. } else {
  152. w.Header().Set("ETag", "\""+etag+"\"")
  153. }
  154. }
  155. }
  156. func getObject(vars map[string]string) string {
  157. object := vars["object"]
  158. if !strings.HasPrefix(object, "/") {
  159. object = "/" + object
  160. }
  161. return object
  162. }
  163. func getEtag(r *http.Request) (etag string) {
  164. etag = r.Header.Get("ETag")
  165. if strings.HasPrefix(etag, "\"") && strings.HasSuffix(etag, "\"") {
  166. etag = etag[1 : len(etag)-1]
  167. }
  168. return
  169. }