@@ -38,11 +38,12 @@ const (
3838// HTTP/1 equivalents. See https://tools.ietf.org/html/rfc7540#section-8.1.2.3
3939func H2RequestHeadersToH1Request (h2 []Header , h1 * http.Request ) error {
4040 for _ , header := range h2 {
41- if ! IsControlHeader (header .Name ) {
41+ name := strings .ToLower (header .Name )
42+ if ! IsControlHeader (name ) {
4243 continue
4344 }
4445
45- switch strings . ToLower ( header . Name ) {
46+ switch name {
4647 case ":method" :
4748 h1 .Method = header .Value
4849 case ":scheme" :
@@ -116,18 +117,14 @@ func ParseUserHeaders(headerNameToParseFrom string, headers []Header) ([]Header,
116117}
117118
118119func IsControlHeader (headerName string ) bool {
119- headerName = strings .ToLower (headerName )
120-
121120 return headerName == "content-length" ||
122121 headerName == "connection" || headerName == "upgrade" || // Websocket headers
123122 strings .HasPrefix (headerName , ":" ) ||
124123 strings .HasPrefix (headerName , "cf-" )
125124}
126125
127- // IsWebsocketClientHeader returns true if the header name is required by the client to upgrade properly
128- func IsWebsocketClientHeader (headerName string ) bool {
129- headerName = strings .ToLower (headerName )
130-
126+ // isWebsocketClientHeader returns true if the header name is required by the client to upgrade properly
127+ func isWebsocketClientHeader (headerName string ) bool {
131128 return headerName == "sec-websocket-accept" ||
132129 headerName == "connection" ||
133130 headerName == "upgrade"
@@ -137,56 +134,73 @@ func H1ResponseToH2ResponseHeaders(h1 *http.Response) (h2 []Header) {
137134 h2 = []Header {
138135 {Name : ":status" , Value : strconv .Itoa (h1 .StatusCode )},
139136 }
140- userHeaders := http.Header {}
137+ userHeaders := make ( http.Header , len ( h1 . Header ))
141138 for header , values := range h1 .Header {
142- for _ , value := range values {
143- if strings .ToLower (header ) == "content-length" {
144- // This header has meaning in HTTP/2 and will be used by the edge,
145- // so it should be sent as an HTTP/2 response header.
146-
147- // Since these are http2 headers, they're required to be lowercase
148- h2 = append (h2 , Header {Name : strings .ToLower (header ), Value : value })
149- } else if ! IsControlHeader (header ) || IsWebsocketClientHeader (header ) {
150- // User headers, on the other hand, must all be serialized so that
151- // HTTP/2 header validation won't be applied to HTTP/1 header values
152- if _ , ok := userHeaders [header ]; ok {
153- userHeaders [header ] = append (userHeaders [header ], value )
154- } else {
155- userHeaders [header ] = []string {value }
156- }
157- }
139+ h2name := strings .ToLower (header )
140+ if h2name == "content-length" {
141+ // This header has meaning in HTTP/2 and will be used by the edge,
142+ // so it should be sent as an HTTP/2 response header.
143+
144+ // Since these are http2 headers, they're required to be lowercase
145+ h2 = append (h2 , Header {Name : "content-length" , Value : values [0 ]})
146+ } else if ! IsControlHeader (h2name ) || isWebsocketClientHeader (h2name ) {
147+ // User headers, on the other hand, must all be serialized so that
148+ // HTTP/2 header validation won't be applied to HTTP/1 header values
149+ userHeaders [header ] = values
158150 }
159151 }
160152
161153 // Perform user header serialization and set them in the single header
162- h2 = append (h2 , CreateSerializedHeaders ( ResponseUserHeadersField , userHeaders )... )
154+ h2 = append (h2 , Header { ResponseUserHeadersField , SerializeHeaders ( userHeaders )} )
163155
164156 return h2
165157}
166158
167159// Serialize HTTP1.x headers by base64-encoding each header name and value,
168160// and then joining them in the format of [key:value;]
169161func SerializeHeaders (h1Headers http.Header ) string {
170- var serializedHeaders []string
162+ // compute size of the fully serialized value and largest temp buffer we will need
163+ serializedLen := 0
164+ maxTempLen := 0
171165 for headerName , headerValues := range h1Headers {
172166 for _ , headerValue := range headerValues {
173- encodedName := make ([]byte , headerEncoding .EncodedLen (len (headerName )))
174- headerEncoding .Encode (encodedName , []byte (headerName ))
175-
176- encodedValue := make ([]byte , headerEncoding .EncodedLen (len (headerValue )))
177- headerEncoding .Encode (encodedValue , []byte (headerValue ))
178-
179- serializedHeaders = append (
180- serializedHeaders ,
181- strings .Join (
182- []string {string (encodedName ), string (encodedValue )},
183- ":" ,
184- ),
185- )
167+ nameLen := headerEncoding .EncodedLen (len (headerName ))
168+ valueLen := headerEncoding .EncodedLen (len (headerValue ))
169+ const delims = 2
170+ serializedLen += delims + nameLen + valueLen
171+ if nameLen > maxTempLen {
172+ maxTempLen = nameLen
173+ }
174+ if valueLen > maxTempLen {
175+ maxTempLen = valueLen
176+ }
186177 }
187178 }
179+ var buf strings.Builder
180+ buf .Grow (serializedLen )
181+
182+ temp := make ([]byte , maxTempLen )
183+ writeB64 := func (s string ) {
184+ n := headerEncoding .EncodedLen (len (s ))
185+ if n > len (temp ) {
186+ temp = make ([]byte , n )
187+ }
188+ headerEncoding .Encode (temp [:n ], []byte (s ))
189+ buf .Write (temp [:n ])
190+ }
188191
189- return strings .Join (serializedHeaders , ";" )
192+ for headerName , headerValues := range h1Headers {
193+ for _ , headerValue := range headerValues {
194+ if buf .Len () > 0 {
195+ buf .WriteByte (';' )
196+ }
197+ writeB64 (headerName )
198+ buf .WriteByte (':' )
199+ writeB64 (headerValue )
200+ }
201+ }
202+
203+ return buf .String ()
190204}
191205
192206// Deserialize headers serialized by `SerializeHeader`
@@ -225,18 +239,6 @@ func DeserializeHeaders(serializedHeaders string) ([]Header, error) {
225239 return deserialized , nil
226240}
227241
228- func CreateSerializedHeaders (headersField string , headers ... http.Header ) []Header {
229- var serializedHeaderChunks []string
230- for _ , headerChunk := range headers {
231- serializedHeaderChunks = append (serializedHeaderChunks , SerializeHeaders (headerChunk ))
232- }
233-
234- return []Header {{
235- headersField ,
236- strings .Join (serializedHeaderChunks , ";" ),
237- }}
238- }
239-
240242type ResponseMetaHeader struct {
241243 Source string `json:"src"`
242244}
0 commit comments