Browse Source
Merge pull request #3047 from shichanglin5/master
Merge pull request #3047 from shichanglin5/master
fix the problem of metadata and tagging loss when files are copiedpull/3051/head
Chris Lu
3 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 551 additions and 8 deletions
-
9weed/s3api/http/header.go
-
124weed/s3api/s3api_object_copy_handlers.go
-
426weed/s3api/s3api_object_copy_handlers_test.go
@ -0,0 +1,426 @@ |
|||||
|
package s3api |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
headers "github.com/chrislusf/seaweedfs/weed/s3api/http" |
||||
|
"net/http" |
||||
|
"reflect" |
||||
|
"sort" |
||||
|
"strings" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
type H map[string]string |
||||
|
|
||||
|
func (h H) String() string { |
||||
|
pairs := make([]string, 0, len(h)) |
||||
|
for k, v := range h { |
||||
|
pairs = append(pairs, fmt.Sprintf("%s : %s", k, v)) |
||||
|
} |
||||
|
sort.Strings(pairs) |
||||
|
join := strings.Join(pairs, "\n") |
||||
|
return "\n" + join + "\n" |
||||
|
} |
||||
|
|
||||
|
var processMetadataTestCases = []struct { |
||||
|
caseId int |
||||
|
request H |
||||
|
existing H |
||||
|
getTags H |
||||
|
want H |
||||
|
}{ |
||||
|
{ |
||||
|
201, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=existing", |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
202, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=existing", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
203, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
204, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
205, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{}, |
||||
|
H{}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
206, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
207, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-Type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"A": "B", |
||||
|
"a": "b", |
||||
|
"type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
var processMetadataBytesTestCases = []struct { |
||||
|
caseId int |
||||
|
request H |
||||
|
existing H |
||||
|
want H |
||||
|
}{ |
||||
|
{ |
||||
|
101, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
102, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
103, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "request", |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
104, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "request", |
||||
|
}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
105, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "existing", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "existing", |
||||
|
}, |
||||
|
H{}, |
||||
|
}, |
||||
|
|
||||
|
{ |
||||
|
107, |
||||
|
H{ |
||||
|
"User-Agent": "firefox", |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging": "A=B&a=b&type=request", |
||||
|
headers.AmzUserMetaDirective: DirectiveReplace, |
||||
|
headers.AmzObjectTaggingDirective: DirectiveReplace, |
||||
|
}, |
||||
|
H{}, |
||||
|
H{ |
||||
|
"X-Amz-Meta-My-Meta": "request", |
||||
|
"X-Amz-Tagging-A": "B", |
||||
|
"X-Amz-Tagging-a": "b", |
||||
|
"X-Amz-Tagging-type": "request", |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
func TestProcessMetadata(t *testing.T) { |
||||
|
for _, tc := range processMetadataTestCases { |
||||
|
reqHeader := transferHToHeader(tc.request) |
||||
|
existing := transferHToHeader(tc.existing) |
||||
|
replaceMeta, replaceTagging := replaceDirective(reqHeader) |
||||
|
|
||||
|
err := processMetadata(reqHeader, existing, replaceMeta, replaceTagging, func(_ string, _ string) (tags map[string]string, err error) { |
||||
|
return tc.getTags, nil |
||||
|
}, "", "") |
||||
|
if err != nil { |
||||
|
t.Error(err) |
||||
|
} |
||||
|
|
||||
|
result := transferHeaderToH(reqHeader) |
||||
|
fmtTagging(result, tc.want) |
||||
|
|
||||
|
if !reflect.DeepEqual(result, tc.want) { |
||||
|
t.Error(fmt.Errorf("\n### CaseID: %d ###"+ |
||||
|
"\nRequest:%v"+ |
||||
|
"\nExisting:%v"+ |
||||
|
"\nGetTags:%v"+ |
||||
|
"\nWant:%v"+ |
||||
|
"\nActual:%v", |
||||
|
tc.caseId, tc.request, tc.existing, tc.getTags, tc.want, result)) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestProcessMetadataBytes(t *testing.T) { |
||||
|
for _, tc := range processMetadataBytesTestCases { |
||||
|
reqHeader := transferHToHeader(tc.request) |
||||
|
existing := transferHToBytesArr(tc.existing) |
||||
|
replaceMeta, replaceTagging := replaceDirective(reqHeader) |
||||
|
extends := processMetadataBytes(reqHeader, existing, replaceMeta, replaceTagging) |
||||
|
|
||||
|
result := transferBytesArrToH(extends) |
||||
|
fmtTagging(result, tc.want) |
||||
|
|
||||
|
if !reflect.DeepEqual(result, tc.want) { |
||||
|
t.Error(fmt.Errorf("\n### CaseID: %d ###"+ |
||||
|
"\nRequest:%v"+ |
||||
|
"\nExisting:%v"+ |
||||
|
"\nWant:%v"+ |
||||
|
"\nActual:%v", |
||||
|
tc.caseId, tc.request, tc.existing, tc.want, result)) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func fmtTagging(maps ...map[string]string) { |
||||
|
for _, m := range maps { |
||||
|
if tagging := m[headers.AmzObjectTagging]; len(tagging) > 0 { |
||||
|
split := strings.Split(tagging, "&") |
||||
|
sort.Strings(split) |
||||
|
m[headers.AmzObjectTagging] = strings.Join(split, "&") |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func transferHToHeader(data map[string]string) http.Header { |
||||
|
header := http.Header{} |
||||
|
for k, v := range data { |
||||
|
header.Add(k, v) |
||||
|
} |
||||
|
return header |
||||
|
} |
||||
|
|
||||
|
func transferHToBytesArr(data map[string]string) map[string][]byte { |
||||
|
m := make(map[string][]byte, len(data)) |
||||
|
for k, v := range data { |
||||
|
m[k] = []byte(v) |
||||
|
} |
||||
|
return m |
||||
|
} |
||||
|
|
||||
|
func transferBytesArrToH(data map[string][]byte) H { |
||||
|
m := make(map[string]string, len(data)) |
||||
|
for k, v := range data { |
||||
|
m[k] = string(v) |
||||
|
} |
||||
|
return m |
||||
|
} |
||||
|
|
||||
|
func transferHeaderToH(data map[string][]string) H { |
||||
|
m := make(map[string]string, len(data)) |
||||
|
for k, v := range data { |
||||
|
m[k] = v[len(v)-1] |
||||
|
} |
||||
|
return m |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue