google.golang.org/grpc@v1.72.2/internal/transport/http_util_test.go (about) 1 /* 2 * 3 * Copyright 2014 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package transport 20 21 import ( 22 "errors" 23 "fmt" 24 "io" 25 "net" 26 "reflect" 27 "testing" 28 "time" 29 ) 30 31 func (s) TestTimeoutDecode(t *testing.T) { 32 for _, test := range []struct { 33 // input 34 s string 35 // output 36 d time.Duration 37 err error 38 }{ 39 {"1234S", time.Second * 1234, nil}, 40 {"1234x", 0, fmt.Errorf("transport: timeout unit is not recognized: %q", "1234x")}, 41 {"1", 0, fmt.Errorf("transport: timeout string is too short: %q", "1")}, 42 {"", 0, fmt.Errorf("transport: timeout string is too short: %q", "")}, 43 } { 44 d, err := decodeTimeout(test.s) 45 if d != test.d || fmt.Sprint(err) != fmt.Sprint(test.err) { 46 t.Fatalf("timeoutDecode(%q) = %d, %v, want %d, %v", test.s, int64(d), err, int64(test.d), test.err) 47 } 48 } 49 } 50 51 func (s) TestEncodeGrpcMessage(t *testing.T) { 52 for _, tt := range []struct { 53 input string 54 expected string 55 }{ 56 {"", ""}, 57 {"Hello", "Hello"}, 58 {"\u0000", "%00"}, 59 {"%", "%25"}, 60 {"系统", "%E7%B3%BB%E7%BB%9F"}, 61 {string([]byte{0xff, 0xfe, 0xfd}), "%EF%BF%BD%EF%BF%BD%EF%BF%BD"}, 62 } { 63 actual := encodeGrpcMessage(tt.input) 64 if tt.expected != actual { 65 t.Errorf("encodeGrpcMessage(%q) = %q, want %q", tt.input, actual, tt.expected) 66 } 67 } 68 69 // make sure that all the visible ASCII chars except '%' are not percent encoded. 70 for i := ' '; i <= '~' && i != '%'; i++ { 71 output := encodeGrpcMessage(string(i)) 72 if output != string(i) { 73 t.Errorf("encodeGrpcMessage(%v) = %v, want %v", string(i), output, string(i)) 74 } 75 } 76 77 // make sure that all the invisible ASCII chars and '%' are percent encoded. 78 for i := rune(0); i == '%' || (i >= rune(0) && i < ' ') || (i > '~' && i <= rune(127)); i++ { 79 output := encodeGrpcMessage(string(i)) 80 expected := fmt.Sprintf("%%%02X", i) 81 if output != expected { 82 t.Errorf("encodeGrpcMessage(%v) = %v, want %v", string(i), output, expected) 83 } 84 } 85 } 86 87 func (s) TestDecodeGrpcMessage(t *testing.T) { 88 for _, tt := range []struct { 89 input string 90 expected string 91 }{ 92 {"", ""}, 93 {"Hello", "Hello"}, 94 {"H%61o", "Hao"}, 95 {"H%6", "H%6"}, 96 {"%G0", "%G0"}, 97 {"%E7%B3%BB%E7%BB%9F", "系统"}, 98 {"%EF%BF%BD", "�"}, 99 } { 100 actual := decodeGrpcMessage(tt.input) 101 if tt.expected != actual { 102 t.Errorf("decodeGrpcMessage(%q) = %q, want %q", tt.input, actual, tt.expected) 103 } 104 } 105 106 // make sure that all the visible ASCII chars except '%' are not percent decoded. 107 for i := ' '; i <= '~' && i != '%'; i++ { 108 output := decodeGrpcMessage(string(i)) 109 if output != string(i) { 110 t.Errorf("decodeGrpcMessage(%v) = %v, want %v", string(i), output, string(i)) 111 } 112 } 113 114 // make sure that all the invisible ASCII chars and '%' are percent decoded. 115 for i := rune(0); i == '%' || (i >= rune(0) && i < ' ') || (i > '~' && i <= rune(127)); i++ { 116 output := decodeGrpcMessage(fmt.Sprintf("%%%02X", i)) 117 if output != string(i) { 118 t.Errorf("decodeGrpcMessage(%v) = %v, want %v", fmt.Sprintf("%%%02X", i), output, string(i)) 119 } 120 } 121 } 122 123 // Decode an encoded string should get the same thing back, except for invalid 124 // utf8 chars. 125 func (s) TestDecodeEncodeGrpcMessage(t *testing.T) { 126 testCases := []struct { 127 orig string 128 want string 129 }{ 130 {"", ""}, 131 {"hello", "hello"}, 132 {"h%6", "h%6"}, 133 {"%G0", "%G0"}, 134 {"系统", "系统"}, 135 {"Hello, 世界", "Hello, 世界"}, 136 137 {string([]byte{0xff, 0xfe, 0xfd}), "���"}, 138 {string([]byte{0xff}) + "Hello" + string([]byte{0xfe}) + "世界" + string([]byte{0xfd}), "�Hello�世界�"}, 139 } 140 for _, tC := range testCases { 141 got := decodeGrpcMessage(encodeGrpcMessage(tC.orig)) 142 if got != tC.want { 143 t.Errorf("decodeGrpcMessage(encodeGrpcMessage(%q)) = %q, want %q", tC.orig, got, tC.want) 144 } 145 } 146 } 147 148 const binaryValue = "\u0080" 149 150 func (s) TestEncodeMetadataHeader(t *testing.T) { 151 for _, test := range []struct { 152 // input 153 kin string 154 vin string 155 // output 156 vout string 157 }{ 158 {"key", "abc", "abc"}, 159 {"KEY", "abc", "abc"}, 160 {"key-bin", "abc", "YWJj"}, 161 {"key-bin", binaryValue, "woA"}, 162 } { 163 v := encodeMetadataHeader(test.kin, test.vin) 164 if !reflect.DeepEqual(v, test.vout) { 165 t.Fatalf("encodeMetadataHeader(%q, %q) = %q, want %q", test.kin, test.vin, v, test.vout) 166 } 167 } 168 } 169 170 func (s) TestDecodeMetadataHeader(t *testing.T) { 171 for _, test := range []struct { 172 // input 173 kin string 174 vin string 175 // output 176 vout string 177 err error 178 }{ 179 {"a", "abc", "abc", nil}, 180 {"key-bin", "Zm9vAGJhcg==", "foo\x00bar", nil}, 181 {"key-bin", "Zm9vAGJhcg", "foo\x00bar", nil}, 182 {"key-bin", "woA=", binaryValue, nil}, 183 {"a", "abc,efg", "abc,efg", nil}, 184 } { 185 v, err := decodeMetadataHeader(test.kin, test.vin) 186 if !reflect.DeepEqual(v, test.vout) || !reflect.DeepEqual(err, test.err) { 187 t.Fatalf("decodeMetadataHeader(%q, %q) = %q, %v, want %q, %v", test.kin, test.vin, v, err, test.vout, test.err) 188 } 189 } 190 } 191 192 func (s) TestParseDialTarget(t *testing.T) { 193 for _, test := range []struct { 194 target, wantNet, wantAddr string 195 }{ 196 {"unix:a", "unix", "a"}, 197 {"unix:a/b/c", "unix", "a/b/c"}, 198 {"unix:/a", "unix", "/a"}, 199 {"unix:/a/b/c", "unix", "/a/b/c"}, 200 {"unix://a", "unix", "a"}, 201 {"unix://a/b/c", "unix", "/b/c"}, 202 {"unix:///a", "unix", "/a"}, 203 {"unix:///a/b/c", "unix", "/a/b/c"}, 204 {"unix:etcd:0", "unix", "etcd:0"}, 205 {"unix:///tmp/unix-3", "unix", "/tmp/unix-3"}, 206 {"unix://domain", "unix", "domain"}, 207 {"unix://etcd:0", "unix", "etcd:0"}, 208 {"unix:///etcd:0", "unix", "/etcd:0"}, 209 {"passthrough://unix://domain", "tcp", "passthrough://unix://domain"}, 210 {"https://google.com:443", "tcp", "https://google.com:443"}, 211 {"dns:///google.com", "tcp", "dns:///google.com"}, 212 {"/unix/socket/address", "tcp", "/unix/socket/address"}, 213 } { 214 gotNet, gotAddr := ParseDialTarget(test.target) 215 if gotNet != test.wantNet || gotAddr != test.wantAddr { 216 t.Errorf("ParseDialTarget(%q) = %s, %s want %s, %s", test.target, gotNet, gotAddr, test.wantNet, test.wantAddr) 217 } 218 } 219 } 220 221 type badNetworkConn struct { 222 net.Conn 223 } 224 225 func (c *badNetworkConn) Write([]byte) (int, error) { 226 return 0, io.EOF 227 } 228 229 // This test ensures Write() on a broken network connection does not lead to 230 // an infinite loop. See https://github.com/grpc/grpc-go/issues/7389 for more details. 231 func (s) TestWriteBadConnection(t *testing.T) { 232 data := []byte("test_data") 233 // Configure the bufWriter with a batchsize that results in data being flushed 234 // to the underlying conn, midway through Write(). 235 writeBufferSize := (len(data) - 1) / 2 236 writer := newBufWriter(&badNetworkConn{}, writeBufferSize, getWriteBufferPool(writeBufferSize)) 237 238 errCh := make(chan error, 1) 239 go func() { 240 _, err := writer.Write(data) 241 errCh <- err 242 }() 243 244 select { 245 case <-time.After(time.Second): 246 t.Fatalf("Write() did not return in time") 247 case err := <-errCh: 248 if !errors.Is(err, io.EOF) { 249 t.Fatalf("Write() = %v, want error presence = %v", err, io.EOF) 250 } 251 } 252 } 253 254 func BenchmarkDecodeGrpcMessage(b *testing.B) { 255 input := "Hello, %E4%B8%96%E7%95%8C" 256 want := "Hello, 世界" 257 b.ReportAllocs() 258 for i := 0; i < b.N; i++ { 259 got := decodeGrpcMessage(input) 260 if got != want { 261 b.Fatalf("decodeGrpcMessage(%q) = %s, want %s", input, got, want) 262 } 263 } 264 } 265 266 func BenchmarkEncodeGrpcMessage(b *testing.B) { 267 input := "Hello, 世界" 268 want := "Hello, %E4%B8%96%E7%95%8C" 269 b.ReportAllocs() 270 for i := 0; i < b.N; i++ { 271 got := encodeGrpcMessage(input) 272 if got != want { 273 b.Fatalf("encodeGrpcMessage(%q) = %s, want %s", input, got, want) 274 } 275 } 276 }