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