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  }