@@ -945,6 +945,19 @@ func appendHeaderFieldsFromMD(headerFields []hpack.HeaderField, md metadata.MD)
945945 return headerFields
946946}
947947
948+ // headerFieldsCountFromMD returns the total number of header fields that
949+ // appendHeaderFieldsFromMD will produce from md. Reserved headers are dropped
950+ // by appendHeaderFieldsFromMD, so this may slightly over-count, which is
951+ // preferable to growing the slice.
952+ func headerFieldsCountFromMD (md metadata.MD ) int {
953+ n := 0
954+ for _ , vv := range md {
955+ n += len (vv )
956+ }
957+
958+ return n
959+ }
960+
948961func (t * http2Server ) checkForHeaderListSize (hf []hpack.HeaderField ) bool {
949962 if t .maxSendHeaderListSize == nil {
950963 return true
@@ -1041,9 +1054,14 @@ func (t *http2Server) writeHeader(s *ServerStream, md metadata.MD) error {
10411054}
10421055
10431056func (t * http2Server ) writeHeaderLocked (s * ServerStream ) error {
1044- // TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
1045- // first and create a slice of that exact size.
1046- headerFields := make ([]hpack.HeaderField , 0 , 2 ) // at least :status, content-type will be there if none else.
1057+ // Count the metadata header fields as well so the slice is not reallocated
1058+ // while they are appended below.
1059+ headerFieldCount := 2 // :status, content-type
1060+ headerFieldCount += headerFieldsCountFromMD (s .header )
1061+ if s .sendCompress != "" {
1062+ headerFieldCount ++
1063+ }
1064+ headerFields := make ([]hpack.HeaderField , 0 , headerFieldCount )
10471065 headerFields = append (headerFields , hpack.HeaderField {Name : ":status" , Value : "200" })
10481066 headerFields = append (headerFields , hpack.HeaderField {Name : "content-type" , Value : grpcutil .ContentType (s .contentSubtype )})
10491067 if s .sendCompress != "" {
@@ -1087,10 +1105,18 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error {
10871105 return nil
10881106 }
10891107
1090- // TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields
1091- // first and create a slice of that exact size.
1092- headerFields := make ([]hpack.HeaderField , 0 , 2 ) // grpc-status and grpc-message will be there if none else.
1093- if ! s .updateHeaderSent () { // No headers have been sent.
1108+ // Count the metadata header fields as well so the slice is not reallocated
1109+ // while they are appended below.
1110+ headersAlreadySent := s .updateHeaderSent ()
1111+ headerFieldCount := 2 // grpc-status, grpc-message
1112+ headerFieldCount += headerFieldsCountFromMD (s .trailer )
1113+ if ! headersAlreadySent && len (s .header ) == 0 {
1114+ // Trailer only response gets :status and content-type as well.
1115+ headerFieldCount += 2
1116+ }
1117+ // +1 for optional grpc-status-details-bin
1118+ headerFields := make ([]hpack.HeaderField , 0 , headerFieldCount + 1 )
1119+ if ! headersAlreadySent { // No headers have been sent.
10941120 if len (s .header ) > 0 { // Send a separate header frame.
10951121 if err := t .writeHeaderLocked (s ); err != nil {
10961122 return err
0 commit comments