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 }