github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/streaming-signature-v4_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bufio" 22 "bytes" 23 "fmt" 24 "io" 25 "strings" 26 "testing" 27 ) 28 29 // Test read chunk line. 30 func TestReadChunkLine(t *testing.T) { 31 type testCase struct { 32 reader *bufio.Reader 33 expectedErr error 34 chunkSize []byte 35 chunkSignature []byte 36 } 37 // List of readers used. 38 readers := []io.Reader{ 39 // Test - 1 40 bytes.NewReader([]byte("1000;chunk-signature=111123333333333333334444211\r\n")), 41 // Test - 2 42 bytes.NewReader([]byte("1000;")), 43 // Test - 3 44 bytes.NewReader([]byte(fmt.Sprintf("%4097d", 1))), 45 // Test - 4 46 bytes.NewReader([]byte("1000;chunk-signature=111123333333333333334444211\r\n")), 47 } 48 testCases := []testCase{ 49 // Test - 1 - small bufio reader. 50 { 51 bufio.NewReaderSize(readers[0], 16), 52 errLineTooLong, 53 nil, 54 nil, 55 }, 56 // Test - 2 - unexpected end of the reader. 57 { 58 bufio.NewReader(readers[1]), 59 io.ErrUnexpectedEOF, 60 nil, 61 nil, 62 }, 63 // Test - 3 - line too long bigger than 4k+1 64 { 65 bufio.NewReader(readers[2]), 66 errLineTooLong, 67 nil, 68 nil, 69 }, 70 // Test - 4 - parse the chunk reader properly. 71 { 72 bufio.NewReader(readers[3]), 73 nil, 74 []byte("1000"), 75 []byte("111123333333333333334444211"), 76 }, 77 } 78 // Valid test cases for each chunk line. 79 for i, tt := range testCases { 80 chunkSize, chunkSignature, err := readChunkLine(tt.reader) 81 if err != tt.expectedErr { 82 t.Errorf("Test %d: Expected %s, got %s", i+1, tt.expectedErr, err) 83 } 84 if !bytes.Equal(chunkSize, tt.chunkSize) { 85 t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSize), string(chunkSize)) 86 } 87 if !bytes.Equal(chunkSignature, tt.chunkSignature) { 88 t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSignature), string(chunkSignature)) 89 } 90 } 91 } 92 93 // Test parsing s3 chunk extension. 94 func TestParseS3ChunkExtension(t *testing.T) { 95 type testCase struct { 96 buf []byte 97 chunkSize []byte 98 chunkSign []byte 99 } 100 101 tests := []testCase{ 102 // Test - 1 valid case. 103 { 104 []byte("10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), 105 []byte("10000"), 106 []byte("ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), 107 }, 108 // Test - 2 no chunk extension, return same buffer. 109 { 110 []byte("10000;"), 111 []byte("10000;"), 112 nil, 113 }, 114 // Test - 3 no chunk size, return error. 115 { 116 []byte(";chunk-signature="), 117 nil, 118 nil, 119 }, 120 // Test - 4 removes trailing slash. 121 { 122 []byte("10000;chunk-signature=ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648 \t \n"), 123 []byte("10000"), 124 []byte("ad80c730a21e5b8d04586a2213dd63b9a0e99e0e2307b0ade35a65485a288648"), 125 }, 126 } 127 // Validate chunk extension removal. 128 for i, tt := range tests { 129 // Extract chunk size and chunk signature after parsing a standard chunk-extension format. 130 hexChunkSize, hexChunkSignature := parseS3ChunkExtension(tt.buf) 131 if !bytes.Equal(hexChunkSize, tt.chunkSize) { 132 t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSize), string(hexChunkSize)) 133 } 134 if !bytes.Equal(hexChunkSignature, tt.chunkSign) { 135 t.Errorf("Test %d: Expected %s, got %s", i+1, string(tt.chunkSign), string(hexChunkSignature)) 136 } 137 } 138 } 139 140 // Test read CRLF characters on input reader. 141 func TestReadCRLF(t *testing.T) { 142 type testCase struct { 143 reader io.Reader 144 expectedErr error 145 } 146 tests := []testCase{ 147 // Test - 1 valid buffer with CRLF. 148 {bytes.NewReader([]byte("\r\n")), nil}, 149 // Test - 2 invalid buffer with no CRLF. 150 {bytes.NewReader([]byte("he")), errMalformedEncoding}, 151 // Test - 3 invalid buffer with more characters. 152 {bytes.NewReader([]byte("he\r\n")), errMalformedEncoding}, 153 // Test - 4 smaller buffer than expected. 154 {bytes.NewReader([]byte("h")), io.ErrUnexpectedEOF}, 155 } 156 for i, tt := range tests { 157 err := readCRLF(tt.reader) 158 if err != tt.expectedErr { 159 t.Errorf("Test %d: Expected %s, got %s this", i+1, tt.expectedErr, err) 160 } 161 } 162 } 163 164 // Tests parsing hex number into its uint64 decimal equivalent. 165 func TestParseHexUint(t *testing.T) { 166 type testCase struct { 167 in string 168 want uint64 169 wantErr string 170 } 171 tests := []testCase{ 172 {"x", 0, "invalid byte in chunk length"}, 173 {"0000000000000000", 0, ""}, 174 {"0000000000000001", 1, ""}, 175 {"ffffffffffffffff", 1<<64 - 1, ""}, 176 {"FFFFFFFFFFFFFFFF", 1<<64 - 1, ""}, 177 {"000000000000bogus", 0, "invalid byte in chunk length"}, 178 {"00000000000000000", 0, "http chunk length too large"}, // could accept if we wanted 179 {"10000000000000000", 0, "http chunk length too large"}, 180 {"00000000000000001", 0, "http chunk length too large"}, // could accept if we wanted 181 } 182 for i := uint64(0); i <= 1234; i++ { 183 tests = append(tests, testCase{in: fmt.Sprintf("%x", i), want: i}) 184 } 185 for _, tt := range tests { 186 got, err := parseHexUint([]byte(tt.in)) 187 if tt.wantErr != "" { 188 if err != nil && !strings.Contains(err.Error(), tt.wantErr) { 189 t.Errorf("parseHexUint(%q) = %v, %v; want error %q", tt.in, got, err, tt.wantErr) 190 } 191 } else { 192 if err != nil || got != tt.want { 193 t.Errorf("parseHexUint(%q) = %v, %v; want %v", tt.in, got, err, tt.want) 194 } 195 } 196 } 197 }