Repository navigation
Expand file tree
/
Copy pathhandler-utils.go
More file actions
190 lines (178 loc) · 6.26 KB
/
Copy pathhandler-utils.go
File metadata and controls
190 lines (178 loc) · 6.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
* Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"strings"
)
// Validates location constraint in PutBucket request body.
// The location value in the request body should match the
// region configured at serverConfig, otherwise error is returned.
func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) {
serverRegion := serverConfig.GetRegion()
// If the request has no body with content-length set to 0,
// we do not have to validate location constraint. Bucket will
// be created at default region.
locationConstraint := createBucketLocationConfiguration{}
err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength)
if err == nil || err == io.EOF {
// Successfully decoded, proceed to verify the region.
// Once region has been obtained we proceed to verify it.
incomingRegion := locationConstraint.Location
if incomingRegion == "" {
// Location constraint is empty for region "us-east-1",
// in accordance with protocol.
incomingRegion = "us-east-1"
}
// Return errInvalidRegion if location constraint does not match
// with configured region.
s3Error = ErrNone
if serverRegion != incomingRegion {
s3Error = ErrInvalidRegion
}
return s3Error
}
errorIf(err, "Unable to xml decode location constraint")
// Treat all other failures as XML parsing errors.
return ErrMalformedXML
}
// Supported headers that needs to be extracted.
var supportedHeaders = []string{
"content-type",
"cache-control",
"content-encoding",
"content-disposition",
// Add more supported headers here.
}
// isMetadataDirectiveValid - check if metadata-directive is valid.
func isMetadataDirectiveValid(h http.Header) bool {
_, ok := h[http.CanonicalHeaderKey("X-Amz-Metadata-Directive")]
if ok {
// Check atleast set metadata-directive is valid.
return (isMetadataCopy(h) || isMetadataReplace(h))
}
// By default if x-amz-metadata-directive is not we
// treat it as 'COPY' this function returns true.
return true
}
// Check if the metadata COPY is requested.
func isMetadataCopy(h http.Header) bool {
return h.Get("X-Amz-Metadata-Directive") == "COPY"
}
// Check if the metadata REPLACE is requested.
func isMetadataReplace(h http.Header) bool {
return h.Get("X-Amz-Metadata-Directive") == "REPLACE"
}
// Splits an incoming path into bucket and object components.
func path2BucketAndObject(path string) (bucket, object string) {
// Skip the first element if it is '/', split the rest.
path = strings.TrimPrefix(path, "/")
pathComponents := strings.SplitN(path, "/", 2)
// Save the bucket and object extracted from path.
switch len(pathComponents) {
case 1:
bucket = pathComponents[0]
case 2:
bucket = pathComponents[0]
object = pathComponents[1]
}
return bucket, object
}
// extractMetadataFromHeader extracts metadata from HTTP header.
func extractMetadataFromHeader(header http.Header) map[string]string {
metadata := make(map[string]string)
// Save standard supported headers.
for _, supportedHeader := range supportedHeaders {
canonicalHeader := http.CanonicalHeaderKey(supportedHeader)
// HTTP headers are case insensitive, look for both canonical
// and non canonical entries.
if _, ok := header[canonicalHeader]; ok {
metadata[supportedHeader] = header.Get(canonicalHeader)
} else if _, ok := header[supportedHeader]; ok {
metadata[supportedHeader] = header.Get(supportedHeader)
}
}
// Go through all other headers for any additional headers that needs to be saved.
for key := range header {
cKey := http.CanonicalHeaderKey(key)
if strings.HasPrefix(cKey, "X-Amz-Meta-") {
metadata[cKey] = header.Get(key)
} else if strings.HasPrefix(key, "X-Minio-Meta-") {
metadata[cKey] = header.Get(key)
}
}
// Return.
return metadata
}
// extractMetadataFromForm extracts metadata from Post Form.
func extractMetadataFromForm(formValues map[string]string) map[string]string {
metadata := make(map[string]string)
// Save standard supported headers.
for _, supportedHeader := range supportedHeaders {
canonicalHeader := http.CanonicalHeaderKey(supportedHeader)
// Form field names are case insensitive, look for both canonical
// and non canonical entries.
if _, ok := formValues[canonicalHeader]; ok {
metadata[supportedHeader] = formValues[canonicalHeader]
} else if _, ok := formValues[supportedHeader]; ok {
metadata[supportedHeader] = formValues[canonicalHeader]
}
}
// Go through all other form values for any additional headers that needs to be saved.
for key := range formValues {
cKey := http.CanonicalHeaderKey(key)
if strings.HasPrefix(cKey, "X-Amz-Meta-") {
metadata[cKey] = formValues[key]
} else if strings.HasPrefix(cKey, "X-Minio-Meta-") {
metadata[cKey] = formValues[key]
}
}
return metadata
}
// Extract form fields and file data from a HTTP POST Policy
func extractPostPolicyFormValues(reader *multipart.Reader) (filePart io.Reader, fileName string, formValues map[string]string, err error) {
/// HTML Form values
formValues = make(map[string]string)
fileName = ""
for err == nil {
var part *multipart.Part
part, err = reader.NextPart()
if part != nil {
canonicalFormName := http.CanonicalHeaderKey(part.FormName())
if canonicalFormName != "File" {
var buffer []byte
limitReader := io.LimitReader(part, maxFormFieldSize+1)
buffer, err = ioutil.ReadAll(limitReader)
if err != nil {
return nil, "", nil, err
}
if int64(len(buffer)) > maxFormFieldSize {
return nil, "", nil, errSizeUnexpected
}
formValues[canonicalFormName] = string(buffer)
} else {
filePart = part
fileName = part.FileName()
// As described in S3 spec, we expect file to be the last form field
break
}
}
}
return filePart, fileName, formValues, nil
}