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 19package transport 20 21import ( 22 "fmt" 23 "reflect" 24 "testing" 25 "time" 26 27 "golang.org/x/net/http2" 28 "golang.org/x/net/http2/hpack" 29) 30 31func (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 51func (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 87func (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. 125func (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 148const binaryValue = "\u0080" 149 150func (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 170func (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 192func (s) TestDecodeHeaderH2ErrCode(t *testing.T) { 193 for _, test := range []struct { 194 name string 195 // input 196 metaHeaderFrame *http2.MetaHeadersFrame 197 serverSide bool 198 // output 199 wantCode http2.ErrCode 200 }{ 201 { 202 name: "valid header", 203 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: []hpack.HeaderField{ 204 {Name: "content-type", Value: "application/grpc"}, 205 }}, 206 wantCode: http2.ErrCodeNo, 207 }, 208 { 209 name: "valid header serverSide", 210 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: []hpack.HeaderField{ 211 {Name: "content-type", Value: "application/grpc"}, 212 }}, 213 serverSide: true, 214 wantCode: http2.ErrCodeNo, 215 }, 216 { 217 name: "invalid grpc status header field", 218 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: []hpack.HeaderField{ 219 {Name: "content-type", Value: "application/grpc"}, 220 {Name: "grpc-status", Value: "xxxx"}, 221 }}, 222 wantCode: http2.ErrCodeProtocol, 223 }, 224 { 225 name: "invalid http content type", 226 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: []hpack.HeaderField{ 227 {Name: "content-type", Value: "application/json"}, 228 }}, 229 wantCode: http2.ErrCodeProtocol, 230 }, 231 { 232 name: "http fallback and invalid http status", 233 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: []hpack.HeaderField{ 234 // No content type provided then fallback into handling http error. 235 {Name: ":status", Value: "xxxx"}, 236 }}, 237 wantCode: http2.ErrCodeProtocol, 238 }, 239 { 240 name: "http2 frame size exceeds", 241 metaHeaderFrame: &http2.MetaHeadersFrame{Fields: nil, Truncated: true}, 242 wantCode: http2.ErrCodeFrameSize, 243 }, 244 } { 245 t.Run(test.name, func(t *testing.T) { 246 state := &decodeState{serverSide: test.serverSide} 247 if h2code, _ := state.decodeHeader(test.metaHeaderFrame); h2code != test.wantCode { 248 t.Fatalf("decodeState.decodeHeader(%v) = %v, want %v", test.metaHeaderFrame, h2code, test.wantCode) 249 } 250 }) 251 } 252} 253 254func (s) TestParseDialTarget(t *testing.T) { 255 for _, test := range []struct { 256 target, wantNet, wantAddr string 257 }{ 258 {"unix:a", "unix", "a"}, 259 {"unix:a/b/c", "unix", "a/b/c"}, 260 {"unix:/a", "unix", "/a"}, 261 {"unix:/a/b/c", "unix", "/a/b/c"}, 262 {"unix://a", "unix", "a"}, 263 {"unix://a/b/c", "unix", "/b/c"}, 264 {"unix:///a", "unix", "/a"}, 265 {"unix:///a/b/c", "unix", "/a/b/c"}, 266 {"unix:etcd:0", "unix", "etcd:0"}, 267 {"unix:///tmp/unix-3", "unix", "/tmp/unix-3"}, 268 {"unix://domain", "unix", "domain"}, 269 {"unix://etcd:0", "unix", "etcd:0"}, 270 {"unix:///etcd:0", "unix", "/etcd:0"}, 271 {"passthrough://unix://domain", "tcp", "passthrough://unix://domain"}, 272 {"https://google.com:443", "tcp", "https://google.com:443"}, 273 {"dns:///google.com", "tcp", "dns:///google.com"}, 274 {"/unix/socket/address", "tcp", "/unix/socket/address"}, 275 } { 276 gotNet, gotAddr := parseDialTarget(test.target) 277 if gotNet != test.wantNet || gotAddr != test.wantAddr { 278 t.Errorf("parseDialTarget(%q) = %s, %s want %s, %s", test.target, gotNet, gotAddr, test.wantNet, test.wantAddr) 279 } 280 } 281} 282