78
78
codes .ResourceExhausted : http2 .ErrCodeEnhanceYourCalm ,
79
79
codes .PermissionDenied : http2 .ErrCodeInadequateSecurity ,
80
80
}
81
- httpStatusConvTab = map [int ]codes.Code {
81
+ // HTTPStatusConvTab is the HTTP status code to gRPC error code conversion table.
82
+ HTTPStatusConvTab = map [int ]codes.Code {
82
83
// 400 Bad Request - INTERNAL.
83
84
http .StatusBadRequest : codes .Internal ,
84
85
// 401 Unauthorized - UNAUTHENTICATED.
@@ -118,31 +119,37 @@ type parsedHeaderData struct {
118
119
statsTags []byte
119
120
statsTrace []byte
120
121
contentSubtype string
121
- }
122
122
123
- func newDecodeState (serverSide bool , ignoreContentType bool ) * decodeState {
124
- return & decodeState {
125
- serverSide : serverSide ,
126
- ignoreContentType : ignoreContentType ,
127
- }
123
+ // isGRPC field indicates whether the peer is speaking gRPC (otherwise HTTP).
124
+ //
125
+ // We are in gRPC mode (peer speaking gRPC) if:
126
+ // * We are client side and have already received a HEADER frame that indicates gRPC peer.
127
+ // * The header contains valid a content-type, i.e. a string starts with "application/grpc"
128
+ // And we should handle error specific to gRPC.
129
+ //
130
+ // Otherwise (i.e. a content-type string starts without "application/grpc", or does not exist), we
131
+ // are in HTTP fallback mode, and should handle error specific to HTTP.
132
+ isGRPC bool
133
+ grpcErr error
134
+ httpErr error
135
+ contentTypeErr string
128
136
}
129
137
130
- // Records the states during HPACK decoding. Must be reset once the
131
- // decoding of the entire headers are finished.
138
+ // decodeState configures decoding criteria and records the decoded data.
132
139
type decodeState struct {
133
140
// whether decoding on server side or not
134
141
serverSide bool
135
142
// ignoreContentType indicates whether when processing the HEADERS frame, ignoring checking the
136
143
// content-type is grpc or not.
137
144
//
138
- // If we've already received a HEADERS frame which indicates gRPC peer, then we can ignore
139
- // content-type for the following HEADERS frame (i.e. Trailers) .
145
+ // Trailers (after headers) should not have a content-type. And thus we will ignore checking the
146
+ // content-type.
140
147
//
141
148
// For server, this field is always false.
142
149
ignoreContentType bool
143
150
144
- // data struct will be filled with info parsed from HTTP HEADERS frame once decodeHeader function
145
- // has been invoked and returned.
151
+ // Records the states during HPACK decoding. It will be filled with info parsed from HTTP HEADERS
152
+ // frame once decodeHeader function has been invoked and returned.
146
153
data parsedHeaderData
147
154
}
148
155
@@ -267,49 +274,19 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
267
274
return status .Error (codes .Internal , "peer header list size exceeded limit" )
268
275
}
269
276
270
- // isGRPC indicates whether the peer is speaking gRPC (otherwise HTTP).
271
- //
272
- // We are in gRPC mode (peer speaking gRPC) if:
273
- // * We are client side and have already received a HEADER frame that indicates gRPC peer.
274
- // * The header contains valid a content-type, i.e. a string starts with "application/grpc"
275
- // And we should handle error specific to gRPC.
276
- //
277
- // Otherwise (i.e. a content-type string starts without "application/grpc", or does not exist), we
278
- // are in HTTP fallback mode, and should handle error specific to HTTP.
279
- //
280
- // d.ignoreContentType is only set on client side after a gRPC ResponseHeader has been received (indicating
281
- // peer speaking gRPC). Therefore, we can initialized isGRPC to d.ignoreContentType.
282
- isGRPC := d .ignoreContentType
283
- var grpcErr , httpErr , contentTypeErr error
284
277
for _ , hf := range frame .Fields {
285
- if hf .Name != "content-type" && hf .Name != ":status" && grpcErr != nil {
278
+ if hf .Name != "content-type" && hf .Name != ":status" && d . data . grpcErr != nil {
286
279
// if we've already encountered grpc related field parsing error, then we skip processing
287
280
// all following grpc related field.
288
281
continue
289
282
}
290
283
291
- if err := d .processHeaderField (hf ); err != nil {
292
- switch hf .Name {
293
- case "content-type" :
294
- contentTypeErr = err
295
- case ":status" :
296
- httpErr = err // In gRPC mode, we don't care about HTTP field parsing error, so we store it separately.
297
- default :
298
- grpcErr = err // store the first encountered gRPC field parsing error.
299
- }
300
- continue
301
- }
302
-
303
- // we got a valid content-type that starts with "applicatin/grpc", so we are operating in grpc
304
- // mode.
305
- if hf .Name == "content-type" {
306
- isGRPC = true
307
- }
284
+ d .processHeaderField (hf )
308
285
}
309
286
310
- if isGRPC {
311
- if grpcErr != nil {
312
- return grpcErr
287
+ if d . data . isGRPC {
288
+ if d . data . grpcErr != nil {
289
+ return d . data . grpcErr
313
290
}
314
291
if d .serverSide {
315
292
return nil
@@ -328,8 +305,8 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
328
305
}
329
306
330
307
// HTTP fallback mode
331
- if httpErr != nil {
332
- return httpErr
308
+ if d . data . httpErr != nil {
309
+ return d . data . httpErr
333
310
}
334
311
335
312
var (
@@ -338,33 +315,33 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
338
315
)
339
316
340
317
if d .data .httpStatus != nil {
341
- code , ok = httpStatusConvTab [* (d .data .httpStatus )]
318
+ code , ok = HTTPStatusConvTab [* (d .data .httpStatus )]
342
319
if ! ok {
343
320
code = codes .Unknown
344
321
}
345
322
}
346
323
347
- return status .Error (code , d .constructHTTPErrMsg (contentTypeErr ))
324
+ return status .Error (code , d .constructHTTPErrMsg ())
348
325
}
349
326
350
327
// constructErrMsg constructs error message to be returned in HTTP fallback mode.
351
328
// Format: HTTP status code and its corresponding message + content-type error message.
352
- func (d * decodeState ) constructHTTPErrMsg (contentTypeErr error ) string {
329
+ func (d * decodeState ) constructHTTPErrMsg () string {
353
330
var errMsgs []string
354
331
355
332
if d .data .httpStatus == nil {
356
- errMsgs = append (errMsgs , "malformed header: in HTTP fallback mode, but doesn't contain HTTP status" )
333
+ errMsgs = append (errMsgs , "malformed header: missing HTTP status" )
357
334
} else {
358
335
errMsgs = append (errMsgs , fmt .Sprintf ("%s: HTTP status code %d" , http .StatusText (* (d .data .httpStatus )), * d .data .httpStatus ))
359
336
}
360
337
361
- if contentTypeErr == nil {
338
+ if d . data . contentTypeErr == "" {
362
339
errMsgs = append (errMsgs , "transport: missing content-type field" )
363
340
} else {
364
- errMsgs = append (errMsgs , status . Convert ( contentTypeErr ). Message () )
341
+ errMsgs = append (errMsgs , d . data . contentTypeErr )
365
342
}
366
343
367
- return strings .Join (errMsgs , "\t " )
344
+ return strings .Join (errMsgs , "; " )
368
345
}
369
346
370
347
func (d * decodeState ) addMetadata (k , v string ) {
@@ -374,64 +351,72 @@ func (d *decodeState) addMetadata(k, v string) {
374
351
d .data .mdata [k ] = append (d .data .mdata [k ], v )
375
352
}
376
353
377
- func (d * decodeState ) processHeaderField (f hpack.HeaderField ) error {
354
+ func (d * decodeState ) processHeaderField (f hpack.HeaderField ) {
378
355
switch f .Name {
379
356
case "content-type" :
380
357
contentSubtype , validContentType := contentSubtype (f .Value )
381
358
if ! validContentType {
382
- return status .Errorf (codes .Internal , "transport: received the unexpected content-type %q" , f .Value )
359
+ d .data .contentTypeErr = fmt .Sprintf ("transport: received the unexpected content-type %q" , f .Value )
360
+ return
383
361
}
384
362
d .data .contentSubtype = contentSubtype
385
363
// TODO: do we want to propagate the whole content-type in the metadata,
386
364
// or come up with a way to just propagate the content-subtype if it was set?
387
365
// ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"}
388
366
// in the metadata?
389
367
d .addMetadata (f .Name , f .Value )
368
+ d .data .isGRPC = true
390
369
case "grpc-encoding" :
391
370
d .data .encoding = f .Value
392
371
case "grpc-status" :
393
372
code , err := strconv .Atoi (f .Value )
394
373
if err != nil {
395
- return status .Errorf (codes .Internal , "transport: malformed grpc-status: %v" , err )
374
+ d .data .grpcErr = status .Errorf (codes .Internal , "transport: malformed grpc-status: %v" , err )
375
+ return
396
376
}
397
377
d .data .rawStatusCode = & code
398
378
case "grpc-message" :
399
379
d .data .rawStatusMsg = decodeGrpcMessage (f .Value )
400
380
case "grpc-status-details-bin" :
401
381
v , err := decodeBinHeader (f .Value )
402
382
if err != nil {
403
- return status .Errorf (codes .Internal , "transport: malformed grpc-status-details-bin: %v" , err )
383
+ d .data .grpcErr = status .Errorf (codes .Internal , "transport: malformed grpc-status-details-bin: %v" , err )
384
+ return
404
385
}
405
386
s := & spb.Status {}
406
387
if err := proto .Unmarshal (v , s ); err != nil {
407
- return status .Errorf (codes .Internal , "transport: malformed grpc-status-details-bin: %v" , err )
388
+ d .data .grpcErr = status .Errorf (codes .Internal , "transport: malformed grpc-status-details-bin: %v" , err )
389
+ return
408
390
}
409
391
d .data .statusGen = status .FromProto (s )
410
392
case "grpc-timeout" :
411
393
d .data .timeoutSet = true
412
394
var err error
413
395
if d .data .timeout , err = decodeTimeout (f .Value ); err != nil {
414
- return status .Errorf (codes .Internal , "transport: malformed time-out: %v" , err )
396
+ d . data . grpcErr = status .Errorf (codes .Internal , "transport: malformed time-out: %v" , err )
415
397
}
416
398
case ":path" :
417
399
d .data .method = f .Value
418
400
case ":status" :
419
401
code , err := strconv .Atoi (f .Value )
420
402
if err != nil {
421
- return status .Errorf (codes .Internal , "transport: malformed http-status: %v" , err )
403
+ d .data .httpErr = status .Errorf (codes .Internal , "transport: malformed http-status: %v" , err )
404
+ return
422
405
}
423
406
d .data .httpStatus = & code
424
407
case "grpc-tags-bin" :
425
408
v , err := decodeBinHeader (f .Value )
426
409
if err != nil {
427
- return status .Errorf (codes .Internal , "transport: malformed grpc-tags-bin: %v" , err )
410
+ d .data .grpcErr = status .Errorf (codes .Internal , "transport: malformed grpc-tags-bin: %v" , err )
411
+ return
428
412
}
429
413
d .data .statsTags = v
430
414
d .addMetadata (f .Name , string (v ))
431
415
case "grpc-trace-bin" :
432
416
v , err := decodeBinHeader (f .Value )
433
417
if err != nil {
434
- return status .Errorf (codes .Internal , "transport: malformed grpc-trace-bin: %v" , err )
418
+ d .data .grpcErr = status .Errorf (codes .Internal , "transport: malformed grpc-trace-bin: %v" , err )
419
+ return
435
420
}
436
421
d .data .statsTrace = v
437
422
d .addMetadata (f .Name , string (v ))
@@ -442,11 +427,10 @@ func (d *decodeState) processHeaderField(f hpack.HeaderField) error {
442
427
v , err := decodeMetadataHeader (f .Name , f .Value )
443
428
if err != nil {
444
429
errorf ("Failed to decode metadata header (%q, %q): %v" , f .Name , f .Value , err )
445
- return nil
430
+ return
446
431
}
447
432
d .addMetadata (f .Name , v )
448
433
}
449
- return nil
450
434
}
451
435
452
436
type timeoutUnit uint8
0 commit comments