merge master, resolve conflicts
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb"
|
||||
xhttp "github.com/chrislusf/seaweedfs/weed/s3api/http"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
|
||||
@@ -51,8 +53,12 @@ func NewIdentityAccessManagement(option *S3ApiServerOption) *IdentityAccessManag
|
||||
return iam
|
||||
}
|
||||
|
||||
func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) error {
|
||||
content, err := filer.ReadContent(option.Filer, filer.IamConfigDirecotry, filer.IamIdentityFile)
|
||||
func (iam *IdentityAccessManagement) loadS3ApiConfigurationFromFiler(option *S3ApiServerOption) (err error) {
|
||||
var content []byte
|
||||
err = pb.WithFilerClient(option.Filer, option.GrpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
|
||||
content, err = filer.ReadInsideFiler(client, filer.IamConfigDirecotry, filer.IamIdentityFile)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("read S3 config: %v", err)
|
||||
}
|
||||
@@ -150,7 +156,7 @@ func (iam *IdentityAccessManagement) Auth(f http.HandlerFunc, action Action) htt
|
||||
f(w, r)
|
||||
return
|
||||
}
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +196,7 @@ func (iam *IdentityAccessManagement) authRequest(r *http.Request, action Action)
|
||||
return identity, s3Err
|
||||
}
|
||||
|
||||
glog.V(3).Infof("user name: %v actions: %v", identity.Name, identity.Actions)
|
||||
glog.V(3).Infof("user name: %v actions: %v, action: %v", identity.Name, identity.Actions, action)
|
||||
|
||||
bucket, _ := getBucketAndObject(r)
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
"io"
|
||||
"time"
|
||||
"github.com/chrislusf/seaweedfs/weed/util"
|
||||
)
|
||||
|
||||
func (s3a *S3ApiServer) subscribeMetaEvents(clientName string, prefix string, lastTsNs int64) error {
|
||||
@@ -34,37 +32,8 @@ func (s3a *S3ApiServer) subscribeMetaEvents(clientName string, prefix string, la
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
err := s3a.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
stream, err := client.SubscribeMetadata(ctx, &filer_pb.SubscribeMetadataRequest{
|
||||
ClientName: clientName,
|
||||
PathPrefix: prefix,
|
||||
SinceNs: lastTsNs,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("subscribe: %v", err)
|
||||
}
|
||||
return util.Retry("followIamChanges", func() error {
|
||||
return pb.WithFilerClientFollowMetadata(s3a, clientName, prefix, lastTsNs, 0, processEventFn, true)
|
||||
})
|
||||
|
||||
for {
|
||||
resp, listenErr := stream.Recv()
|
||||
if listenErr == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if listenErr != nil {
|
||||
return listenErr
|
||||
}
|
||||
|
||||
if err := processEventFn(resp); err != nil {
|
||||
glog.Fatalf("process %v: %v", resp, err)
|
||||
}
|
||||
lastTsNs = resp.TsNs
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
glog.Errorf("subscribing filer meta change: %v", err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +85,17 @@ func (iam *IdentityAccessManagement) calculateSeedSignature(r *http.Request) (cr
|
||||
return nil, "", "", time.Time{}, errCode
|
||||
}
|
||||
// Verify if the access key id matches.
|
||||
_, cred, found := iam.lookupByAccessKey(signV4Values.Credential.accessKey)
|
||||
identity, cred, found := iam.lookupByAccessKey(signV4Values.Credential.accessKey)
|
||||
if !found {
|
||||
return nil, "", "", time.Time{}, s3err.ErrInvalidAccessKeyID
|
||||
}
|
||||
|
||||
bucket, _ := getBucketAndObject(r)
|
||||
if !identity.canDo("Write", bucket) {
|
||||
errCode = s3err.ErrAccessDenied
|
||||
return
|
||||
}
|
||||
|
||||
// Verify if region is valid.
|
||||
region = signV4Values.Credential.scope.region
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ func (s3a *S3ApiServer) createMultipartUpload(input *s3.CreateMultipartUploadInp
|
||||
entry.Extended = make(map[string][]byte)
|
||||
}
|
||||
entry.Extended["key"] = []byte(*input.Key)
|
||||
for k, v := range input.Metadata {
|
||||
entry.Extended[k] = []byte(*v)
|
||||
}
|
||||
}); err != nil {
|
||||
glog.Errorf("NewMultipartUpload error: %v", err)
|
||||
return nil, s3err.ErrInternalError
|
||||
@@ -68,6 +71,12 @@ func (s3a *S3ApiServer) completeMultipartUpload(input *s3.CompleteMultipartUploa
|
||||
return nil, s3err.ErrNoSuchUpload
|
||||
}
|
||||
|
||||
pentry, err := s3a.getEntry(s3a.genUploadsFolder(*input.Bucket), *input.UploadId)
|
||||
if err != nil {
|
||||
glog.Errorf("completeMultipartUpload %s %s error: %v", *input.Bucket, *input.UploadId, err)
|
||||
return nil, s3err.ErrNoSuchUpload
|
||||
}
|
||||
|
||||
var finalParts []*filer_pb.FileChunk
|
||||
var offset int64
|
||||
|
||||
@@ -103,7 +112,16 @@ func (s3a *S3ApiServer) completeMultipartUpload(input *s3.CompleteMultipartUploa
|
||||
dirName = dirName[:len(dirName)-1]
|
||||
}
|
||||
|
||||
err = s3a.mkFile(dirName, entryName, finalParts)
|
||||
err = s3a.mkFile(dirName, entryName, finalParts, func(entry *filer_pb.Entry) {
|
||||
if entry.Extended == nil {
|
||||
entry.Extended = make(map[string][]byte)
|
||||
}
|
||||
for k, v := range pentry.Extended {
|
||||
if k != "key" {
|
||||
entry.Extended[k] = v
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("completeMultipartUpload %s/%s error: %v", dirName, entryName, err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package s3api
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ func TestInitiateMultipartUploadResult(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
encoded := string(encodeResponse(response))
|
||||
encoded := string(s3err.EncodeXMLResponse(response))
|
||||
if encoded != expected {
|
||||
t.Errorf("unexpected output: %s\nexpecting:%s", encoded, expected)
|
||||
}
|
||||
@@ -41,7 +42,7 @@ func TestListPartsResult(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
encoded := string(encodeResponse(response))
|
||||
encoded := string(s3err.EncodeXMLResponse(response))
|
||||
if encoded != expected {
|
||||
t.Errorf("unexpected output: %s\nexpecting:%s", encoded, expected)
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ func (s3a *S3ApiServer) mkdir(parentDirectoryPath string, dirName string, fn fun
|
||||
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) mkFile(parentDirectoryPath string, fileName string, chunks []*filer_pb.FileChunk) error {
|
||||
func (s3a *S3ApiServer) mkFile(parentDirectoryPath string, fileName string, chunks []*filer_pb.FileChunk, fn func(entry *filer_pb.Entry)) error {
|
||||
|
||||
return filer_pb.MkFile(s3a, parentDirectoryPath, fileName, chunks)
|
||||
return filer_pb.MkFile(s3a, parentDirectoryPath, fileName, chunks, fn)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques
|
||||
if s3a.iam.isEnabled() {
|
||||
identity, s3Err = s3a.iam.authUser(r)
|
||||
if s3Err != s3err.ErrNone {
|
||||
writeErrorResponse(w, s3Err, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3Err, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques
|
||||
entries, _, err := s3a.list(s3a.option.BucketsPath, "", "", false, math.MaxInt32)
|
||||
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Reques
|
||||
Buckets: buckets,
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -95,14 +95,14 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
if exist, err := s3a.exists(s3a.option.BucketsPath, bucket, true); err == nil && exist {
|
||||
errCode = s3err.ErrBucketAlreadyExists
|
||||
}
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
|
||||
// create the folder for bucket, but lazily create actual collection
|
||||
if err := s3a.mkdir(s3a.option.BucketsPath, bucket, fn); err != nil {
|
||||
glog.Errorf("PutBucketHandler mkdir: %v", err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque
|
||||
bucket, _ := getBucketAndObject(r)
|
||||
|
||||
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
|
||||
writeErrorResponse(w, err, r.URL)
|
||||
s3err.WriteErrorResponse(w, err, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -152,11 +152,11 @@ func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Reque
|
||||
err = s3a.rm(s3a.option.BucketsPath, bucket, false, true)
|
||||
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
writeResponse(w, http.StatusNoContent, nil, mimeNone)
|
||||
s3err.WriteEmptyResponse(w, http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -164,7 +164,7 @@ func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request
|
||||
bucket, _ := getBucketAndObject(r)
|
||||
|
||||
if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
|
||||
writeErrorResponse(w, err, r.URL)
|
||||
s3err.WriteErrorResponse(w, err, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -32,7 +33,7 @@ func TestListBucketsHandler(t *testing.T) {
|
||||
Buckets: buckets,
|
||||
}
|
||||
|
||||
encoded := string(encodeResponse(response))
|
||||
encoded := string(s3err.EncodeXMLResponse(response))
|
||||
if encoded != expected {
|
||||
t.Errorf("unexpected output: %s\nexpecting:%s", encoded, expected)
|
||||
}
|
||||
|
||||
@@ -1,45 +1,16 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"net/http"
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb"
|
||||
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
|
||||
)
|
||||
|
||||
type mimeType string
|
||||
|
||||
const (
|
||||
mimeNone mimeType = ""
|
||||
mimeJSON mimeType = "application/json"
|
||||
mimeXML mimeType = "application/xml"
|
||||
)
|
||||
|
||||
func setCommonHeaders(w http.ResponseWriter) {
|
||||
w.Header().Set("x-amz-request-id", fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
w.Header().Set("Accept-Ranges", "bytes")
|
||||
}
|
||||
|
||||
// Encodes the response headers into XML format.
|
||||
func encodeResponse(response interface{}) []byte {
|
||||
var bytesBuffer bytes.Buffer
|
||||
bytesBuffer.WriteString(xml.Header)
|
||||
e := xml.NewEncoder(&bytesBuffer)
|
||||
e.Encode(response)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
var _ = filer_pb.FilerClient(&S3ApiServer{})
|
||||
|
||||
func (s3a *S3ApiServer) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error {
|
||||
@@ -54,53 +25,12 @@ func (s3a *S3ApiServer) AdjustedUrl(location *filer_pb.Location) string {
|
||||
return location.Url
|
||||
}
|
||||
|
||||
// If none of the http routes match respond with MethodNotAllowed
|
||||
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||
glog.V(0).Infof("unsupported %s %s", r.Method, r.RequestURI)
|
||||
writeErrorResponse(w, s3err.ErrMethodNotAllowed, r.URL)
|
||||
}
|
||||
|
||||
func writeErrorResponse(w http.ResponseWriter, errorCode s3err.ErrorCode, reqURL *url.URL) {
|
||||
apiError := s3err.GetAPIError(errorCode)
|
||||
errorResponse := getRESTErrorResponse(apiError, reqURL.Path)
|
||||
encodedErrorResponse := encodeResponse(errorResponse)
|
||||
writeResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, mimeXML)
|
||||
}
|
||||
|
||||
func getRESTErrorResponse(err s3err.APIError, resource string) s3err.RESTErrorResponse {
|
||||
return s3err.RESTErrorResponse{
|
||||
Code: err.Code,
|
||||
Message: err.Description,
|
||||
Resource: resource,
|
||||
RequestID: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
}
|
||||
|
||||
func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
|
||||
setCommonHeaders(w)
|
||||
if response != nil {
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
|
||||
}
|
||||
if mType != mimeNone {
|
||||
w.Header().Set("Content-Type", string(mType))
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
if response != nil {
|
||||
glog.V(4).Infof("status %d %s: %s", statusCode, mType, string(response))
|
||||
_, err := w.Write(response)
|
||||
if err != nil {
|
||||
glog.V(0).Infof("write err: %v", err)
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func writeSuccessResponseXML(w http.ResponseWriter, response []byte) {
|
||||
writeResponse(w, http.StatusOK, response, mimeXML)
|
||||
func writeSuccessResponseXML(w http.ResponseWriter, response interface{}) {
|
||||
s3err.WriteXMLResponse(w, http.StatusOK, response)
|
||||
}
|
||||
|
||||
func writeSuccessResponseEmpty(w http.ResponseWriter) {
|
||||
writeResponse(w, http.StatusOK, nil, mimeNone)
|
||||
s3err.WriteEmptyResponse(w, http.StatusOK)
|
||||
}
|
||||
|
||||
func validateContentMd5(h http.Header) ([]byte, error) {
|
||||
|
||||
@@ -32,28 +32,28 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request
|
||||
dir, name := fullPath.DirAndName()
|
||||
entry, err := s3a.getEntry(dir, name)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
}
|
||||
entry.Extended = weed_server.SaveAmzMetaData(r, entry.Extended, isReplace(r))
|
||||
err = s3a.touch(dir, name, entry)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
}
|
||||
writeSuccessResponseXML(w, encodeResponse(CopyObjectResult{
|
||||
writeSuccessResponseXML(w, CopyObjectResult{
|
||||
ETag: fmt.Sprintf("%x", entry.Attributes.Md5),
|
||||
LastModified: time.Now().UTC(),
|
||||
}))
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// If source object is empty or bucket is empty, reply back invalid copy source.
|
||||
if srcObject == "" || srcBucket == "" {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
return
|
||||
}
|
||||
|
||||
if srcBucket == dstBucket && srcObject == dstObject {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopyDest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopyDest, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request
|
||||
|
||||
_, _, resp, err := util.DownloadFile(srcUrl)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
return
|
||||
}
|
||||
defer util.CloseResponse(resp)
|
||||
@@ -73,7 +73,7 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request
|
||||
etag, errCode := s3a.putToFiler(r, dstUrl, resp.Body)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func (s3a *S3ApiServer) CopyObjectHandler(w http.ResponseWriter, r *http.Request
|
||||
LastModified: time.Now().UTC(),
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req
|
||||
srcBucket, srcObject := pathToBucketAndObject(cpSrcPath)
|
||||
// If source object is empty or bucket is empty, reply back invalid copy source.
|
||||
if srcObject == "" || srcBucket == "" {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -126,13 +126,13 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req
|
||||
|
||||
partID, err := strconv.Atoi(partIDString)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidPart, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidPart, r)
|
||||
return
|
||||
}
|
||||
|
||||
// check partID with maximum part ID for multipart objects
|
||||
if partID > globalMaxPartID {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxParts, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxParts, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req
|
||||
|
||||
dataReader, err := util.ReadUrlAsReaderCloser(srcUrl, rangeHeader)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidCopySource, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidCopySource, r)
|
||||
return
|
||||
}
|
||||
defer dataReader.Close()
|
||||
@@ -154,7 +154,7 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req
|
||||
etag, errCode := s3a.putToFiler(r, dstUrl, dataReader)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func (s3a *S3ApiServer) CopyObjectPartHandler(w http.ResponseWriter, r *http.Req
|
||||
LastModified: time.Now().UTC(),
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -44,27 +44,27 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
_, err := validateContentMd5(r.Header)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidDigest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidDigest, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.Header.Get("Cache-Control") != "" {
|
||||
if _, err = cacheobject.ParseRequestCacheControl(r.Header.Get("Cache-Control")); err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidDigest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidDigest, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if r.Header.Get("Expires") != "" {
|
||||
if _, err = time.Parse(http.TimeFormat, r.Header.Get("Expires")); err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidDigest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidDigest, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dataReader := r.Body
|
||||
rAuthType := getRequestAuthType(r)
|
||||
if s3a.iam.isEnabled() {
|
||||
rAuthType := getRequestAuthType(r)
|
||||
var s3ErrCode s3err.ErrorCode
|
||||
switch rAuthType {
|
||||
case authTypeStreamingSigned:
|
||||
@@ -75,13 +75,12 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
|
||||
_, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
|
||||
}
|
||||
if s3ErrCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, s3ErrCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3ErrCode, r)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
rAuthType := getRequestAuthType(r)
|
||||
if authTypeAnonymous != rAuthType {
|
||||
writeErrorResponse(w, s3err.ErrAuthNotSetup, r.URL)
|
||||
if authTypeStreamingSigned == rAuthType {
|
||||
s3err.WriteErrorResponse(w, s3err.ErrAuthNotSetup, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -89,7 +88,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
if strings.HasSuffix(object, "/") {
|
||||
if err := s3a.mkdir(s3a.option.BucketsPath, bucket+object, nil); err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -98,7 +97,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request)
|
||||
etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,7 +120,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request)
|
||||
bucket, object := getBucketAndObject(r)
|
||||
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNotImplemented, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -196,13 +195,13 @@ func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *h
|
||||
|
||||
deleteXMLBytes, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
deleteObjects := &DeleteObjectsRequest{}
|
||||
if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedXML, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedXML, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -254,7 +253,7 @@ func (s3a *S3ApiServer) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *h
|
||||
}
|
||||
deleteResp.Errors = deleteErrors
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(deleteResp))
|
||||
writeSuccessResponseXML(w, deleteResp)
|
||||
|
||||
}
|
||||
|
||||
@@ -298,7 +297,7 @@ func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, des
|
||||
|
||||
if err != nil {
|
||||
glog.Errorf("NewRequest %s: %v", destUrl, err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -328,19 +327,19 @@ func (s3a *S3ApiServer) proxyToFiler(w http.ResponseWriter, r *http.Request, des
|
||||
|
||||
if postErr != nil {
|
||||
glog.Errorf("post to filer: %v", postErr)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
defer util.CloseResponse(resp)
|
||||
|
||||
if resp.StatusCode == http.StatusPreconditionFailed {
|
||||
writeErrorResponse(w, s3err.ErrPreconditionFailed, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrPreconditionFailed, r)
|
||||
return
|
||||
}
|
||||
|
||||
if (resp.ContentLength == -1 || resp.StatusCode == 404) && resp.StatusCode != 304 {
|
||||
if r.Method != "DELETE" {
|
||||
writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchKey, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,23 +26,23 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
|
||||
reader, err := r.MultipartReader()
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedPOSTRequest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r)
|
||||
return
|
||||
}
|
||||
form, err := reader.ReadForm(int64(5 * humanize.MiByte))
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedPOSTRequest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r)
|
||||
return
|
||||
}
|
||||
defer form.RemoveAll()
|
||||
|
||||
fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(form)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedPOSTRequest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r)
|
||||
return
|
||||
}
|
||||
if fileBody == nil {
|
||||
writeErrorResponse(w, s3err.ErrPOSTFileRequired, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrPOSTFileRequired, r)
|
||||
return
|
||||
}
|
||||
defer fileBody.Close()
|
||||
@@ -60,7 +60,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
if successRedirect != "" {
|
||||
redirectURL, err = url.Parse(successRedirect)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedPOSTRequest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -68,13 +68,13 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
// Verify policy signature.
|
||||
errCode := s3a.iam.doesPolicySignatureMatch(formValues)
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
policyBytes, err := base64.StdEncoding.DecodeString(formValues.Get("Policy"))
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrMalformedPOSTRequest, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedPOSTRequest, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
|
||||
postPolicyForm, err := policy.ParsePostPolicyForm(string(policyBytes))
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrPostPolicyConditionInvalidFormat, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrPostPolicyConditionInvalidFormat, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,12 +99,12 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
lengthRange := postPolicyForm.Conditions.ContentLengthRange
|
||||
if lengthRange.Valid {
|
||||
if fileSize < lengthRange.Min {
|
||||
writeErrorResponse(w, s3err.ErrEntityTooSmall, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrEntityTooSmall, r)
|
||||
return
|
||||
}
|
||||
|
||||
if fileSize > lengthRange.Max {
|
||||
writeErrorResponse(w, s3err.ErrEntityTooLarge, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrEntityTooLarge, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
etag, errCode := s3a.putToFiler(r, uploadUrl, fileBody)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
// Replace raw query params..
|
||||
redirectURL.RawQuery = getRedirectPostRawQuery(bucket, object, etag)
|
||||
w.Header().Set("Location", redirectURL.String())
|
||||
writeResponse(w, http.StatusSeeOther, nil, mimeNone)
|
||||
s3err.WriteEmptyResponse(w, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,15 +132,15 @@ func (s3a *S3ApiServer) PostPolicyBucketHandler(w http.ResponseWriter, r *http.R
|
||||
// Decide what http response to send depending on success_action_status parameter
|
||||
switch successStatus {
|
||||
case "201":
|
||||
resp := encodeResponse(PostResponse{
|
||||
resp := PostResponse{
|
||||
Bucket: bucket,
|
||||
Key: object,
|
||||
ETag: `"` + etag + `"`,
|
||||
Location: w.Header().Get("Location"),
|
||||
})
|
||||
writeResponse(w, http.StatusCreated, resp, mimeXML)
|
||||
}
|
||||
s3err.WriteXMLResponse(w, http.StatusCreated, resp)
|
||||
case "200":
|
||||
writeResponse(w, http.StatusOK, nil, mimeNone)
|
||||
s3err.WriteEmptyResponse(w, http.StatusOK)
|
||||
default:
|
||||
writeSuccessResponseEmpty(w)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
weed_server "github.com/chrislusf/seaweedfs/weed/server"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -24,19 +25,27 @@ const (
|
||||
func (s3a *S3ApiServer) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
bucket, object := getBucketAndObject(r)
|
||||
|
||||
response, errCode := s3a.createMultipartUpload(&s3.CreateMultipartUploadInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: objectKey(aws.String(object)),
|
||||
})
|
||||
createMultipartUploadInput := &s3.CreateMultipartUploadInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: objectKey(aws.String(object)),
|
||||
Metadata: make(map[string]*string),
|
||||
}
|
||||
|
||||
glog.V(2).Info("NewMultipartUploadHandler", string(encodeResponse(response)), errCode)
|
||||
metadata := weed_server.SaveAmzMetaData(r, nil, false)
|
||||
for k, v := range metadata {
|
||||
createMultipartUploadInput.Metadata[k] = aws.String(string(v))
|
||||
}
|
||||
|
||||
response, errCode := s3a.createMultipartUpload(createMultipartUploadInput)
|
||||
|
||||
glog.V(2).Info("NewMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)), errCode)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
@@ -53,14 +62,14 @@ func (s3a *S3ApiServer) CompleteMultipartUploadHandler(w http.ResponseWriter, r
|
||||
UploadId: aws.String(uploadID),
|
||||
})
|
||||
|
||||
glog.V(2).Info("CompleteMultipartUploadHandler", string(encodeResponse(response)), errCode)
|
||||
glog.V(2).Info("CompleteMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)), errCode)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
@@ -78,13 +87,13 @@ func (s3a *S3ApiServer) AbortMultipartUploadHandler(w http.ResponseWriter, r *ht
|
||||
})
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
glog.V(2).Info("AbortMultipartUploadHandler", string(encodeResponse(response)))
|
||||
glog.V(2).Info("AbortMultipartUploadHandler", string(s3err.EncodeXMLResponse(response)))
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
@@ -94,13 +103,13 @@ func (s3a *S3ApiServer) ListMultipartUploadsHandler(w http.ResponseWriter, r *ht
|
||||
|
||||
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType := getBucketMultipartResources(r.URL.Query())
|
||||
if maxUploads < 0 {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxUploads, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxUploads, r)
|
||||
return
|
||||
}
|
||||
if keyMarker != "" {
|
||||
// Marker not common with prefix is not implemented.
|
||||
if !strings.HasPrefix(keyMarker, prefix) {
|
||||
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNotImplemented, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -115,16 +124,16 @@ func (s3a *S3ApiServer) ListMultipartUploadsHandler(w http.ResponseWriter, r *ht
|
||||
UploadIdMarker: aws.String(uploadIDMarker),
|
||||
})
|
||||
|
||||
glog.V(2).Info("ListMultipartUploadsHandler", string(encodeResponse(response)), errCode)
|
||||
glog.V(2).Info("ListMultipartUploadsHandler", string(s3err.EncodeXMLResponse(response)), errCode)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO handle encodingType
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
}
|
||||
|
||||
// ListObjectPartsHandler - Lists object parts in a multipart upload.
|
||||
@@ -133,11 +142,11 @@ func (s3a *S3ApiServer) ListObjectPartsHandler(w http.ResponseWriter, r *http.Re
|
||||
|
||||
uploadID, partNumberMarker, maxParts, _ := getObjectResources(r.URL.Query())
|
||||
if partNumberMarker < 0 {
|
||||
writeErrorResponse(w, s3err.ErrInvalidPartNumberMarker, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidPartNumberMarker, r)
|
||||
return
|
||||
}
|
||||
if maxParts < 0 {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxParts, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxParts, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -149,14 +158,14 @@ func (s3a *S3ApiServer) ListObjectPartsHandler(w http.ResponseWriter, r *http.Re
|
||||
UploadId: aws.String(uploadID),
|
||||
})
|
||||
|
||||
glog.V(2).Info("ListObjectPartsHandler", string(encodeResponse(response)), errCode)
|
||||
glog.V(2).Info("ListObjectPartsHandler", string(s3err.EncodeXMLResponse(response)), errCode)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
|
||||
}
|
||||
|
||||
@@ -167,18 +176,18 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ
|
||||
uploadID := r.URL.Query().Get("uploadId")
|
||||
exists, err := s3a.exists(s3a.genUploadsFolder(bucket), uploadID, true)
|
||||
if !exists {
|
||||
writeErrorResponse(w, s3err.ErrNoSuchUpload, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchUpload, r)
|
||||
return
|
||||
}
|
||||
|
||||
partIDString := r.URL.Query().Get("partNumber")
|
||||
partID, err := strconv.Atoi(partIDString)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInvalidPart, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidPart, r)
|
||||
return
|
||||
}
|
||||
if partID > globalMaxPartID {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxParts, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxParts, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -195,7 +204,7 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ
|
||||
_, s3ErrCode = s3a.iam.reqSignatureV4Verify(r)
|
||||
}
|
||||
if s3ErrCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, s3ErrCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3ErrCode, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -207,7 +216,7 @@ func (s3a *S3ApiServer) PutObjectPartHandler(w http.ResponseWriter, r *http.Requ
|
||||
etag, errCode := s3a.putToFiler(r, uploadUrl, dataReader)
|
||||
|
||||
if errCode != s3err.ErrNone {
|
||||
writeErrorResponse(w, errCode, r.URL)
|
||||
s3err.WriteErrorResponse(w, errCode, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -25,15 +25,15 @@ func (s3a *S3ApiServer) GetObjectTaggingHandler(w http.ResponseWriter, r *http.R
|
||||
if err != nil {
|
||||
if err == filer_pb.ErrNotFound {
|
||||
glog.Errorf("GetObjectTaggingHandler %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchKey, r)
|
||||
} else {
|
||||
glog.Errorf("GetObjectTaggingHandler %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(FromTags(tags)))
|
||||
writeSuccessResponseXML(w, FromTags(tags))
|
||||
|
||||
}
|
||||
|
||||
@@ -50,29 +50,29 @@ func (s3a *S3ApiServer) PutObjectTaggingHandler(w http.ResponseWriter, r *http.R
|
||||
input, err := ioutil.ReadAll(io.LimitReader(r.Body, r.ContentLength))
|
||||
if err != nil {
|
||||
glog.Errorf("PutObjectTaggingHandler read input %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
if err = xml.Unmarshal(input, tagging); err != nil {
|
||||
glog.Errorf("PutObjectTaggingHandler Unmarshal %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrMalformedXML, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrMalformedXML, r)
|
||||
return
|
||||
}
|
||||
tags := tagging.ToTags()
|
||||
if len(tags) > 10 {
|
||||
glog.Errorf("PutObjectTaggingHandler tags %s: %d tags more than 10", r.URL, len(tags))
|
||||
writeErrorResponse(w, s3err.ErrInvalidTag, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidTag, r)
|
||||
return
|
||||
}
|
||||
for k, v := range tags {
|
||||
if len(k) > 128 {
|
||||
glog.Errorf("PutObjectTaggingHandler tags %s: tag key %s longer than 128", r.URL, k)
|
||||
writeErrorResponse(w, s3err.ErrInvalidTag, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidTag, r)
|
||||
return
|
||||
}
|
||||
if len(v) > 256 {
|
||||
glog.Errorf("PutObjectTaggingHandler tags %s: tag value %s longer than 256", r.URL, v)
|
||||
writeErrorResponse(w, s3err.ErrInvalidTag, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidTag, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -80,10 +80,10 @@ func (s3a *S3ApiServer) PutObjectTaggingHandler(w http.ResponseWriter, r *http.R
|
||||
if err = s3a.setTags(dir, name, tagging.ToTags()); err != nil {
|
||||
if err == filer_pb.ErrNotFound {
|
||||
glog.Errorf("PutObjectTaggingHandler setTags %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchKey, r)
|
||||
} else {
|
||||
glog.Errorf("PutObjectTaggingHandler setTags %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -105,10 +105,10 @@ func (s3a *S3ApiServer) DeleteObjectTaggingHandler(w http.ResponseWriter, r *htt
|
||||
if err != nil {
|
||||
if err == filer_pb.ErrNotFound {
|
||||
glog.Errorf("DeleteObjectTaggingHandler %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrNoSuchKey, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchKey, r)
|
||||
} else {
|
||||
glog.Errorf("DeleteObjectTaggingHandler %s: %v", r.URL, err)
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Requ
|
||||
originalPrefix, continuationToken, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())
|
||||
|
||||
if maxKeys < 0 {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxKeys, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxKeys, r)
|
||||
return
|
||||
}
|
||||
if delimiter != "" && delimiter != "/" {
|
||||
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNotImplemented, r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,13 +60,13 @@ func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Requ
|
||||
response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)
|
||||
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
if len(response.Contents) == 0 {
|
||||
if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
|
||||
writeErrorResponse(w, s3err.ErrNoSuchBucket, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchBucket, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Requ
|
||||
StartAfter: startAfter,
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(responseV2))
|
||||
writeSuccessResponseXML(w, responseV2)
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -99,29 +99,29 @@ func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Requ
|
||||
originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())
|
||||
|
||||
if maxKeys < 0 {
|
||||
writeErrorResponse(w, s3err.ErrInvalidMaxKeys, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInvalidMaxKeys, r)
|
||||
return
|
||||
}
|
||||
if delimiter != "" && delimiter != "/" {
|
||||
writeErrorResponse(w, s3err.ErrNotImplemented, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNotImplemented, r)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)
|
||||
|
||||
if err != nil {
|
||||
writeErrorResponse(w, s3err.ErrInternalError, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrInternalError, r)
|
||||
return
|
||||
}
|
||||
|
||||
if len(response.Contents) == 0 {
|
||||
if exists, existErr := s3a.exists(s3a.option.BucketsPath, bucket, true); existErr == nil && !exists {
|
||||
writeErrorResponse(w, s3err.ErrNoSuchBucket, r.URL)
|
||||
s3err.WriteErrorResponse(w, s3err.ErrNoSuchBucket, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
writeSuccessResponseXML(w, encodeResponse(response))
|
||||
writeSuccessResponseXML(w, response)
|
||||
}
|
||||
|
||||
func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, maxKeys int, marker string, delimiter string) (response ListBucketResult, err error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -31,7 +32,7 @@ func TestListObjectsHandler(t *testing.T) {
|
||||
}},
|
||||
}
|
||||
|
||||
encoded := string(encodeResponse(response))
|
||||
encoded := string(s3err.EncodeXMLResponse(response))
|
||||
if encoded != expected {
|
||||
t.Errorf("unexpected output: %s\nexpecting:%s", encoded, expected)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/chrislusf/seaweedfs/weed/filer"
|
||||
. "github.com/chrislusf/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"github.com/gorilla/mux"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@@ -136,6 +137,6 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) {
|
||||
apiRouter.Methods("GET").Path("/").HandlerFunc(track(s3a.ListBucketsHandler, "LIST"))
|
||||
|
||||
// NotFound
|
||||
apiRouter.NotFoundHandler = http.HandlerFunc(notFoundHandler)
|
||||
apiRouter.NotFoundHandler = http.HandlerFunc(s3err.NotFoundHandler)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -14,7 +15,7 @@ func TestCopyObjectResponse(t *testing.T) {
|
||||
LastModified: time.Now(),
|
||||
}
|
||||
|
||||
println(string(encodeResponse(response)))
|
||||
println(string(s3err.EncodeXMLResponse(response)))
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +28,6 @@ func TestCopyPartResponse(t *testing.T) {
|
||||
LastModified: time.Now(),
|
||||
}
|
||||
|
||||
println(string(encodeResponse(response)))
|
||||
println(string(s3err.EncodeXMLResponse(response)))
|
||||
|
||||
}
|
||||
|
||||
92
weed/s3api/s3err/error_handler.go
Normal file
92
weed/s3api/s3err/error_handler.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package s3err
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/chrislusf/seaweedfs/weed/glog"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type mimeType string
|
||||
|
||||
const (
|
||||
mimeNone mimeType = ""
|
||||
MimeXML mimeType = "application/xml"
|
||||
)
|
||||
|
||||
func WriteXMLResponse(w http.ResponseWriter, statusCode int, response interface{}) {
|
||||
WriteResponse(w, statusCode, EncodeXMLResponse(response), MimeXML)
|
||||
}
|
||||
|
||||
func WriteEmptyResponse(w http.ResponseWriter, statusCode int) {
|
||||
WriteResponse(w, statusCode, []byte{}, mimeNone)
|
||||
}
|
||||
|
||||
func WriteErrorResponse(w http.ResponseWriter, errorCode ErrorCode, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
if strings.HasPrefix(object, "/") {
|
||||
object = object[1:]
|
||||
}
|
||||
|
||||
apiError := GetAPIError(errorCode)
|
||||
errorResponse := getRESTErrorResponse(apiError, r.URL.Path, bucket, object)
|
||||
encodedErrorResponse := EncodeXMLResponse(errorResponse)
|
||||
WriteResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, MimeXML)
|
||||
}
|
||||
|
||||
func getRESTErrorResponse(err APIError, resource string, bucket, object string) RESTErrorResponse {
|
||||
return RESTErrorResponse{
|
||||
Code: err.Code,
|
||||
BucketName: bucket,
|
||||
Key: object,
|
||||
Message: err.Description,
|
||||
Resource: resource,
|
||||
RequestID: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
}
|
||||
|
||||
// Encodes the response headers into XML format.
|
||||
func EncodeXMLResponse(response interface{}) []byte {
|
||||
var bytesBuffer bytes.Buffer
|
||||
bytesBuffer.WriteString(xml.Header)
|
||||
e := xml.NewEncoder(&bytesBuffer)
|
||||
e.Encode(response)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
func setCommonHeaders(w http.ResponseWriter) {
|
||||
w.Header().Set("x-amz-request-id", fmt.Sprintf("%d", time.Now().UnixNano()))
|
||||
w.Header().Set("Accept-Ranges", "bytes")
|
||||
}
|
||||
|
||||
func WriteResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
|
||||
setCommonHeaders(w)
|
||||
if response != nil {
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
|
||||
}
|
||||
if mType != mimeNone {
|
||||
w.Header().Set("Content-Type", string(mType))
|
||||
}
|
||||
w.WriteHeader(statusCode)
|
||||
if response != nil {
|
||||
glog.V(4).Infof("status %d %s: %s", statusCode, mType, string(response))
|
||||
_, err := w.Write(response)
|
||||
if err != nil {
|
||||
glog.V(0).Infof("write err: %v", err)
|
||||
}
|
||||
w.(http.Flusher).Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the http routes match respond with MethodNotAllowed
|
||||
func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||
glog.V(0).Infof("unsupported %s %s", r.Method, r.RequestURI)
|
||||
WriteErrorResponse(w, ErrMethodNotAllowed, r)
|
||||
}
|
||||
@@ -15,11 +15,13 @@ type APIError struct {
|
||||
|
||||
// RESTErrorResponse - error response format
|
||||
type RESTErrorResponse struct {
|
||||
XMLName xml.Name `xml:"Error" json:"-"`
|
||||
Code string `xml:"Code" json:"Code"`
|
||||
Message string `xml:"Message" json:"Message"`
|
||||
Resource string `xml:"Resource" json:"Resource"`
|
||||
RequestID string `xml:"RequestId" json:"RequestId"`
|
||||
XMLName xml.Name `xml:"Error" json:"-"`
|
||||
Code string `xml:"Code" json:"Code"`
|
||||
Message string `xml:"Message" json:"Message"`
|
||||
Resource string `xml:"Resource" json:"Resource"`
|
||||
RequestID string `xml:"RequestId" json:"RequestId"`
|
||||
Key string `xml:"Key,omitempty" json:"Key,omitempty"`
|
||||
BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"`
|
||||
|
||||
// Underlying HTTP status code for the returned error
|
||||
StatusCode int `xml:"-" json:"-"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package s3api
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"github.com/chrislusf/seaweedfs/weed/s3api/s3err"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
@@ -41,7 +42,7 @@ func TestXMLMarshall(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
actual := string(encodeResponse(tags))
|
||||
actual := string(s3err.EncodeXMLResponse(tags))
|
||||
|
||||
expected := `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Tagging xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><TagSet><Tag><Key>key1</Key><Value>value1</Value></Tag></TagSet></Tagging>`
|
||||
|
||||
Reference in New Issue
Block a user