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