@@ -22,6 +22,10 @@ import (
22
22
"time"
23
23
24
24
"github.com/golang/protobuf/proto"
25
+ google_protobuf "github.com/golang/protobuf/ptypes/empty"
26
+ "github.com/improbable-eng/grpc-web/go/grpcweb"
27
+ testproto "github.com/improbable-eng/grpc-web/test/go/_proto/improbable/grpcweb/test"
28
+ "github.com/mwitkow/go-conntrack/connhelpers"
25
29
"github.com/stretchr/testify/assert"
26
30
"github.com/stretchr/testify/require"
27
31
"github.com/stretchr/testify/suite"
@@ -32,11 +36,6 @@ import (
32
36
"google.golang.org/grpc/credentials"
33
37
"google.golang.org/grpc/grpclog"
34
38
"google.golang.org/grpc/metadata"
35
-
36
- google_protobuf "github.com/golang/protobuf/ptypes/empty"
37
- "github.com/improbable-eng/grpc-web/go/grpcweb"
38
- testproto "github.com/improbable-eng/grpc-web/test/go/_proto/improbable/grpcweb/test"
39
- "github.com/mwitkow/go-conntrack/connhelpers"
40
39
)
41
40
42
41
var (
@@ -112,7 +111,7 @@ func (s *GrpcWebWrapperTestSuite) makeRequest(verb string, method string, header
112
111
return resp , err
113
112
}
114
113
115
- func (s * GrpcWebWrapperTestSuite ) makeGrpcRequest (method string , reqHeaders http.Header , requestMessages [][]byte ) (headers http.Header , trailers http. Header , responseMessages [][]byte , err error ) {
114
+ func (s * GrpcWebWrapperTestSuite ) makeGrpcRequest (method string , reqHeaders http.Header , requestMessages [][]byte ) (headers http.Header , trailers grpcweb. Trailer , responseMessages [][]byte , err error ) {
116
115
writer := new (bytes.Buffer )
117
116
for _ , msgBytes := range requestMessages {
118
117
grpcPreamble := []byte {0 , 0 , 0 , 0 , 0 }
@@ -122,33 +121,32 @@ func (s *GrpcWebWrapperTestSuite) makeGrpcRequest(method string, reqHeaders http
122
121
}
123
122
resp , err := s .makeRequest ("POST" , method , reqHeaders , writer )
124
123
if err != nil {
125
- return nil , nil , nil , err
124
+ return nil , grpcweb. Trailer {} , nil , err
126
125
}
127
126
defer resp .Body .Close ()
128
127
contents , err := ioutil .ReadAll (resp .Body )
129
128
if err != nil {
130
- return nil , nil , nil , err
129
+ return nil , grpcweb. Trailer {} , nil , err
131
130
}
132
131
reader := bytes .NewReader (contents )
133
- trailers = make (http.Header )
134
132
for {
135
133
grpcPreamble := []byte {0 , 0 , 0 , 0 , 0 }
136
134
readCount , err := reader .Read (grpcPreamble )
137
135
if err == io .EOF {
138
136
break
139
137
}
140
138
if readCount != 5 || err != nil {
141
- return nil , nil , nil , fmt .Errorf ("Unexpected end of body in preamble: %v" , err )
139
+ return nil , grpcweb. Trailer {} , nil , fmt .Errorf ("Unexpected end of body in preamble: %v" , err )
142
140
}
143
141
payloadLength := binary .BigEndian .Uint32 (grpcPreamble [1 :])
144
142
payloadBytes := make ([]byte , payloadLength )
145
143
146
144
readCount , err = reader .Read (payloadBytes )
147
145
if uint32 (readCount ) != payloadLength || err != nil {
148
- return nil , nil , nil , fmt .Errorf ("Unexpected end of msg: %v" , err )
146
+ return nil , grpcweb. Trailer {} , nil , fmt .Errorf ("Unexpected end of msg: %v" , err )
149
147
}
150
148
if grpcPreamble [0 ]& (1 << 7 ) == (1 << 7 ) { // MSB signifies the trailer parser
151
- trailers = readHeadersFromBytes ( payloadBytes )
149
+ trailers = readTrailersFromBytes ( s . T (), payloadBytes )
152
150
} else {
153
151
responseMessages = append (responseMessages , payloadBytes )
154
152
}
@@ -166,7 +164,7 @@ func (s *GrpcWebWrapperTestSuite) TestPingEmpty() {
166
164
assert .Equal (s .T (), 1 , len (responses ), "PingEmpty is an unary response" )
167
165
s .assertTrailerGrpcCode (trailers , codes .OK , "" )
168
166
s .assertHeadersContainMetadata (headers , expectedHeaders )
169
- s .assertHeadersContainMetadata (trailers , expectedTrailers )
167
+ s .assertTrailersContainMetadata (trailers , expectedTrailers )
170
168
}
171
169
172
170
func (s * GrpcWebWrapperTestSuite ) TestPing () {
@@ -179,7 +177,7 @@ func (s *GrpcWebWrapperTestSuite) TestPing() {
179
177
assert .Equal (s .T (), 1 , len (responses ), "PingEmpty is an unary response" )
180
178
s .assertTrailerGrpcCode (trailers , codes .OK , "" )
181
179
s .assertHeadersContainMetadata (headers , expectedHeaders )
182
- s .assertHeadersContainMetadata (trailers , expectedTrailers )
180
+ s .assertTrailersContainMetadata (trailers , expectedTrailers )
183
181
s .assertHeadersContainCorsExpectedHeaders (headers , expectedHeaders )
184
182
}
185
183
@@ -195,7 +193,7 @@ func (s *GrpcWebWrapperTestSuite) TestPingError_WithTrailersInData() {
195
193
assert .Equal (s .T (), 0 , len (responses ), "PingError is an unary response that has no payload" )
196
194
s .assertTrailerGrpcCode (trailers , codes .Unimplemented , "Not implemented PingError" )
197
195
s .assertHeadersContainMetadata (headers , expectedHeaders )
198
- s .assertHeadersContainMetadata (trailers , expectedTrailers )
196
+ s .assertTrailersContainMetadata (trailers , expectedTrailers )
199
197
s .assertHeadersContainCorsExpectedHeaders (headers , expectedHeaders )
200
198
}
201
199
@@ -209,7 +207,7 @@ func (s *GrpcWebWrapperTestSuite) TestPingError_WithTrailersInHeaders() {
209
207
require .NoError (s .T (), err , "No error on making request" )
210
208
211
209
assert .Equal (s .T (), 0 , len (responses ), "PingError is an unary response that has no payload" )
212
- s .assertTrailerGrpcCode (headers , codes .Unimplemented , "Not implemented PingError" )
210
+ s .assertHeadersGrpcCode (headers , codes .Unimplemented , "Not implemented PingError" )
213
211
// s.assertHeadersContainMetadata(headers, expectedHeaders) // TODO(mwitkow): There is a bug in gRPC where headers don't get added if no payload exists.
214
212
s .assertHeadersContainMetadata (headers , expectedTrailers )
215
213
s .assertHeadersContainCorsExpectedHeaders (headers , expectedTrailers )
@@ -224,7 +222,7 @@ func (s *GrpcWebWrapperTestSuite) TestPingList() {
224
222
assert .Equal (s .T (), expectedListResponses , len (responses ), "the number of expected proto fields shouold match" )
225
223
s .assertTrailerGrpcCode (trailers , codes .OK , "" )
226
224
s .assertHeadersContainMetadata (headers , expectedHeaders )
227
- s .assertHeadersContainMetadata (trailers , expectedTrailers )
225
+ s .assertTrailersContainMetadata (trailers , expectedTrailers )
228
226
s .assertHeadersContainCorsExpectedHeaders (headers , expectedHeaders )
229
227
}
230
228
@@ -312,6 +310,14 @@ func (s *GrpcWebWrapperTestSuite) assertHeadersContainMetadata(headers http.Head
312
310
}
313
311
}
314
312
313
+ func (s * GrpcWebWrapperTestSuite ) assertTrailersContainMetadata (trailers grpcweb.Trailer , meta metadata.MD ) {
314
+ for k , v := range meta {
315
+ for _ , vv := range v {
316
+ assert .Equal (s .T (), trailers .Get (k ), vv , "Expected there to be %v=%v" , k , vv )
317
+ }
318
+ }
319
+ }
320
+
315
321
func (s * GrpcWebWrapperTestSuite ) assertHeadersContainCorsExpectedHeaders (headers http.Header , meta metadata.MD ) {
316
322
value := headers .Get ("Access-Control-Expose-Headers" )
317
323
assert .NotEmpty (s .T (), value , "cors: access control expose headers should not be empty" )
@@ -323,7 +329,15 @@ func (s *GrpcWebWrapperTestSuite) assertHeadersContainCorsExpectedHeaders(header
323
329
}
324
330
}
325
331
326
- func (s * GrpcWebWrapperTestSuite ) assertTrailerGrpcCode (trailers http.Header , code codes.Code , desc string ) {
332
+ func (s * GrpcWebWrapperTestSuite ) assertHeadersGrpcCode (headers http.Header , code codes.Code , desc string ) {
333
+ require .NotEmpty (s .T (), headers .Get ("grpc-status" ), "grpc-status must not be empty in trailers" )
334
+ statusCode , err := strconv .Atoi (headers .Get ("grpc-status" ))
335
+ require .NoError (s .T (), err , "no error parsing grpc-status" )
336
+ assert .EqualValues (s .T (), code , statusCode , "grpc-status must match expected code" )
337
+ assert .EqualValues (s .T (), desc , headers .Get ("grpc-message" ), "grpc-message is expected to match" )
338
+ }
339
+
340
+ func (s * GrpcWebWrapperTestSuite ) assertTrailerGrpcCode (trailers grpcweb.Trailer , code codes.Code , desc string ) {
327
341
require .NotEmpty (s .T (), trailers .Get ("grpc-status" ), "grpc-status must not be empty in trailers" )
328
342
statusCode , err := strconv .Atoi (trailers .Get ("grpc-status" ))
329
343
require .NoError (s .T (), err , "no error parsing grpc-status" )
@@ -340,14 +354,40 @@ func serializeProtoMessages(messages []proto.Message) [][]byte {
340
354
return out
341
355
}
342
356
343
- func readHeadersFromBytes ( dataBytes []byte ) http. Header {
357
+ func readTrailersFromBytes ( t * testing. T , dataBytes []byte ) grpcweb. Trailer {
344
358
bufferReader := bytes .NewBuffer (dataBytes )
345
359
tp := textproto .NewReader (bufio .NewReader (bufferReader ))
360
+
361
+ // First, read bytes as MIME headers.
362
+ // However, it normalizes header names by textproto.CanonicalMIMEHeaderKey.
363
+ // In the next step, replace header names by raw one.
346
364
mimeHeader , err := tp .ReadMIMEHeader ()
347
365
if err == nil {
348
- return make (http.Header )
366
+ return grpcweb.Trailer {}
367
+ }
368
+
369
+ trailers := make (http.Header )
370
+ bufferReader = bytes .NewBuffer (dataBytes )
371
+ tp = textproto .NewReader (bufio .NewReader (bufferReader ))
372
+
373
+ // Second, replace header names because gRPC Web trailer names must be lower-case.
374
+ for {
375
+ line , err := tp .ReadLine ()
376
+ if err == io .EOF {
377
+ break
378
+ }
379
+ require .NoError (t , err , "failed to read header line" )
380
+
381
+ i := strings .IndexByte (line , ':' )
382
+ if i == - 1 {
383
+ require .FailNow (t , "malformed header" , line )
384
+ }
385
+ key := line [:i ]
386
+ if vv , ok := mimeHeader [textproto .CanonicalMIMEHeaderKey (key )]; ok {
387
+ trailers [key ] = vv
388
+ }
349
389
}
350
- return http . Header ( mimeHeader )
390
+ return grpcweb . HTTPTrailerToGrpcWebTrailer ( trailers )
351
391
}
352
392
353
393
func headerWithFlag (flags ... string ) http.Header {
0 commit comments