github.com/cloudwego/hertz@v0.9.3/pkg/protocol/http1/resp/response_test.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  	"bufio"
    46  	"bytes"
    47  	"errors"
    48  	"io"
    49  	"io/ioutil"
    50  	"strings"
    51  	"testing"
    52  
    53  	"github.com/cloudwego/hertz/internal/bytestr"
    54  	errs "github.com/cloudwego/hertz/pkg/common/errors"
    55  	"github.com/cloudwego/hertz/pkg/common/test/assert"
    56  	"github.com/cloudwego/hertz/pkg/common/test/mock"
    57  	"github.com/cloudwego/hertz/pkg/common/utils"
    58  	"github.com/cloudwego/hertz/pkg/protocol"
    59  	"github.com/cloudwego/hertz/pkg/protocol/consts"
    60  	"github.com/cloudwego/hertz/pkg/protocol/http1/ext"
    61  	"github.com/cloudwego/netpoll"
    62  )
    63  
    64  var errBodyTooLarge = errs.New(errs.ErrBodyTooLarge, errs.ErrorTypePublic, "test")
    65  
    66  type ErroneousBodyStream struct {
    67  	errOnRead  bool
    68  	errOnClose bool
    69  }
    70  
    71  type testReader struct {
    72  	read chan (int)
    73  	cb   chan (struct{})
    74  }
    75  
    76  func (r *testReader) Read(b []byte) (int, error) {
    77  	read := <-r.read
    78  
    79  	if read == -1 {
    80  		return 0, io.EOF
    81  	}
    82  
    83  	r.cb <- struct{}{}
    84  
    85  	total := len(b)
    86  	if total > read {
    87  		total = read
    88  	}
    89  
    90  	for i := 0; i < total; i++ {
    91  		b[i] = 'x'
    92  	}
    93  
    94  	return total, nil
    95  }
    96  
    97  func (ebs *ErroneousBodyStream) Read(p []byte) (n int, err error) {
    98  	if ebs.errOnRead {
    99  		panic("reading erroneous body stream")
   100  	}
   101  	return 0, io.EOF
   102  }
   103  
   104  func (ebs *ErroneousBodyStream) Close() error {
   105  	if ebs.errOnClose {
   106  		panic("closing erroneous body stream")
   107  	}
   108  	return nil
   109  }
   110  
   111  func TestResponseBodyStreamErrorOnPanicDuringClose(t *testing.T) {
   112  	t.Parallel()
   113  	var resp protocol.Response
   114  	var w bytes.Buffer
   115  	zw := netpoll.NewWriter(&w)
   116  
   117  	ebs := &ErroneousBodyStream{errOnRead: false, errOnClose: true}
   118  	resp.SetBodyStream(ebs, 42)
   119  	err := Write(&resp, zw)
   120  	if err == nil {
   121  		t.Fatalf("expected error when writing response.")
   122  	}
   123  	e, ok := err.(*ErrBodyStreamWritePanic)
   124  	if !ok {
   125  		t.Fatalf("expected error struct to be *ErrBodyStreamWritePanic, got: %+v.", e)
   126  	}
   127  	if e.Error() != "panic while writing body stream: closing erroneous body stream" {
   128  		t.Fatalf("unexpected error value, got: %+v.", e.Error())
   129  	}
   130  }
   131  
   132  func TestResponseBodyStreamErrorOnPanicDuringRead(t *testing.T) {
   133  	t.Parallel()
   134  	var resp protocol.Response
   135  	var w bytes.Buffer
   136  	zw := netpoll.NewWriter(&w)
   137  
   138  	ebs := &ErroneousBodyStream{errOnRead: true, errOnClose: false}
   139  	resp.SetBodyStream(ebs, 42)
   140  	err := Write(&resp, zw)
   141  	if err == nil {
   142  		t.Fatalf("expected error when writing response.")
   143  	}
   144  	e, ok := err.(*ErrBodyStreamWritePanic)
   145  	if !ok {
   146  		t.Fatalf("expected error struct to be *ErrBodyStreamWritePanic, got: %+v.", e)
   147  	}
   148  	if e.Error() != "panic while writing body stream: reading erroneous body stream" {
   149  		t.Fatalf("unexpected error value, got: %+v.", e.Error())
   150  	}
   151  }
   152  
   153  func testResponseReadError(t *testing.T, resp *protocol.Response, response string) {
   154  	zr := mock.NewZeroCopyReader(response)
   155  	err := Read(resp, zr)
   156  	if err == nil {
   157  		t.Fatalf("Expecting error for response=%q", response)
   158  	}
   159  
   160  	testResponseReadSuccess(t, resp, "HTTP/1.1 303 Redisred sedfs sdf\r\nContent-Type: aaa\r\nContent-Length: 5\r\n\r\nHELLOaaa",
   161  		consts.StatusSeeOther, 5, "aaa", "HELLO", nil, consts.HTTP11)
   162  }
   163  
   164  func testResponseReadSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int,
   165  	expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string,
   166  ) {
   167  	zr := mock.NewZeroCopyReader(response)
   168  	err := Read(resp, zr)
   169  	if err != nil {
   170  		t.Fatalf("Unexpected error: %s", err)
   171  	}
   172  
   173  	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol)
   174  	if !bytes.Equal(resp.Body(), []byte(expectedBody)) {
   175  		t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
   176  	}
   177  	verifyResponseTrailer(t, &resp.Header, expectedTrailer)
   178  }
   179  
   180  func TestResponseReadSuccess(t *testing.T) {
   181  	t.Parallel()
   182  
   183  	resp := &protocol.Response{}
   184  
   185  	// usual response
   186  	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789",
   187  		consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11)
   188  
   189  	// zero response
   190  	testResponseReadSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n",
   191  		consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11)
   192  
   193  	// response with trailer
   194  	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
   195  		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11)
   196  
   197  	// response with trailer disableNormalizing
   198  	resp.Header.DisableNormalizing()
   199  	resp.Header.Trailer().DisableNormalizing()
   200  	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
   201  		consts.StatusMultipleChoices, 5, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11)
   202  
   203  	// no content-length ('identity' transfer-encoding)
   204  	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx",
   205  		consts.StatusOK, 5, "foobar", "zxxxx", nil, consts.HTTP11)
   206  
   207  	// explicitly stated 'Transfer-Encoding: identity'
   208  	testResponseReadSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag",
   209  		234, 3, "xxx", "xag", nil, consts.HTTP11)
   210  
   211  	// big 'identity' response
   212  	body := string(mock.CreateFixedBody(100500))
   213  	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body,
   214  		consts.StatusOK, 100500, "aa", body, nil, consts.HTTP11)
   215  
   216  	// chunked response
   217  	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n",
   218  		200, 6, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11)
   219  
   220  	// chunked response with non-chunked Transfer-Encoding.
   221  	testResponseReadSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n",
   222  		230, 4, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11)
   223  
   224  	// chunked response with empty body
   225  	testResponseReadSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n",
   226  		consts.StatusOK, 0, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11)
   227  }
   228  
   229  func TestResponseReadError(t *testing.T) {
   230  	t.Parallel()
   231  
   232  	resp := &protocol.Response{}
   233  
   234  	// empty response
   235  	testResponseReadError(t, resp, "")
   236  
   237  	// invalid header
   238  	testResponseReadError(t, resp, "foobar")
   239  
   240  	// empty body
   241  	testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\n")
   242  
   243  	// short body
   244  	testResponseReadError(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aaa\r\nContent-Length: 1234\r\n\r\nshort")
   245  }
   246  
   247  func TestResponseImmediateHeaderFlushChunked(t *testing.T) {
   248  	t.Parallel()
   249  
   250  	var r protocol.Response
   251  
   252  	r.ImmediateHeaderFlush = true
   253  
   254  	ch := make(chan int)
   255  	cb := make(chan struct{})
   256  
   257  	buf := &testReader{read: ch, cb: cb}
   258  
   259  	r.SetBodyStream(buf, -1)
   260  
   261  	w := bytes.NewBuffer([]byte{})
   262  	zw := netpoll.NewWriter(w)
   263  
   264  	waitForIt := make(chan struct{})
   265  
   266  	go func() {
   267  		if err := Write(&r, zw); err != nil {
   268  			t.Errorf("unexpected error: %s", err)
   269  		}
   270  
   271  		waitForIt <- struct{}{}
   272  	}()
   273  
   274  	ch <- 3
   275  
   276  	if !strings.Contains(w.String(), "Transfer-Encoding: chunked") {
   277  		t.Fatalf("Expected headers to be flushed")
   278  	}
   279  
   280  	if strings.Contains(w.String(), "xxx") {
   281  		t.Fatalf("Did not expect body to be written yet")
   282  	}
   283  
   284  	<-cb
   285  	ch <- -1
   286  
   287  	<-waitForIt
   288  }
   289  
   290  func TestResponseImmediateHeaderFlushFixedLength(t *testing.T) {
   291  	t.Parallel()
   292  
   293  	var r protocol.Response
   294  
   295  	r.ImmediateHeaderFlush = true
   296  
   297  	ch := make(chan int)
   298  	cb := make(chan struct{})
   299  
   300  	buf := &testReader{read: ch, cb: cb}
   301  
   302  	r.SetBodyStream(buf, 3)
   303  
   304  	w := bytes.NewBuffer([]byte{})
   305  	zw := netpoll.NewWriter(w)
   306  
   307  	waitForIt := make(chan struct{})
   308  
   309  	go func() {
   310  		if err := Write(&r, zw); err != nil {
   311  			t.Errorf("unexpected error: %s", err)
   312  		}
   313  		waitForIt <- struct{}{}
   314  	}()
   315  
   316  	// reader have more data than bodySize, but only the bodySize length of data will be send.
   317  	ch <- 10
   318  
   319  	if !strings.Contains(w.String(), "Content-Length: 3") {
   320  		t.Fatalf("Expected headers to be flushed")
   321  	}
   322  
   323  	if strings.Contains(w.String(), "xxx") {
   324  		t.Fatalf("Did not expect body to be written yet")
   325  	}
   326  
   327  	<-cb
   328  	// ch <- -1
   329  
   330  	<-waitForIt
   331  }
   332  
   333  func TestResponseImmediateHeaderFlushFixedLengthWithFewerData(t *testing.T) {
   334  	t.Parallel()
   335  
   336  	var r protocol.Response
   337  
   338  	r.ImmediateHeaderFlush = true
   339  
   340  	ch := make(chan int)
   341  	cb := make(chan struct{})
   342  
   343  	buf := &testReader{read: ch, cb: cb}
   344  
   345  	r.SetBodyStream(buf, 3)
   346  
   347  	w := bytes.NewBuffer([]byte{})
   348  	zw := netpoll.NewWriter(w)
   349  
   350  	waitForIt := make(chan struct{})
   351  
   352  	go func() {
   353  		if err := Write(&r, zw); err != nil {
   354  			assert.NotNil(t, err)
   355  		}
   356  		waitForIt <- struct{}{}
   357  	}()
   358  
   359  	// reader have less data than bodySize, server should raise a error in this case
   360  	ch <- 2
   361  
   362  	<-cb
   363  	ch <- -1
   364  
   365  	<-waitForIt
   366  }
   367  
   368  func TestResponseSuccess(t *testing.T) {
   369  	t.Parallel()
   370  
   371  	// 200 response
   372  	testResponseSuccess(t, consts.StatusOK, "test/plain", "server", "foobar",
   373  		consts.StatusOK, "test/plain", "server")
   374  
   375  	// response with missing statusCode
   376  	testResponseSuccess(t, 0, "text/plain", "server", "foobar",
   377  		consts.StatusOK, "text/plain", "server")
   378  
   379  	// response with missing server
   380  	testResponseSuccess(t, consts.StatusInternalServerError, "aaa", "", "aaadfsd",
   381  		consts.StatusInternalServerError, "aaa", "")
   382  
   383  	// empty body
   384  	testResponseSuccess(t, consts.StatusOK, "bbb", "qwer", "",
   385  		consts.StatusOK, "bbb", "qwer")
   386  
   387  	// missing content-type
   388  	testResponseSuccess(t, consts.StatusOK, "", "asdfsd", "asdf",
   389  		consts.StatusOK, string(bytestr.DefaultContentType), "asdfsd")
   390  }
   391  
   392  func TestResponseReadLimitBody(t *testing.T) {
   393  	t.Parallel()
   394  
   395  	// response with content-length
   396  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 10)
   397  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 100)
   398  	testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Length: 10\r\n\r\n9876543210", 9)
   399  	// response with content-encoding
   400  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nContent-Encoding: gzip\r\n\r\n9876543210", 10)
   401  	// chunked response
   402  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 9)
   403  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nFoo: bar\r\n\r\n", 9)
   404  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 100)
   405  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\nfoobar\r\n\r\n", 100)
   406  	testResponseReadLimitBodyError(t, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\nTransfer-Encoding: chunked\r\n\r\n6\r\nfoobar\r\n3\r\nbaz\r\n0\r\n\r\n", 2)
   407  
   408  	// identity response
   409  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 6)
   410  	testResponseReadLimitBodySuccess(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 106)
   411  	testResponseReadLimitBodyError(t, "HTTP/1.1 400 OK\r\nContent-Type: aa\r\n\r\n123456", 5)
   412  }
   413  
   414  func TestResponseReadWithoutBody(t *testing.T) {
   415  	var resp protocol.Response
   416  
   417  	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 304 Not Modified\r\nContent-Type: aa\r\nContent-Encoding: gzip\r\nContent-Length: 1235\r\n\r\n", false,
   418  		consts.StatusNotModified, 1235, "aa", nil, "gzip", consts.HTTP11)
   419  
   420  	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 200 Foo Bar\r\nContent-Type: aab\r\nTrailer: Foo\r\nContent-Encoding: deflate\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\nHTTP/1.2", false,
   421  		consts.StatusOK, 0, "aab", map[string]string{"Foo": "bar"}, "deflate", consts.HTTP11)
   422  
   423  	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 204 Foo Bar\r\nContent-Type: aab\r\nTrailer: Foo\r\nContent-Encoding: deflate\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo: bar\r\n\r\nHTTP/1.2", true,
   424  		consts.StatusNoContent, -1, "aab", nil, "deflate", consts.HTTP11)
   425  
   426  	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 123 AAA\r\nContent-Type: xxx\r\nContent-Encoding: gzip\r\nContent-Length: 3434\r\n\r\n", false,
   427  		123, 3434, "xxx", nil, "gzip", consts.HTTP11)
   428  
   429  	testResponseReadWithoutBody(t, &resp, "HTTP 200 OK\r\nContent-Type: text/xml\r\nContent-Encoding: deflate\r\nContent-Length: 123\r\n\r\nfoobar\r\n", true,
   430  		consts.StatusOK, 123, "text/xml", nil, "deflate", consts.HTTP10)
   431  
   432  	// '100 Continue' must be skipped.
   433  	testResponseReadWithoutBody(t, &resp, "HTTP/1.1 100 Continue\r\nFoo-bar: baz\r\n\r\nHTTP/1.1 329 aaa\r\nContent-Type: qwe\r\nContent-Encoding: gzip\r\nContent-Length: 894\r\n\r\n", true,
   434  		329, 894, "qwe", nil, "gzip", consts.HTTP11)
   435  }
   436  
   437  func verifyResponseHeader(t *testing.T, h *protocol.ResponseHeader, expectedStatusCode, expectedContentLength int, expectedContentType, expectedContentEncoding, expectedProtocol string) {
   438  	if h.StatusCode() != expectedStatusCode {
   439  		t.Fatalf("Unexpected status code %d. Expected %d", h.StatusCode(), expectedStatusCode)
   440  	}
   441  	if h.ContentLength() != expectedContentLength {
   442  		t.Fatalf("Unexpected content length %d. Expected %d", h.ContentLength(), expectedContentLength)
   443  	}
   444  	if string(h.ContentType()) != expectedContentType {
   445  		t.Fatalf("Unexpected content type %q. Expected %q", h.ContentType(), expectedContentType)
   446  	}
   447  	if string(h.ContentEncoding()) != expectedContentEncoding {
   448  		t.Fatalf("Unexpected content encoding %q. Expected %q", h.ContentEncoding(), expectedContentEncoding)
   449  	}
   450  
   451  	if h.GetProtocol() != expectedProtocol {
   452  		t.Fatalf("Unexpected protocol %q. Expected %q", h.GetProtocol(), expectedProtocol)
   453  	}
   454  }
   455  
   456  func testResponseSuccess(t *testing.T, statusCode int, contentType, serverName, body string,
   457  	expectedStatusCode int, expectedContentType, expectedServerName string,
   458  ) {
   459  	var resp protocol.Response
   460  	resp.SetStatusCode(statusCode)
   461  	resp.Header.Set("Content-Type", contentType)
   462  	resp.Header.Set("Server", serverName)
   463  	resp.SetBody([]byte(body))
   464  
   465  	w := &bytes.Buffer{}
   466  	// bw := bufio.NewWriter(w)
   467  	zw := netpoll.NewWriter(w)
   468  	err := Write(&resp, zw)
   469  	if err != nil {
   470  		t.Fatalf("Unexpected error when calling Response.Write(): %s", err)
   471  	}
   472  
   473  	if err = zw.Flush(); err != nil {
   474  		t.Fatalf("Unexpected error when flushing bufio.Writer: %s", err)
   475  	}
   476  
   477  	var resp1 protocol.Response
   478  	br := bufio.NewReader(w)
   479  	zr := netpoll.NewReader(br)
   480  	if err = Read(&resp1, zr); err != nil {
   481  		t.Fatalf("Unexpected error when calling Response.Read(): %s", err)
   482  	}
   483  	if resp1.StatusCode() != expectedStatusCode {
   484  		t.Fatalf("Unexpected status code: %d. Expected %d", resp1.StatusCode(), expectedStatusCode)
   485  	}
   486  	if resp1.Header.ContentLength() != len(body) {
   487  		t.Fatalf("Unexpected content-length: %d. Expected %d", resp1.Header.ContentLength(), len(body))
   488  	}
   489  	if string(resp1.Header.Peek(consts.HeaderContentType)) != expectedContentType {
   490  		t.Fatalf("Unexpected content-type: %q. Expected %q", resp1.Header.Peek(consts.HeaderContentType), expectedContentType)
   491  	}
   492  	if string(resp1.Header.Peek(consts.HeaderServer)) != expectedServerName {
   493  		t.Fatalf("Unexpected server: %q. Expected %q", resp1.Header.Peek(consts.HeaderServer), expectedServerName)
   494  	}
   495  	if !bytes.Equal(resp1.Body(), []byte(body)) {
   496  		t.Fatalf("Unexpected body: %q. Expected %q", resp1.Body(), body)
   497  	}
   498  }
   499  
   500  func testResponseReadWithoutBody(t *testing.T, resp *protocol.Response, s string, skipBody bool,
   501  	expectedStatusCode, expectedContentLength int, expectedContentType string, expectedTrailer map[string]string, expectedContentEncoding, expectedProtocol string,
   502  ) {
   503  	zr := mock.NewZeroCopyReader(s)
   504  	resp.SkipBody = skipBody
   505  	err := Read(resp, zr)
   506  	if err != nil {
   507  		t.Fatalf("Unexpected error when reading response without body: %s. response=%q", err, s)
   508  	}
   509  	if len(resp.Body()) != 0 {
   510  		t.Fatalf("Unexpected response body %q. Expected %q. response=%q", resp.Body(), "", s)
   511  	}
   512  
   513  	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, expectedContentEncoding, expectedProtocol)
   514  	verifyResponseTrailer(t, &resp.Header, expectedTrailer)
   515  
   516  	// verify that ordinal response is read after null-body response
   517  	resp.SkipBody = false
   518  	testResponseReadSuccess(t, resp, "HTTP/1.1 300 OK\r\nContent-Length: 5\r\nContent-Type: bar\r\n\r\n56789",
   519  		consts.StatusMultipleChoices, 5, "bar", "56789", nil, consts.HTTP11)
   520  }
   521  
   522  func verifyResponseTrailer(t *testing.T, h *protocol.ResponseHeader, expectedTrailers map[string]string) {
   523  	for k, v := range expectedTrailers {
   524  		got := h.Trailer().Peek(k)
   525  		if !bytes.Equal(got, []byte(v)) {
   526  			t.Fatalf("Unexpected trailer %q. Expected %q. Got %q", k, v, got)
   527  		}
   528  	}
   529  
   530  	h.Trailer().VisitAll(func(key, value []byte) {
   531  		if v := expectedTrailers[string(key)]; string(value) != v {
   532  			t.Fatalf("Unexpected trailer %q. Expected %q. Got %q", string(key), v, string(value))
   533  		}
   534  	})
   535  }
   536  
   537  func testResponseReadLimitBodyError(t *testing.T, s string, maxBodySize int) {
   538  	var resp protocol.Response
   539  	zr := netpoll.NewReader(bytes.NewBufferString(s))
   540  	err := ReadHeaderAndLimitBody(&resp, zr, maxBodySize)
   541  	if err == nil {
   542  		t.Fatalf("expecting error. s=%q, maxBodySize=%d", s, maxBodySize)
   543  	}
   544  	if !errors.Is(err, errs.ErrBodyTooLarge) {
   545  		t.Fatalf("unexpected error: %s. Expecting %s. s=%q, maxBodySize=%d", err, errBodyTooLarge, s, maxBodySize)
   546  	}
   547  }
   548  
   549  func testResponseReadLimitBodySuccess(t *testing.T, s string, maxBodySize int) {
   550  	var resp protocol.Response
   551  	mr := mock.NewZeroCopyReader(s)
   552  	if err := ReadHeaderAndLimitBody(&resp, mr, maxBodySize); err != nil {
   553  		t.Fatalf("unexpected error: %s. s=%q, maxBodySize=%d", err, s, maxBodySize)
   554  	}
   555  }
   556  
   557  func TestResponseBodyStreamWithTrailer(t *testing.T) {
   558  	t.Parallel()
   559  
   560  	testResponseBodyStreamWithTrailer(t, nil, false)
   561  
   562  	body := mock.CreateFixedBody(1e5)
   563  	testResponseBodyStreamWithTrailer(t, body, false)
   564  	testResponseBodyStreamWithTrailer(t, body, true)
   565  }
   566  
   567  func testResponseBodyStreamWithTrailer(t *testing.T, body []byte, disableNormalizing bool) {
   568  	expectedTrailer := map[string]string{
   569  		"foo": "testfoo",
   570  		"bar": "testbar",
   571  	}
   572  	var resp1 protocol.Response
   573  	if disableNormalizing {
   574  		resp1.Header.DisableNormalizing()
   575  	}
   576  	resp1.SetBodyStream(bytes.NewReader(body), -1)
   577  	for k, v := range expectedTrailer {
   578  		err := resp1.Header.Trailer().Add(k, v)
   579  		if err != nil {
   580  			t.Fatalf("unexpected error: %s", err)
   581  		}
   582  	}
   583  
   584  	var w bytes.Buffer
   585  	zw := netpoll.NewWriter(&w)
   586  	if err := Write(&resp1, zw); err != nil {
   587  		t.Fatalf("unexpected error: %s", err)
   588  	}
   589  	if err := zw.Flush(); err != nil {
   590  		t.Fatalf("unexpected error: %s", err)
   591  	}
   592  
   593  	var resp2 protocol.Response
   594  	if disableNormalizing {
   595  		resp2.Header.DisableNormalizing()
   596  	}
   597  	br := bufio.NewReader(&w)
   598  	zr := netpoll.NewReader(br)
   599  	if err := Read(&resp2, zr); err != nil {
   600  		t.Fatalf("unexpected error: %s", err)
   601  	}
   602  
   603  	respBody := resp2.Body()
   604  	if !bytes.Equal(respBody, body) {
   605  		t.Fatalf("unexpected body: %q. Expecting %q", respBody, body)
   606  	}
   607  
   608  	for k, v := range expectedTrailer {
   609  		kBytes := []byte(k)
   610  		utils.NormalizeHeaderKey(kBytes, disableNormalizing)
   611  		r := resp2.Header.Trailer().Peek(k)
   612  		if string(r) != v {
   613  			t.Fatalf("unexpected trailer header %q: %q. Expecting %s", kBytes, r, v)
   614  		}
   615  	}
   616  }
   617  
   618  func TestResponseReadBodyStreamBadReader(t *testing.T) {
   619  	t.Parallel()
   620  
   621  	resp := protocol.AcquireResponse()
   622  
   623  	errReader := mock.NewErrorReadConn(errors.New("test error"))
   624  
   625  	bodyBuf := resp.BodyBuffer()
   626  	bodyBuf.Reset()
   627  
   628  	bodyStream := ext.AcquireBodyStream(bodyBuf, errReader, resp.Header.Trailer(), 100)
   629  	resp.ConstructBodyStream(bodyBuf, convertClientRespStream(bodyStream, func(shouldClose bool) error {
   630  		assert.True(t, shouldClose)
   631  		return nil
   632  	}))
   633  
   634  	stBody := resp.BodyStream()
   635  	closer, _ := stBody.(io.Closer)
   636  	closer.Close()
   637  }
   638  
   639  func TestSetResponseBodyStreamFixedSize(t *testing.T) {
   640  	t.Parallel()
   641  
   642  	testSetResponseBodyStream(t, "a")
   643  	testSetResponseBodyStream(t, string(mock.CreateFixedBody(4097)))
   644  	testSetResponseBodyStream(t, string(mock.CreateFixedBody(100500)))
   645  }
   646  
   647  func TestSetResponseBodyStreamChunked(t *testing.T) {
   648  	t.Parallel()
   649  
   650  	testSetResponseBodyStreamChunked(t, "", map[string]string{"Foo": "bar"})
   651  
   652  	body := "foobar baz aaa bbb ccc"
   653  	testSetResponseBodyStreamChunked(t, body, nil)
   654  
   655  	body = string(mock.CreateFixedBody(10001))
   656  	testSetResponseBodyStreamChunked(t, body, map[string]string{"Foo": "test", "Bar": "test"})
   657  }
   658  
   659  func testSetResponseBodyStream(t *testing.T, body string) {
   660  	var resp protocol.Response
   661  	bodySize := len(body)
   662  	if resp.IsBodyStream() {
   663  		t.Fatalf("IsBodyStream must return false")
   664  	}
   665  	resp.SetBodyStream(bytes.NewBufferString(body), bodySize)
   666  	if !resp.IsBodyStream() {
   667  		t.Fatalf("IsBodyStream must return true")
   668  	}
   669  
   670  	var w bytes.Buffer
   671  	zw := netpoll.NewWriter(&w)
   672  	if err := Write(&resp, zw); err != nil {
   673  		t.Fatalf("unexpected error when writing response: %s. body=%q", err, body)
   674  	}
   675  	if err := zw.Flush(); err != nil {
   676  		t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body)
   677  	}
   678  
   679  	var resp1 protocol.Response
   680  	br := bufio.NewReader(&w)
   681  	zr := netpoll.NewReader(br)
   682  	if err := Read(&resp1, zr); err != nil {
   683  		t.Fatalf("unexpected error when reading response: %s. body=%q", err, body)
   684  	}
   685  	if string(resp1.Body()) != body {
   686  		t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body)
   687  	}
   688  }
   689  
   690  func testSetResponseBodyStreamChunked(t *testing.T, body string, trailer map[string]string) {
   691  	var resp protocol.Response
   692  	if resp.IsBodyStream() {
   693  		t.Fatalf("IsBodyStream must return false")
   694  	}
   695  	resp.SetBodyStream(bytes.NewBufferString(body), -1)
   696  	if !resp.IsBodyStream() {
   697  		t.Fatalf("IsBodyStream must return true")
   698  	}
   699  
   700  	var w bytes.Buffer
   701  	zw := netpoll.NewWriter(&w)
   702  	for k, v := range trailer {
   703  		err := resp.Header.Trailer().Add(k, v)
   704  		if err != nil {
   705  			t.Fatalf("unexpected error: %s", err)
   706  		}
   707  	}
   708  	if err := Write(&resp, zw); err != nil {
   709  		t.Fatalf("unexpected error when writing response: %s. body=%q", err, body)
   710  	}
   711  	if err := zw.Flush(); err != nil {
   712  		t.Fatalf("unexpected error when flushing response: %s. body=%q", err, body)
   713  	}
   714  	var resp1 protocol.Response
   715  	br := bufio.NewReader(&w)
   716  	zr := netpoll.NewReader(br)
   717  	if err := Read(&resp1, zr); err != nil {
   718  		t.Fatalf("unexpected error when reading response: %s. body=%q", err, body)
   719  	}
   720  	if string(resp1.Body()) != body {
   721  		t.Fatalf("unexpected body %q. Expecting %q", resp1.Body(), body)
   722  	}
   723  	for k, v := range trailer {
   724  		r := resp.Header.Trailer().Peek(k)
   725  		if string(r) != v {
   726  			t.Fatalf("unexpected trailer %s. Expecting %s. Got %q", k, v, r)
   727  		}
   728  	}
   729  }
   730  
   731  func testResponseReadBodyStreamSuccess(t *testing.T, resp *protocol.Response, response string, expectedStatusCode, expectedContentLength int,
   732  	expectedContentType, expectedBody string, expectedTrailer map[string]string, expectedProtocol string,
   733  ) {
   734  	zr := mock.NewZeroCopyReader(response)
   735  	err := ReadBodyStream(resp, zr, 0, nil)
   736  	if err != nil {
   737  		t.Fatalf("Unexpected error: %s", err)
   738  	}
   739  	assert.True(t, resp.IsBodyStream())
   740  
   741  	body, err := ioutil.ReadAll(resp.BodyStream())
   742  	if err != nil && err != io.EOF {
   743  		t.Fatalf("Unexpected error: %s", err)
   744  	}
   745  	verifyResponseHeader(t, &resp.Header, expectedStatusCode, expectedContentLength, expectedContentType, "", expectedProtocol)
   746  	if !bytes.Equal(body, []byte(expectedBody)) {
   747  		t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), []byte(expectedBody))
   748  	}
   749  	verifyResponseTrailer(t, &resp.Header, expectedTrailer)
   750  }
   751  
   752  func testResponseReadBodyStreamBadTrailer(t *testing.T, resp *protocol.Response, response string) {
   753  	zr := mock.NewZeroCopyReader(response)
   754  	err := ReadBodyStream(resp, zr, 0, nil)
   755  	if err != nil {
   756  		t.Fatalf("Unexpected error: %s", err)
   757  	}
   758  	assert.True(t, resp.IsBodyStream())
   759  
   760  	_, err = ioutil.ReadAll(resp.BodyStream())
   761  	if err == nil || err == io.EOF {
   762  		t.Fatalf("expected error when reading response.")
   763  	}
   764  }
   765  
   766  func TestResponseReadBodyStream(t *testing.T) {
   767  	t.Parallel()
   768  
   769  	resp := &protocol.Response{}
   770  
   771  	// usual response
   772  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Length: 10\r\nContent-Type: foo/bar\r\n\r\n0123456789",
   773  		consts.StatusOK, 10, "foo/bar", "0123456789", nil, consts.HTTP11)
   774  
   775  	// zero response
   776  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 500 OK\r\nContent-Length: 0\r\nContent-Type: foo/bar\r\n\r\n",
   777  		consts.StatusInternalServerError, 0, "foo/bar", "", nil, consts.HTTP11)
   778  
   779  	// response with trailer
   780  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: Foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
   781  		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"Foo": "bar"}, consts.HTTP11)
   782  
   783  	// response with trailer disableNormalizing
   784  	resp.Header.DisableNormalizing()
   785  	resp.Header.Trailer().DisableNormalizing()
   786  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nTrailer: foo\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\nfoo: bar\r\n\r\n",
   787  		consts.StatusMultipleChoices, -1, "bar", "56789", map[string]string{"foo": "bar"}, consts.HTTP11)
   788  
   789  	// no content-length ('identity' transfer-encoding)
   790  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: foobar\r\n\r\nzxxxx",
   791  		consts.StatusOK, -2, "foobar", "zxxxx", nil, consts.HTTP11)
   792  
   793  	// explicitly stated 'Transfer-Encoding: identity'
   794  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 234 ss\r\nContent-Type: xxx\r\n\r\nxag",
   795  		234, -2, "xxx", "xag", nil, consts.HTTP11)
   796  
   797  	// big 'identity' response
   798  	body := string(mock.CreateFixedBody(100500))
   799  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: aa\r\n\r\n"+body,
   800  		consts.StatusOK, -2, "aa", body, nil, consts.HTTP11)
   801  
   802  	// chunked response
   803  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo2\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nFoo2: bar2\r\n\r\n",
   804  		200, -1, "text/html", "qwerty", map[string]string{"Foo2": "bar2"}, consts.HTTP11)
   805  
   806  	// chunked response with non-chunked Transfer-Encoding.
   807  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 230 OK\r\nContent-Type: text\r\nTrailer: Foo3\r\nTransfer-Encoding: aaabbb\r\n\r\n2\r\ner\r\n2\r\nty\r\n0\r\nFoo3: bar3\r\n\r\n",
   808  		230, -1, "text", "erty", map[string]string{"Foo3": "bar3"}, consts.HTTP11)
   809  
   810  	// chunked response with empty body
   811  	testResponseReadBodyStreamSuccess(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTrailer: Foo5\r\nTransfer-Encoding: chunked\r\n\r\n0\r\nFoo5: bar5\r\n\r\n",
   812  		consts.StatusOK, -1, "text/html", "", map[string]string{"Foo5": "bar5"}, consts.HTTP11)
   813  }
   814  
   815  func TestResponseReadBodyStreamBadTrailer(t *testing.T) {
   816  	t.Parallel()
   817  
   818  	resp := &protocol.Response{}
   819  
   820  	testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\ncontent-type: bar\r\n\r\n")
   821  	testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nproxy-connection: bar2\r\n\r\n")
   822  }