github.com/cloudwego/hertz@v0.9.3/pkg/protocol/multipart_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  	"mime/multipart"
    47  	"net/textproto"
    48  	"os"
    49  	"strings"
    50  	"testing"
    51  
    52  	"github.com/cloudwego/hertz/pkg/common/test/assert"
    53  )
    54  
    55  func TestWriteMultipartForm(t *testing.T) {
    56  	t.Parallel()
    57  	var w bytes.Buffer
    58  	s := strings.Replace(`--foo
    59  Content-Disposition: form-data; name="key"
    60  
    61  value
    62  --foo
    63  Content-Disposition: form-data; name="file"; filename="test.json"
    64  Content-Type: application/json
    65  
    66  {"foo": "bar"}
    67  --foo--
    68  `, "\n", "\r\n", -1)
    69  	mr := multipart.NewReader(strings.NewReader(s), "foo")
    70  	form, err := mr.ReadForm(1024)
    71  	if err != nil {
    72  		t.Fatalf("unexpected error: %s", err)
    73  	}
    74  
    75  	// The length of boundary is in the range of [1,70], which can be verified for strings outside this range.
    76  	err = WriteMultipartForm(&w, form, s)
    77  	assert.NotNil(t, err)
    78  
    79  	// set Boundary as empty
    80  	assert.Panic(t, func() {
    81  		err = WriteMultipartForm(&w, form, "")
    82  	})
    83  
    84  	// call WriteField as twice
    85  	var body bytes.Buffer
    86  	mw := multipart.NewWriter(&body)
    87  	if err = mw.WriteField("field1", "value1"); err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	err = WriteMultipartForm(&w, form, s)
    91  	assert.NotNil(t, err)
    92  
    93  	// normal test
    94  	err = WriteMultipartForm(&w, form, "foo")
    95  	if err != nil {
    96  		t.Fatalf("unexpected error: %s", err)
    97  	}
    98  
    99  	if w.String() != s {
   100  		t.Fatalf("unexpected output %q", w.Bytes())
   101  	}
   102  }
   103  
   104  func TestParseMultipartForm(t *testing.T) {
   105  	t.Parallel()
   106  	s := strings.Replace(`--foo
   107  Content-Disposition: form-data; name="key"
   108  
   109  value
   110  --foo--
   111  `, "\n", "\r\n", -1)
   112  	req1 := Request{}
   113  	req1.SetMultipartFormBoundary("foo")
   114  	// test size 0
   115  	assert.NotNil(t, ParseMultipartForm(strings.NewReader(s), &req1, 0, 0))
   116  
   117  	err := ParseMultipartForm(strings.NewReader(s), &req1, 1024, 1024)
   118  	if err != nil {
   119  		t.Fatalf("unexpected error %s", err)
   120  	}
   121  
   122  	req2 := Request{}
   123  	mr := multipart.NewReader(strings.NewReader(s), "foo")
   124  	form, err := mr.ReadForm(1024)
   125  	if err != nil {
   126  		t.Fatalf("unexpected error: %s", err)
   127  	}
   128  	SetMultipartFormWithBoundary(&req2, form, "foo")
   129  	assert.DeepEqual(t, &req1, &req2)
   130  
   131  	// set Boundary as " "
   132  	req1.SetMultipartFormBoundary(" ")
   133  	err = ParseMultipartForm(strings.NewReader(s), &req1, 1024, 1024)
   134  	assert.NotNil(t, err)
   135  
   136  	// set size 0
   137  	err = ParseMultipartForm(strings.NewReader(s), &req1, 0, 0)
   138  	assert.NotNil(t, err)
   139  }
   140  
   141  func TestWriteMultipartFormFile(t *testing.T) {
   142  	t.Parallel()
   143  	bodyBuffer := &bytes.Buffer{}
   144  	w := multipart.NewWriter(bodyBuffer)
   145  
   146  	// read multipart.go to buf1
   147  	f1, err := os.Open("./multipart.go")
   148  	if err != nil {
   149  		t.Fatalf("open file %s error: %s", f1.Name(), err)
   150  	}
   151  	defer f1.Close()
   152  
   153  	multipartFile := File{
   154  		Name:      f1.Name(),
   155  		ParamName: "multipartCode",
   156  		Reader:    f1,
   157  	}
   158  
   159  	err = WriteMultipartFormFile(w, multipartFile.ParamName, f1.Name(), multipartFile.Reader)
   160  	if err != nil {
   161  		t.Fatalf("write multipart error: %s", err)
   162  	}
   163  
   164  	fileInfo1, err := f1.Stat()
   165  	if err != nil {
   166  		t.Fatalf("get file state error: %s", err)
   167  	}
   168  
   169  	buf1 := make([]byte, fileInfo1.Size())
   170  	_, err = f1.ReadAt(buf1, 0)
   171  	if err != nil {
   172  		t.Fatalf("read file to bytes error: %s", err)
   173  	}
   174  	assert.True(t, strings.Contains(bodyBuffer.String(), string(buf1)))
   175  
   176  	// test file not found
   177  	assert.Nil(t, WriteMultipartFormFile(w, multipartFile.ParamName, "test.go", multipartFile.Reader))
   178  
   179  	// Test Add File Function
   180  	err = AddFile(w, "responseCode", "./response.go")
   181  	if err != nil {
   182  		t.Fatalf("add file error: %s", err)
   183  	}
   184  
   185  	// read response.go to buf2
   186  	f2, err := os.Open("./response.go")
   187  	if err != nil {
   188  		t.Fatalf("open file %s error: %s", f2.Name(), err)
   189  	}
   190  	defer f2.Close()
   191  
   192  	fileInfo2, err := f2.Stat()
   193  	if err != nil {
   194  		t.Fatalf("get file state error: %s", err)
   195  	}
   196  	buf2 := make([]byte, fileInfo2.Size())
   197  	_, err = f2.ReadAt(buf2, 0)
   198  	if err != nil {
   199  		t.Fatalf("read file to bytes error: %s", err)
   200  	}
   201  	assert.True(t, strings.Contains(bodyBuffer.String(), string(buf2)))
   202  
   203  	// test file not found
   204  	err = AddFile(w, "responseCode", "./test.go")
   205  	assert.NotNil(t, err)
   206  
   207  	// test WriteMultipartFormFile without file name
   208  	bodyBuffer = &bytes.Buffer{}
   209  	w = multipart.NewWriter(bodyBuffer)
   210  	// read multipart.go to buf1
   211  	f3, err := os.Open("./multipart.go")
   212  	if err != nil {
   213  		t.Fatalf("open file %s error: %s", f3.Name(), err)
   214  	}
   215  	defer f3.Close()
   216  	err = WriteMultipartFormFile(w, "multipart", " ", f3)
   217  	if err != nil {
   218  		t.Fatalf("write multipart error: %s", err)
   219  	}
   220  	assert.False(t, strings.Contains(bodyBuffer.String(), f3.Name()))
   221  
   222  	// test empty file
   223  	assert.Nil(t, WriteMultipartFormFile(w, "empty_test", "test.data", bytes.NewBuffer(nil)))
   224  }
   225  
   226  func TestMarshalMultipartForm(t *testing.T) {
   227  	s := strings.Replace(`--foo
   228  Content-Disposition: form-data; name="key"
   229  
   230  value
   231  --foo
   232  Content-Disposition: form-data; name="file"; filename="test.json"
   233  Content-Type: application/json
   234  
   235  {"foo": "bar"}
   236  --foo--
   237  `, "\n", "\r\n", -1)
   238  	mr := multipart.NewReader(strings.NewReader(s), "foo")
   239  	form, err := mr.ReadForm(1024)
   240  	if err != nil {
   241  		t.Fatalf("unexpected error: %s", err)
   242  	}
   243  	bufs, err := MarshalMultipartForm(form, "foo")
   244  	assert.Nil(t, err)
   245  	assert.DeepEqual(t, s, string(bufs))
   246  
   247  	// set boundary invalid
   248  	_, err = MarshalMultipartForm(form, " ")
   249  	assert.NotNil(t, err)
   250  }
   251  
   252  func TestAddFile(t *testing.T) {
   253  	t.Parallel()
   254  	bodyBuffer := &bytes.Buffer{}
   255  	w := multipart.NewWriter(bodyBuffer)
   256  	// add null file
   257  	err := AddFile(w, "test", "/test")
   258  	assert.NotNil(t, err)
   259  }
   260  
   261  func TestCreateMultipartHeader(t *testing.T) {
   262  	t.Parallel()
   263  
   264  	// filename == Null
   265  	hdr1 := make(textproto.MIMEHeader)
   266  	hdr1.Set("Content-Disposition", `form-data; name="test"`)
   267  	hdr1.Set("Content-Type", "application/json")
   268  	assert.DeepEqual(t, hdr1, CreateMultipartHeader("test", "", "application/json"))
   269  
   270  	// normal test
   271  	hdr2 := make(textproto.MIMEHeader)
   272  	hdr2.Set("Content-Disposition", `form-data; name="test"; filename="/test.go"`)
   273  	hdr2.Set("Content-Type", "application/json")
   274  	assert.DeepEqual(t, hdr2, CreateMultipartHeader("test", "/test.go", "application/json"))
   275  }