From 12cd2da265e02a74af4647e38a92fc497aa1fc21 Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Wed, 26 Oct 2016 14:36:22 +0530 Subject: [PATCH] Add PutObjectHandler unit tests covering failure cases (#3096) --- cmd/object-handlers_test.go | 108 ++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/cmd/object-handlers_test.go b/cmd/object-handlers_test.go index a7ab5464a..f1c6099d5 100644 --- a/cmd/object-handlers_test.go +++ b/cmd/object-handlers_test.go @@ -292,6 +292,7 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam unexpectedEOF signatureMismatch chunkDateMismatch + tooBigDecodedLength ) // byte data for PutObject. @@ -444,9 +445,27 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam shouldPass: false, fault: chunkDateMismatch, }, + // Test case - 10 + // Set x-amz-decoded-content-length to a value too big to hold in int64. + { + bucketName: bucketName, + objectName: objectName, + data: oneKData, + dataLen: 1024, + chunkSize: 1024, + expectedContent: []byte{}, + expectedRespStatus: http.StatusInternalServerError, + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + shouldPass: false, + fault: tooBigDecodedLength, + }, } // Iterating over the cases, fetching the object validating the response. for i, testCase := range testCases { + if i != 9 { + continue + } // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. rec := httptest.NewRecorder() // construct HTTP request for Put Object end point. @@ -477,6 +496,9 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam req, err = malformDataSigV4(req, 'z') case unexpectedEOF: req, err = truncateChunkByHalfSigv4(req) + case tooBigDecodedLength: + // Set decoded length to a large value out of int64 range to simulate parse failure. + req.Header.Set("x-amz-decoded-content-length", "9999999999999999999999") } // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. // Call the ServeHTTP to execute the handler,`func (api objectAPIHandlers) GetObjectHandler` handles the request. @@ -529,14 +551,29 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a // byte data for PutObject. bytesData := generateBytesData(6 * 1024 * 1024) + copySourceHeader := http.Header{} + copySourceHeader.Set("X-Amz-Copy-Source", "somewhere") + invalidMD5Header := http.Header{} + invalidMD5Header.Set("Content-Md5", "42") + + addCustomHeaders := func(req *http.Request, customHeaders http.Header) { + for k, values := range customHeaders { + for _, value := range values { + req.Header.Set(k, value) + } + } + } + // test cases with inputs and expected result for GetObject. testCases := []struct { bucketName string objectName string + headers http.Header data []byte dataLen int accessKey string secretKey string + fault Fault // expected output. expectedRespStatus int // expected response status body. }{ @@ -564,6 +601,54 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a expectedRespStatus: http.StatusForbidden, }, + // Test case - 3. + // Test Case with invalid header key X-Amz-Copy-Source. + { + bucketName: bucketName, + objectName: objectName, + headers: copySourceHeader, + data: bytesData, + dataLen: len(bytesData), + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + expectedRespStatus: http.StatusBadRequest, + }, + // Test case - 4. + // Test Case with invalid Content-Md5 value + { + bucketName: bucketName, + objectName: objectName, + headers: invalidMD5Header, + data: bytesData, + dataLen: len(bytesData), + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + expectedRespStatus: http.StatusBadRequest, + }, + // Test case - 5. + // Test Case with object greater than maximum allowed size. + { + bucketName: bucketName, + objectName: objectName, + data: bytesData, + dataLen: len(bytesData), + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + fault: TooBigObject, + expectedRespStatus: http.StatusBadRequest, + }, + // Test case - 6. + // Test Case with missing content length + { + bucketName: bucketName, + objectName: objectName, + data: bytesData, + dataLen: len(bytesData), + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + fault: MissingContentLength, + expectedRespStatus: http.StatusLengthRequired, + }, } // Iterating over the cases, fetching the object validating the response. for i, testCase := range testCases { @@ -576,6 +661,17 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a if err != nil { t.Fatalf("Test %d: Failed to create HTTP request for Put Object: %v", i+1, err) } + // Add test case specific headers to the request. + addCustomHeaders(req, testCase.headers) + + // Inject faults if specified in testCase.fault + switch testCase.fault { + case MissingContentLength: + req.ContentLength = -1 + req.TransferEncoding = []string{} + case TooBigObject: + req.ContentLength = maxObjectSize + 1 + } // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. // Call the ServeHTTP to execute the handler,`func (api objectAPIHandlers) GetObjectHandler` handles the request. apiRouter.ServeHTTP(rec, req) @@ -608,6 +704,18 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a t.Fatalf("Test %d: %s: Failed to create HTTP request for PutObject: %v", i+1, instanceType, err) } + // Add test case specific headers to the request. + addCustomHeaders(reqV2, testCase.headers) + + // Inject faults if specified in testCase.fault + switch testCase.fault { + case MissingContentLength: + reqV2.ContentLength = -1 + reqV2.TransferEncoding = []string{} + case TooBigObject: + reqV2.ContentLength = maxObjectSize + 1 + } + // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. // Call the ServeHTTP to execute the handler. apiRouter.ServeHTTP(recV2, reqV2)