github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/resp/header.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * The MIT License (MIT) 17 * 18 * Copyright (c) 2015-present Aliaksandr Valialkin, VertaMedia, Kirill Danshin, Erik Dubbelboer, FastHTTP Authors 19 * 20 * Permission is hereby granted, free of charge, to any person obtaining a copy 21 * of this software and associated documentation files (the "Software"), to deal 22 * in the Software without restriction, including without limitation the rights 23 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24 * copies of the Software, and to permit persons to whom the Software is 25 * furnished to do so, subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be included in 28 * all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 36 * THE SOFTWARE. 37 * 38 * This file may have been modified by CloudWeGo authors. All CloudWeGo 39 * Modifications are Copyright 2022 CloudWeGo Authors. 40 */ 41 42 package resp 43 44 import ( 45 "bytes" 46 "errors" 47 "fmt" 48 "io" 49 "strings" 50 51 "github.com/cloudwego/hertz/internal/bytesconv" 52 "github.com/cloudwego/hertz/internal/bytestr" 53 errs "github.com/cloudwego/hertz/pkg/common/errors" 54 "github.com/cloudwego/hertz/pkg/common/utils" 55 "github.com/cloudwego/hertz/pkg/network" 56 "github.com/cloudwego/hertz/pkg/protocol" 57 "github.com/cloudwego/hertz/pkg/protocol/consts" 58 "github.com/cloudwego/hertz/pkg/protocol/http1/ext" 59 ) 60 61 var errTimeout = errs.New(errs.ErrTimeout, errs.ErrorTypePublic, "read response header") 62 63 // Read reads response header from r. 64 // 65 // io.EOF is returned if r is closed before reading the first header byte. 66 func ReadHeader(h *protocol.ResponseHeader, r network.Reader) error { 67 n := 1 68 for { 69 err := tryRead(h, r, n) 70 if err == nil { 71 return nil 72 } 73 if !errors.Is(err, errs.ErrNeedMore) { 74 h.ResetSkipNormalize() 75 return err 76 } 77 78 // No more data available on the wire, try block peek(by netpoll) 79 if n == r.Len() { 80 n++ 81 82 continue 83 } 84 n = r.Len() 85 } 86 } 87 88 // WriteHeader writes response header to w. 89 func WriteHeader(h *protocol.ResponseHeader, w network.Writer) error { 90 header := h.Header() 91 h.SetHeaderLength(len(header)) 92 _, err := w.WriteBinary(header) 93 if err != nil { 94 return err 95 } 96 return nil 97 } 98 99 // ConnectionUpgrade returns true if 'Connection: Upgrade' header is set. 100 func ConnectionUpgrade(h *protocol.ResponseHeader) bool { 101 return ext.HasHeaderValue(h.Peek(consts.HeaderConnection), bytestr.StrKeepAlive) 102 } 103 104 func tryRead(h *protocol.ResponseHeader, r network.Reader, n int) error { 105 h.ResetSkipNormalize() 106 b, err := r.Peek(n) 107 if len(b) == 0 { 108 // Return ErrTimeout on any timeout. 109 if err != nil && strings.Contains(err.Error(), "timeout") { 110 return errTimeout 111 } 112 // treat all other errors on the first byte read as EOF 113 if n == 1 || err == io.EOF { 114 return io.EOF 115 } 116 117 return fmt.Errorf("error when reading response headers: %s", err) 118 } 119 b = ext.MustPeekBuffered(r) 120 headersLen, errParse := parse(h, b) 121 if errParse != nil { 122 return ext.HeaderError("response", err, errParse, b) 123 } 124 ext.MustDiscard(r, headersLen) 125 return nil 126 } 127 128 func parseHeaders(h *protocol.ResponseHeader, buf []byte) (int, error) { 129 // 'identity' content-length by default 130 h.InitContentLengthWithValue(-2) 131 132 var s ext.HeaderScanner 133 s.B = buf 134 s.DisableNormalizing = h.IsDisableNormalizing() 135 var err error 136 for s.Next() { 137 if len(s.Key) > 0 { 138 switch s.Key[0] | 0x20 { 139 case 'c': 140 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrContentType) { 141 h.SetContentTypeBytes(s.Value) 142 continue 143 } 144 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrContentEncoding) { 145 h.SetContentEncodingBytes(s.Value) 146 continue 147 } 148 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrContentLength) { 149 var contentLength int 150 if h.ContentLength() != -1 { 151 if contentLength, err = protocol.ParseContentLength(s.Value); err != nil { 152 h.InitContentLengthWithValue(-2) 153 } else { 154 h.InitContentLengthWithValue(contentLength) 155 h.SetContentLengthBytes(s.Value) 156 } 157 } 158 continue 159 } 160 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrConnection) { 161 if bytes.Equal(s.Value, bytestr.StrClose) { 162 h.SetConnectionClose(true) 163 } else { 164 h.SetConnectionClose(false) 165 h.AddArgBytes(s.Key, s.Value, protocol.ArgsHasValue) 166 } 167 continue 168 } 169 case 's': 170 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrServer) { 171 h.SetServerBytes(s.Value) 172 continue 173 } 174 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrSetCookie) { 175 h.ParseSetCookie(s.Value) 176 continue 177 } 178 case 't': 179 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrTransferEncoding) { 180 if !bytes.Equal(s.Value, bytestr.StrIdentity) { 181 h.InitContentLengthWithValue(-1) 182 h.SetArgBytes(bytestr.StrTransferEncoding, bytestr.StrChunked, protocol.ArgsHasValue) 183 } 184 continue 185 } 186 if utils.CaseInsensitiveCompare(s.Key, bytestr.StrTrailer) { 187 err = h.Trailer().SetTrailers(s.Value) 188 continue 189 } 190 } 191 h.AddArgBytes(s.Key, s.Value, protocol.ArgsHasValue) 192 } 193 } 194 if s.Err != nil { 195 h.SetConnectionClose(true) 196 return 0, s.Err 197 } 198 199 if h.ContentLength() < 0 { 200 h.SetContentLengthBytes(h.ContentLengthBytes()[:0]) 201 } 202 if h.ContentLength() == -2 && !ConnectionUpgrade(h) && !h.MustSkipContentLength() { 203 h.SetArgBytes(bytestr.StrTransferEncoding, bytestr.StrIdentity, protocol.ArgsHasValue) 204 h.SetConnectionClose(true) 205 } 206 if !h.IsHTTP11() && !h.ConnectionClose() { 207 // close connection for non-http/1.1 response unless 'Connection: keep-alive' is set. 208 v := h.PeekArgBytes(bytestr.StrConnection) 209 h.SetConnectionClose(!ext.HasHeaderValue(v, bytestr.StrKeepAlive)) 210 } 211 212 return len(buf) - len(s.B), err 213 } 214 215 func parse(h *protocol.ResponseHeader, buf []byte) (int, error) { 216 m, err := parseFirstLine(h, buf) 217 if err != nil { 218 return 0, err 219 } 220 n, err := parseHeaders(h, buf[m:]) 221 if err != nil { 222 return 0, err 223 } 224 return m + n, nil 225 } 226 227 func parseFirstLine(h *protocol.ResponseHeader, buf []byte) (int, error) { 228 bNext := buf 229 var b []byte 230 var err error 231 for len(b) == 0 { 232 if b, bNext, err = utils.NextLine(bNext); err != nil { 233 return 0, err 234 } 235 } 236 237 // parse protocol 238 n := bytes.IndexByte(b, ' ') 239 if n < 0 { 240 return 0, fmt.Errorf("cannot find whitespace in the first line of response %q", buf) 241 } 242 243 isHTTP11 := bytes.Equal(b[:n], bytestr.StrHTTP11) 244 if !isHTTP11 { 245 h.SetProtocol(consts.HTTP10) 246 } else { 247 h.SetProtocol(consts.HTTP11) 248 } 249 250 b = b[n+1:] 251 252 // parse status code 253 var statusCode int 254 statusCode, n, err = bytesconv.ParseUintBuf(b) 255 h.SetStatusCode(statusCode) 256 if err != nil { 257 return 0, fmt.Errorf("cannot parse response status code: %s. Response %q", err, buf) 258 } 259 if len(b) > n && b[n] != ' ' { 260 return 0, fmt.Errorf("unexpected char at the end of status code. Response %q", buf) 261 } 262 263 return len(buf) - len(bNext), nil 264 }