seaweedfs/weed/s3api/s3api_object_handlers.go

200 lines
4.7 KiB
Go
Raw Normal View History

package s3api
import (
2018-07-22 00:39:10 +00:00
"encoding/json"
"fmt"
2018-07-22 00:39:10 +00:00
"github.com/chrislusf/seaweedfs/weed/glog"
"github.com/gorilla/mux"
2018-07-22 08:15:11 +00:00
"io"
"io/ioutil"
2018-07-22 00:39:10 +00:00
"net/http"
2018-07-23 08:15:59 +00:00
"strings"
)
var (
client *http.Client
)
func init() {
client = &http.Client{Transport: &http.Transport{
MaxIdleConnsPerHost: 1024,
}}
}
type UploadResult struct {
Name string `json:"name,omitempty"`
Size uint32 `json:"size,omitempty"`
Error string `json:"error,omitempty"`
}
func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
// http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html
vars := mux.Vars(r)
bucket := vars["bucket"]
object := vars["object"]
_, err := validateContentMd5(r.Header)
if err != nil {
writeErrorResponse(w, ErrInvalidDigest, r.URL)
return
}
rAuthType := getRequestAuthType(r)
dataReader := r.Body
2018-09-03 23:47:00 +00:00
if rAuthType == authTypeStreamingSigned {
dataReader = newSignV4ChunkedReader(r)
}
uploadUrl := fmt.Sprintf("http://%s%s/%s/%s?collection=%s",
s3a.option.Filer, s3a.option.BucketsPath, bucket, object, bucket)
etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)
if errCode != ErrNone {
writeErrorResponse(w, errCode, r.URL)
return
}
setEtag(w, etag)
writeSuccessResponseEmpty(w)
}
2018-07-22 01:49:47 +00:00
func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
2018-07-23 08:15:59 +00:00
if strings.HasSuffix(r.URL.Path, "/") {
writeErrorResponse(w, ErrNotImplemented, r.URL)
return
}
2018-07-22 01:49:47 +00:00
destUrl := fmt.Sprintf("http://%s%s%s",
s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
2018-07-22 02:12:44 +00:00
s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
2018-07-22 01:49:47 +00:00
}
func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
destUrl := fmt.Sprintf("http://%s%s%s",
s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
2018-07-22 02:12:44 +00:00
s3a.proxyToFiler(w, r, destUrl, passThroghResponse)
2018-07-22 01:49:47 +00:00
}
func (s3a *S3ApiServer) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
destUrl := fmt.Sprintf("http://%s%s%s",
s3a.option.Filer, s3a.option.BucketsPath, r.RequestURI)
2018-07-22 02:12:44 +00:00
s3a.proxyToFiler(w, r, destUrl, func(proxyResonse *http.Response, w http.ResponseWriter) {
for k, v := range proxyResonse.Header {
w.Header()[k] = v
}
w.WriteHeader(http.StatusNoContent)
})
2018-07-22 01:49:47 +00:00
}
// DeleteMultipleObjectsHandler - Delete multiple objects
func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
// TODO
writeErrorResponse(w, ErrNotImplemented, r.URL)
}
2018-07-22 02:12:44 +00:00
func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, destUrl string, responseFn func(proxyResonse *http.Response, w http.ResponseWriter)) {
2018-07-22 01:49:47 +00:00
glog.V(2).Infof("s3 proxying %s to %s", r.Method, destUrl)
proxyReq, err := http.NewRequest(r.Method, destUrl, r.Body)
if err != nil {
glog.Errorf("NewRequest %s: %v", destUrl, err)
writeErrorResponse(w, ErrInternalError, r.URL)
return
}
proxyReq.Header.Set("Host", s3a.option.Filer)
proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
for header, values := range r.Header {
for _, value := range values {
proxyReq.Header.Add(header, value)
}
}
resp, postErr := client.Do(proxyReq)
if postErr != nil {
glog.Errorf("post to filer: %v", postErr)
writeErrorResponse(w, ErrInternalError, r.URL)
return
}
defer resp.Body.Close()
2018-07-22 02:12:44 +00:00
responseFn(resp, w)
}
func passThroghResponse(proxyResonse *http.Response, w http.ResponseWriter) {
for k, v := range proxyResonse.Header {
2018-07-22 01:49:47 +00:00
w.Header()[k] = v
}
2018-07-22 02:12:44 +00:00
w.WriteHeader(proxyResonse.StatusCode)
io.Copy(w, proxyResonse.Body)
2018-07-22 01:49:47 +00:00
}
func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader io.ReadCloser) (etag string, code ErrorCode) {
proxyReq, err := http.NewRequest("PUT", uploadUrl, dataReader)
if err != nil {
glog.Errorf("NewRequest %s: %v", uploadUrl, err)
return "", ErrInternalError
}
proxyReq.Header.Set("Host", s3a.option.Filer)
proxyReq.Header.Set("X-Forwarded-For", r.RemoteAddr)
for header, values := range r.Header {
for _, value := range values {
proxyReq.Header.Add(header, value)
}
}
resp, postErr := client.Do(proxyReq)
if postErr != nil {
glog.Errorf("post to filer: %v", postErr)
return "", ErrInternalError
}
defer resp.Body.Close()
etag = resp.Header.Get("ETag")
resp_body, ra_err := ioutil.ReadAll(resp.Body)
if ra_err != nil {
glog.Errorf("upload to filer response read: %v", ra_err)
return etag, ErrInternalError
}
var ret UploadResult
unmarshal_err := json.Unmarshal(resp_body, &ret)
if unmarshal_err != nil {
glog.Errorf("failing to read upload to %s : %v", uploadUrl, string(resp_body))
return etag, ErrInternalError
}
if ret.Error != "" {
glog.Errorf("upload to filer error: %v", ret.Error)
return etag, ErrInternalError
}
return etag, ErrNone
}
func setEtag(w http.ResponseWriter, etag string) {
if etag != "" {
w.Header().Set("ETag", "\""+etag+"\"")
}
}