github.com/cloudwego/hertz@v0.9.3/pkg/protocol/request_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 protocol
    43  
    44  import (
    45  	"bytes"
    46  	"encoding/base64"
    47  	"fmt"
    48  	"io"
    49  	"io/ioutil"
    50  	"math"
    51  	"mime/multipart"
    52  	"strings"
    53  	"testing"
    54  
    55  	"github.com/cloudwego/hertz/pkg/common/bytebufferpool"
    56  	"github.com/cloudwego/hertz/pkg/common/compress"
    57  	"github.com/cloudwego/hertz/pkg/common/config"
    58  	"github.com/cloudwego/hertz/pkg/common/test/assert"
    59  	"github.com/cloudwego/hertz/pkg/protocol/consts"
    60  )
    61  
    62  type errorReader struct{}
    63  
    64  func (er errorReader) Read(p []byte) (int, error) {
    65  	return 0, fmt.Errorf("dummy!")
    66  }
    67  
    68  func TestMultiForm(t *testing.T) {
    69  	var r Request
    70  	// r.Header.Set()
    71  	_, err := r.MultipartForm()
    72  	fmt.Println(err)
    73  }
    74  
    75  func TestRequestBodyWriterWrite(t *testing.T) {
    76  	w := requestBodyWriter{&Request{}}
    77  	w.Write([]byte("test"))
    78  	assert.DeepEqual(t, "test", string(w.r.body.B))
    79  }
    80  
    81  func TestRequestScheme(t *testing.T) {
    82  	req := NewRequest("", "ptth://127.0.0.1:8080", nil)
    83  	assert.DeepEqual(t, "ptth", string(req.Scheme()))
    84  	req = NewRequest("", "127.0.0.1:8080", nil)
    85  	assert.DeepEqual(t, "http", string(req.Scheme()))
    86  	assert.DeepEqual(t, true, req.IsURIParsed())
    87  }
    88  
    89  func TestRequestHost(t *testing.T) {
    90  	req := &Request{}
    91  	req.SetHost("127.0.0.1:8080")
    92  	assert.DeepEqual(t, "127.0.0.1:8080", string(req.Host()))
    93  }
    94  
    95  func TestRequestSwapBody(t *testing.T) {
    96  	reqA := &Request{}
    97  	reqA.SetBodyRaw([]byte("testA"))
    98  	reqB := &Request{}
    99  	reqB.SetBodyRaw([]byte("testB"))
   100  	SwapRequestBody(reqA, reqB)
   101  	assert.DeepEqual(t, "testA", string(reqB.bodyRaw))
   102  	assert.DeepEqual(t, "testB", string(reqA.bodyRaw))
   103  	reqA.SetBody([]byte("testA"))
   104  	reqB.SetBody([]byte("testB"))
   105  	SwapRequestBody(reqA, reqB)
   106  	assert.DeepEqual(t, "testA", string(reqB.body.B))
   107  	assert.DeepEqual(t, "", string(reqB.bodyRaw))
   108  	assert.DeepEqual(t, "testB", string(reqA.body.B))
   109  	assert.DeepEqual(t, "", string(reqA.bodyRaw))
   110  	reqA.SetBodyStream(strings.NewReader("testA"), len("testA"))
   111  	reqB.SetBodyStream(strings.NewReader("testB"), len("testB"))
   112  	SwapRequestBody(reqA, reqB)
   113  	body := make([]byte, 5)
   114  	reqB.bodyStream.Read(body)
   115  	assert.DeepEqual(t, "testA", string(body))
   116  	reqA.bodyStream.Read(body)
   117  	assert.DeepEqual(t, "testB", string(body))
   118  }
   119  
   120  func TestRequestKnownSizeStreamMultipartFormWithFile(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	s := `------WebKitFormBoundaryJwfATyF8tmxSJnLg
   124  Content-Disposition: form-data; name="f1"
   125  
   126  value1
   127  ------WebKitFormBoundaryJwfATyF8tmxSJnLg
   128  Content-Disposition: form-data; name="fileaaa"; filename="TODO"
   129  Content-Type: application/octet-stream
   130  
   131  - SessionClient with referer and cookies support.
   132  - Client with requests' pipelining support.
   133  - ProxyHandler similar to FSHandler.
   134  - WebSockets. See https://tools.ietf.org/html/rfc6455 .
   135  - HTTP/2.0. See https://tools.ietf.org/html/rfc7540 .
   136  
   137  ------WebKitFormBoundaryJwfATyF8tmxSJnLg--
   138  tailfoobar`
   139  	mr := strings.NewReader(s)
   140  	r := NewRequest("POST", "/upload", mr)
   141  	r.Header.SetContentLength(521)
   142  	r.Header.SetContentTypeBytes([]byte("multipart/form-data; boundary=----WebKitFormBoundaryJwfATyF8tmxSJnLg"))
   143  	assert.DeepEqual(t, false, r.HasMultipartForm())
   144  	f, err := r.MultipartForm()
   145  	assert.DeepEqual(t, true, r.HasMultipartForm())
   146  	if err != nil {
   147  		t.Fatalf("unexpected error: %s", err)
   148  	}
   149  	defer r.RemoveMultipartFormFiles()
   150  
   151  	// verify tail
   152  	tail, err := ioutil.ReadAll(mr)
   153  	if err != nil {
   154  		t.Fatalf("unexpected error: %s", err)
   155  	}
   156  	if string(tail) != "tailfoobar" {
   157  		t.Fatalf("unexpected tail %q. Expecting %q", tail, "tailfoobar")
   158  	}
   159  
   160  	// verify values
   161  	if len(f.Value) != 1 {
   162  		t.Fatalf("unexpected number of values in multipart form: %d. Expecting 1", len(f.Value))
   163  	}
   164  	for k, vv := range f.Value {
   165  		if k != "f1" {
   166  			t.Fatalf("unexpected value name %q. Expecting %q", k, "f1")
   167  		}
   168  		if len(vv) != 1 {
   169  			t.Fatalf("unexpected number of values %d. Expecting 1", len(vv))
   170  		}
   171  		v := vv[0]
   172  		if v != "value1" {
   173  			t.Fatalf("unexpected value %q. Expecting %q", v, "value1")
   174  		}
   175  	}
   176  
   177  	// verify files
   178  	if len(f.File) != 1 {
   179  		t.Fatalf("unexpected number of file values in multipart form: %d. Expecting 1", len(f.File))
   180  	}
   181  	for k, vv := range f.File {
   182  		if k != "fileaaa" {
   183  			t.Fatalf("unexpected file value name %q. Expecting %q", k, "fileaaa")
   184  		}
   185  		if len(vv) != 1 {
   186  			t.Fatalf("unexpected number of file values %d. Expecting 1", len(vv))
   187  		}
   188  		v := vv[0]
   189  		if v.Filename != "TODO" {
   190  			t.Fatalf("unexpected filename %q. Expecting %q", v.Filename, "TODO")
   191  		}
   192  		ct := v.Header.Get("Content-Type")
   193  		if ct != "application/octet-stream" {
   194  			t.Fatalf("unexpected content-type %q. Expecting %q", ct, "application/octet-stream")
   195  		}
   196  	}
   197  
   198  	firstFile, err := r.FormFile("fileaaa")
   199  	assert.DeepEqual(t, "TODO", firstFile.Filename)
   200  	assert.Nil(t, err)
   201  }
   202  
   203  func TestRequestUnknownSizeStreamMultipartFormWithFile(t *testing.T) {
   204  	t.Parallel()
   205  
   206  	s := `------WebKitFormBoundaryJwfATyF8tmxSJnLg
   207  Content-Disposition: form-data; name="f1"
   208  
   209  value1
   210  ------WebKitFormBoundaryJwfATyF8tmxSJnLg
   211  Content-Disposition: form-data; name="fileaaa"; filename="TODO"
   212  Content-Type: application/octet-stream
   213  
   214  - SessionClient with referer and cookies support.
   215  - Client with requests' pipelining support.
   216  - ProxyHandler similar to FSHandler.
   217  - WebSockets. See https://tools.ietf.org/html/rfc6455 .
   218  - HTTP/2.0. See https://tools.ietf.org/html/rfc7540 .
   219  
   220  ------WebKitFormBoundaryJwfATyF8tmxSJnLg--
   221  tailfoobar`
   222  	mr := strings.NewReader(s)
   223  	r := NewRequest("POST", "/upload", mr)
   224  	r.Header.SetContentLength(-1)
   225  	r.Header.SetContentTypeBytes([]byte("multipart/form-data; boundary=----WebKitFormBoundaryJwfATyF8tmxSJnLg"))
   226  
   227  	f, err := r.MultipartForm()
   228  	if err != nil {
   229  		t.Fatalf("unexpected error: %s", err)
   230  	}
   231  	defer r.RemoveMultipartFormFiles()
   232  
   233  	// all data must be consumed if the content length is unknown
   234  	tail, err := ioutil.ReadAll(mr)
   235  	if err != nil {
   236  		t.Fatalf("unexpected error: %s", err)
   237  	}
   238  	if string(tail) != "" {
   239  		t.Fatalf("unexpected tail %q. Expecting empty string", tail)
   240  	}
   241  
   242  	// verify values
   243  	if len(f.Value) != 1 {
   244  		t.Fatalf("unexpected number of values in multipart form: %d. Expecting 1", len(f.Value))
   245  	}
   246  	for k, vv := range f.Value {
   247  		if k != "f1" {
   248  			t.Fatalf("unexpected value name %q. Expecting %q", k, "f1")
   249  		}
   250  		if len(vv) != 1 {
   251  			t.Fatalf("unexpected number of values %d. Expecting 1", len(vv))
   252  		}
   253  		v := vv[0]
   254  		if v != "value1" {
   255  			t.Fatalf("unexpected value %q. Expecting %q", v, "value1")
   256  		}
   257  	}
   258  
   259  	// verify files
   260  	if len(f.File) != 1 {
   261  		t.Fatalf("unexpected number of file values in multipart form: %d. Expecting 1", len(f.File))
   262  	}
   263  	for k, vv := range f.File {
   264  		if k != "fileaaa" {
   265  			t.Fatalf("unexpected file value name %q. Expecting %q", k, "fileaaa")
   266  		}
   267  		if len(vv) != 1 {
   268  			t.Fatalf("unexpected number of file values %d. Expecting 1", len(vv))
   269  		}
   270  		v := vv[0]
   271  		if v.Filename != "TODO" {
   272  			t.Fatalf("unexpected filename %q. Expecting %q", v.Filename, "TODO")
   273  		}
   274  		ct := v.Header.Get("Content-Type")
   275  		if ct != "application/octet-stream" {
   276  			t.Fatalf("unexpected content-type %q. Expecting %q", ct, "application/octet-stream")
   277  		}
   278  	}
   279  }
   280  
   281  func TestRequestStreamMultipartFormWithFileGzip(t *testing.T) {
   282  	t.Parallel()
   283  
   284  	s := `------WebKitFormBoundaryJwfATyF8tmxSJnLg
   285  Content-Disposition: form-data; name="f1"
   286  
   287  value1
   288  ------WebKitFormBoundaryJwfATyF8tmxSJnLg
   289  Content-Disposition: form-data; name="fileaaa"; filename="TODO"
   290  Content-Type: application/octet-stream
   291  
   292  - SessionClient with referer and cookies support.
   293  - Client with requests' pipelining support.
   294  - ProxyHandler similar to FSHandler.
   295  - WebSockets. See https://tools.ietf.org/html/rfc6455 .
   296  - HTTP/2.0. See https://tools.ietf.org/html/rfc7540 .
   297  
   298  ------WebKitFormBoundaryJwfATyF8tmxSJnLg--
   299  tailfoobar`
   300  
   301  	ns := compress.AppendGzipBytes(nil, []byte(s))
   302  
   303  	mr := bytes.NewBuffer(ns)
   304  	r := NewRequest("POST", "/upload", mr)
   305  	r.Header.Set("Content-Encoding", "gzip")
   306  	r.Header.SetContentLength(len(s))
   307  	r.Header.SetContentTypeBytes([]byte("multipart/form-data; boundary=----WebKitFormBoundaryJwfATyF8tmxSJnLg"))
   308  
   309  	f, err := r.MultipartForm()
   310  	if err != nil {
   311  		t.Fatalf("unexpected error: %s", err)
   312  	}
   313  	defer r.RemoveMultipartFormFiles()
   314  
   315  	// verify values
   316  	if len(f.Value) != 1 {
   317  		t.Fatalf("unexpected number of values in multipart form: %d. Expecting 1", len(f.Value))
   318  	}
   319  	for k, vv := range f.Value {
   320  		if k != "f1" {
   321  			t.Fatalf("unexpected value name %q. Expecting %q", k, "f1")
   322  		}
   323  		if len(vv) != 1 {
   324  			t.Fatalf("unexpected number of values %d. Expecting 1", len(vv))
   325  		}
   326  		v := vv[0]
   327  		if v != "value1" {
   328  			t.Fatalf("unexpected value %q. Expecting %q", v, "value1")
   329  		}
   330  	}
   331  
   332  	// verify files
   333  	if len(f.File) != 1 {
   334  		t.Fatalf("unexpected number of file values in multipart form: %d. Expecting 1", len(f.File))
   335  	}
   336  	for k, vv := range f.File {
   337  		if k != "fileaaa" {
   338  			t.Fatalf("unexpected file value name %q. Expecting %q", k, "fileaaa")
   339  		}
   340  		if len(vv) != 1 {
   341  			t.Fatalf("unexpected number of file values %d. Expecting 1", len(vv))
   342  		}
   343  		v := vv[0]
   344  		if v.Filename != "TODO" {
   345  			t.Fatalf("unexpected filename %q. Expecting %q", v.Filename, "TODO")
   346  		}
   347  		ct := v.Header.Get("Content-Type")
   348  		if ct != "application/octet-stream" {
   349  			t.Fatalf("unexpected content-type %q. Expecting %q", ct, "application/octet-stream")
   350  		}
   351  	}
   352  }
   353  
   354  func TestRequestMultipartFormBoundary(t *testing.T) {
   355  	r := &Request{}
   356  	r.SetMultipartFormBoundary("----boundary----")
   357  	assert.DeepEqual(t, "----boundary----", r.MultipartFormBoundary())
   358  }
   359  
   360  func TestRequestSetQueryString(t *testing.T) {
   361  	r := &Request{}
   362  	r.SetQueryString("test")
   363  	assert.DeepEqual(t, "test", string(r.URI().queryString))
   364  }
   365  
   366  func TestRequestSetFormData(t *testing.T) {
   367  	r := &Request{}
   368  	data := map[string]string{"username": "admin"}
   369  	r.SetFormData(data)
   370  	assert.DeepEqual(t, "username", string(r.postArgs.args[0].key))
   371  	assert.DeepEqual(t, "admin", string(r.postArgs.args[0].value))
   372  	assert.DeepEqual(t, true, r.parsedPostArgs)
   373  	assert.DeepEqual(t, consts.MIMEApplicationHTMLForm, string(r.Header.contentType))
   374  
   375  	r = &Request{}
   376  	value := map[string][]string{"item": {"apple", "peach"}}
   377  	r.SetFormDataFromValues(value)
   378  	assert.DeepEqual(t, "item", string(r.postArgs.args[0].key))
   379  	assert.DeepEqual(t, "apple", string(r.postArgs.args[0].value))
   380  	assert.DeepEqual(t, "item", string(r.postArgs.args[1].key))
   381  	assert.DeepEqual(t, "peach", string(r.postArgs.args[1].value))
   382  }
   383  
   384  func TestRequestSetFile(t *testing.T) {
   385  	r := &Request{}
   386  	r.SetFile("file", "/usr/bin/test.txt")
   387  	assert.DeepEqual(t, &File{"/usr/bin/test.txt", "file", nil}, r.multipartFiles[0])
   388  
   389  	files := map[string]string{"f1": "/usr/bin/test1.txt"}
   390  	r.SetFiles(files)
   391  	assert.DeepEqual(t, &File{"/usr/bin/test1.txt", "f1", nil}, r.multipartFiles[1])
   392  
   393  	assert.DeepEqual(t, []*File{{"/usr/bin/test.txt", "file", nil}, {"/usr/bin/test1.txt", "f1", nil}}, r.MultipartFiles())
   394  }
   395  
   396  func TestRequestSetFileReader(t *testing.T) {
   397  	r := &Request{}
   398  	r.SetFileReader("file", "/usr/bin/test.txt", nil)
   399  	assert.DeepEqual(t, &File{"/usr/bin/test.txt", "file", nil}, r.multipartFiles[0])
   400  }
   401  
   402  func TestRequestSetMultipartFormData(t *testing.T) {
   403  	r := &Request{}
   404  	data := map[string]string{"item": "apple"}
   405  	r.SetMultipartFormData(data)
   406  	assert.DeepEqual(t, &MultipartField{"item", "", "", strings.NewReader("apple")}, r.multipartFields[0])
   407  
   408  	r = &Request{}
   409  	fields := []*MultipartField{{"item2", "", "", strings.NewReader("apple2")}, {"item3", "", "", strings.NewReader("apple3")}}
   410  	r.SetMultipartFields(fields...)
   411  	assert.DeepEqual(t, fields, r.MultipartFields())
   412  }
   413  
   414  func TestRequestSetBasicAuth(t *testing.T) {
   415  	r := &Request{}
   416  	r.SetBasicAuth("admin", "admin")
   417  	assert.DeepEqual(t, "Authorization", string(r.Header.h[0].key))
   418  	assert.DeepEqual(t, "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:admin")), string(r.Header.h[0].value))
   419  }
   420  
   421  func TestRequestSetAuthToken(t *testing.T) {
   422  	r := &Request{}
   423  	r.SetAuthToken("token")
   424  	assert.DeepEqual(t, "Authorization", string(r.Header.h[0].key))
   425  	assert.DeepEqual(t, "Bearer token", string(r.Header.h[0].value))
   426  
   427  	r = &Request{}
   428  	r.SetAuthSchemeToken("http", "token")
   429  	assert.DeepEqual(t, "Authorization", string(r.Header.h[0].key))
   430  	assert.DeepEqual(t, "http token", string(r.Header.h[0].value))
   431  }
   432  
   433  func TestRequestSetHeaders(t *testing.T) {
   434  	r := &Request{}
   435  	headers := map[string]string{"Key1": "value1"}
   436  	r.SetHeaders(headers)
   437  	assert.DeepEqual(t, "Key1", string(r.Header.h[0].key))
   438  	assert.DeepEqual(t, "value1", string(r.Header.h[0].value))
   439  }
   440  
   441  func TestRequestSetCookie(t *testing.T) {
   442  	r := &Request{}
   443  	r.SetCookie("cookie1", "cookie1")
   444  	assert.DeepEqual(t, "cookie1", string(r.Header.cookies[0].key))
   445  	assert.DeepEqual(t, "cookie1", string(r.Header.cookies[0].value))
   446  
   447  	r.SetCookies(map[string]string{"cookie2": "cookie2"})
   448  	assert.DeepEqual(t, "cookie2", string(r.Header.cookies[1].key))
   449  	assert.DeepEqual(t, "cookie2", string(r.Header.cookies[1].value))
   450  }
   451  
   452  func TestRequestPath(t *testing.T) {
   453  	r := NewRequest("POST", "/upload?test", nil)
   454  	assert.DeepEqual(t, "/upload", string(r.Path()))
   455  	assert.DeepEqual(t, "test", string(r.QueryString()))
   456  }
   457  
   458  func TestRequestConnectionClose(t *testing.T) {
   459  	r := NewRequest("POST", "/upload?test", nil)
   460  	assert.DeepEqual(t, false, r.ConnectionClose())
   461  	r.SetConnectionClose()
   462  	assert.DeepEqual(t, true, r.ConnectionClose())
   463  }
   464  
   465  func TestRequestBodyWriteToPlain(t *testing.T) {
   466  	t.Parallel()
   467  
   468  	var r Request
   469  
   470  	expectedS := "foobarbaz"
   471  	r.AppendBodyString(expectedS)
   472  
   473  	testBodyWriteTo(t, &r, expectedS, true)
   474  }
   475  
   476  func TestRequestBodyWriteToMultipart(t *testing.T) {
   477  	t.Parallel()
   478  
   479  	expectedS := "--foobar\r\nContent-Disposition: form-data; name=\"key_0\"\r\n\r\nvalue_0\r\n--foobar--\r\n"
   480  
   481  	var r Request
   482  	SetMultipartFormWithBoundary(&r, &multipart.Form{Value: map[string][]string{"key_0": {"value_0"}}}, "foobar")
   483  
   484  	testBodyWriteTo(t, &r, expectedS, true)
   485  }
   486  
   487  func TestNewRequest(t *testing.T) {
   488  	// get
   489  	req := NewRequest("GET", "http://www.google.com/hi", bytes.NewReader([]byte("hello")))
   490  	assert.NotNil(t, req)
   491  	assert.DeepEqual(t, "GET /hi HTTP/1.1\r\nHost: www.google.com\r\n\r\n", string(req.Header.Header()))
   492  	assert.Nil(t, req.Body())
   493  
   494  	// post + bytes reader
   495  	req = NewRequest("POST", "http://www.google.com/hi", bytes.NewReader([]byte("hello")))
   496  	assert.NotNil(t, req)
   497  	assert.DeepEqual(t, "POST /hi HTTP/1.1\r\nHost: www.google.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 5\r\n\r\n", string(req.Header.Header()))
   498  	assert.DeepEqual(t, "hello", string(req.Body()))
   499  
   500  	// post + string reader
   501  	req = NewRequest("POST", "http://www.google.com/hi", strings.NewReader("hello world"))
   502  	assert.NotNil(t, req)
   503  	assert.DeepEqual(t, "POST /hi HTTP/1.1\r\nHost: www.google.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 11\r\n\r\n", string(req.Header.Header()))
   504  	assert.DeepEqual(t, "hello world", string(req.Body()))
   505  
   506  	// post + bytes buffer
   507  	req = NewRequest("POST", "http://www.google.com/hi", bytes.NewBuffer([]byte("hello hertz!")))
   508  	assert.NotNil(t, req)
   509  	assert.DeepEqual(t, "POST /hi HTTP/1.1\r\nHost: www.google.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 12\r\n\r\n", string(req.Header.Header()))
   510  	assert.DeepEqual(t, "hello hertz!", string(req.Body()))
   511  
   512  	// empty method
   513  	req = NewRequest("", "/", bytes.NewBufferString(""))
   514  	assert.DeepEqual(t, "GET", string(req.Method()))
   515  	// unstandard method
   516  	req = NewRequest("DUMMY", "/", bytes.NewBufferString(""))
   517  	assert.DeepEqual(t, "DUMMY", string(req.Method()))
   518  
   519  	// empty body
   520  	req = NewRequest("GET", "/", nil)
   521  	assert.NotNil(t, req)
   522  	// wrong body
   523  	req = NewRequest("POST", "/", errorReader{})
   524  	_, err := req.BodyE()
   525  	assert.DeepEqual(t, err.Error(), "dummy!")
   526  	req = NewRequest("POST", "/", errorReader{})
   527  	body := req.Body()
   528  	assert.Nil(t, body)
   529  
   530  	// GET RequestURI
   531  	req = NewRequest("GET", "http://www.google.com/hi?a=1&b=2", nil)
   532  	assert.DeepEqual(t, "/hi?a=1&b=2", string(req.RequestURI()))
   533  
   534  	// POST RequestURI
   535  	req = NewRequest("POST", "http://www.google.com/hi?a=1&b=2", nil)
   536  	assert.DeepEqual(t, "/hi?a=1&b=2", string(req.RequestURI()))
   537  
   538  	// nil-interface body
   539  	assert.Panic(t, func() {
   540  		fake := func() *errorReader {
   541  			return nil
   542  		}
   543  		req = NewRequest("POST", "/", fake())
   544  		req.Body()
   545  	})
   546  }
   547  
   548  func TestRequestResetBody(t *testing.T) {
   549  	req := Request{}
   550  	req.BodyBuffer()
   551  	assert.NotNil(t, req.body)
   552  	req.maxKeepBodySize = math.MaxUint32
   553  	req.ResetBody()
   554  	assert.NotNil(t, req.body)
   555  	req.maxKeepBodySize = -1
   556  	req.ResetBody()
   557  	assert.Nil(t, req.body)
   558  }
   559  
   560  func TestRequestConstructBodyStream(t *testing.T) {
   561  	r := &Request{}
   562  	b := []byte("test")
   563  	r.ConstructBodyStream(&bytebufferpool.ByteBuffer{B: b}, strings.NewReader("test"))
   564  	assert.DeepEqual(t, "test", string(r.body.B))
   565  	stream := make([]byte, 4)
   566  	r.bodyStream.Read(stream)
   567  	assert.DeepEqual(t, "test", string(stream))
   568  }
   569  
   570  func TestRequestPostArgs(t *testing.T) {
   571  	t.Parallel()
   572  
   573  	s := `username=admin&password=admin`
   574  	mr := strings.NewReader(s)
   575  	r := &Request{}
   576  	r.SetBodyStream(mr, len(s))
   577  	r.Header.contentType = []byte(consts.MIMEApplicationHTMLForm)
   578  	arg := r.PostArgs()
   579  	assert.DeepEqual(t, "username", string(arg.args[0].key))
   580  	assert.DeepEqual(t, "admin", string(arg.args[0].value))
   581  	assert.DeepEqual(t, "password", string(arg.args[1].key))
   582  	assert.DeepEqual(t, "admin", string(arg.args[1].value))
   583  	assert.DeepEqual(t, "username=admin&password=admin", string(r.PostArgString()))
   584  }
   585  
   586  func TestRequestMayContinue(t *testing.T) {
   587  	t.Parallel()
   588  
   589  	var r Request
   590  	if r.MayContinue() {
   591  		t.Fatalf("MayContinue on empty request must return false")
   592  	}
   593  
   594  	r.Header.Set("Expect", "123sdfds")
   595  	if r.MayContinue() {
   596  		t.Fatalf("MayContinue on invalid Expect header must return false")
   597  	}
   598  
   599  	r.Header.Set("Expect", "100-continue")
   600  	if !r.MayContinue() {
   601  		t.Fatalf("MayContinue on 'Expect: 100-continue' header must return true")
   602  	}
   603  }
   604  
   605  func TestRequestSwapBodySerial(t *testing.T) {
   606  	t.Parallel()
   607  
   608  	testRequestSwapBody(t)
   609  }
   610  
   611  func testRequestSwapBody(t *testing.T) {
   612  	var b []byte
   613  	r := &Request{}
   614  	for i := 0; i < 20; i++ {
   615  		bOrig := r.Body()
   616  		b = r.SwapBody(b)
   617  		if !bytes.Equal(bOrig, b) {
   618  			t.Fatalf("unexpected body returned: %q. Expecting %q", b, bOrig)
   619  		}
   620  		r.AppendBodyString("foobar")
   621  	}
   622  
   623  	s := "aaaabbbbcccc"
   624  	b = b[:0]
   625  	for i := 0; i < 10; i++ {
   626  		r.SetBodyStream(bytes.NewBufferString(s), len(s))
   627  		b = r.SwapBody(b)
   628  		if string(b) != s {
   629  			t.Fatalf("unexpected body returned: %q. Expecting %q", b, s)
   630  		}
   631  		b = r.SwapBody(b)
   632  		if len(b) > 0 {
   633  			t.Fatalf("unexpected body with non-zero size returned: %q", b)
   634  		}
   635  	}
   636  }
   637  
   638  // Test case for testing BasicAuth
   639  var BasicAuthTests = []struct {
   640  	header, username, password string
   641  	ok                         bool
   642  }{
   643  	{"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
   644  
   645  	// Case doesn't matter:
   646  	{"BASIC " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
   647  	{"basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
   648  
   649  	{"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true},
   650  	{"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true},
   651  	{"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
   652  	{base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
   653  	{"Basic ", "", "", false},
   654  	{"Basic Aladdin:open sesame", "", "", false},
   655  	{`Digest username="Aladdin"`, "", "", false},
   656  }
   657  
   658  // struct for
   659  type getBasicAuthTest struct {
   660  	username, password string
   661  	ok                 bool
   662  }
   663  
   664  func TestRequestBasicAuth(t *testing.T) {
   665  	for _, tt := range BasicAuthTests {
   666  		req := NewRequest("GET", "http://www.google.com/hi", bytes.NewReader([]byte("hello")))
   667  		req.SetHeader("Authorization", tt.header)
   668  		username, password, ok := req.BasicAuth()
   669  		if ok != tt.ok || username != tt.username || password != tt.password {
   670  			t.Fatalf("BasicAuth() = %+v, want %+v", getBasicAuthTest{username, password, ok},
   671  				getBasicAuthTest{tt.username, tt.password, tt.ok})
   672  		}
   673  	}
   674  }
   675  
   676  // Issue: NewRequest should create a Request that doesn't use input parameters as its struct,
   677  // otherwise it will cause panic when we pass a const string as method to NewRequest and call req.SetMethod()
   678  func TestNewRequestWithConstParam(t *testing.T) {
   679  	const method = "POST"
   680  	const uri = "http://www.google.com/hi"
   681  	req := NewRequest(method, uri, nil)
   682  	req.SetMethod("POST")
   683  	req.SetRequestURI("http://www.google.com/hi")
   684  }
   685  
   686  func TestRequestCopyToWithOptions(t *testing.T) {
   687  	req := AcquireRequest()
   688  	k1 := "a"
   689  	v1 := "A"
   690  	k2 := "b"
   691  	v2 := "B"
   692  	req.SetOptions(config.WithTag(k1, v1), config.WithTag(k2, v2), config.WithSD(true))
   693  	reqCopy := AcquireRequest()
   694  	req.CopyTo(reqCopy)
   695  	assert.DeepEqual(t, v1, reqCopy.options.Tag(k1))
   696  	assert.DeepEqual(t, v2, reqCopy.options.Tag(k2))
   697  	assert.DeepEqual(t, true, reqCopy.options.IsSD())
   698  }
   699  
   700  func TestRequestSetMaxKeepBodySize(t *testing.T) {
   701  	r := &Request{}
   702  	r.SetMaxKeepBodySize(1024)
   703  	assert.DeepEqual(t, 1024, r.maxKeepBodySize)
   704  }
   705  
   706  func TestRequestGetBodyAfterGetBodyStream(t *testing.T) {
   707  	req := AcquireRequest()
   708  	req.SetBodyString("abc")
   709  	req.BodyStream()
   710  	assert.DeepEqual(t, req.Body(), []byte("abc"))
   711  }
   712  
   713  func TestRequestSetOptionsNotOverwrite(t *testing.T) {
   714  	req := AcquireRequest()
   715  	req.SetOptions(config.WithSD(true))
   716  	req.SetOptions(config.WithTag("a", "b"))
   717  	req.SetOptions(config.WithTag("c", "d"))
   718  	assert.DeepEqual(t, true, req.Options().IsSD())
   719  	assert.DeepEqual(t, "b", req.Options().Tag("a"))
   720  	assert.DeepEqual(t, "d", req.Options().Tag("c"))
   721  
   722  	req.SetOptions(config.WithTag("a", "c"))
   723  	assert.DeepEqual(t, "c", req.Options().Tag("a"))
   724  }
   725  
   726  type bodyWriterTo interface {
   727  	BodyWriteTo(writer io.Writer) error
   728  	Body() []byte
   729  }
   730  
   731  func testBodyWriteTo(t *testing.T, bw bodyWriterTo, expectedS string, isRetainedBody bool) {
   732  	var buf bytebufferpool.ByteBuffer
   733  	if err := bw.BodyWriteTo(&buf); err != nil {
   734  		t.Fatalf("unexpected error: %s", err)
   735  	}
   736  
   737  	s := buf.B
   738  	if string(s) != expectedS {
   739  		t.Fatalf("unexpected result %q. Expecting %q", s, expectedS)
   740  	}
   741  
   742  	body := bw.Body()
   743  	if isRetainedBody {
   744  		if string(body) != expectedS {
   745  			t.Fatalf("unexpected body %q. Expecting %q", body, expectedS)
   746  		}
   747  	} else {
   748  		if len(body) > 0 {
   749  			t.Fatalf("unexpected non-zero body after BodyWriteTo: %q", body)
   750  		}
   751  	}
   752  }
   753  
   754  func TestReqSafeCopy(t *testing.T) {
   755  	req := AcquireRequest()
   756  	req.bodyRaw = make([]byte, 1)
   757  	reqs := make([]*Request, 10)
   758  	for i := 0; i < 10; i++ {
   759  		req.bodyRaw[0] = byte(i)
   760  		tmpReq := AcquireRequest()
   761  		req.CopyTo(tmpReq)
   762  		reqs[i] = tmpReq
   763  	}
   764  	for i := 0; i < 10; i++ {
   765  		assert.DeepEqual(t, []byte{byte(i)}, reqs[i].Body())
   766  	}
   767  }