@@ -131,7 +131,7 @@ func (g *grpcHandler) ContentTypes() map[string]struct{} {
131
131
}
132
132
133
133
func (* grpcHandler ) SetTimeout (request * http.Request ) (context.Context , context.CancelFunc , error ) {
134
- timeout , err := grpcParseTimeout (request .Header . Get ( grpcHeaderTimeout ))
134
+ timeout , err := grpcParseTimeout (getHeaderCanonical ( request .Header , grpcHeaderTimeout ))
135
135
if err != nil && ! errors .Is (err , errNoTimeout ) {
136
136
// Errors here indicate that the client sent an invalid timeout header, so
137
137
// the error text is safe to send back.
@@ -152,8 +152,8 @@ func (g *grpcHandler) NewConn(
152
152
// send the error to the client later on.
153
153
requestCompression , responseCompression , failed := negotiateCompression (
154
154
g .CompressionPools ,
155
- request .Header . Get ( grpcHeaderCompression ),
156
- request .Header . Get ( grpcHeaderAcceptCompression ),
155
+ getHeaderCanonical ( request .Header , grpcHeaderCompression ),
156
+ getHeaderCanonical ( request .Header , grpcHeaderAcceptCompression ),
157
157
)
158
158
if failed == nil {
159
159
failed = checkServerStreamsCanFlush (g .Spec , responseWriter )
@@ -167,13 +167,13 @@ func (g *grpcHandler) NewConn(
167
167
// Since we know that these header keys are already in canonical form, we can
168
168
// skip the normalization in Header.Set.
169
169
header := responseWriter .Header ()
170
- header [headerContentType ] = []string {request .Header . Get ( headerContentType )}
170
+ header [headerContentType ] = []string {getHeaderCanonical ( request .Header , headerContentType )}
171
171
header [grpcHeaderAcceptCompression ] = []string {g .CompressionPools .CommaSeparatedNames ()}
172
172
if responseCompression != compressionIdentity {
173
173
header [grpcHeaderCompression ] = []string {responseCompression }
174
174
}
175
175
176
- codecName := grpcCodecFromContentType (g .web , request .Header . Get ( headerContentType ))
176
+ codecName := grpcCodecFromContentType (g .web , getHeaderCanonical ( request .Header , headerContentType ))
177
177
codec := g .Codecs .Get (codecName ) // handler.go guarantees this is not nil
178
178
protocolName := ProtocolGRPC
179
179
if g .web {
@@ -237,7 +237,7 @@ func (g *grpcClient) Peer() Peer {
237
237
func (g * grpcClient ) WriteRequestHeader (_ StreamType , header http.Header ) {
238
238
// We know these header keys are in canonical form, so we can bypass all the
239
239
// checks in Header.Set.
240
- if header . Get ( headerUserAgent ) == "" {
240
+ if getHeaderCanonical ( header , headerUserAgent ) == "" {
241
241
header [headerUserAgent ] = []string {defaultGrpcUserAgent }
242
242
}
243
243
header [headerContentType ] = []string {grpcContentTypeFromCodecName (g .web , g .Codec .Name ())}
@@ -365,7 +365,7 @@ func (cc *grpcClientConn) Receive(msg any) error {
365
365
if err == nil {
366
366
return nil
367
367
}
368
- if cc .responseHeader . Get ( grpcHeaderStatus ) != "" {
368
+ if getHeaderCanonical ( cc .responseHeader , grpcHeaderStatus ) != "" {
369
369
// We got what gRPC calls a trailers-only response, which puts the trailing
370
370
// metadata (including errors) into HTTP headers. validateResponse has
371
371
// already extracted the error.
@@ -423,7 +423,7 @@ func (cc *grpcClientConn) validateResponse(response *http.Response) *Error {
423
423
); err != nil {
424
424
return err
425
425
}
426
- compression := response .Header . Get ( grpcHeaderCompression )
426
+ compression := getHeaderCanonical ( response .Header , grpcHeaderCompression )
427
427
cc .unmarshaler .envelopeReader .compressionPool = cc .compressionPools .Get (compression )
428
428
return nil
429
429
}
@@ -542,11 +542,13 @@ func (hc *grpcHandlerConn) Close(err error) (retErr error) {
542
542
// implement http.Flusher, we must pre-declare our HTTP trailers. We can
543
543
// remove this when Go 1.21 ships and we drop support for Go 1.19.
544
544
for key := range mergedTrailers {
545
- hc .responseWriter .Header (). Add ( "Trailer" , key )
545
+ addHeaderCanonical ( hc .responseWriter .Header (), headerTrailer , key )
546
546
}
547
547
hc .responseWriter .WriteHeader (http .StatusOK )
548
548
for key , values := range mergedTrailers {
549
549
for _ , value := range values {
550
+ // These are potentially user-supplied, so we can't assume they're in
551
+ // canonical form. Don't use addHeaderCanonical.
550
552
hc .responseWriter .Header ().Add (key , value )
551
553
}
552
554
}
@@ -561,6 +563,8 @@ func (hc *grpcHandlerConn) Close(err error) (retErr error) {
561
563
// logic breaks Envoy's gRPC-Web translation.
562
564
for key , values := range mergedTrailers {
563
565
for _ , value := range values {
566
+ // These are potentially user-supplied, so we can't assume they're in
567
+ // canonical form. Don't use addHeaderCanonical.
564
568
hc .responseWriter .Header ().Add (http .TrailerPrefix + key , value )
565
569
}
566
570
}
@@ -636,7 +640,7 @@ func grpcValidateResponse(
636
640
if response .StatusCode != http .StatusOK {
637
641
return errorf (grpcHTTPToCode (response .StatusCode ), "HTTP status %v" , response .Status )
638
642
}
639
- if compression := response .Header . Get ( grpcHeaderCompression ); compression != "" &&
643
+ if compression := getHeaderCanonical ( response .Header , grpcHeaderCompression ); compression != "" &&
640
644
compression != compressionIdentity &&
641
645
! availableCompressors .Contains (compression ) {
642
646
// Per https://github.com/grpc/grpc/blob/master/doc/compression.md, we
@@ -658,11 +662,11 @@ func grpcValidateResponse(
658
662
); err != nil && ! errors .Is (err , errTrailersWithoutGRPCStatus ) {
659
663
// Per the specification, only the HTTP status code and Content-Type should
660
664
// be treated as headers. The rest should be treated as trailing metadata.
661
- if contentType := response .Header . Get ( headerContentType ); contentType != "" {
662
- header . Set ( headerContentType , contentType )
665
+ if contentType := getHeaderCanonical ( response .Header , headerContentType ); contentType != "" {
666
+ setHeaderCanonical ( header , headerContentType , contentType )
663
667
}
664
668
mergeHeaders (trailer , response .Header )
665
- trailer . Del ( headerContentType )
669
+ delHeaderCanonical ( trailer , headerContentType )
666
670
// Also set the error metadata
667
671
err .meta = header .Clone ()
668
672
mergeHeaders (err .meta , trailer )
@@ -699,7 +703,7 @@ func grpcHTTPToCode(httpCode int) Code {
699
703
// use a different codec. Consequently, this function needs a Protobuf codec to
700
704
// unmarshal error information in the headers.
701
705
func grpcErrorFromTrailer (bufferPool * bufferPool , protobuf Codec , trailer http.Header ) * Error {
702
- codeHeader := trailer . Get ( grpcHeaderStatus )
706
+ codeHeader := getHeaderCanonical ( trailer , grpcHeaderStatus )
703
707
if codeHeader == "" {
704
708
return NewError (CodeInternal , errTrailersWithoutGRPCStatus )
705
709
}
@@ -711,10 +715,10 @@ func grpcErrorFromTrailer(bufferPool *bufferPool, protobuf Codec, trailer http.H
711
715
if err != nil {
712
716
return errorf (CodeInternal , "gRPC protocol error: invalid error code %q" , codeHeader )
713
717
}
714
- message := grpcPercentDecode (bufferPool , trailer . Get ( grpcHeaderMessage ))
718
+ message := grpcPercentDecode (bufferPool , getHeaderCanonical ( trailer , grpcHeaderMessage ))
715
719
retErr := NewWireError (Code (code ), errors .New (message ))
716
720
717
- detailsBinaryEncoded := trailer . Get ( grpcHeaderDetails )
721
+ detailsBinaryEncoded := getHeaderCanonical ( trailer , grpcHeaderDetails )
718
722
if len (detailsBinaryEncoded ) > 0 {
719
723
detailsBinary , err := DecodeBinaryHeader (detailsBinaryEncoded )
720
724
if err != nil {
@@ -794,19 +798,21 @@ func grpcContentTypeFromCodecName(web bool, name string) string {
794
798
795
799
func grpcErrorToTrailer (bufferPool * bufferPool , trailer http.Header , protobuf Codec , err error ) {
796
800
if err == nil {
797
- trailer . Set ( grpcHeaderStatus , "0" ) // zero is the gRPC OK status
798
- trailer . Set ( grpcHeaderMessage , "" )
801
+ setHeaderCanonical ( trailer , grpcHeaderStatus , "0" ) // zero is the gRPC OK status
802
+ setHeaderCanonical ( trailer , grpcHeaderMessage , "" )
799
803
return
800
804
}
801
805
status := grpcStatusFromError (err )
802
806
code := strconv .Itoa (int (status .Code ))
803
807
bin , binErr := protobuf .Marshal (status )
804
808
if binErr != nil {
805
- trailer .Set (
809
+ setHeaderCanonical (
810
+ trailer ,
806
811
grpcHeaderStatus ,
807
812
strconv .FormatInt (int64 (CodeInternal ), 10 /* base */ ),
808
813
)
809
- trailer .Set (
814
+ setHeaderCanonical (
815
+ trailer ,
810
816
grpcHeaderMessage ,
811
817
grpcPercentEncode (
812
818
bufferPool ,
@@ -818,9 +824,9 @@ func grpcErrorToTrailer(bufferPool *bufferPool, trailer http.Header, protobuf Co
818
824
if connectErr , ok := asError (err ); ok {
819
825
mergeHeaders (trailer , connectErr .meta )
820
826
}
821
- trailer . Set ( grpcHeaderStatus , code )
822
- trailer . Set ( grpcHeaderMessage , grpcPercentEncode (bufferPool , status .Message ))
823
- trailer . Set ( grpcHeaderDetails , EncodeBinaryHeader (bin ))
827
+ setHeaderCanonical ( trailer , grpcHeaderStatus , code )
828
+ setHeaderCanonical ( trailer , grpcHeaderMessage , grpcPercentEncode (bufferPool , status .Message ))
829
+ setHeaderCanonical ( trailer , grpcHeaderDetails , EncodeBinaryHeader (bin ))
824
830
}
825
831
826
832
func grpcStatusFromError (err error ) * statusv1.Status {
0 commit comments