github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/resp/response.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 "errors" 46 "fmt" 47 "io" 48 "runtime" 49 "sync" 50 51 "github.com/cloudwego/hertz/pkg/common/bytebufferpool" 52 errs "github.com/cloudwego/hertz/pkg/common/errors" 53 "github.com/cloudwego/hertz/pkg/common/hlog" 54 "github.com/cloudwego/hertz/pkg/network" 55 "github.com/cloudwego/hertz/pkg/protocol" 56 "github.com/cloudwego/hertz/pkg/protocol/consts" 57 "github.com/cloudwego/hertz/pkg/protocol/http1/ext" 58 ) 59 60 // ErrBodyStreamWritePanic is returned when panic happens during writing body stream. 61 type ErrBodyStreamWritePanic struct { 62 error 63 } 64 65 type h1Response struct { 66 *protocol.Response 67 } 68 69 // String returns request representation. 70 // 71 // Returns error message instead of request representation on error. 72 // 73 // Use Write instead of String for performance-critical code. 74 func (h1Resp *h1Response) String() string { 75 w := bytebufferpool.Get() 76 zw := network.NewWriter(w) 77 if err := Write(h1Resp.Response, zw); err != nil { 78 return err.Error() 79 } 80 if err := zw.Flush(); err != nil { 81 return err.Error() 82 } 83 s := string(w.B) 84 bytebufferpool.Put(w) 85 return s 86 } 87 88 func GetHTTP1Response(resp *protocol.Response) fmt.Stringer { 89 return &h1Response{resp} 90 } 91 92 // ReadHeaderAndLimitBody reads response from the given r, limiting the body size. 93 // 94 // If maxBodySize > 0 and the body size exceeds maxBodySize, 95 // then ErrBodyTooLarge is returned. 96 // 97 // io.EOF is returned if r is closed before reading the first header byte. 98 func ReadHeaderAndLimitBody(resp *protocol.Response, r network.Reader, maxBodySize int) error { 99 resp.ResetBody() 100 err := ReadHeader(&resp.Header, r) 101 if err != nil { 102 return err 103 } 104 if resp.Header.StatusCode() == consts.StatusContinue { 105 // Read the next response according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html . 106 if err = ReadHeader(&resp.Header, r); err != nil { 107 return err 108 } 109 } 110 111 if !resp.MustSkipBody() { 112 bodyBuf := resp.BodyBuffer() 113 bodyBuf.Reset() 114 bodyBuf.B, err = ext.ReadBody(r, resp.Header.ContentLength(), maxBodySize, bodyBuf.B) 115 if err != nil { 116 return err 117 } 118 if resp.Header.ContentLength() == -1 { 119 err = ext.ReadTrailer(resp.Header.Trailer(), r) 120 if err != nil && err != io.EOF { 121 return err 122 } 123 } 124 resp.Header.SetContentLength(len(bodyBuf.B)) 125 } 126 127 return nil 128 } 129 130 type clientRespStream struct { 131 r io.Reader 132 closeCallback func(shouldClose bool) error 133 } 134 135 func (c *clientRespStream) Close() (err error) { 136 runtime.SetFinalizer(c, nil) 137 // If error happened in release, the connection may be in abnormal state. 138 // Close it in the callback in order to avoid other unexpected problems. 139 err = ext.ReleaseBodyStream(c.r) 140 shouldClose := false 141 if err != nil { 142 shouldClose = true 143 hlog.Warnf("connection will be closed instead of recycled because an error occurred during the stream body release: %s", err.Error()) 144 } 145 if c.closeCallback != nil { 146 err = c.closeCallback(shouldClose) 147 } 148 c.reset() 149 return 150 } 151 152 func (c *clientRespStream) Read(p []byte) (n int, err error) { 153 return c.r.Read(p) 154 } 155 156 func (c *clientRespStream) reset() { 157 c.closeCallback = nil 158 c.r = nil 159 clientRespStreamPool.Put(c) 160 } 161 162 var clientRespStreamPool = sync.Pool{ 163 New: func() interface{} { 164 return &clientRespStream{} 165 }, 166 } 167 168 func convertClientRespStream(bs io.Reader, fn func(shouldClose bool) error) *clientRespStream { 169 clientStream := clientRespStreamPool.Get().(*clientRespStream) 170 clientStream.r = bs 171 clientStream.closeCallback = fn 172 runtime.SetFinalizer(clientStream, (*clientRespStream).Close) 173 return clientStream 174 } 175 176 // ReadBodyStream reads response body in stream 177 func ReadBodyStream(resp *protocol.Response, r network.Reader, maxBodySize int, closeCallBack func(shouldClose bool) error) error { 178 resp.ResetBody() 179 err := ReadHeader(&resp.Header, r) 180 if err != nil { 181 return err 182 } 183 184 if resp.Header.StatusCode() == consts.StatusContinue { 185 // Read the next response according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html . 186 if err = ReadHeader(&resp.Header, r); err != nil { 187 return err 188 } 189 } 190 191 if resp.MustSkipBody() { 192 return nil 193 } 194 195 bodyBuf := resp.BodyBuffer() 196 bodyBuf.Reset() 197 bodyBuf.B, err = ext.ReadBodyWithStreaming(r, resp.Header.ContentLength(), maxBodySize, bodyBuf.B) 198 if err != nil { 199 if errors.Is(err, errs.ErrBodyTooLarge) { 200 bodyStream := ext.AcquireBodyStream(bodyBuf, r, resp.Header.Trailer(), resp.Header.ContentLength()) 201 resp.ConstructBodyStream(bodyBuf, convertClientRespStream(bodyStream, closeCallBack)) 202 return nil 203 } 204 205 if errors.Is(err, errs.ErrChunkedStream) { 206 bodyStream := ext.AcquireBodyStream(bodyBuf, r, resp.Header.Trailer(), -1) 207 resp.ConstructBodyStream(bodyBuf, convertClientRespStream(bodyStream, closeCallBack)) 208 return nil 209 } 210 211 resp.Reset() 212 return err 213 } 214 215 bodyStream := ext.AcquireBodyStream(bodyBuf, r, resp.Header.Trailer(), resp.Header.ContentLength()) 216 resp.ConstructBodyStream(bodyBuf, convertClientRespStream(bodyStream, closeCallBack)) 217 return nil 218 } 219 220 // Read reads response (including body) from the given r. 221 // 222 // io.EOF is returned if r is closed before reading the first header byte. 223 func Read(resp *protocol.Response, r network.Reader) error { 224 return ReadHeaderAndLimitBody(resp, r, 0) 225 } 226 227 // Write writes response to w. 228 // 229 // Write doesn't flush response to w for performance reasons. 230 // 231 // See also WriteTo. 232 func Write(resp *protocol.Response, w network.Writer) error { 233 sendBody := !resp.MustSkipBody() 234 235 if resp.IsBodyStream() { 236 return writeBodyStream(resp, w, sendBody) 237 } 238 239 body := resp.BodyBytes() 240 bodyLen := len(body) 241 if sendBody || bodyLen > 0 { 242 resp.Header.SetContentLength(bodyLen) 243 } 244 245 header := resp.Header.Header() 246 _, err := w.WriteBinary(header) 247 if err != nil { 248 return err 249 } 250 resp.Header.SetHeaderLength(len(header)) 251 // Write body 252 if sendBody && bodyLen > 0 { 253 _, err = w.WriteBinary(body) 254 } 255 return err 256 } 257 258 func writeBodyStream(resp *protocol.Response, w network.Writer, sendBody bool) (err error) { 259 defer func() { 260 if r := recover(); r != nil { 261 err = &ErrBodyStreamWritePanic{ 262 error: fmt.Errorf("panic while writing body stream: %+v", r), 263 } 264 } 265 }() 266 267 contentLength := resp.Header.ContentLength() 268 if contentLength < 0 { 269 lrSize := ext.LimitedReaderSize(resp.BodyStream()) 270 if lrSize >= 0 { 271 contentLength = int(lrSize) 272 if int64(contentLength) != lrSize { 273 contentLength = -1 274 } 275 if contentLength >= 0 { 276 resp.Header.SetContentLength(contentLength) 277 } 278 } 279 } 280 if contentLength >= 0 { 281 if err = WriteHeader(&resp.Header, w); err == nil && sendBody { 282 if resp.ImmediateHeaderFlush { 283 err = w.Flush() 284 } 285 if err == nil { 286 err = ext.WriteBodyFixedSize(w, resp.BodyStream(), int64(contentLength)) 287 } 288 } 289 } else { 290 resp.Header.SetContentLength(-1) 291 if err = WriteHeader(&resp.Header, w); err == nil && sendBody { 292 if resp.ImmediateHeaderFlush { 293 err = w.Flush() 294 } 295 if err == nil { 296 err = ext.WriteBodyChunked(w, resp.BodyStream()) 297 } 298 if err == nil { 299 err = ext.WriteTrailer(resp.Header.Trailer(), w) 300 } 301 } 302 } 303 err1 := resp.CloseBodyStream() 304 if err == nil { 305 err = err1 306 } 307 return err 308 }