aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api
diff options
context:
space:
mode:
Diffstat (limited to 'weed/s3api')
-rw-r--r--weed/s3api/s3api_errors.go8
-rw-r--r--weed/s3api/s3api_handlers.go12
-rw-r--r--weed/s3api/s3api_object_handlers.go90
-rw-r--r--weed/s3api/s3api_server.go4
4 files changed, 113 insertions, 1 deletions
diff --git a/weed/s3api/s3api_errors.go b/weed/s3api/s3api_errors.go
index 8af024700..10b48c2c8 100644
--- a/weed/s3api/s3api_errors.go
+++ b/weed/s3api/s3api_errors.go
@@ -31,8 +31,9 @@ const (
ErrBucketNotEmpty
ErrBucketAlreadyExists
ErrBucketAlreadyOwnedByYou
- ErrInvalidBucketName
ErrNoSuchBucket
+ ErrInvalidBucketName
+ ErrInvalidDigest
ErrInternalError
)
@@ -64,6 +65,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The specified bucket is not valid.",
HTTPStatusCode: http.StatusBadRequest,
},
+ ErrInvalidDigest: {
+ Code: "InvalidDigest",
+ Description: "The Content-Md5 you specified is not valid.",
+ HTTPStatusCode: http.StatusBadRequest,
+ },
ErrNoSuchBucket: {
Code: "NoSuchBucket",
Description: "The specified bucket does not exist",
diff --git a/weed/s3api/s3api_handlers.go b/weed/s3api/s3api_handlers.go
index 229b3a740..13dfc8d15 100644
--- a/weed/s3api/s3api_handlers.go
+++ b/weed/s3api/s3api_handlers.go
@@ -11,6 +11,7 @@ import (
"github.com/chrislusf/seaweedfs/weed/util"
"bytes"
"encoding/xml"
+ "encoding/base64"
)
type mimeType string
@@ -93,3 +94,14 @@ func writeSuccessResponseXML(w http.ResponseWriter, response []byte) {
func writeSuccessResponseEmpty(w http.ResponseWriter) {
writeResponse(w, http.StatusOK, nil, mimeNone)
}
+
+func validateContentMd5(h http.Header) ([]byte, error) {
+ md5B64, ok := h["Content-Md5"]
+ if ok {
+ if md5B64[0] == "" {
+ return nil, fmt.Errorf("Content-Md5 header set to empty value")
+ }
+ return base64.StdEncoding.DecodeString(md5B64[0])
+ }
+ return []byte{}, nil
+}
diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go
new file mode 100644
index 000000000..079509792
--- /dev/null
+++ b/weed/s3api/s3api_object_handlers.go
@@ -0,0 +1,90 @@
+package s3api
+
+import (
+ "net/http"
+ "github.com/chrislusf/seaweedfs/weed/glog"
+ "fmt"
+ "github.com/gorilla/mux"
+ "io/ioutil"
+ "encoding/json"
+)
+
+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
+ }
+
+ uploadUrl := fmt.Sprintf("http://%s%s/%s/%s?collection=%s",
+ s3a.option.Filer, s3a.option.BucketsPath, bucket, object, bucket)
+ proxyReq, err := http.NewRequest("PUT", uploadUrl, r.Body)
+
+ if err != nil {
+ glog.Errorf("NewRequest %s: %v", uploadUrl, 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()
+
+ resp_body, ra_err := ioutil.ReadAll(resp.Body)
+ if ra_err != nil {
+ glog.Errorf("upload to filer response read: %v", ra_err)
+ writeErrorResponse(w, ErrInternalError, r.URL)
+ return
+ }
+ 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))
+ writeErrorResponse(w, ErrInternalError, r.URL)
+ return
+ }
+ if ret.Error != "" {
+ glog.Errorf("upload to filer error: %v", ret.Error)
+ writeErrorResponse(w, ErrInternalError, r.URL)
+ return
+ }
+
+ writeSuccessResponseEmpty(w)
+}
diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go
index 8730f0b88..d8e6de1f7 100644
--- a/weed/s3api/s3api_server.go
+++ b/weed/s3api/s3api_server.go
@@ -42,6 +42,10 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) {
routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter())
for _, bucket := range routers {
+
+ // PutObject
+ bucket.Methods("PUT").Path("/{object:.+}").HandlerFunc(s3a.PutObjectHandler)
+
// PutBucket
bucket.Methods("PUT").HandlerFunc(s3a.PutBucketHandler)
// DeleteBucket