| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test read chunk line.
 | 
					
						
							|  |  |  | func TestReadChunkLine(t *testing.T) { | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		reader         *bufio.Reader | 
					
						
							|  |  |  | 		expectedErr    error | 
					
						
							|  |  |  | 		chunkSize      []byte | 
					
						
							|  |  |  | 		chunkSignature []byte | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// List of readers used.
 | 
					
						
							|  |  |  | 	readers := []io.Reader{ | 
					
						
							|  |  |  | 		// Test - 1
 | 
					
						
							|  |  |  | 		bytes.NewReader([]byte("1000;chunk-signature=111123333333333333334444211\r\n")), | 
					
						
							|  |  |  | 		// Test - 2
 | 
					
						
							|  |  |  | 		bytes.NewReader([]byte("1000;")), | 
					
						
							|  |  |  | 		// Test - 3
 | 
					
						
							|  |  |  | 		bytes.NewReader([]byte(fmt.Sprintf("%4097d", 1))), | 
					
						
							|  |  |  | 		// Test - 4
 | 
					
						
							|  |  |  | 		bytes.NewReader([]byte("1000;chunk-signature=111123333333333333334444211\r\n")), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	testCases := []testCase{ | 
					
						
							|  |  |  | 		// Test - 1 - small bufio reader.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bufio.NewReaderSize(readers[0], 16), | 
					
						
							|  |  |  | 			errLineTooLong, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 2 - unexpected end of the reader.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bufio.NewReader(readers[1]), | 
					
						
							|  |  |  | 			io.ErrUnexpectedEOF, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 3 - line too long bigger than 4k+1
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bufio.NewReader(readers[2]), | 
					
						
							|  |  |  | 			errLineTooLong, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 4 - parse the chunk reader properly.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bufio.NewReader(readers[3]), | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			[]byte("1000"), | 
					
						
							|  |  |  | 			[]byte("111123333333333333334444211"), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Valid test cases for each chunk line.
 | 
					
						
							|  |  |  | 	for i, tt := range testCases { | 
					
						
							|  |  |  | 		chunkSize, chunkSignature, err := readChunkLine(tt.reader) | 
					
						
							|  |  |  | 		if err != tt.expectedErr { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s", i+1, tt.expectedErr, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(chunkSize, tt.chunkSize) { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSize), string(chunkSize)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(chunkSignature, tt.chunkSignature) { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSignature), string(chunkSignature)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test parsing s3 chunk extension.
 | 
					
						
							|  |  |  | func TestParseS3ChunkExtension(t *testing.T) { | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		buf       []byte | 
					
						
							|  |  |  | 		chunkSize []byte | 
					
						
							|  |  |  | 		chunkSign []byte | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tests := []testCase{ | 
					
						
							|  |  |  | 		// Test - 1 valid case.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			[]byte("10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), | 
					
						
							|  |  |  | 			[]byte("10000"), | 
					
						
							|  |  |  | 			[]byte("ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 2 no chunk extension, return same buffer.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			[]byte("10000;"), | 
					
						
							|  |  |  | 			[]byte("10000;"), | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 3 no chunk size, return error.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			[]byte(";chunk-signature="), | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test - 4 removes trailing slash.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			[]byte("10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648    \t \n"), | 
					
						
							|  |  |  | 			[]byte("10000"), | 
					
						
							|  |  |  | 			[]byte("ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Validate chunk extension removal.
 | 
					
						
							|  |  |  | 	for i, tt := range tests { | 
					
						
							|  |  |  | 		// Extract chunk size and chunk signature after parsing a standard chunk-extension format.
 | 
					
						
							|  |  |  | 		hexChunkSize, hexChunkSignature := parseS3ChunkExtension(tt.buf) | 
					
						
							|  |  |  | 		if !bytes.Equal(hexChunkSize, tt.chunkSize) { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSize), string(hexChunkSize)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(hexChunkSignature, tt.chunkSign) { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSign), string(hexChunkSignature)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-10 16:42:32 +08:00
										 |  |  | // Test read CRLF characters on input reader.
 | 
					
						
							|  |  |  | func TestReadCRLF(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		reader      io.Reader | 
					
						
							|  |  |  | 		expectedErr error | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tests := []testCase{ | 
					
						
							|  |  |  | 		// Test - 1 valid buffer with CRLF.
 | 
					
						
							|  |  |  | 		{bytes.NewReader([]byte("\r\n")), nil}, | 
					
						
							|  |  |  | 		// Test - 2 invalid buffer with no CRLF.
 | 
					
						
							|  |  |  | 		{bytes.NewReader([]byte("he")), errMalformedEncoding}, | 
					
						
							|  |  |  | 		// Test - 3 invalid buffer with more characters.
 | 
					
						
							|  |  |  | 		{bytes.NewReader([]byte("he\r\n")), errMalformedEncoding}, | 
					
						
							|  |  |  | 		// Test - 4 smaller buffer than expected.
 | 
					
						
							|  |  |  | 		{bytes.NewReader([]byte("h")), io.ErrUnexpectedEOF}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, tt := range tests { | 
					
						
							| 
									
										
										
										
											2016-10-10 16:42:32 +08:00
										 |  |  | 		err := readCRLF(tt.reader) | 
					
						
							| 
									
										
										
										
											2016-08-09 11:56:29 +08:00
										 |  |  | 		if err != tt.expectedErr { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %s, got %s this", i+1, tt.expectedErr, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests parsing hex number into its uint64 decimal equivalent.
 | 
					
						
							|  |  |  | func TestParseHexUint(t *testing.T) { | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		in      string | 
					
						
							|  |  |  | 		want    uint64 | 
					
						
							|  |  |  | 		wantErr string | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tests := []testCase{ | 
					
						
							|  |  |  | 		{"x", 0, "invalid byte in chunk length"}, | 
					
						
							|  |  |  | 		{"0000000000000000", 0, ""}, | 
					
						
							|  |  |  | 		{"0000000000000001", 1, ""}, | 
					
						
							|  |  |  | 		{"ffffffffffffffff", 1<<64 - 1, ""}, | 
					
						
							|  |  |  | 		{"FFFFFFFFFFFFFFFF", 1<<64 - 1, ""}, | 
					
						
							|  |  |  | 		{"000000000000bogus", 0, "invalid byte in chunk length"}, | 
					
						
							|  |  |  | 		{"00000000000000000", 0, "http chunk length too large"}, // could accept if we wanted
 | 
					
						
							|  |  |  | 		{"10000000000000000", 0, "http chunk length too large"}, | 
					
						
							|  |  |  | 		{"00000000000000001", 0, "http chunk length too large"}, // could accept if we wanted
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := uint64(0); i <= 1234; i++ { | 
					
						
							|  |  |  | 		tests = append(tests, testCase{in: fmt.Sprintf("%x", i), want: i}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		got, err := parseHexUint([]byte(tt.in)) | 
					
						
							|  |  |  | 		if tt.wantErr != "" { | 
					
						
							|  |  |  | 			if !strings.Contains(fmt.Sprint(err), tt.wantErr) { | 
					
						
							|  |  |  | 				t.Errorf("parseHexUint(%q) = %v, %v; want error %q", tt.in, got, err, tt.wantErr) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if err != nil || got != tt.want { | 
					
						
							|  |  |  | 				t.Errorf("parseHexUint(%q) = %v, %v; want %v", tt.in, got, err, tt.want) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |