github.com/cloudwego/hertz@v0.9.3/pkg/protocol/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 protocol 43 44 import ( 45 "io" 46 "net" 47 "sync" 48 49 "github.com/cloudwego/hertz/internal/bytesconv" 50 "github.com/cloudwego/hertz/internal/nocopy" 51 "github.com/cloudwego/hertz/pkg/common/bytebufferpool" 52 "github.com/cloudwego/hertz/pkg/common/compress" 53 "github.com/cloudwego/hertz/pkg/common/utils" 54 "github.com/cloudwego/hertz/pkg/network" 55 ) 56 57 var ( 58 responsePool sync.Pool 59 // NoResponseBody is an io.ReadCloser with no bytes. Read always returns EOF 60 // and Close always returns nil. It can be used in an ingoing client 61 // response to explicitly signal that a response has zero bytes. 62 NoResponseBody = noBody{} 63 ) 64 65 // Response represents HTTP response. 66 // 67 // It is forbidden copying Response instances. Create new instances 68 // and use CopyTo instead. 69 // 70 // Response instance MUST NOT be used from concurrently running goroutines. 71 type Response struct { 72 noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used 73 74 // Response header 75 // 76 // Copying Header by value is forbidden. Use pointer to Header instead. 77 Header ResponseHeader 78 79 // Flush headers as soon as possible without waiting for first body bytes. 80 // Relevant for bodyStream only. 81 ImmediateHeaderFlush bool 82 83 bodyStream io.Reader 84 w responseBodyWriter 85 body *bytebufferpool.ByteBuffer 86 bodyRaw []byte 87 maxKeepBodySize int 88 89 // Response.Read() skips reading body if set to true. 90 // Use it for reading HEAD responses. 91 // 92 // Response.Write() skips writing body if set to true. 93 // Use it for writing HEAD responses. 94 SkipBody bool 95 96 // Remote TCPAddr from concurrently net.Conn 97 raddr net.Addr 98 // Local TCPAddr from concurrently net.Conn 99 laddr net.Addr 100 101 // If set a hijackWriter, hertz will skip the default header/body writer process. 102 hijackWriter network.ExtWriter 103 } 104 105 func (resp *Response) GetHijackWriter() network.ExtWriter { 106 return resp.hijackWriter 107 } 108 109 func (resp *Response) HijackWriter(writer network.ExtWriter) { 110 resp.hijackWriter = writer 111 } 112 113 type responseBodyWriter struct { 114 r *Response 115 } 116 117 func (w *responseBodyWriter) Write(p []byte) (int, error) { 118 w.r.AppendBody(p) 119 return len(p), nil 120 } 121 122 func (resp *Response) MustSkipBody() bool { 123 return resp.SkipBody || resp.Header.MustSkipContentLength() 124 } 125 126 // BodyGunzip returns un-gzipped body data. 127 // 128 // This method may be used if the response header contains 129 // 'Content-Encoding: gzip' for reading un-gzipped body. 130 // Use Body for reading gzipped response body. 131 func (resp *Response) BodyGunzip() ([]byte, error) { 132 return gunzipData(resp.Body()) 133 } 134 135 // SetConnectionClose sets 'Connection: close' header. 136 func (resp *Response) SetConnectionClose() { 137 resp.Header.SetConnectionClose(true) 138 } 139 140 // SetBodyString sets response body. 141 func (resp *Response) SetBodyString(body string) { 142 resp.CloseBodyStream() //nolint:errcheck 143 resp.BodyBuffer().SetString(body) //nolint:errcheck 144 } 145 146 func (resp *Response) ConstructBodyStream(body *bytebufferpool.ByteBuffer, bodyStream io.Reader) { 147 resp.body = body 148 resp.bodyStream = bodyStream 149 } 150 151 // BodyWriter returns writer for populating response body. 152 // 153 // If used inside RequestHandler, the returned writer must not be used 154 // after returning from RequestHandler. Use RequestContext.Write 155 // or SetBodyStreamWriter in this case. 156 func (resp *Response) BodyWriter() io.Writer { 157 resp.w.r = resp 158 return &resp.w 159 } 160 161 // SetStatusCode sets response status code. 162 func (resp *Response) SetStatusCode(statusCode int) { 163 resp.Header.SetStatusCode(statusCode) 164 } 165 166 func (resp *Response) SetMaxKeepBodySize(n int) { 167 resp.maxKeepBodySize = n 168 } 169 170 func (resp *Response) BodyBytes() []byte { 171 if resp.bodyRaw != nil { 172 return resp.bodyRaw 173 } 174 if resp.body == nil { 175 return nil 176 } 177 return resp.body.B 178 } 179 180 func (resp *Response) HasBodyBytes() bool { 181 return len(resp.BodyBytes()) != 0 182 } 183 184 func (resp *Response) CopyToSkipBody(dst *Response) { 185 dst.Reset() 186 resp.Header.CopyTo(&dst.Header) 187 dst.SkipBody = resp.SkipBody 188 dst.raddr = resp.raddr 189 dst.laddr = resp.laddr 190 } 191 192 // IsBodyStream returns true if body is set via SetBodyStream* 193 func (resp *Response) IsBodyStream() bool { 194 return resp.bodyStream != nil 195 } 196 197 // SetBodyStream sets response body stream and, optionally body size. 198 // 199 // If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes 200 // before returning io.EOF. 201 // 202 // If bodySize < 0, then bodyStream is read until io.EOF. 203 // 204 // bodyStream.Close() is called after finishing reading all body data 205 // if it implements io.Closer. 206 // 207 // See also SetBodyStreamWriter. 208 func (resp *Response) SetBodyStream(bodyStream io.Reader, bodySize int) { 209 resp.ResetBody() 210 resp.bodyStream = bodyStream 211 resp.Header.SetContentLength(bodySize) 212 } 213 214 // SetBodyStreamNoReset is almost the same as SetBodyStream, 215 // but it doesn't reset the bodyStream before. 216 func (resp *Response) SetBodyStreamNoReset(bodyStream io.Reader, bodySize int) { 217 resp.bodyStream = bodyStream 218 resp.Header.SetContentLength(bodySize) 219 } 220 221 // BodyE returns response body. 222 func (resp *Response) BodyE() ([]byte, error) { 223 if resp.bodyStream != nil { 224 bodyBuf := resp.BodyBuffer() 225 bodyBuf.Reset() 226 zw := network.NewWriter(bodyBuf) 227 _, err := utils.CopyZeroAlloc(zw, resp.bodyStream) 228 resp.CloseBodyStream() //nolint:errcheck 229 if err != nil { 230 return nil, err 231 } 232 } 233 return resp.BodyBytes(), nil 234 } 235 236 // Body returns response body. 237 // if get body failed, returns nil. 238 func (resp *Response) Body() []byte { 239 body, _ := resp.BodyE() 240 return body 241 } 242 243 // BodyWriteTo writes response body to w. 244 func (resp *Response) BodyWriteTo(w io.Writer) error { 245 zw := network.NewWriter(w) 246 if resp.bodyStream != nil { 247 _, err := utils.CopyZeroAlloc(zw, resp.bodyStream) 248 resp.CloseBodyStream() //nolint:errcheck 249 return err 250 } 251 252 body := resp.BodyBytes() 253 zw.WriteBinary(body) //nolint:errcheck 254 return zw.Flush() 255 } 256 257 // CopyTo copies resp contents to dst except of body stream. 258 func (resp *Response) CopyTo(dst *Response) { 259 resp.CopyToSkipBody(dst) 260 if resp.bodyRaw != nil { 261 dst.bodyRaw = append(dst.bodyRaw[:0], resp.bodyRaw...) 262 if dst.body != nil { 263 dst.body.Reset() 264 } 265 } else if resp.body != nil { 266 dst.BodyBuffer().Set(resp.body.B) 267 } else if dst.body != nil { 268 dst.body.Reset() 269 } 270 } 271 272 func SwapResponseBody(a, b *Response) { 273 a.body, b.body = b.body, a.body 274 a.bodyRaw, b.bodyRaw = b.bodyRaw, a.bodyRaw 275 a.bodyStream, b.bodyStream = b.bodyStream, a.bodyStream 276 } 277 278 // Reset clears response contents. 279 func (resp *Response) Reset() { 280 resp.Header.Reset() 281 resp.resetSkipHeader() 282 resp.SkipBody = false 283 resp.raddr = nil 284 resp.laddr = nil 285 resp.ImmediateHeaderFlush = false 286 resp.hijackWriter = nil 287 } 288 289 func (resp *Response) resetSkipHeader() { 290 resp.ResetBody() 291 } 292 293 // ResetBody resets response body. 294 func (resp *Response) ResetBody() { 295 resp.bodyRaw = nil 296 resp.CloseBodyStream() //nolint:errcheck 297 if resp.body != nil { 298 if resp.body.Len() <= resp.maxKeepBodySize { 299 resp.body.Reset() 300 return 301 } 302 responseBodyPool.Put(resp.body) 303 resp.body = nil 304 } 305 } 306 307 // SetBodyRaw sets response body, but without copying it. 308 // 309 // From this point onward the body argument must not be changed. 310 func (resp *Response) SetBodyRaw(body []byte) { 311 resp.ResetBody() 312 resp.bodyRaw = body 313 } 314 315 // StatusCode returns response status code. 316 func (resp *Response) StatusCode() int { 317 return resp.Header.StatusCode() 318 } 319 320 // SetBody sets response body. 321 // 322 // It is safe re-using body argument after the function returns. 323 func (resp *Response) SetBody(body []byte) { 324 resp.CloseBodyStream() //nolint:errcheck 325 if resp.GetHijackWriter() == nil { 326 resp.BodyBuffer().Set(body) //nolint:errcheck 327 return 328 } 329 330 // If the hijack writer support .SetBody() api, then use it. 331 if setter, ok := resp.GetHijackWriter().(interface { 332 SetBody(b []byte) 333 }); ok { 334 setter.SetBody(body) 335 return 336 } 337 338 // Otherwise, call .Write() api instead. 339 resp.GetHijackWriter().Write(body) //nolint:errcheck 340 } 341 342 func (resp *Response) BodyStream() io.Reader { 343 if resp.bodyStream == nil { 344 resp.bodyStream = NoResponseBody 345 } 346 return resp.bodyStream 347 } 348 349 // AppendBody appends p to response body. 350 // 351 // It is safe re-using p after the function returns. 352 func (resp *Response) AppendBody(p []byte) { 353 resp.CloseBodyStream() //nolint:errcheck 354 if resp.hijackWriter != nil { 355 resp.hijackWriter.Write(p) //nolint:errcheck 356 return 357 } 358 resp.BodyBuffer().Write(p) //nolint:errcheck 359 } 360 361 // AppendBodyString appends s to response body. 362 func (resp *Response) AppendBodyString(s string) { 363 resp.CloseBodyStream() //nolint:errcheck 364 if resp.hijackWriter != nil { 365 resp.hijackWriter.Write(bytesconv.S2b(s)) //nolint:errcheck 366 return 367 } 368 resp.BodyBuffer().WriteString(s) //nolint:errcheck 369 } 370 371 // ConnectionClose returns true if 'Connection: close' header is set. 372 func (resp *Response) ConnectionClose() bool { 373 return resp.Header.ConnectionClose() 374 } 375 376 func (resp *Response) CloseBodyStream() error { 377 if resp.bodyStream == nil { 378 return nil 379 } 380 var err error 381 if bsc, ok := resp.bodyStream.(io.Closer); ok { 382 err = bsc.Close() 383 } 384 resp.bodyStream = nil 385 return err 386 } 387 388 func (resp *Response) BodyBuffer() *bytebufferpool.ByteBuffer { 389 if resp.body == nil { 390 resp.body = responseBodyPool.Get() 391 } 392 resp.bodyRaw = nil 393 return resp.body 394 } 395 396 func gunzipData(p []byte) ([]byte, error) { 397 var bb bytebufferpool.ByteBuffer 398 _, err := compress.WriteGunzip(&bb, p) 399 if err != nil { 400 return nil, err 401 } 402 return bb.B, nil 403 } 404 405 // RemoteAddr returns the remote network address. The Addr returned is shared 406 // by all invocations of RemoteAddr, so do not modify it. 407 func (resp *Response) RemoteAddr() net.Addr { 408 return resp.raddr 409 } 410 411 // LocalAddr returns the local network address. The Addr returned is shared 412 // by all invocations of LocalAddr, so do not modify it. 413 func (resp *Response) LocalAddr() net.Addr { 414 return resp.laddr 415 } 416 417 func (resp *Response) ParseNetAddr(conn network.Conn) { 418 resp.raddr = conn.RemoteAddr() 419 resp.laddr = conn.LocalAddr() 420 } 421 422 // AcquireResponse returns an empty Response instance from response pool. 423 // 424 // The returned Response instance may be passed to ReleaseResponse when it is 425 // no longer needed. This allows Response recycling, reduces GC pressure 426 // and usually improves performance. 427 func AcquireResponse() *Response { 428 v := responsePool.Get() 429 if v == nil { 430 return &Response{} 431 } 432 return v.(*Response) 433 } 434 435 // ReleaseResponse return resp acquired via AcquireResponse to response pool. 436 // 437 // It is forbidden accessing resp and/or its members after returning 438 // it to response pool. 439 func ReleaseResponse(resp *Response) { 440 resp.Reset() 441 responsePool.Put(resp) 442 }