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.

324 lines
8.1 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
6 years ago
6 years ago
  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. glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)
  90. proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)
  91. if err != nil {
  92. glog.Errorf("NewRequest %s: %v", destUrl, err)
  93. writeErrorResponse(w, ErrInternalError, r.URL)
  94. return
  95. }
  96. proxyReq.Header.Set("Host", s3a.option.Filer)
  97. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  98. proxyReq.Header.Set("Etag-MD5", "True")
  99. for header, values := range r.Header {
  100. for _, value := range values {
  101. proxyReq.Header.Add(header, value)
  102. }
  103. }
  104. resp, postErr := client.Do(proxyReq)
  105. if postErr != nil {
  106. glog.Errorf("post to filer: %v", postErr)
  107. writeErrorResponse(w, ErrInternalError, r.URL)
  108. return
  109. }
  110. defer resp.Body.Close()
  111. responseFn(resp, w)
  112. }
  113. func passThroughResponse(proxyResonse *http.Response, w http.ResponseWriter) {
  114. for k, v := range proxyResonse.Header {
  115. w.Header()[k] = v
  116. }
  117. w.WriteHeader(proxyResonse.StatusCode)
  118. io.Copy(w, proxyResonse.Body)
  119. }
  120. func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.ReadCloser) (etag string, code ErrorCode) {
  121. hash := md5.New()
  122. var body io.Reader = io.TeeReader(dataReader, hash)
  123. proxyReq, err := http.NewRequest("PUT", uploadUrl, body)
  124. if err != nil {
  125. glog.Errorf("NewRequest %s: %v", uploadUrl, err)
  126. return "", ErrInternalError
  127. }
  128. proxyReq.Header.Set("Host", s3a.option.Filer)
  129. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  130. for header, values := range r.Header {
  131. for _, value := range values {
  132. proxyReq.Header.Add(header, value)
  133. }
  134. }
  135. resp, postErr := client.Do(proxyReq)
  136. dataReader.Close()
  137. if postErr != nil {
  138. glog.Errorf("post to filer: %v", postErr)
  139. return "", ErrInternalError
  140. }
  141. defer resp.Body.Close()
  142. etag = fmt.Sprintf("%x", hash.Sum(nil))
  143. resp_body, ra_err := ioutil.ReadAll(resp.Body)
  144. if ra_err != nil {
  145. glog.Errorf("upload to filer response read: %v", ra_err)
  146. return etag, ErrInternalError
  147. }
  148. var ret weed_server.FilerPostResult
  149. unmarshal_err := json.Unmarshal(resp_body, &ret)
  150. if unmarshal_err != nil {
  151. glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
  152. return "", ErrInternalError
  153. }
  154. if ret.Error != "" {
  155. glog.Errorf("upload to filer error: %v", ret.Error)
  156. return "", ErrInternalError
  157. }
  158. return etag, ErrNone
  159. }
  160. func setEtag(w http.ResponseWriter, etag string) {
  161. if etag != "" {
  162. if strings.HasPrefix(etag, "\"") {
  163. w.Header().Set("ETag", etag)
  164. } else {
  165. w.Header().Set("ETag", "\""+etag+"\"")
  166. }
  167. }
  168. }
  169. func getObject(vars map[string]string) string {
  170. object := vars["object"]
  171. if !strings.HasPrefix(object, "/") {
  172. object = "/" + object
  173. }
  174. return object
  175. }
  176. func (s3a *S3ApiServer) SelectObjectContent(w http.ResponseWriter, r *http.Request) {
  177. vars := mux.Vars(r)
  178. bucket := vars["bucket"]
  179. object := getObject(vars)
  180. s3Select, err := s3select.NewS3Select(r.Body)
  181. if err != nil {
  182. if serr, ok := err.(s3select.SelectError); ok {
  183. encodedErrorResponse := encodeResponse(cmd.APIErrorResponse{
  184. Code: serr.ErrorCode(),
  185. Message: serr.ErrorMessage(),
  186. BucketName: bucket,
  187. Key: object,
  188. Resource: r.URL.Path,
  189. RequestID: w.Header().Get(xhttp.AmzRequestID),
  190. HostID: "",
  191. })
  192. writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, "application/xml")
  193. } else {
  194. writeErrorResponse(w, ErrInternalError, r.URL)
  195. }
  196. return
  197. }
  198. if strings.HasSuffix(r.URL.Path, "/") {
  199. writeErrorResponse(w, ErrNotImplemented, r.URL)
  200. return
  201. }
  202. destUrl := fmt.Sprintf("http://%s%s/%s%s",
  203. s3a.option.Filer, s3a.option.BucketsPath, bucket, object)
  204. passThroughResponseSelectObjectContent := func(proxyResonse *http.Response,
  205. w http.ResponseWriter) {
  206. getObject := func(offset, length int64) (io.ReadCloser, error) {
  207. return proxyResonse.Body, nil
  208. }
  209. if err = s3Select.Open(getObject); err != nil {
  210. if serr, ok := err.(s3select.SelectError); ok {
  211. encodedErrorResponse := encodeResponse(cmd.APIErrorResponse{
  212. Code: serr.ErrorCode(),
  213. Message: serr.ErrorMessage(),
  214. BucketName: bucket,
  215. Key: object,
  216. Resource: r.URL.Path,
  217. RequestID: w.Header().Get(xhttp.AmzRequestID),
  218. HostID: "",
  219. })
  220. writeResponse(w, serr.HTTPStatusCode(), encodedErrorResponse, mimeXML)
  221. } else {
  222. writeResponse(w, http.StatusInternalServerError, encodeResponse("not s3select.SelectError"), mimeXML)
  223. }
  224. return
  225. }
  226. s3Select.Evaluate(w)
  227. s3Select.Close()
  228. }
  229. s3a.proxyToFilerS3Select(w, r, destUrl, passThroughResponseSelectObjectContent)
  230. }
  231. func (s3a *S3ApiServer) proxyToFilerS3Select(w http.ResponseWriter, r *http.Request, destUrl string,
  232. responseFn func(proxyResonse *http.Response, w http.ResponseWriter)) {
  233. glog.V(2).Infof("s3 proxying %s to %s", "GET", destUrl)
  234. proxyReq, err := http.NewRequest("GET", destUrl, nil)
  235. if err != nil {
  236. glog.Errorf("NewRequest %s: %v", destUrl, err)
  237. writeErrorResponse(w, ErrInternalError, r.URL)
  238. return
  239. }
  240. proxyReq.Header.Set("Host", s3a.option.Filer)
  241. proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
  242. proxyReq.Header.Set("Etag-MD5", "True")
  243. for header, values := range r.Header {
  244. for _, value := range values {
  245. proxyReq.Header.Add(header, value)
  246. }
  247. }
  248. resp, postErr := client.Do(proxyReq)
  249. if postErr != nil {
  250. glog.Errorf("post to filer: %v", postErr)
  251. writeErrorResponse(w, ErrInternalError, r.URL)
  252. return
  253. }
  254. defer resp.Body.Close()
  255. responseFn(resp, w)
  256. }