1
18 package test
19
20 import (
21 "context"
22 "fmt"
23 "net"
24 "testing"
25
26 "google.golang.org/grpc"
27 "google.golang.org/grpc/codes"
28 "google.golang.org/grpc/credentials/insecure"
29 "google.golang.org/grpc/internal/transport"
30 "google.golang.org/grpc/status"
31
32 testgrpc "google.golang.org/grpc/interop/grpc_testing"
33 )
34
35 func (s) TestHTTPHeaderFrameErrorHandlingHTTPMode(t *testing.T) {
36 type test struct {
37 name string
38 header []string
39 errCode codes.Code
40 }
41
42 var tests []test
43
44
45 for httpCode := range transport.HTTPStatusConvTab {
46 tests = append(tests, test{
47 name: fmt.Sprintf("Non-gRPC content-type fallback path with httpCode: %v", httpCode),
48 header: []string{
49 ":status", fmt.Sprintf("%d", httpCode),
50 "content-type", "text/html",
51 "grpc-status", "1",
52 "grpc-status-details-bin", "???",
53 },
54 errCode: transport.HTTPStatusConvTab[int(httpCode)],
55 })
56 }
57
58
59 for httpCode := range transport.HTTPStatusConvTab {
60 tests = append(tests, test{
61 name: fmt.Sprintf("Missing content-type fallback path with httpCode: %v", httpCode),
62 header: []string{
63 ":status", fmt.Sprintf("%d", httpCode),
64
65 "grpc-status", "1",
66 "grpc-status-details-bin", "???",
67 },
68 errCode: transport.HTTPStatusConvTab[int(httpCode)],
69 })
70 }
71
72
73 tests = append(tests, test{
74 name: "Malformed HTTP status when fallback",
75 header: []string{
76 ":status", "abc",
77
78 "grpc-status", "1",
79 "grpc-status-details-bin", "???",
80 },
81 errCode: codes.Internal,
82 })
83
84 for _, test := range tests {
85 t.Run(test.name, func(t *testing.T) {
86 serverAddr, cleanup, err := startServer(t, test.header)
87 if err != nil {
88 t.Fatal(err)
89 }
90 defer cleanup()
91 if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
92 t.Error(err)
93 }
94 })
95 }
96 }
97
98
99 func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) {
100 for _, test := range []struct {
101 name string
102 header []string
103 errCode codes.Code
104 }{
105 {
106 name: "missing gRPC status",
107 header: []string{
108 ":status", "403",
109 "content-type", "application/grpc",
110 },
111 errCode: codes.PermissionDenied,
112 },
113 {
114 name: "malformed grpc-status",
115 header: []string{
116 ":status", "502",
117 "content-type", "application/grpc",
118 "grpc-status", "abc",
119 },
120 errCode: codes.Internal,
121 },
122 {
123 name: "Malformed grpc-tags-bin field",
124 header: []string{
125 ":status", "502",
126 "content-type", "application/grpc",
127 "grpc-status", "0",
128 "grpc-tags-bin", "???",
129 },
130 errCode: codes.Unavailable,
131 },
132 {
133 name: "gRPC status error",
134 header: []string{
135 ":status", "502",
136 "content-type", "application/grpc",
137 "grpc-status", "3",
138 },
139 errCode: codes.Unavailable,
140 },
141 } {
142 t.Run(test.name, func(t *testing.T) {
143 serverAddr, cleanup, err := startServer(t, test.header)
144 if err != nil {
145 t.Fatal(err)
146 }
147 defer cleanup()
148 if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
149 t.Error(err)
150 }
151 })
152 }
153 }
154
155
156 func (s) TestHTTPHeaderFrameErrorHandlingNormalTrailer(t *testing.T) {
157 tests := []struct {
158 name string
159 responseHeader []string
160 trailer []string
161 errCode codes.Code
162 }{
163 {
164 name: "trailer missing grpc-status",
165 responseHeader: []string{
166 ":status", "200",
167 "content-type", "application/grpc",
168 },
169 trailer: []string{
170
171 ":status", "502",
172 },
173 errCode: codes.Unavailable,
174 },
175 {
176 name: "malformed grpc-status-details-bin field with status 404",
177 responseHeader: []string{
178 ":status", "404",
179 "content-type", "application/grpc",
180 },
181 trailer: []string{
182
183 "grpc-status", "0",
184 "grpc-status-details-bin", "????",
185 },
186 errCode: codes.Unimplemented,
187 },
188 {
189 name: "malformed grpc-status-details-bin field with status 200",
190 responseHeader: []string{
191 ":status", "200",
192 "content-type", "application/grpc",
193 },
194 trailer: []string{
195
196 "grpc-status", "0",
197 "grpc-status-details-bin", "????",
198 },
199 errCode: codes.Internal,
200 },
201 }
202 for _, test := range tests {
203 t.Run(test.name, func(t *testing.T) {
204 serverAddr, cleanup, err := startServer(t, test.responseHeader, test.trailer)
205 if err != nil {
206 t.Fatal(err)
207 }
208 defer cleanup()
209 if err := doHTTPHeaderTest(serverAddr, test.errCode); err != nil {
210 t.Error(err)
211 }
212 })
213
214 }
215 }
216
217 func (s) TestHTTPHeaderFrameErrorHandlingMoreThanTwoHeaders(t *testing.T) {
218 header := []string{
219 ":status", "200",
220 "content-type", "application/grpc",
221 }
222 serverAddr, cleanup, err := startServer(t, header, header, header)
223 if err != nil {
224 t.Fatal(err)
225 }
226 defer cleanup()
227 if err := doHTTPHeaderTest(serverAddr, codes.Internal); err != nil {
228 t.Fatal(err)
229 }
230 }
231
232 func startServer(t *testing.T, headerFields ...[]string) (serverAddr string, cleanup func(), err error) {
233 t.Helper()
234
235 lis, err := net.Listen("tcp", "localhost:0")
236 if err != nil {
237 return "", nil, fmt.Errorf("listening on %q: %v", "localhost:0", err)
238 }
239 server := &httpServer{responses: []httpServerResponse{{trailers: headerFields}}}
240 server.start(t, lis)
241 return lis.Addr().String(), func() { lis.Close() }, nil
242 }
243
244 func doHTTPHeaderTest(lisAddr string, errCode codes.Code) error {
245 cc, err := grpc.NewClient(lisAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
246 if err != nil {
247 return fmt.Errorf("NewClient(%q): %v", lisAddr, err)
248 }
249 defer cc.Close()
250 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
251 defer cancel()
252 client := testgrpc.NewTestServiceClient(cc)
253 stream, err := client.FullDuplexCall(ctx)
254 if err != nil {
255 return fmt.Errorf("creating FullDuplex stream: %v", err)
256 }
257 if _, err := stream.Recv(); err == nil || status.Code(err) != errCode {
258 return fmt.Errorf("stream.Recv() = %v, want error code: %v", err, errCode)
259 }
260 return nil
261 }
262
View as plain text