google.golang.org/grpc@v1.72.2/encoding/encoding_test.go (about) 1 /* 2 * 3 * Copyright 2023 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 encoding_test 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "strings" 26 "sync/atomic" 27 "testing" 28 "time" 29 30 "github.com/google/go-cmp/cmp" 31 "google.golang.org/grpc" 32 "google.golang.org/grpc/codes" 33 "google.golang.org/grpc/credentials/insecure" 34 "google.golang.org/grpc/encoding" 35 "google.golang.org/grpc/encoding/proto" 36 "google.golang.org/grpc/internal/grpctest" 37 "google.golang.org/grpc/internal/grpcutil" 38 "google.golang.org/grpc/internal/stubserver" 39 "google.golang.org/grpc/mem" 40 "google.golang.org/grpc/metadata" 41 "google.golang.org/grpc/status" 42 43 testgrpc "google.golang.org/grpc/interop/grpc_testing" 44 testpb "google.golang.org/grpc/interop/grpc_testing" 45 ) 46 47 const defaultTestTimeout = 10 * time.Second 48 49 type s struct { 50 grpctest.Tester 51 } 52 53 func Test(t *testing.T) { 54 grpctest.RunSubTests(t, s{}) 55 } 56 57 type mockNamedCompressor struct { 58 encoding.Compressor 59 } 60 61 func (mockNamedCompressor) Name() string { 62 return "mock-compressor" 63 } 64 65 // Tests the case where a compressor with the same name is registered multiple 66 // times. Test verifies the following: 67 // - the most recent registration is the one which is active 68 // - grpcutil.RegisteredCompressorNames contains a single instance of the 69 // previously registered compressor's name 70 func (s) TestDuplicateCompressorRegister(t *testing.T) { 71 encoding.RegisterCompressor(&mockNamedCompressor{}) 72 73 // Register another instance of the same compressor. 74 mc := &mockNamedCompressor{} 75 encoding.RegisterCompressor(mc) 76 if got := encoding.GetCompressor("mock-compressor"); got != mc { 77 t.Fatalf("Unexpected compressor, got: %+v, want:%+v", got, mc) 78 } 79 80 wantNames := []string{"mock-compressor"} 81 if !cmp.Equal(wantNames, grpcutil.RegisteredCompressorNames) { 82 t.Fatalf("Unexpected compressor names, got: %+v, want:%+v", grpcutil.RegisteredCompressorNames, wantNames) 83 } 84 } 85 86 // errProtoCodec wraps the proto codec and delegates to it if it is configured 87 // to return a nil error. Else, it returns the configured error. 88 type errProtoCodec struct { 89 name string 90 encodingErr error 91 decodingErr error 92 } 93 94 func (c *errProtoCodec) Marshal(v any) (mem.BufferSlice, error) { 95 if c.encodingErr != nil { 96 return nil, c.encodingErr 97 } 98 return encoding.GetCodecV2(proto.Name).Marshal(v) 99 } 100 101 func (c *errProtoCodec) Unmarshal(data mem.BufferSlice, v any) error { 102 if c.decodingErr != nil { 103 return c.decodingErr 104 } 105 return encoding.GetCodecV2(proto.Name).Unmarshal(data, v) 106 } 107 108 func (c *errProtoCodec) Name() string { 109 return c.name 110 } 111 112 // Tests the case where encoding fails on the server. Verifies that there is 113 // no panic and that the encoding error is propagated to the client. 114 func (s) TestEncodeDoesntPanicOnServer(t *testing.T) { 115 grpctest.TLogger.ExpectError("grpc: server failed to encode response") 116 117 // Create a codec that errors when encoding messages. 118 encodingErr := errors.New("encoding failed") 119 ec := &errProtoCodec{name: t.Name(), encodingErr: encodingErr} 120 121 // Start a server with the above codec. 122 backend := stubserver.StartTestService(t, nil, grpc.ForceServerCodecV2(ec)) 123 defer backend.Stop() 124 125 // Create a channel to the above server. 126 cc, err := grpc.NewClient(backend.Address, grpc.WithTransportCredentials(insecure.NewCredentials())) 127 if err != nil { 128 t.Fatalf("Failed to dial test backend at %q: %v", backend.Address, err) 129 } 130 defer cc.Close() 131 132 // Make an RPC and expect it to fail. Since we do not specify any codec 133 // here, the proto codec will get automatically used. 134 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 135 defer cancel() 136 client := testgrpc.NewTestServiceClient(cc) 137 _, err = client.EmptyCall(ctx, &testpb.Empty{}) 138 if err == nil || !strings.Contains(err.Error(), encodingErr.Error()) { 139 t.Fatalf("RPC failed with error: %v, want: %v", err, encodingErr) 140 } 141 142 // Configure the codec on the server to not return errors anymore and expect 143 // the RPC to succeed. 144 ec.encodingErr = nil 145 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 146 t.Fatalf("RPC failed with error: %v", err) 147 } 148 } 149 150 // Tests the case where decoding fails on the server. Verifies that there is 151 // no panic and that the decoding error is propagated to the client. 152 func (s) TestDecodeDoesntPanicOnServer(t *testing.T) { 153 // Create a codec that errors when decoding messages. 154 decodingErr := errors.New("decoding failed") 155 ec := &errProtoCodec{name: t.Name(), decodingErr: decodingErr} 156 157 // Start a server with the above codec. 158 backend := stubserver.StartTestService(t, nil, grpc.ForceServerCodecV2(ec)) 159 defer backend.Stop() 160 161 // Create a channel to the above server. Since we do not specify any codec 162 // here, the proto codec will get automatically used. 163 cc, err := grpc.NewClient(backend.Address, grpc.WithTransportCredentials(insecure.NewCredentials())) 164 if err != nil { 165 t.Fatalf("Failed to dial test backend at %q: %v", backend.Address, err) 166 } 167 defer cc.Close() 168 169 // Make an RPC and expect it to fail. Since we do not specify any codec 170 // here, the proto codec will get automatically used. 171 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 172 defer cancel() 173 client := testgrpc.NewTestServiceClient(cc) 174 _, err = client.EmptyCall(ctx, &testpb.Empty{}) 175 if err == nil || !strings.Contains(err.Error(), decodingErr.Error()) || !strings.Contains(err.Error(), "grpc: error unmarshalling request") { 176 t.Fatalf("RPC failed with error: %v, want: %v", err, decodingErr) 177 } 178 179 // Configure the codec on the server to not return errors anymore and expect 180 // the RPC to succeed. 181 ec.decodingErr = nil 182 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 183 t.Fatalf("RPC failed with error: %v", err) 184 } 185 } 186 187 // Tests the case where encoding fails on the client . Verifies that there is 188 // no panic and that the encoding error is propagated to the RPC caller. 189 func (s) TestEncodeDoesntPanicOnClient(t *testing.T) { 190 // Start a server and since we do not specify any codec here, the proto 191 // codec will get automatically used. 192 backend := stubserver.StartTestService(t, nil) 193 defer backend.Stop() 194 195 // Create a codec that errors when encoding messages. 196 encodingErr := errors.New("encoding failed") 197 ec := &errProtoCodec{name: t.Name(), encodingErr: encodingErr} 198 199 // Create a channel to the above server. 200 cc, err := grpc.NewClient(backend.Address, grpc.WithTransportCredentials(insecure.NewCredentials())) 201 if err != nil { 202 t.Fatalf("Failed to dial test backend at %q: %v", backend.Address, err) 203 } 204 defer cc.Close() 205 206 // Make an RPC with the erroring codec and expect it to fail. 207 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 208 defer cancel() 209 client := testgrpc.NewTestServiceClient(cc) 210 _, err = client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodecV2(ec)) 211 if err == nil || !strings.Contains(err.Error(), encodingErr.Error()) { 212 t.Fatalf("RPC failed with error: %v, want: %v", err, encodingErr) 213 } 214 215 // Configure the codec on the client to not return errors anymore and expect 216 // the RPC to succeed. 217 ec.encodingErr = nil 218 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodecV2(ec)); err != nil { 219 t.Fatalf("RPC failed with error: %v", err) 220 } 221 } 222 223 // Tests the case where decoding fails on the server. Verifies that there is 224 // no panic and that the decoding error is propagated to the RPC caller. 225 func (s) TestDecodeDoesntPanicOnClient(t *testing.T) { 226 // Start a server and since we do not specify any codec here, the proto 227 // codec will get automatically used. 228 backend := stubserver.StartTestService(t, nil) 229 defer backend.Stop() 230 231 // Create a codec that errors when decoding messages. 232 decodingErr := errors.New("decoding failed") 233 ec := &errProtoCodec{name: t.Name(), decodingErr: decodingErr} 234 235 // Create a channel to the above server. 236 cc, err := grpc.NewClient(backend.Address, grpc.WithTransportCredentials(insecure.NewCredentials())) 237 if err != nil { 238 t.Fatalf("Failed to dial test backend at %q: %v", backend.Address, err) 239 } 240 defer cc.Close() 241 242 // Make an RPC with the erroring codec and expect it to fail. 243 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 244 defer cancel() 245 client := testgrpc.NewTestServiceClient(cc) 246 _, err = client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodecV2(ec)) 247 if err == nil || !strings.Contains(err.Error(), decodingErr.Error()) { 248 t.Fatalf("RPC failed with error: %v, want: %v", err, decodingErr) 249 } 250 251 // Configure the codec on the client to not return errors anymore and expect 252 // the RPC to succeed. 253 ec.decodingErr = nil 254 if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodecV2(ec)); err != nil { 255 t.Fatalf("RPC failed with error: %v", err) 256 } 257 } 258 259 // countingProtoCodec wraps the proto codec and counts the number of times 260 // Marshal and Unmarshal are called. 261 type countingProtoCodec struct { 262 name string 263 264 // The following fields are accessed atomically. 265 marshalCount int32 266 unmarshalCount int32 267 } 268 269 func (p *countingProtoCodec) Marshal(v any) (mem.BufferSlice, error) { 270 atomic.AddInt32(&p.marshalCount, 1) 271 return encoding.GetCodecV2(proto.Name).Marshal(v) 272 } 273 274 func (p *countingProtoCodec) Unmarshal(data mem.BufferSlice, v any) error { 275 atomic.AddInt32(&p.unmarshalCount, 1) 276 return encoding.GetCodecV2(proto.Name).Unmarshal(data, v) 277 } 278 279 func (p *countingProtoCodec) Name() string { 280 return p.name 281 } 282 283 // Tests the case where ForceServerCodec option is used on the server. Verifies 284 // that encoding and decoding happen once per RPC. 285 func (s) TestForceServerCodec(t *testing.T) { 286 // Create a server with the counting proto codec. 287 codec := &countingProtoCodec{name: t.Name()} 288 backend := stubserver.StartTestService(t, nil, grpc.ForceServerCodecV2(codec)) 289 defer backend.Stop() 290 291 // Create a channel to the above server. 292 cc, err := grpc.NewClient(backend.Address, grpc.WithTransportCredentials(insecure.NewCredentials())) 293 if err != nil { 294 t.Fatalf("Failed to dial test backend at %q: %v", backend.Address, err) 295 } 296 defer cc.Close() 297 298 // Make an RPC and expect it to fail. Since we do not specify any codec 299 // here, the proto codec will get automatically used. 300 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 301 defer cancel() 302 client := testgrpc.NewTestServiceClient(cc) 303 if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 304 t.Fatalf("ss.Client.EmptyCall(_, _) = _, %v; want _, nil", err) 305 } 306 307 unmarshalCount := atomic.LoadInt32(&codec.unmarshalCount) 308 const wantUnmarshalCount = 1 309 if unmarshalCount != wantUnmarshalCount { 310 t.Fatalf("Unmarshal Count = %d; want %d", unmarshalCount, wantUnmarshalCount) 311 } 312 marshalCount := atomic.LoadInt32(&codec.marshalCount) 313 const wantMarshalCount = 1 314 if marshalCount != wantMarshalCount { 315 t.Fatalf("MarshalCount = %d; want %d", marshalCount, wantMarshalCount) 316 } 317 } 318 319 // renameProtoCodec wraps the proto codec and allows customizing the Name(). 320 type renameProtoCodec struct { 321 encoding.CodecV2 322 name string 323 } 324 325 func (r *renameProtoCodec) Name() string { return r.name } 326 327 // TestForceCodecName confirms that the ForceCodec call option sets the subtype 328 // in the content-type header according to the Name() of the codec provided. 329 // Verifies that the name is converted to lowercase before transmitting. 330 func (s) TestForceCodecName(t *testing.T) { 331 wantContentTypeCh := make(chan []string, 1) 332 defer close(wantContentTypeCh) 333 334 // Create a test service backend that pushes the received content-type on a 335 // channel for the test to inspect. 336 ss := &stubserver.StubServer{ 337 EmptyCallF: func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 338 md, ok := metadata.FromIncomingContext(ctx) 339 if !ok { 340 return nil, status.Errorf(codes.Internal, "no metadata in context") 341 } 342 if got, want := md["content-type"], <-wantContentTypeCh; !cmp.Equal(got, want) { 343 return nil, status.Errorf(codes.Internal, "got content-type=%q; want [%q]", got, want) 344 } 345 return &testpb.Empty{}, nil 346 }, 347 } 348 // Since we don't specify a codec as a server option, it will end up 349 // automatically using the proto codec. 350 if err := ss.Start(nil); err != nil { 351 t.Fatalf("Error starting endpoint server: %v", err) 352 } 353 defer ss.Stop() 354 355 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 356 defer cancel() 357 358 // Force the use of the custom codec on the client with the ForceCodec call 359 // option. Confirm the name is converted to lowercase before transmitting. 360 codec := &renameProtoCodec{CodecV2: encoding.GetCodecV2(proto.Name), name: t.Name()} 361 wantContentTypeCh <- []string{fmt.Sprintf("application/grpc+%s", strings.ToLower(t.Name()))} 362 if _, err := ss.Client.EmptyCall(ctx, &testpb.Empty{}, grpc.ForceCodecV2(codec)); err != nil { 363 t.Fatalf("ss.Client.EmptyCall(_, _) = _, %v; want _, nil", err) 364 } 365 }