handler: simplify parsing valid location constraint. (#4040)

Separate out validating v/s parsing logic in
isValidLocationConstraint() into parseLocationConstraint()
and isValidLocation()

Additionally also set `X-Amz-Bucket-Region` as part of the
common headers for the clients to fallback on in-case of any
region related errors.
This commit is contained in:
Harshavardhana 2017-04-03 14:50:09 -07:00 committed by GitHub
parent 4041e5f20d
commit 3fe33e7b15
5 changed files with 35 additions and 31 deletions

View file

@ -1346,6 +1346,11 @@ func TestToAdminAPIErr(t *testing.T) {
}
func TestWriteSetConfigResponse(t *testing.T) {
rootPath, err := newTestConfig(globalMinioDefaultRegion)
if err != nil {
t.Fatal(err)
}
defer removeAll(rootPath)
testCases := []struct {
status bool
errs []error

View file

@ -36,6 +36,7 @@ func setCommonHeaders(w http.ResponseWriter) {
// Set unique request ID for each reply.
w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow()))
w.Header().Set("Server", globalServerUserAgent)
w.Header().Set("X-Amz-Bucket-Region", serverConfig.GetRegion())
w.Header().Set("Accept-Ranges", "bytes")
}

View file

@ -370,13 +370,20 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
vars := mux.Vars(r)
bucket := vars["bucket"]
// Validate if incoming location constraint is valid, reject
// requests which do not follow valid region requirements.
if s3Error := isValidLocationConstraint(r); s3Error != ErrNone {
// Parse incoming location constraint.
location, s3Error := parseLocationConstraint(r)
if s3Error != ErrNone {
writeErrorResponse(w, s3Error, r.URL)
return
}
// Validate if location sent by the client is valid, reject
// requests which do not follow valid region requirements.
if !isValidLocation(location) {
writeErrorResponse(w, ErrInvalidRegion, r.URL)
return
}
bucketLock := globalNSMutex.NewNSLock(bucket, "")
bucketLock.Lock()
defer bucketLock.Unlock()

View file

@ -24,36 +24,29 @@ import (
"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()
// Parses location constraint from the incoming reader.
func parseLocationConstraint(r *http.Request) (location string, s3Error APIErrorCode) {
// 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 globalMinioDefaultRegion,
// in accordance with protocol.
incomingRegion = globalMinioDefaultRegion
}
// Return errInvalidRegion if location constraint does not match
// with configured region.
s3Error = ErrNone
if serverRegion != incomingRegion {
s3Error = ErrInvalidRegion
}
return s3Error
if err != nil && err != io.EOF {
errorIf(err, "Unable to xml decode location constraint")
// Treat all other failures as XML parsing errors.
return "", ErrMalformedXML
} // else for both err as nil or io.EOF
location = locationConstraint.Location
if location == "" {
location = globalMinioDefaultRegion
}
errorIf(err, "Unable to xml decode location constraint")
// Treat all other failures as XML parsing errors.
return ErrMalformedXML
return location, ErrNone
}
// Validates input location is same as configured region
// of Minio server.
func isValidLocation(location string) bool {
return serverConfig.GetRegion() == location
}
// Supported headers that needs to be extracted.

View file

@ -39,7 +39,7 @@ func TestIsValidLocationContraint(t *testing.T) {
Body: ioutil.NopCloser(bytes.NewBuffer([]byte("<>"))),
ContentLength: int64(len("<>")),
}
if err := isValidLocationConstraint(malformedReq); err != ErrMalformedXML {
if _, err := parseLocationConstraint(malformedReq); err != ErrMalformedXML {
t.Fatal("Unexpected error: ", err)
}
@ -68,8 +68,6 @@ func TestIsValidLocationContraint(t *testing.T) {
// Test case - 2.
// In case of empty request body ErrNone is returned.
{"", globalMinioDefaultRegion, ErrNone},
// Test case - 3.
{"eu-central-1", globalMinioDefaultRegion, ErrInvalidRegion},
}
for i, testCase := range testCases {
inputRequest, e := createExpectedRequest(&http.Request{}, testCase.locationForInputRequest)
@ -77,7 +75,7 @@ func TestIsValidLocationContraint(t *testing.T) {
t.Fatalf("Test %d: Failed to Marshal bucket configuration", i+1)
}
serverConfig.SetRegion(testCase.serverConfigRegion)
actualCode := isValidLocationConstraint(inputRequest)
_, actualCode := parseLocationConstraint(inputRequest)
if testCase.expectedCode != actualCode {
t.Errorf("Test %d: Expected the APIErrCode to be %d, but instead found %d", i+1, testCase.expectedCode, actualCode)
}