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 }