s3api_object_handlers.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. package s3api
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/minio/minio/cmd"
  7. "github.com/minio/minio/pkg/s3select"
  8. "io"
  9. "io/ioutil"
  10. "net/http"
  11. "strings"
  12. "github.com/chrislusf/seaweedfs/weed/glog"
  13. "github.com/chrislusf/seaweedfs/weed/server"
  14. "github.com/gorilla/mux"
  15. xhttp "github.com/minio/minio/cmd/http"
  16. )
  17. var (
  18. client *http.Client
  19. )
  20. func init() {
  21. client = &http.Client{Transport: &http.Transport{
  22. MaxIdleConnsPerHost: 1024,
  23. }}
  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 := getObject(vars)
  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. vars := mux.Vars(r)
  52. bucket := vars["bucket"]
  53. object := getObject(vars)
  54. if strings.HasSuffix(r.URL.Path, "/") {
  55. writeErrorResponse(w, ErrNotImplemented, r.URL)
  56. return
  57. }
  58. destUrl := fmt.Sprintf("http://%s%s/%s%s",
  59. s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
  60. s3a.proxyToFiler(w, r, destUrl, passThroughResponse)
  61. }
  62. func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
  63. vars := mux.Vars(r)
  64. bucket := vars["bucket"]
  65. object := getObject(vars)
  66. destUrl := fmt.Sprintf("http://%s%s/%s%s",
  67. s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
  68. s3a.proxyToFiler(w, r, destUrl, passThroughResponse)
  69. }
  70. func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
  71. vars := mux.Vars(r)
  72. bucket := vars["bucket"]
  73. object := getObject(vars)
  74. destUrl := fmt.Sprintf("http://%s%s/%s%s",
  75. s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
  76. s3a.proxyToFiler(w, r, destUrl, func(proxyResonse *http.Response, w http.ResponseWriter) {
  77. for k, v := range proxyResonse.Header {
  78. w.Header()[k] = v
  79. }
  80. w.WriteHeader(http.StatusNoContent)
  81. })
  82. }
  83. // DeleteMultipleObjectsHandler - Delete multiple objects
  84. func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
  85. // TODO
  86. writeErrorResponse(w, ErrNotImplemented, r.URL)
  87. }
  88. func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResonse *http.Response, w http.ResponseWriter)) {
  89. var method string
  90. method = r.Method
  91. if r.URL.RawQuery == "select&select-type=2" {
  92. method = "GET"
  93. }
  94. glog.V(2).Infof("s3 proxying %s to %s", method, destUrl)
  95. proxyReq, err := http.NewRequest(method, destUrl, r.Body)
  96. if err != nil {
  97. glog.Errorf("NewRequest %s: %v", destUrl, err)
  98. writeErrorResponse(w, ErrInternalError, r.URL)
  99. return
  100. }
  101. proxyReq.Header.Set("Host", s3a.option.Filer)
  102. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  103. proxyReq.Header.Set("Etag-MD5", "True")
  104. for header, values := range r.Header {
  105. for _, value := range values {
  106. proxyReq.Header.Add(header, value)
  107. }
  108. }
  109. resp, postErr := client.Do(proxyReq)
  110. if postErr != nil {
  111. glog.Errorf("post to filer: %v", postErr)
  112. writeErrorResponse(w, ErrInternalError, r.URL)
  113. return
  114. }
  115. defer resp.Body.Close()
  116. responseFn(resp, w)
  117. }
  118. func passThroughResponse(proxyResonse *http.Response, w http.ResponseWriter) {
  119. for k, v := range proxyResonse.Header {
  120. w.Header()[k] = v
  121. }
  122. w.WriteHeader(proxyResonse.StatusCode)
  123. io.Copy(w, proxyResonse.Body)
  124. }
  125. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.ReadCloser) (etag string, code ErrorCode) {
  126. hash := md5.New()
  127. var body io.Reader = io.TeeReader(dataReader, hash)
  128. proxyReq, err := http.NewRequest("PUT", uploadUrl, body)
  129. if err != nil {
  130. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  131. return "", ErrInternalError
  132. }
  133. proxyReq.Header.Set("Host", s3a.option.Filer)
  134. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  135. for header, values := range r.Header {
  136. for _, value := range values {
  137. proxyReq.Header.Add(header, value)
  138. }
  139. }
  140. resp, postErr := client.Do(proxyReq)
  141. dataReader.Close()
  142. if postErr != nil {
  143. glog.Errorf("post to filer: %v", postErr)
  144. return "", ErrInternalError
  145. }
  146. defer resp.Body.Close()
  147. etag = fmt.Sprintf("%x", hash.Sum(nil))
  148. resp_body, ra_err := ioutil.ReadAll(resp.Body)
  149. if ra_err != nil {
  150. glog.Errorf("upload to filer response read: %v", ra_err)
  151. return etag, ErrInternalError
  152. }
  153. var ret weed_server.FilerPostResult
  154. unmarshal_err := json.Unmarshal(resp_body, &ret)
  155. if unmarshal_err != nil {
  156. glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
  157. return "", ErrInternalError
  158. }
  159. if ret.Error != "" {
  160. glog.Errorf("upload to filer error: %v", ret.Error)
  161. return "", ErrInternalError
  162. }
  163. return etag, ErrNone
  164. }
  165. func setEtag(w http.ResponseWriter, etag string) {
  166. if etag != "" {
  167. if strings.HasPrefix(etag, "\"") {
  168. w.Header().Set("ETag", etag)
  169. } else {
  170. w.Header().Set("ETag", "\""+etag+"\"")
  171. }
  172. }
  173. }
  174. func getObject(vars map[string]string) string {
  175. object := vars["object"]
  176. if !strings.HasPrefix(object, "/") {
  177. object = "/" + object
  178. }
  179. return object
  180. }
  181. func (s3a *S3ApiServer) SelectObjectContent(w http.ResponseWriter, r *http.Request) {
  182. vars := mux.Vars(r)
  183. bucket := vars["bucket"]
  184. object := getObject(vars)
  185. s3Select, err := s3select.NewS3Select(r.Body)
  186. if err != nil {
  187. if serr, ok := err.(s3select.SelectError); ok {
  188. encodedErrorResponse := encodeResponse(cmd.APIErrorResponse{
  189. Code: serr.ErrorCode(),
  190. Message: serr.ErrorMessage(),
  191. BucketName: bucket,
  192. Key: object,
  193. Resource: r.URL.Path,
  194. RequestID: w.Header().Get(xhttp.AmzRequestID),
  195. HostID: "",
  196. })
  197. writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, "application/xml")
  198. } else {
  199. writeErrorResponse(w, ErrInternalError, r.URL)
  200. }
  201. return
  202. }
  203. if strings.HasSuffix(r.URL.Path, "/") {
  204. writeErrorResponse(w, ErrNotImplemented, r.URL)
  205. return
  206. }
  207. destUrl := fmt.Sprintf("http://%s%s/%s%s",
  208. s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
  209. passThroughResponseSelectObjectContent := func(proxyResonse *http.Response,
  210. w http.ResponseWriter) {
  211. getObject := func(offset, length int64) (io.ReadCloser, error) {
  212. return proxyResonse.Body, nil
  213. }
  214. if err = s3Select.Open(getObject); err != nil {
  215. if serr, ok := err.(s3select.SelectError); ok {
  216. encodedErrorResponse := encodeResponse(cmd.APIErrorResponse{
  217. Code: serr.ErrorCode(),
  218. Message: serr.ErrorMessage(),
  219. BucketName: bucket,
  220. Key: object,
  221. Resource: r.URL.Path,
  222. RequestID: w.Header().Get(xhttp.AmzRequestID),
  223. HostID: "",
  224. })
  225. writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML)
  226. } else {
  227. writeResponse(w, http.StatusInternalServerError, encodeResponse("not s3select.SelectError"), mimeXML)
  228. }
  229. return
  230. }
  231. s3Select.Evaluate(w)
  232. s3Select.Close()
  233. }
  234. s3a.proxyToFiler(w, r, destUrl, passThroughResponseSelectObjectContent)
  235. }