diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 48a0fe82b..1051cba83 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -396,6 +396,12 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) if versioningConfigured { // For versioned objects, reuse the already-fetched entry objectEntryForSSE = entry + // Safety check - this should never happen as versioned path handles errors above + if objectEntryForSSE == nil { + glog.Errorf("GetObjectHandler: unexpected nil entry for versioned object %s/%s", bucket, object) + s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey) + return + } } else { // For non-versioned objects, try to reuse entry from conditional header check if result.Entry != nil { @@ -411,7 +417,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) var fetchErr error objectEntryForSSE, fetchErr = s3a.fetchObjectEntry(bucket, object) if fetchErr != nil { - glog.Errorf("GetObjectHandler: failed to get entry for SSE check: %v", fetchErr) + glog.Warningf("GetObjectHandler: failed to get entry for %s/%s: %v", bucket, object, fetchErr) s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) return } @@ -1135,6 +1141,12 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request var objectEntryForSSE *filer_pb.Entry if versioningConfigured { objectEntryForSSE = entry + // Safety check - this should never happen as versioned path handles errors above + if objectEntryForSSE == nil { + glog.Errorf("HeadObjectHandler: unexpected nil entry for versioned object %s/%s", bucket, object) + s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey) + return + } } else { // For non-versioned objects, try to reuse entry from conditional header check if result.Entry != nil { @@ -1149,7 +1161,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request var fetchErr error objectEntryForSSE, fetchErr = s3a.fetchObjectEntry(bucket, object) if fetchErr != nil { - glog.Errorf("HeadObjectHandler: failed to get entry for SSE check: %v", fetchErr) + glog.Warningf("HeadObjectHandler: failed to get entry for %s/%s: %v", bucket, object, fetchErr) s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) return } diff --git a/weed/s3api/s3api_object_handlers_put.go b/weed/s3api/s3api_object_handlers_put.go index fe54a87a2..3c2b4647c 100644 --- a/weed/s3api/s3api_object_handlers_put.go +++ b/weed/s3api/s3api_object_handlers_put.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "net/url" "path/filepath" "strconv" "strings" @@ -281,8 +282,18 @@ func (s3a *S3ApiServer) putToFiler(r *http.Request, uploadUrl string, dataReader // Parse the upload URL to extract the file path // uploadUrl format: http://filer:8888/path/to/bucket/object + // The uploadUrl is URL-encoded by toFilerUrl(), so we need to decode it filePath := strings.TrimPrefix(uploadUrl, "http://"+string(s3a.option.Filer)) + // URL-decode the path to get the actual file path + // This is critical because toFilerUrl() encodes special characters like (, ), etc. + decodedPath, decodeErr := url.PathUnescape(filePath) + if decodeErr != nil { + glog.Errorf("putToFiler: failed to decode path %q: %v", filePath, decodeErr) + return "", s3err.ErrInternalError, "" + } + filePath = decodedPath + // Calculate MD5 hash hash := md5.New() var body = io.TeeReader(dataReader, hash)