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  }