github.com/boomhut/fiber/v2@v2.0.0-20230603160335-b65c856e57d3/ctx_test.go (about) 1 // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ 2 // 🤖 Github Repository: https://github.com/gofiber/fiber 3 // 📌 API Documentation: https://docs.gofiber.io 4 5 //nolint:bodyclose // Much easier to just ignore memory leaks in tests 6 package fiber 7 8 import ( 9 "bufio" 10 "bytes" 11 "compress/gzip" 12 "context" 13 "crypto/tls" 14 "encoding/xml" 15 "errors" 16 "fmt" 17 "io" 18 "mime/multipart" 19 "net/http/httptest" 20 "net/url" 21 "os" 22 "path/filepath" 23 "reflect" 24 "strconv" 25 "strings" 26 "testing" 27 "text/template" 28 "time" 29 30 "github.com/boomhut/fiber/v2/internal/storage/memory" 31 "github.com/boomhut/fiber/v2/utils" 32 33 "github.com/valyala/bytebufferpool" 34 "github.com/valyala/fasthttp" 35 ) 36 37 // go test -run Test_Ctx_Accepts 38 func Test_Ctx_Accepts(t *testing.T) { 39 t.Parallel() 40 app := New() 41 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 42 defer app.ReleaseCtx(c) 43 c.Request().Header.Set(HeaderAccept, "text/html,application/xhtml+xml,application/xml;q=0.9") 44 utils.AssertEqual(t, "", c.Accepts("")) 45 utils.AssertEqual(t, "", c.Accepts()) 46 utils.AssertEqual(t, ".xml", c.Accepts(".xml")) 47 utils.AssertEqual(t, "", c.Accepts(".john")) 48 49 c.Request().Header.Set(HeaderAccept, "text/*, application/json") 50 utils.AssertEqual(t, "html", c.Accepts("html")) 51 utils.AssertEqual(t, "text/html", c.Accepts("text/html")) 52 utils.AssertEqual(t, "json", c.Accepts("json", "text")) 53 utils.AssertEqual(t, "application/json", c.Accepts("application/json")) 54 utils.AssertEqual(t, "", c.Accepts("image/png")) 55 utils.AssertEqual(t, "", c.Accepts("png")) 56 57 c.Request().Header.Set(HeaderAccept, "text/html, application/json") 58 utils.AssertEqual(t, "text/*", c.Accepts("text/*")) 59 60 c.Request().Header.Set(HeaderAccept, "*/*") 61 utils.AssertEqual(t, "html", c.Accepts("html")) 62 } 63 64 // go test -v -run=^$ -bench=Benchmark_Ctx_Accepts -benchmem -count=4 65 func Benchmark_Ctx_Accepts(b *testing.B) { 66 app := New() 67 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 68 defer app.ReleaseCtx(c) 69 acceptHeader := "text/html,application/xhtml+xml,application/xml;q=0.9" 70 c.Request().Header.Set("Accept", acceptHeader) 71 acceptValues := [][]string{ 72 {".xml"}, 73 {"json", "xml"}, 74 {"application/json", "application/xml"}, 75 } 76 expectedResults := []string{".xml", "xml", "application/xml"} 77 78 for i := 0; i < len(acceptValues); i++ { 79 b.Run(fmt.Sprintf("run-%#v", acceptValues[i]), func(bb *testing.B) { 80 var res string 81 bb.ReportAllocs() 82 bb.ResetTimer() 83 84 for n := 0; n < bb.N; n++ { 85 res = c.Accepts(acceptValues[i]...) 86 } 87 utils.AssertEqual(bb, expectedResults[i], res) 88 }) 89 } 90 } 91 92 // go test -run Test_Ctx_Accepts_EmptyAccept 93 func Test_Ctx_Accepts_EmptyAccept(t *testing.T) { 94 t.Parallel() 95 app := New() 96 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 97 defer app.ReleaseCtx(c) 98 utils.AssertEqual(t, ".forwarded", c.Accepts(".forwarded")) 99 } 100 101 // go test -run Test_Ctx_Accepts_Wildcard 102 func Test_Ctx_Accepts_Wildcard(t *testing.T) { 103 t.Parallel() 104 app := New() 105 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 106 defer app.ReleaseCtx(c) 107 c.Request().Header.Set(HeaderAccept, "*/*;q=0.9") 108 utils.AssertEqual(t, "html", c.Accepts("html")) 109 utils.AssertEqual(t, "foo", c.Accepts("foo")) 110 utils.AssertEqual(t, ".bar", c.Accepts(".bar")) 111 c.Request().Header.Set(HeaderAccept, "text/html,application/*;q=0.9") 112 utils.AssertEqual(t, "xml", c.Accepts("xml")) 113 } 114 115 // go test -run Test_Ctx_AcceptsCharsets 116 func Test_Ctx_AcceptsCharsets(t *testing.T) { 117 t.Parallel() 118 app := New() 119 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 120 defer app.ReleaseCtx(c) 121 c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") 122 utils.AssertEqual(t, "utf-8", c.AcceptsCharsets("utf-8")) 123 } 124 125 // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsCharsets -benchmem -count=4 126 func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { 127 app := New() 128 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 129 defer app.ReleaseCtx(c) 130 c.Request().Header.Set("Accept-Charset", "utf-8, iso-8859-1;q=0.5") 131 var res string 132 b.ReportAllocs() 133 b.ResetTimer() 134 for n := 0; n < b.N; n++ { 135 res = c.AcceptsCharsets("utf-8") 136 } 137 utils.AssertEqual(b, "utf-8", res) 138 } 139 140 // go test -run Test_Ctx_AcceptsEncodings 141 func Test_Ctx_AcceptsEncodings(t *testing.T) { 142 t.Parallel() 143 app := New() 144 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 145 defer app.ReleaseCtx(c) 146 c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") 147 utils.AssertEqual(t, "gzip", c.AcceptsEncodings("gzip")) 148 utils.AssertEqual(t, "abc", c.AcceptsEncodings("abc")) 149 } 150 151 // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsEncodings -benchmem -count=4 152 func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { 153 app := New() 154 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 155 defer app.ReleaseCtx(c) 156 c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") 157 var res string 158 b.ReportAllocs() 159 b.ResetTimer() 160 for n := 0; n < b.N; n++ { 161 res = c.AcceptsEncodings("gzip") 162 } 163 utils.AssertEqual(b, "gzip", res) 164 } 165 166 // go test -run Test_Ctx_AcceptsLanguages 167 func Test_Ctx_AcceptsLanguages(t *testing.T) { 168 t.Parallel() 169 app := New() 170 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 171 defer app.ReleaseCtx(c) 172 c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") 173 utils.AssertEqual(t, "fr", c.AcceptsLanguages("fr")) 174 } 175 176 // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsLanguages -benchmem -count=4 177 func Benchmark_Ctx_AcceptsLanguages(b *testing.B) { 178 app := New() 179 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 180 defer app.ReleaseCtx(c) 181 c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") 182 var res string 183 b.ReportAllocs() 184 b.ResetTimer() 185 for n := 0; n < b.N; n++ { 186 res = c.AcceptsLanguages("fr") 187 } 188 utils.AssertEqual(b, "fr", res) 189 } 190 191 // go test -run Test_Ctx_App 192 func Test_Ctx_App(t *testing.T) { 193 t.Parallel() 194 app := New() 195 app.config.BodyLimit = 1000 196 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 197 defer app.ReleaseCtx(c) 198 utils.AssertEqual(t, 1000, c.App().config.BodyLimit) 199 } 200 201 // go test -run Test_Ctx_Append 202 func Test_Ctx_Append(t *testing.T) { 203 t.Parallel() 204 app := New() 205 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 206 defer app.ReleaseCtx(c) 207 c.Append("X-Test", "Hello") 208 c.Append("X-Test", "World") 209 c.Append("X-Test", "Hello", "World") 210 // similar value in the middle 211 c.Append("X2-Test", "World") 212 c.Append("X2-Test", "XHello") 213 c.Append("X2-Test", "Hello", "World") 214 // similar value at the start 215 c.Append("X3-Test", "XHello") 216 c.Append("X3-Test", "World") 217 c.Append("X3-Test", "Hello", "World") 218 // try it with multiple similar values 219 c.Append("X4-Test", "XHello") 220 c.Append("X4-Test", "Hello") 221 c.Append("X4-Test", "HelloZ") 222 c.Append("X4-Test", "YHello") 223 c.Append("X4-Test", "Hello") 224 c.Append("X4-Test", "YHello") 225 c.Append("X4-Test", "HelloZ") 226 c.Append("X4-Test", "XHello") 227 // without append value 228 c.Append("X-Custom-Header") 229 230 utils.AssertEqual(t, "Hello, World", string(c.Response().Header.Peek("X-Test"))) 231 utils.AssertEqual(t, "World, XHello, Hello", string(c.Response().Header.Peek("X2-Test"))) 232 utils.AssertEqual(t, "XHello, World, Hello", string(c.Response().Header.Peek("X3-Test"))) 233 utils.AssertEqual(t, "XHello, Hello, HelloZ, YHello", string(c.Response().Header.Peek("X4-Test"))) 234 utils.AssertEqual(t, "", string(c.Response().Header.Peek("x-custom-header"))) 235 } 236 237 // go test -v -run=^$ -bench=Benchmark_Ctx_Append -benchmem -count=4 238 func Benchmark_Ctx_Append(b *testing.B) { 239 app := New() 240 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 241 defer app.ReleaseCtx(c) 242 b.ReportAllocs() 243 b.ResetTimer() 244 for n := 0; n < b.N; n++ { 245 c.Append("X-Custom-Header", "Hello") 246 c.Append("X-Custom-Header", "World") 247 c.Append("X-Custom-Header", "Hello") 248 } 249 utils.AssertEqual(b, "Hello, World", app.getString(c.Response().Header.Peek("X-Custom-Header"))) 250 } 251 252 // go test -run Test_Ctx_Attachment 253 func Test_Ctx_Attachment(t *testing.T) { 254 t.Parallel() 255 app := New() 256 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 257 defer app.ReleaseCtx(c) 258 // empty 259 c.Attachment() 260 utils.AssertEqual(t, `attachment`, string(c.Response().Header.Peek(HeaderContentDisposition))) 261 // real filename 262 c.Attachment("./static/img/logo.png") 263 utils.AssertEqual(t, `attachment; filename="logo.png"`, string(c.Response().Header.Peek(HeaderContentDisposition))) 264 utils.AssertEqual(t, "image/png", string(c.Response().Header.Peek(HeaderContentType))) 265 // check quoting 266 c.Attachment("another document.pdf\"\r\nBla: \"fasel") 267 utils.AssertEqual(t, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) 268 } 269 270 // go test -v -run=^$ -bench=Benchmark_Ctx_Attachment -benchmem -count=4 271 func Benchmark_Ctx_Attachment(b *testing.B) { 272 app := New() 273 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 274 defer app.ReleaseCtx(c) 275 b.ReportAllocs() 276 b.ResetTimer() 277 for n := 0; n < b.N; n++ { 278 // example with quote params 279 c.Attachment("another document.pdf\"\r\nBla: \"fasel") 280 } 281 utils.AssertEqual(b, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) 282 } 283 284 // go test -run Test_Ctx_BaseURL 285 func Test_Ctx_BaseURL(t *testing.T) { 286 t.Parallel() 287 app := New() 288 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 289 defer app.ReleaseCtx(c) 290 c.Request().SetRequestURI("http://google.com/test") 291 utils.AssertEqual(t, "http://google.com", c.BaseURL()) 292 // Check cache 293 utils.AssertEqual(t, "http://google.com", c.BaseURL()) 294 } 295 296 // go test -v -run=^$ -bench=Benchmark_Ctx_BaseURL -benchmem 297 func Benchmark_Ctx_BaseURL(b *testing.B) { 298 app := New() 299 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 300 defer app.ReleaseCtx(c) 301 c.Request().SetHost("google.com:1337") 302 c.Request().URI().SetPath("/haha/oke/lol") 303 var res string 304 b.ReportAllocs() 305 b.ResetTimer() 306 for n := 0; n < b.N; n++ { 307 res = c.BaseURL() 308 } 309 utils.AssertEqual(b, "http://google.com:1337", res) 310 } 311 312 // go test -run Test_Ctx_Body 313 func Test_Ctx_Body(t *testing.T) { 314 t.Parallel() 315 app := New() 316 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 317 defer app.ReleaseCtx(c) 318 c.Request().SetBody([]byte("john=doe")) 319 utils.AssertEqual(t, []byte("john=doe"), c.Body()) 320 } 321 322 // go test -run Test_Ctx_Body_With_Compression 323 func Test_Ctx_Body_With_Compression(t *testing.T) { 324 t.Parallel() 325 app := New() 326 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 327 defer app.ReleaseCtx(c) 328 c.Request().Header.Set("Content-Encoding", "gzip") 329 var b bytes.Buffer 330 gz := gzip.NewWriter(&b) 331 _, err := gz.Write([]byte("john=doe")) 332 utils.AssertEqual(t, nil, err) 333 err = gz.Flush() 334 utils.AssertEqual(t, nil, err) 335 err = gz.Close() 336 utils.AssertEqual(t, nil, err) 337 c.Request().SetBody(b.Bytes()) 338 utils.AssertEqual(t, []byte("john=doe"), c.Body()) 339 } 340 341 // go test -v -run=^$ -bench=Benchmark_Ctx_Body_With_Compression -benchmem -count=4 342 func Benchmark_Ctx_Body_With_Compression(b *testing.B) { 343 app := New() 344 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 345 defer app.ReleaseCtx(c) 346 c.Request().Header.Set("Content-Encoding", "gzip") 347 var buf bytes.Buffer 348 gz := gzip.NewWriter(&buf) 349 _, err := gz.Write([]byte("john=doe")) 350 utils.AssertEqual(b, nil, err) 351 err = gz.Flush() 352 utils.AssertEqual(b, nil, err) 353 err = gz.Close() 354 utils.AssertEqual(b, nil, err) 355 356 c.Request().SetBody(buf.Bytes()) 357 358 for i := 0; i < b.N; i++ { 359 _ = c.Body() 360 } 361 362 utils.AssertEqual(b, []byte("john=doe"), c.Body()) 363 } 364 365 // go test -run Test_Ctx_BodyParser 366 func Test_Ctx_BodyParser(t *testing.T) { 367 t.Parallel() 368 app := New() 369 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 370 defer app.ReleaseCtx(c) 371 372 type Demo struct { 373 Name string `json:"name" xml:"name" form:"name" query:"name"` 374 } 375 376 { 377 var gzipJSON bytes.Buffer 378 w := gzip.NewWriter(&gzipJSON) 379 _, err := w.Write([]byte(`{"name":"john"}`)) 380 utils.AssertEqual(t, nil, err) 381 err = w.Close() 382 utils.AssertEqual(t, nil, err) 383 384 c.Request().Header.SetContentType(MIMEApplicationJSON) 385 c.Request().Header.Set(HeaderContentEncoding, "gzip") 386 c.Request().SetBody(gzipJSON.Bytes()) 387 c.Request().Header.SetContentLength(len(gzipJSON.Bytes())) 388 d := new(Demo) 389 utils.AssertEqual(t, nil, c.BodyParser(d)) 390 utils.AssertEqual(t, "john", d.Name) 391 c.Request().Header.Del(HeaderContentEncoding) 392 } 393 394 testDecodeParser := func(contentType, body string) { 395 c.Request().Header.SetContentType(contentType) 396 c.Request().SetBody([]byte(body)) 397 c.Request().Header.SetContentLength(len(body)) 398 d := new(Demo) 399 utils.AssertEqual(t, nil, c.BodyParser(d)) 400 utils.AssertEqual(t, "john", d.Name) 401 } 402 403 testDecodeParser(MIMEApplicationJSON, `{"name":"john"}`) 404 testDecodeParser(MIMEApplicationXML, `<Demo><name>john</name></Demo>`) 405 testDecodeParser(MIMEApplicationForm, "name=john") 406 testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--") 407 408 testDecodeParserError := func(contentType, body string) { 409 c.Request().Header.SetContentType(contentType) 410 c.Request().SetBody([]byte(body)) 411 c.Request().Header.SetContentLength(len(body)) 412 utils.AssertEqual(t, false, c.BodyParser(nil) == nil) 413 } 414 415 testDecodeParserError("invalid-content-type", "") 416 testDecodeParserError(MIMEMultipartForm+`;boundary="b"`, "--b") 417 418 type CollectionQuery struct { 419 Data []Demo `query:"data"` 420 } 421 422 c.Request().Reset() 423 c.Request().Header.SetContentType(MIMEApplicationForm) 424 c.Request().SetBody([]byte("data[0][name]=john&data[1][name]=doe")) 425 c.Request().Header.SetContentLength(len(c.Body())) 426 cq := new(CollectionQuery) 427 utils.AssertEqual(t, nil, c.BodyParser(cq)) 428 utils.AssertEqual(t, 2, len(cq.Data)) 429 utils.AssertEqual(t, "john", cq.Data[0].Name) 430 utils.AssertEqual(t, "doe", cq.Data[1].Name) 431 432 c.Request().Reset() 433 c.Request().Header.SetContentType(MIMEApplicationForm) 434 c.Request().SetBody([]byte("data.0.name=john&data.1.name=doe")) 435 c.Request().Header.SetContentLength(len(c.Body())) 436 cq = new(CollectionQuery) 437 utils.AssertEqual(t, nil, c.BodyParser(cq)) 438 utils.AssertEqual(t, 2, len(cq.Data)) 439 utils.AssertEqual(t, "john", cq.Data[0].Name) 440 utils.AssertEqual(t, "doe", cq.Data[1].Name) 441 } 442 443 func Test_Ctx_ParamParser(t *testing.T) { 444 t.Parallel() 445 app := New() 446 app.Get("/test1/:userId/role/:roleId", func(ctx *Ctx) error { 447 type Demo struct { 448 UserID uint `params:"userId"` 449 RoleID uint `params:"roleId"` 450 } 451 d := new(Demo) 452 if err := ctx.ParamsParser(d); err != nil { 453 t.Fatal(err) 454 } 455 utils.AssertEqual(t, uint(111), d.UserID) 456 utils.AssertEqual(t, uint(222), d.RoleID) 457 return nil 458 }) 459 _, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) 460 utils.AssertEqual(t, nil, err) 461 462 _, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) 463 utils.AssertEqual(t, nil, err) 464 } 465 466 // go test -run Test_Ctx_BodyParser_WithSetParserDecoder 467 func Test_Ctx_BodyParser_WithSetParserDecoder(t *testing.T) { 468 t.Parallel() 469 type CustomTime time.Time 470 471 timeConverter := func(value string) reflect.Value { 472 if v, err := time.Parse("2006-01-02", value); err == nil { 473 return reflect.ValueOf(v) 474 } 475 return reflect.Value{} 476 } 477 478 customTime := ParserType{ 479 Customtype: CustomTime{}, 480 Converter: timeConverter, 481 } 482 483 SetParserDecoder(ParserConfig{ 484 IgnoreUnknownKeys: true, 485 ParserType: []ParserType{customTime}, 486 ZeroEmpty: true, 487 SetAliasTag: "form", 488 }) 489 490 app := New() 491 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 492 defer app.ReleaseCtx(c) 493 494 type Demo struct { 495 Date CustomTime `form:"date"` 496 Title string `form:"title"` 497 Body string `form:"body"` 498 } 499 500 testDecodeParser := func(contentType, body string) { 501 c.Request().Header.SetContentType(contentType) 502 c.Request().SetBody([]byte(body)) 503 c.Request().Header.SetContentLength(len(body)) 504 d := Demo{ 505 Title: "Existing title", 506 Body: "Existing Body", 507 } 508 utils.AssertEqual(t, nil, c.BodyParser(&d)) 509 date := fmt.Sprintf("%v", d.Date) 510 utils.AssertEqual(t, "{0 63743587200 <nil>}", date) 511 utils.AssertEqual(t, "", d.Title) 512 utils.AssertEqual(t, "New Body", d.Body) 513 } 514 515 testDecodeParser(MIMEApplicationForm, "date=2020-12-15&title=&body=New Body") 516 testDecodeParser(MIMEMultipartForm+`; boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"date\"\r\n\r\n2020-12-15\r\n--b\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n\r\n--b\r\nContent-Disposition: form-data; name=\"body\"\r\n\r\nNew Body\r\n--b--") 517 } 518 519 // go test -v -run=^$ -bench=Benchmark_Ctx_BodyParser_JSON -benchmem -count=4 520 func Benchmark_Ctx_BodyParser_JSON(b *testing.B) { 521 app := New() 522 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 523 defer app.ReleaseCtx(c) 524 type Demo struct { 525 Name string `json:"name"` 526 } 527 body := []byte(`{"name":"john"}`) 528 c.Request().SetBody(body) 529 c.Request().Header.SetContentType(MIMEApplicationJSON) 530 c.Request().Header.SetContentLength(len(body)) 531 d := new(Demo) 532 533 b.ReportAllocs() 534 b.ResetTimer() 535 536 for n := 0; n < b.N; n++ { 537 _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below 538 } 539 utils.AssertEqual(b, nil, c.BodyParser(d)) 540 utils.AssertEqual(b, "john", d.Name) 541 } 542 543 // go test -v -run=^$ -bench=Benchmark_Ctx_BodyParser_XML -benchmem -count=4 544 func Benchmark_Ctx_BodyParser_XML(b *testing.B) { 545 app := New() 546 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 547 defer app.ReleaseCtx(c) 548 type Demo struct { 549 Name string `xml:"name"` 550 } 551 body := []byte("<Demo><name>john</name></Demo>") 552 c.Request().SetBody(body) 553 c.Request().Header.SetContentType(MIMEApplicationXML) 554 c.Request().Header.SetContentLength(len(body)) 555 d := new(Demo) 556 557 b.ReportAllocs() 558 b.ResetTimer() 559 560 for n := 0; n < b.N; n++ { 561 _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below 562 } 563 utils.AssertEqual(b, nil, c.BodyParser(d)) 564 utils.AssertEqual(b, "john", d.Name) 565 } 566 567 // go test -v -run=^$ -bench=Benchmark_Ctx_BodyParser_Form -benchmem -count=4 568 func Benchmark_Ctx_BodyParser_Form(b *testing.B) { 569 app := New() 570 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 571 defer app.ReleaseCtx(c) 572 type Demo struct { 573 Name string `form:"name"` 574 } 575 body := []byte("name=john") 576 c.Request().SetBody(body) 577 c.Request().Header.SetContentType(MIMEApplicationForm) 578 c.Request().Header.SetContentLength(len(body)) 579 d := new(Demo) 580 581 b.ReportAllocs() 582 b.ResetTimer() 583 584 for n := 0; n < b.N; n++ { 585 _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below 586 } 587 utils.AssertEqual(b, nil, c.BodyParser(d)) 588 utils.AssertEqual(b, "john", d.Name) 589 } 590 591 // go test -v -run=^$ -bench=Benchmark_Ctx_BodyParser_MultipartForm -benchmem -count=4 592 func Benchmark_Ctx_BodyParser_MultipartForm(b *testing.B) { 593 app := New() 594 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 595 defer app.ReleaseCtx(c) 596 type Demo struct { 597 Name string `form:"name"` 598 } 599 600 body := []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--") 601 c.Request().SetBody(body) 602 c.Request().Header.SetContentType(MIMEMultipartForm + `;boundary="b"`) 603 c.Request().Header.SetContentLength(len(body)) 604 d := new(Demo) 605 606 b.ReportAllocs() 607 b.ResetTimer() 608 609 for n := 0; n < b.N; n++ { 610 _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below 611 } 612 utils.AssertEqual(b, nil, c.BodyParser(d)) 613 utils.AssertEqual(b, "john", d.Name) 614 } 615 616 // go test -run Test_Ctx_Context 617 func Test_Ctx_Context(t *testing.T) { 618 t.Parallel() 619 app := New() 620 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 621 defer app.ReleaseCtx(c) 622 623 utils.AssertEqual(t, "*fasthttp.RequestCtx", fmt.Sprintf("%T", c.Context())) 624 } 625 626 // go test -run Test_Ctx_UserContext 627 func Test_Ctx_UserContext(t *testing.T) { 628 app := New() 629 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 630 defer app.ReleaseCtx(c) 631 632 t.Run("Nil_Context", func(t *testing.T) { 633 ctx := c.UserContext() 634 utils.AssertEqual(t, ctx, context.Background()) 635 }) 636 t.Run("ValueContext", func(t *testing.T) { 637 testKey := struct{}{} 638 testValue := "Test Value" 639 ctx := context.WithValue(context.Background(), testKey, testValue) 640 utils.AssertEqual(t, testValue, ctx.Value(testKey)) 641 }) 642 } 643 644 // go test -run Test_Ctx_SetUserContext 645 func Test_Ctx_SetUserContext(t *testing.T) { 646 t.Parallel() 647 app := New() 648 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 649 defer app.ReleaseCtx(c) 650 651 testKey := struct{}{} 652 testValue := "Test Value" 653 ctx := context.WithValue(context.Background(), testKey, testValue) 654 c.SetUserContext(ctx) 655 utils.AssertEqual(t, testValue, c.UserContext().Value(testKey)) 656 } 657 658 // go test -run Test_Ctx_UserContext_Multiple_Requests 659 func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { 660 t.Parallel() 661 testKey := struct{}{} 662 testValue := "foobar-value" 663 664 app := New() 665 app.Get("/", func(c *Ctx) error { 666 ctx := c.UserContext() 667 668 if ctx.Value(testKey) != nil { 669 return c.SendStatus(StatusInternalServerError) 670 } 671 672 input := utils.CopyString(c.Query("input", "NO_VALUE")) 673 ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) 674 c.SetUserContext(ctx) 675 676 return c.Status(StatusOK).SendString(fmt.Sprintf("resp_%s_returned", input)) 677 }) 678 679 // Consecutive Requests 680 for i := 1; i <= 10; i++ { 681 t.Run(fmt.Sprintf("request_%d", i), func(t *testing.T) { 682 resp, err := app.Test(httptest.NewRequest(MethodGet, fmt.Sprintf("/?input=%d", i), nil)) 683 684 utils.AssertEqual(t, nil, err, "Unexpected error from response") 685 utils.AssertEqual(t, StatusOK, resp.StatusCode, "context.Context returned from c.UserContext() is reused") 686 687 b, err := io.ReadAll(resp.Body) 688 utils.AssertEqual(t, nil, err, "Unexpected error from reading response body") 689 utils.AssertEqual(t, fmt.Sprintf("resp_%d_returned", i), string(b), "response text incorrect") 690 }) 691 } 692 } 693 694 // go test -run Test_Ctx_Cookie 695 func Test_Ctx_Cookie(t *testing.T) { 696 t.Parallel() 697 app := New() 698 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 699 defer app.ReleaseCtx(c) 700 expire := time.Now().Add(24 * time.Hour) 701 var dst []byte 702 dst = expire.In(time.UTC).AppendFormat(dst, time.RFC1123) 703 httpdate := strings.ReplaceAll(string(dst), "UTC", "GMT") 704 cookie := &Cookie{ 705 Name: "username", 706 Value: "john", 707 Expires: expire, 708 // SameSite: CookieSameSiteStrictMode, // default is "lax" 709 } 710 c.Cookie(cookie) 711 expect := "username=john; expires=" + httpdate + "; path=/; SameSite=Lax" 712 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 713 714 expect = "username=john; expires=" + httpdate + "; path=/" 715 cookie.SameSite = CookieSameSiteDisabled 716 c.Cookie(cookie) 717 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 718 719 expect = "username=john; expires=" + httpdate + "; path=/; SameSite=Strict" 720 cookie.SameSite = CookieSameSiteStrictMode 721 c.Cookie(cookie) 722 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 723 724 expect = "username=john; expires=" + httpdate + "; path=/; secure; SameSite=None" 725 cookie.Secure = true 726 cookie.SameSite = CookieSameSiteNoneMode 727 c.Cookie(cookie) 728 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 729 730 expect = "username=john; path=/; secure; SameSite=None" 731 // should remove expires and max-age headers 732 cookie.SessionOnly = true 733 cookie.Expires = expire 734 cookie.MaxAge = 10000 735 c.Cookie(cookie) 736 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 737 738 expect = "username=john; path=/; secure; SameSite=None" 739 // should remove expires and max-age headers when no expire and no MaxAge (default time) 740 cookie.SessionOnly = false 741 cookie.Expires = time.Time{} 742 cookie.MaxAge = 0 743 c.Cookie(cookie) 744 utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) 745 } 746 747 // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 748 func Benchmark_Ctx_Cookie(b *testing.B) { 749 app := New() 750 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 751 defer app.ReleaseCtx(c) 752 b.ReportAllocs() 753 b.ResetTimer() 754 for n := 0; n < b.N; n++ { 755 c.Cookie(&Cookie{ 756 Name: "John", 757 Value: "Doe", 758 }) 759 } 760 utils.AssertEqual(b, "John=Doe; path=/; SameSite=Lax", app.getString(c.Response().Header.Peek("Set-Cookie"))) 761 } 762 763 // go test -run Test_Ctx_Cookies 764 func Test_Ctx_Cookies(t *testing.T) { 765 t.Parallel() 766 app := New() 767 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 768 defer app.ReleaseCtx(c) 769 c.Request().Header.Set("Cookie", "john=doe") 770 utils.AssertEqual(t, "doe", c.Cookies("john")) 771 utils.AssertEqual(t, "default", c.Cookies("unknown", "default")) 772 } 773 774 // go test -run Test_Ctx_Format 775 func Test_Ctx_Format(t *testing.T) { 776 t.Parallel() 777 app := New() 778 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 779 defer app.ReleaseCtx(c) 780 c.Request().Header.Set(HeaderAccept, MIMETextPlain) 781 err := c.Format([]byte("Hello, World!")) 782 utils.AssertEqual(t, nil, err) 783 utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) 784 785 c.Request().Header.Set(HeaderAccept, MIMETextHTML) 786 err = c.Format("Hello, World!") 787 utils.AssertEqual(t, nil, err) 788 utils.AssertEqual(t, "<p>Hello, World!</p>", string(c.Response().Body())) 789 790 c.Request().Header.Set(HeaderAccept, MIMEApplicationJSON) 791 err = c.Format("Hello, World!") 792 utils.AssertEqual(t, nil, err) 793 utils.AssertEqual(t, `"Hello, World!"`, string(c.Response().Body())) 794 795 c.Request().Header.Set(HeaderAccept, MIMETextPlain) 796 err = c.Format(complex(1, 1)) 797 utils.AssertEqual(t, nil, err) 798 utils.AssertEqual(t, "(1+1i)", string(c.Response().Body())) 799 800 c.Request().Header.Set(HeaderAccept, MIMEApplicationXML) 801 err = c.Format("Hello, World!") 802 utils.AssertEqual(t, nil, err) 803 utils.AssertEqual(t, `<string>Hello, World!</string>`, string(c.Response().Body())) 804 805 err = c.Format(complex(1, 1)) 806 utils.AssertEqual(t, true, err != nil) 807 808 c.Request().Header.Set(HeaderAccept, MIMETextPlain) 809 err = c.Format(Map{}) 810 utils.AssertEqual(t, nil, err) 811 utils.AssertEqual(t, "map[]", string(c.Response().Body())) 812 813 type broken string 814 c.Request().Header.Set(HeaderAccept, "broken/accept") 815 err = c.Format(broken("Hello, World!")) 816 utils.AssertEqual(t, nil, err) 817 utils.AssertEqual(t, `Hello, World!`, string(c.Response().Body())) 818 } 819 820 // go test -v -run=^$ -bench=Benchmark_Ctx_Format -benchmem -count=4 821 func Benchmark_Ctx_Format(b *testing.B) { 822 app := New() 823 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 824 defer app.ReleaseCtx(c) 825 c.Request().Header.Set("Accept", "text/plain") 826 b.ReportAllocs() 827 b.ResetTimer() 828 829 var err error 830 for n := 0; n < b.N; n++ { 831 err = c.Format("Hello, World!") 832 } 833 834 utils.AssertEqual(b, nil, err) 835 utils.AssertEqual(b, `Hello, World!`, string(c.Response().Body())) 836 } 837 838 // go test -v -run=^$ -bench=Benchmark_Ctx_Format_HTML -benchmem -count=4 839 func Benchmark_Ctx_Format_HTML(b *testing.B) { 840 app := New() 841 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 842 defer app.ReleaseCtx(c) 843 c.Request().Header.Set("Accept", "text/html") 844 b.ReportAllocs() 845 b.ResetTimer() 846 847 var err error 848 for n := 0; n < b.N; n++ { 849 err = c.Format("Hello, World!") 850 } 851 852 utils.AssertEqual(b, nil, err) 853 utils.AssertEqual(b, "<p>Hello, World!</p>", string(c.Response().Body())) 854 } 855 856 // go test -v -run=^$ -bench=Benchmark_Ctx_Format_JSON -benchmem -count=4 857 func Benchmark_Ctx_Format_JSON(b *testing.B) { 858 app := New() 859 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 860 defer app.ReleaseCtx(c) 861 c.Request().Header.Set("Accept", "application/json") 862 b.ReportAllocs() 863 b.ResetTimer() 864 865 var err error 866 for n := 0; n < b.N; n++ { 867 err = c.Format("Hello, World!") 868 } 869 870 utils.AssertEqual(b, nil, err) 871 utils.AssertEqual(b, `"Hello, World!"`, string(c.Response().Body())) 872 } 873 874 // go test -v -run=^$ -bench=Benchmark_Ctx_Format_XML -benchmem -count=4 875 func Benchmark_Ctx_Format_XML(b *testing.B) { 876 app := New() 877 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 878 defer app.ReleaseCtx(c) 879 c.Request().Header.Set("Accept", "application/xml") 880 b.ReportAllocs() 881 b.ResetTimer() 882 883 var err error 884 for n := 0; n < b.N; n++ { 885 err = c.Format("Hello, World!") 886 } 887 888 utils.AssertEqual(b, nil, err) 889 utils.AssertEqual(b, `<string>Hello, World!</string>`, string(c.Response().Body())) 890 } 891 892 // go test -run Test_Ctx_FormFile 893 func Test_Ctx_FormFile(t *testing.T) { 894 // TODO: We should clean this up 895 t.Parallel() 896 app := New() 897 898 app.Post("/test", func(c *Ctx) error { 899 fh, err := c.FormFile("file") 900 utils.AssertEqual(t, nil, err) 901 utils.AssertEqual(t, "test", fh.Filename) 902 903 f, err := fh.Open() 904 utils.AssertEqual(t, nil, err) 905 defer func() { 906 utils.AssertEqual(t, nil, f.Close()) 907 }() 908 909 b := new(bytes.Buffer) 910 _, err = io.Copy(b, f) 911 utils.AssertEqual(t, nil, err) 912 utils.AssertEqual(t, "hello world", b.String()) 913 return nil 914 }) 915 916 body := &bytes.Buffer{} 917 writer := multipart.NewWriter(body) 918 919 ioWriter, err := writer.CreateFormFile("file", "test") 920 utils.AssertEqual(t, nil, err) 921 922 _, err = ioWriter.Write([]byte("hello world")) 923 utils.AssertEqual(t, nil, err) 924 utils.AssertEqual(t, nil, writer.Close()) 925 926 req := httptest.NewRequest(MethodPost, "/test", body) 927 req.Header.Set(HeaderContentType, writer.FormDataContentType()) 928 req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) 929 930 resp, err := app.Test(req) 931 utils.AssertEqual(t, nil, err, "app.Test(req)") 932 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 933 } 934 935 // go test -run Test_Ctx_FormValue 936 func Test_Ctx_FormValue(t *testing.T) { 937 t.Parallel() 938 app := New() 939 940 app.Post("/test", func(c *Ctx) error { 941 utils.AssertEqual(t, "john", c.FormValue("name")) 942 return nil 943 }) 944 945 body := &bytes.Buffer{} 946 writer := multipart.NewWriter(body) 947 utils.AssertEqual(t, nil, writer.WriteField("name", "john")) 948 utils.AssertEqual(t, nil, writer.Close()) 949 950 req := httptest.NewRequest(MethodPost, "/test", body) 951 req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) 952 req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) 953 954 resp, err := app.Test(req) 955 utils.AssertEqual(t, nil, err, "app.Test(req)") 956 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 957 } 958 959 // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_StaleEtag -benchmem -count=4 960 func Benchmark_Ctx_Fresh_StaleEtag(b *testing.B) { 961 app := New() 962 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 963 defer app.ReleaseCtx(c) 964 965 for n := 0; n < b.N; n++ { 966 c.Request().Header.Set(HeaderIfNoneMatch, "a, b, c, d") 967 c.Request().Header.Set(HeaderCacheControl, "c") 968 c.Fresh() 969 970 c.Request().Header.Set(HeaderIfNoneMatch, "a, b, c, d") 971 c.Request().Header.Set(HeaderCacheControl, "e") 972 c.Fresh() 973 } 974 } 975 976 // go test -run Test_Ctx_Fresh 977 func Test_Ctx_Fresh(t *testing.T) { 978 t.Parallel() 979 app := New() 980 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 981 defer app.ReleaseCtx(c) 982 utils.AssertEqual(t, false, c.Fresh()) 983 984 c.Request().Header.Set(HeaderIfNoneMatch, "*") 985 c.Request().Header.Set(HeaderCacheControl, "no-cache") 986 utils.AssertEqual(t, false, c.Fresh()) 987 988 c.Request().Header.Set(HeaderIfNoneMatch, "*") 989 c.Request().Header.Set(HeaderCacheControl, ",no-cache,") 990 utils.AssertEqual(t, false, c.Fresh()) 991 992 c.Request().Header.Set(HeaderIfNoneMatch, "*") 993 c.Request().Header.Set(HeaderCacheControl, "aa,no-cache,") 994 utils.AssertEqual(t, false, c.Fresh()) 995 996 c.Request().Header.Set(HeaderIfNoneMatch, "*") 997 c.Request().Header.Set(HeaderCacheControl, ",no-cache,bb") 998 utils.AssertEqual(t, false, c.Fresh()) 999 1000 c.Request().Header.Set(HeaderIfNoneMatch, "675af34563dc-tr34") 1001 c.Request().Header.Set(HeaderCacheControl, "public") 1002 utils.AssertEqual(t, false, c.Fresh()) 1003 1004 c.Request().Header.Set(HeaderIfNoneMatch, "a, b") 1005 c.Response().Header.Set(HeaderETag, "c") 1006 utils.AssertEqual(t, false, c.Fresh()) 1007 1008 c.Response().Header.Set(HeaderETag, "a") 1009 utils.AssertEqual(t, true, c.Fresh()) 1010 1011 c.Request().Header.Set(HeaderIfModifiedSince, "xxWed, 21 Oct 2015 07:28:00 GMT") 1012 c.Response().Header.Set(HeaderLastModified, "xxWed, 21 Oct 2015 07:28:00 GMT") 1013 utils.AssertEqual(t, false, c.Fresh()) 1014 1015 c.Response().Header.Set(HeaderLastModified, "Wed, 21 Oct 2015 07:28:00 GMT") 1016 utils.AssertEqual(t, false, c.Fresh()) 1017 1018 c.Request().Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:28:00 GMT") 1019 utils.AssertEqual(t, false, c.Fresh()) 1020 } 1021 1022 // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_WithNoCache -benchmem -count=4 1023 func Benchmark_Ctx_Fresh_WithNoCache(b *testing.B) { 1024 app := New() 1025 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1026 defer app.ReleaseCtx(c) 1027 1028 c.Request().Header.Set(HeaderIfNoneMatch, "*") 1029 c.Request().Header.Set(HeaderCacheControl, "no-cache") 1030 for n := 0; n < b.N; n++ { 1031 c.Fresh() 1032 } 1033 } 1034 1035 // go test -run Test_Ctx_Get 1036 func Test_Ctx_Get(t *testing.T) { 1037 t.Parallel() 1038 app := New() 1039 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1040 defer app.ReleaseCtx(c) 1041 c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") 1042 c.Request().Header.Set(HeaderReferer, "Monster") 1043 utils.AssertEqual(t, "utf-8, iso-8859-1;q=0.5", c.Get(HeaderAcceptCharset)) 1044 utils.AssertEqual(t, "Monster", c.Get(HeaderReferer)) 1045 utils.AssertEqual(t, "default", c.Get("unknown", "default")) 1046 } 1047 1048 // go test -run Test_Ctx_IsProxyTrusted 1049 func Test_Ctx_IsProxyTrusted(t *testing.T) { 1050 t.Parallel() 1051 1052 { 1053 app := New() 1054 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1055 defer app.ReleaseCtx(c) 1056 utils.AssertEqual(t, true, c.IsProxyTrusted()) 1057 } 1058 { 1059 app := New(Config{ 1060 EnableTrustedProxyCheck: false, 1061 }) 1062 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1063 defer app.ReleaseCtx(c) 1064 utils.AssertEqual(t, true, c.IsProxyTrusted()) 1065 } 1066 1067 { 1068 app := New(Config{ 1069 EnableTrustedProxyCheck: true, 1070 }) 1071 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1072 defer app.ReleaseCtx(c) 1073 utils.AssertEqual(t, false, c.IsProxyTrusted()) 1074 } 1075 { 1076 app := New(Config{ 1077 EnableTrustedProxyCheck: true, 1078 1079 TrustedProxies: []string{}, 1080 }) 1081 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1082 defer app.ReleaseCtx(c) 1083 utils.AssertEqual(t, false, c.IsProxyTrusted()) 1084 } 1085 { 1086 app := New(Config{ 1087 EnableTrustedProxyCheck: true, 1088 1089 TrustedProxies: []string{ 1090 "127.0.0.1", 1091 }, 1092 }) 1093 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1094 defer app.ReleaseCtx(c) 1095 utils.AssertEqual(t, false, c.IsProxyTrusted()) 1096 } 1097 { 1098 app := New(Config{ 1099 EnableTrustedProxyCheck: true, 1100 1101 TrustedProxies: []string{ 1102 "127.0.0.1/8", 1103 }, 1104 }) 1105 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1106 defer app.ReleaseCtx(c) 1107 utils.AssertEqual(t, false, c.IsProxyTrusted()) 1108 } 1109 { 1110 app := New(Config{ 1111 EnableTrustedProxyCheck: true, 1112 1113 TrustedProxies: []string{ 1114 "0.0.0.0", 1115 }, 1116 }) 1117 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1118 defer app.ReleaseCtx(c) 1119 utils.AssertEqual(t, true, c.IsProxyTrusted()) 1120 } 1121 { 1122 app := New(Config{ 1123 EnableTrustedProxyCheck: true, 1124 1125 TrustedProxies: []string{ 1126 "0.0.0.1/31", 1127 }, 1128 }) 1129 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1130 defer app.ReleaseCtx(c) 1131 utils.AssertEqual(t, true, c.IsProxyTrusted()) 1132 } 1133 { 1134 app := New(Config{ 1135 EnableTrustedProxyCheck: true, 1136 1137 TrustedProxies: []string{ 1138 "0.0.0.1/31junk", 1139 }, 1140 }) 1141 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1142 defer app.ReleaseCtx(c) 1143 utils.AssertEqual(t, false, c.IsProxyTrusted()) 1144 } 1145 } 1146 1147 // go test -run Test_Ctx_Hostname 1148 func Test_Ctx_Hostname(t *testing.T) { 1149 t.Parallel() 1150 app := New() 1151 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1152 defer app.ReleaseCtx(c) 1153 c.Request().SetRequestURI("http://google.com/test") 1154 utils.AssertEqual(t, "google.com", c.Hostname()) 1155 } 1156 1157 // go test -run Test_Ctx_Hostname_Untrusted 1158 func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { 1159 t.Parallel() 1160 // Don't trust any proxy 1161 { 1162 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{}}) 1163 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1164 c.Request().SetRequestURI("http://google.com/test") 1165 c.Request().Header.Set(HeaderXForwardedHost, "google1.com") 1166 utils.AssertEqual(t, "google.com", c.Hostname()) 1167 app.ReleaseCtx(c) 1168 } 1169 // Trust to specific proxy list 1170 { 1171 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.0", "0.8.0.1"}}) 1172 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1173 c.Request().SetRequestURI("http://google.com/test") 1174 c.Request().Header.Set(HeaderXForwardedHost, "google1.com") 1175 utils.AssertEqual(t, "google.com", c.Hostname()) 1176 app.ReleaseCtx(c) 1177 } 1178 } 1179 1180 // go test -run Test_Ctx_Hostname_Trusted 1181 func Test_Ctx_Hostname_TrustedProxy(t *testing.T) { 1182 t.Parallel() 1183 { 1184 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) 1185 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1186 c.Request().SetRequestURI("http://google.com/test") 1187 c.Request().Header.Set(HeaderXForwardedHost, "google1.com") 1188 utils.AssertEqual(t, "google1.com", c.Hostname()) 1189 app.ReleaseCtx(c) 1190 } 1191 } 1192 1193 // go test -run Test_Ctx_Hostname_Trusted_Multiple 1194 func Test_Ctx_Hostname_TrustedProxy_Multiple(t *testing.T) { 1195 t.Parallel() 1196 { 1197 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) 1198 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1199 c.Request().SetRequestURI("http://google.com/test") 1200 c.Request().Header.Set(HeaderXForwardedHost, "google1.com, google2.com") 1201 utils.AssertEqual(t, "google1.com", c.Hostname()) 1202 app.ReleaseCtx(c) 1203 } 1204 } 1205 1206 // go test -run Test_Ctx_Hostname_UntrustedProxyRange 1207 func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) { 1208 t.Parallel() 1209 1210 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) 1211 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1212 c.Request().SetRequestURI("http://google.com/test") 1213 c.Request().Header.Set(HeaderXForwardedHost, "google1.com") 1214 utils.AssertEqual(t, "google1.com", c.Hostname()) 1215 app.ReleaseCtx(c) 1216 } 1217 1218 // go test -run Test_Ctx_Hostname_UntrustedProxyRange 1219 func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) { 1220 t.Parallel() 1221 1222 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}}) 1223 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1224 c.Request().SetRequestURI("http://google.com/test") 1225 c.Request().Header.Set(HeaderXForwardedHost, "google1.com") 1226 utils.AssertEqual(t, "google.com", c.Hostname()) 1227 app.ReleaseCtx(c) 1228 } 1229 1230 // go test -run Test_Ctx_Port 1231 func Test_Ctx_Port(t *testing.T) { 1232 t.Parallel() 1233 app := New() 1234 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1235 defer app.ReleaseCtx(c) 1236 utils.AssertEqual(t, "0", c.Port()) 1237 } 1238 1239 // go test -run Test_Ctx_PortInHandler 1240 func Test_Ctx_PortInHandler(t *testing.T) { 1241 t.Parallel() 1242 app := New() 1243 1244 app.Get("/port", func(c *Ctx) error { 1245 return c.SendString(c.Port()) 1246 }) 1247 1248 resp, err := app.Test(httptest.NewRequest(MethodGet, "/port", nil)) 1249 utils.AssertEqual(t, nil, err, "app.Test(req)") 1250 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1251 1252 body, err := io.ReadAll(resp.Body) 1253 utils.AssertEqual(t, nil, err) 1254 utils.AssertEqual(t, "0", string(body)) 1255 } 1256 1257 // go test -run Test_Ctx_IP 1258 func Test_Ctx_IP(t *testing.T) { 1259 t.Parallel() 1260 1261 app := New() 1262 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1263 defer app.ReleaseCtx(c) 1264 1265 // default behavior will return the remote IP from the stack 1266 utils.AssertEqual(t, "0.0.0.0", c.IP()) 1267 1268 // X-Forwarded-For is set, but it is ignored because proxyHeader is not set 1269 c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") 1270 utils.AssertEqual(t, "0.0.0.0", c.IP()) 1271 } 1272 1273 // go test -run Test_Ctx_IP_ProxyHeader 1274 func Test_Ctx_IP_ProxyHeader(t *testing.T) { 1275 t.Parallel() 1276 1277 // make sure that the same behavior exists for different proxy header names 1278 proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} 1279 1280 for _, proxyHeaderName := range proxyHeaderNames { 1281 app := New(Config{ProxyHeader: proxyHeaderName}) 1282 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1283 1284 c.Request().Header.Set(proxyHeaderName, "0.0.0.1") 1285 utils.AssertEqual(t, "0.0.0.1", c.IP()) 1286 1287 // without IP validation we return the full string 1288 c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2") 1289 utils.AssertEqual(t, "0.0.0.1, 0.0.0.2", c.IP()) 1290 1291 // without IP validation we return invalid IPs 1292 c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3") 1293 utils.AssertEqual(t, "invalid, 0.0.0.2, 0.0.0.3", c.IP()) 1294 1295 // when proxy header is enabled but the value is empty, without IP validation we return an empty string 1296 c.Request().Header.Set(proxyHeaderName, "") 1297 utils.AssertEqual(t, "", c.IP()) 1298 1299 // without IP validation we return an invalid IP 1300 c.Request().Header.Set(proxyHeaderName, "not-valid-ip") 1301 utils.AssertEqual(t, "not-valid-ip", c.IP()) 1302 1303 app.ReleaseCtx(c) 1304 } 1305 } 1306 1307 // go test -run Test_Ctx_IP_ProxyHeader 1308 func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) { 1309 t.Parallel() 1310 1311 // make sure that the same behavior exists for different proxy header names 1312 proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} 1313 1314 for _, proxyHeaderName := range proxyHeaderNames { 1315 app := New(Config{EnableIPValidation: true, ProxyHeader: proxyHeaderName}) 1316 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1317 1318 // when proxy header & validation is enabled and the value is a valid IP, we return it 1319 c.Request().Header.Set(proxyHeaderName, "0.0.0.1") 1320 utils.AssertEqual(t, "0.0.0.1", c.IP()) 1321 1322 // when proxy header & validation is enabled and the value is a list of IPs, we return the first valid IP 1323 c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2") 1324 utils.AssertEqual(t, "0.0.0.1", c.IP()) 1325 1326 c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3") 1327 utils.AssertEqual(t, "0.0.0.2", c.IP()) 1328 1329 // when proxy header & validation is enabled but the value is empty, we will ignore the header 1330 c.Request().Header.Set(proxyHeaderName, "") 1331 utils.AssertEqual(t, "0.0.0.0", c.IP()) 1332 1333 // when proxy header & validation is enabled but the value is not an IP, we will ignore the header 1334 // and return the IP of the caller 1335 c.Request().Header.Set(proxyHeaderName, "not-valid-ip") 1336 utils.AssertEqual(t, "0.0.0.0", c.IP()) 1337 1338 app.ReleaseCtx(c) 1339 } 1340 } 1341 1342 // go test -run Test_Ctx_IP_UntrustedProxy 1343 func Test_Ctx_IP_UntrustedProxy(t *testing.T) { 1344 t.Parallel() 1345 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}, ProxyHeader: HeaderXForwardedFor}) 1346 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1347 c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") 1348 defer app.ReleaseCtx(c) 1349 utils.AssertEqual(t, "0.0.0.0", c.IP()) 1350 } 1351 1352 // go test -run Test_Ctx_IP_TrustedProxy 1353 func Test_Ctx_IP_TrustedProxy(t *testing.T) { 1354 t.Parallel() 1355 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}, ProxyHeader: HeaderXForwardedFor}) 1356 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1357 c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") 1358 defer app.ReleaseCtx(c) 1359 utils.AssertEqual(t, "0.0.0.1", c.IP()) 1360 } 1361 1362 // go test -run Test_Ctx_IPs -parallel 1363 func Test_Ctx_IPs(t *testing.T) { 1364 t.Parallel() 1365 app := New() 1366 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1367 defer app.ReleaseCtx(c) 1368 1369 // normal happy path test case 1370 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") 1371 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) 1372 1373 // inconsistent space formatting 1374 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2 ,127.0.0.3") 1375 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) 1376 1377 // invalid IPs are allowed to be returned 1378 c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2") 1379 utils.AssertEqual(t, []string{"invalid", "127.0.0.1", "127.0.0.2"}, c.IPs()) 1380 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2") 1381 utils.AssertEqual(t, []string{"127.0.0.1", "invalid", "127.0.0.2"}, c.IPs()) 1382 1383 // ensure that the ordering of IPs in the header is maintained 1384 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") 1385 utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) 1386 1387 // ensure for IPv6 1388 c.Request().Header.Set(HeaderXForwardedFor, "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d, invalid, 2345:0425:2CA1::0567:5673:23b5") 1389 utils.AssertEqual(t, []string{"9396:9549:b4f7:8ed0:4791:1330:8c06:e62d", "invalid", "2345:0425:2CA1::0567:5673:23b5"}, c.IPs()) 1390 1391 // empty header 1392 c.Request().Header.Set(HeaderXForwardedFor, "") 1393 utils.AssertEqual(t, 0, len(c.IPs())) 1394 1395 // missing header 1396 c.Request() 1397 utils.AssertEqual(t, 0, len(c.IPs())) 1398 } 1399 1400 func Test_Ctx_IPs_With_IP_Validation(t *testing.T) { 1401 t.Parallel() 1402 app := New(Config{EnableIPValidation: true}) 1403 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1404 defer app.ReleaseCtx(c) 1405 1406 // normal happy path test case 1407 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") 1408 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) 1409 1410 // inconsistent space formatting 1411 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2 ,127.0.0.3") 1412 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) 1413 1414 // invalid IPs are in the header 1415 c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2") 1416 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs()) 1417 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2") 1418 utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs()) 1419 1420 // ensure that the ordering of IPs in the header is maintained 1421 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") 1422 utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) 1423 1424 // ensure for IPv6 1425 c.Request().Header.Set(HeaderXForwardedFor, "f037:825e:eadb:1b7b:1667:6f0a:5356:f604, invalid, 9396:9549:b4f7:8ed0:4791:1330:8c06:e62d") 1426 utils.AssertEqual(t, []string{"f037:825e:eadb:1b7b:1667:6f0a:5356:f604", "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d"}, c.IPs()) 1427 1428 // empty header 1429 c.Request().Header.Set(HeaderXForwardedFor, "") 1430 utils.AssertEqual(t, 0, len(c.IPs())) 1431 1432 // missing header 1433 c.Request() 1434 utils.AssertEqual(t, 0, len(c.IPs())) 1435 } 1436 1437 // go test -v -run=^$ -bench=Benchmark_Ctx_IPs -benchmem -count=4 1438 func Benchmark_Ctx_IPs(b *testing.B) { 1439 app := New() 1440 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1441 defer app.ReleaseCtx(c) 1442 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") 1443 var res []string 1444 b.ReportAllocs() 1445 b.ResetTimer() 1446 for n := 0; n < b.N; n++ { 1447 res = c.IPs() 1448 } 1449 utils.AssertEqual(b, []string{"127.0.0.1", "invalid", "127.0.0.1"}, res) 1450 } 1451 1452 func Benchmark_Ctx_IPs_v6(b *testing.B) { 1453 app := New() 1454 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1455 defer app.ReleaseCtx(c) 1456 c.Request().Header.Set(HeaderXForwardedFor, "f037:825e:eadb:1b7b:1667:6f0a:5356:f604, invalid, 2345:0425:2CA1::0567:5673:23b5") 1457 var res []string 1458 b.ReportAllocs() 1459 b.ResetTimer() 1460 for n := 0; n < b.N; n++ { 1461 res = c.IPs() 1462 } 1463 utils.AssertEqual(b, []string{"f037:825e:eadb:1b7b:1667:6f0a:5356:f604", "invalid", "2345:0425:2CA1::0567:5673:23b5"}, res) 1464 } 1465 1466 func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { 1467 app := New(Config{EnableIPValidation: true}) 1468 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1469 defer app.ReleaseCtx(c) 1470 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") 1471 var res []string 1472 b.ReportAllocs() 1473 b.ResetTimer() 1474 for n := 0; n < b.N; n++ { 1475 res = c.IPs() 1476 } 1477 utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1"}, res) 1478 } 1479 1480 func Benchmark_Ctx_IPs_v6_With_IP_Validation(b *testing.B) { 1481 app := New(Config{EnableIPValidation: true}) 1482 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1483 defer app.ReleaseCtx(c) 1484 c.Request().Header.Set(HeaderXForwardedFor, "2345:0425:2CA1:0000:0000:0567:5673:23b5, invalid, 2345:0425:2CA1::0567:5673:23b5") 1485 var res []string 1486 b.ReportAllocs() 1487 b.ResetTimer() 1488 for n := 0; n < b.N; n++ { 1489 res = c.IPs() 1490 } 1491 utils.AssertEqual(b, []string{"2345:0425:2CA1:0000:0000:0567:5673:23b5", "2345:0425:2CA1::0567:5673:23b5"}, res) 1492 } 1493 1494 func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { 1495 app := New(Config{ProxyHeader: HeaderXForwardedFor}) 1496 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1497 defer app.ReleaseCtx(c) 1498 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") 1499 var res string 1500 b.ReportAllocs() 1501 b.ResetTimer() 1502 for n := 0; n < b.N; n++ { 1503 res = c.IP() 1504 } 1505 utils.AssertEqual(b, "127.0.0.1", res) 1506 } 1507 1508 func Benchmark_Ctx_IP_With_ProxyHeader_and_IP_Validation(b *testing.B) { 1509 app := New(Config{ProxyHeader: HeaderXForwardedFor, EnableIPValidation: true}) 1510 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1511 defer app.ReleaseCtx(c) 1512 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") 1513 var res string 1514 b.ReportAllocs() 1515 b.ResetTimer() 1516 for n := 0; n < b.N; n++ { 1517 res = c.IP() 1518 } 1519 utils.AssertEqual(b, "127.0.0.1", res) 1520 } 1521 1522 func Benchmark_Ctx_IP(b *testing.B) { 1523 app := New() 1524 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1525 defer app.ReleaseCtx(c) 1526 c.Request() 1527 var res string 1528 b.ReportAllocs() 1529 b.ResetTimer() 1530 for n := 0; n < b.N; n++ { 1531 res = c.IP() 1532 } 1533 utils.AssertEqual(b, "0.0.0.0", res) 1534 } 1535 1536 // go test -run Test_Ctx_Is 1537 func Test_Ctx_Is(t *testing.T) { 1538 t.Parallel() 1539 app := New() 1540 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1541 defer app.ReleaseCtx(c) 1542 c.Request().Header.Set(HeaderContentType, MIMETextHTML+"; boundary=something") 1543 utils.AssertEqual(t, true, c.Is(".html")) 1544 utils.AssertEqual(t, true, c.Is("html")) 1545 utils.AssertEqual(t, false, c.Is("json")) 1546 utils.AssertEqual(t, false, c.Is(".json")) 1547 utils.AssertEqual(t, false, c.Is("")) 1548 utils.AssertEqual(t, false, c.Is(".foooo")) 1549 1550 c.Request().Header.Set(HeaderContentType, MIMEApplicationJSONCharsetUTF8) 1551 utils.AssertEqual(t, false, c.Is("html")) 1552 utils.AssertEqual(t, true, c.Is("json")) 1553 utils.AssertEqual(t, true, c.Is(".json")) 1554 1555 c.Request().Header.Set(HeaderContentType, " application/json;charset=UTF-8") 1556 utils.AssertEqual(t, false, c.Is("html")) 1557 utils.AssertEqual(t, true, c.Is("json")) 1558 utils.AssertEqual(t, true, c.Is(".json")) 1559 1560 c.Request().Header.Set(HeaderContentType, MIMEApplicationXMLCharsetUTF8) 1561 utils.AssertEqual(t, false, c.Is("html")) 1562 utils.AssertEqual(t, true, c.Is("xml")) 1563 utils.AssertEqual(t, true, c.Is(".xml")) 1564 1565 c.Request().Header.Set(HeaderContentType, MIMETextPlain) 1566 utils.AssertEqual(t, false, c.Is("html")) 1567 utils.AssertEqual(t, true, c.Is("txt")) 1568 utils.AssertEqual(t, true, c.Is(".txt")) 1569 } 1570 1571 // go test -v -run=^$ -bench=Benchmark_Ctx_Is -benchmem -count=4 1572 func Benchmark_Ctx_Is(b *testing.B) { 1573 app := New() 1574 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1575 defer app.ReleaseCtx(c) 1576 c.Request().Header.Set(HeaderContentType, MIMEApplicationJSON) 1577 var res bool 1578 b.ReportAllocs() 1579 b.ResetTimer() 1580 for n := 0; n < b.N; n++ { 1581 _ = c.Is(".json") 1582 res = c.Is("json") 1583 } 1584 utils.AssertEqual(b, true, res) 1585 } 1586 1587 // go test -run Test_Ctx_Locals 1588 func Test_Ctx_Locals(t *testing.T) { 1589 t.Parallel() 1590 app := New() 1591 app.Use(func(c *Ctx) error { 1592 c.Locals("john", "doe") 1593 return c.Next() 1594 }) 1595 app.Get("/test", func(c *Ctx) error { 1596 utils.AssertEqual(t, "doe", c.Locals("john")) 1597 return nil 1598 }) 1599 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) 1600 utils.AssertEqual(t, nil, err, "app.Test(req)") 1601 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1602 } 1603 1604 // go test -run Test_Ctx_Method 1605 func Test_Ctx_Method(t *testing.T) { 1606 t.Parallel() 1607 fctx := &fasthttp.RequestCtx{} 1608 fctx.Request.Header.SetMethod(MethodGet) 1609 app := New() 1610 c := app.AcquireCtx(fctx) 1611 defer app.ReleaseCtx(c) 1612 utils.AssertEqual(t, MethodGet, c.Method()) 1613 c.Method(MethodPost) 1614 utils.AssertEqual(t, MethodPost, c.Method()) 1615 1616 c.Method("MethodInvalid") 1617 utils.AssertEqual(t, MethodPost, c.Method()) 1618 } 1619 1620 // go test -run Test_Ctx_ClientHelloInfo 1621 func Test_Ctx_ClientHelloInfo(t *testing.T) { 1622 t.Parallel() 1623 app := New() 1624 app.Get("/ServerName", func(c *Ctx) error { 1625 result := c.ClientHelloInfo() 1626 if result != nil { 1627 return c.SendString(result.ServerName) 1628 } 1629 1630 return c.SendString("ClientHelloInfo is nil") 1631 }) 1632 app.Get("/SignatureSchemes", func(c *Ctx) error { 1633 result := c.ClientHelloInfo() 1634 if result != nil { 1635 return c.JSON(result.SignatureSchemes) 1636 } 1637 1638 return c.SendString("ClientHelloInfo is nil") 1639 }) 1640 app.Get("/SupportedVersions", func(c *Ctx) error { 1641 result := c.ClientHelloInfo() 1642 if result != nil { 1643 return c.JSON(result.SupportedVersions) 1644 } 1645 1646 return c.SendString("ClientHelloInfo is nil") 1647 }) 1648 1649 // Test without TLS handler 1650 resp, err := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) 1651 utils.AssertEqual(t, nil, err) 1652 body, err := io.ReadAll(resp.Body) 1653 utils.AssertEqual(t, nil, err) 1654 utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) 1655 1656 // Test with TLS Handler 1657 const ( 1658 pssWithSHA256 = 0x0804 1659 versionTLS13 = 0x0304 1660 ) 1661 app.tlsHandler = &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ 1662 ServerName: "example.golang", 1663 SignatureSchemes: []tls.SignatureScheme{pssWithSHA256}, 1664 SupportedVersions: []uint16{versionTLS13}, 1665 }} 1666 1667 // Test ServerName 1668 resp, err = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) 1669 utils.AssertEqual(t, nil, err) 1670 body, err = io.ReadAll(resp.Body) 1671 utils.AssertEqual(t, nil, err) 1672 utils.AssertEqual(t, []byte("example.golang"), body) 1673 1674 // Test SignatureSchemes 1675 resp, err = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) 1676 utils.AssertEqual(t, nil, err) 1677 body, err = io.ReadAll(resp.Body) 1678 utils.AssertEqual(t, nil, err) 1679 utils.AssertEqual(t, "["+strconv.Itoa(pssWithSHA256)+"]", string(body)) 1680 1681 // Test SupportedVersions 1682 resp, err = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) 1683 utils.AssertEqual(t, nil, err) 1684 body, err = io.ReadAll(resp.Body) 1685 utils.AssertEqual(t, nil, err) 1686 utils.AssertEqual(t, "["+strconv.Itoa(versionTLS13)+"]", string(body)) 1687 } 1688 1689 // go test -run Test_Ctx_InvalidMethod 1690 func Test_Ctx_InvalidMethod(t *testing.T) { 1691 t.Parallel() 1692 app := New() 1693 app.Get("/", func(c *Ctx) error { 1694 return nil 1695 }) 1696 1697 fctx := &fasthttp.RequestCtx{} 1698 fctx.Request.Header.SetMethod("InvalidMethod") 1699 fctx.Request.SetRequestURI("/") 1700 1701 app.Handler()(fctx) 1702 1703 utils.AssertEqual(t, 400, fctx.Response.StatusCode()) 1704 utils.AssertEqual(t, []byte("Invalid http method"), fctx.Response.Body()) 1705 } 1706 1707 // go test -run Test_Ctx_MultipartForm 1708 func Test_Ctx_MultipartForm(t *testing.T) { 1709 t.Parallel() 1710 app := New() 1711 1712 app.Post("/test", func(c *Ctx) error { 1713 result, err := c.MultipartForm() 1714 utils.AssertEqual(t, nil, err) 1715 utils.AssertEqual(t, "john", result.Value["name"][0]) 1716 return nil 1717 }) 1718 1719 body := &bytes.Buffer{} 1720 writer := multipart.NewWriter(body) 1721 utils.AssertEqual(t, nil, writer.WriteField("name", "john")) 1722 utils.AssertEqual(t, nil, writer.Close()) 1723 1724 req := httptest.NewRequest(MethodPost, "/test", body) 1725 req.Header.Set(HeaderContentType, fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) 1726 req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) 1727 1728 resp, err := app.Test(req) 1729 utils.AssertEqual(t, nil, err, "app.Test(req)") 1730 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1731 } 1732 1733 // go test -v -run=^$ -bench=Benchmark_Ctx_MultipartForm -benchmem -count=4 1734 func Benchmark_Ctx_MultipartForm(b *testing.B) { 1735 app := New() 1736 1737 app.Post("/", func(c *Ctx) error { 1738 _, err := c.MultipartForm() 1739 return err 1740 }) 1741 1742 c := &fasthttp.RequestCtx{} 1743 1744 body := []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--") 1745 c.Request.SetBody(body) 1746 c.Request.Header.SetContentType(MIMEMultipartForm + `;boundary="b"`) 1747 c.Request.Header.SetContentLength(len(body)) 1748 1749 h := app.Handler() 1750 1751 b.ReportAllocs() 1752 b.ResetTimer() 1753 1754 for n := 0; n < b.N; n++ { 1755 h(c) 1756 } 1757 } 1758 1759 // go test -run Test_Ctx_OriginalURL 1760 func Test_Ctx_OriginalURL(t *testing.T) { 1761 t.Parallel() 1762 app := New() 1763 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1764 defer app.ReleaseCtx(c) 1765 c.Request().Header.SetRequestURI("http://google.com/test?search=demo") 1766 utils.AssertEqual(t, "http://google.com/test?search=demo", c.OriginalURL()) 1767 } 1768 1769 // go test -race -run Test_Ctx_Params 1770 func Test_Ctx_Params(t *testing.T) { 1771 t.Parallel() 1772 app := New() 1773 app.Get("/test/:user", func(c *Ctx) error { 1774 utils.AssertEqual(t, "john", c.Params("user")) 1775 return nil 1776 }) 1777 app.Get("/test2/*", func(c *Ctx) error { 1778 utils.AssertEqual(t, "im/a/cookie", c.Params("*")) 1779 return nil 1780 }) 1781 app.Get("/test3/*/blafasel/*", func(c *Ctx) error { 1782 utils.AssertEqual(t, "1111", c.Params("*1")) 1783 utils.AssertEqual(t, "2222", c.Params("*2")) 1784 utils.AssertEqual(t, "1111", c.Params("*")) 1785 return nil 1786 }) 1787 app.Get("/test4/:optional?", func(c *Ctx) error { 1788 utils.AssertEqual(t, "", c.Params("optional")) 1789 return nil 1790 }) 1791 app.Get("/test5/:id/:Id", func(c *Ctx) error { 1792 utils.AssertEqual(t, "first", c.Params("id")) 1793 utils.AssertEqual(t, "first", c.Params("Id")) 1794 return nil 1795 }) 1796 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) 1797 utils.AssertEqual(t, nil, err, "app.Test(req)") 1798 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1799 1800 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/im/a/cookie", nil)) 1801 utils.AssertEqual(t, nil, err, "app.Test(req)") 1802 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1803 1804 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test3/1111/blafasel/2222", nil)) 1805 utils.AssertEqual(t, nil, err, "app.Test(req)") 1806 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1807 1808 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil)) 1809 utils.AssertEqual(t, nil, err, "app.Test(req)") 1810 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1811 1812 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test5/first/second", nil)) 1813 utils.AssertEqual(t, nil, err) 1814 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1815 } 1816 1817 func Test_Ctx_Params_Case_Sensitive(t *testing.T) { 1818 t.Parallel() 1819 app := New(Config{CaseSensitive: true}) 1820 app.Get("/test/:User", func(c *Ctx) error { 1821 utils.AssertEqual(t, "john", c.Params("User")) 1822 utils.AssertEqual(t, "", c.Params("user")) 1823 return nil 1824 }) 1825 app.Get("/test2/:id/:Id", func(c *Ctx) error { 1826 utils.AssertEqual(t, "first", c.Params("id")) 1827 utils.AssertEqual(t, "second", c.Params("Id")) 1828 return nil 1829 }) 1830 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) 1831 utils.AssertEqual(t, nil, err, "app.Test(req)") 1832 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1833 1834 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/first/second", nil)) 1835 utils.AssertEqual(t, nil, err) 1836 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1837 } 1838 1839 // go test -race -run Test_Ctx_AllParams 1840 func Test_Ctx_AllParams(t *testing.T) { 1841 t.Parallel() 1842 app := New() 1843 app.Get("/test/:user", func(c *Ctx) error { 1844 utils.AssertEqual(t, map[string]string{"user": "john"}, c.AllParams()) 1845 return nil 1846 }) 1847 app.Get("/test2/*", func(c *Ctx) error { 1848 utils.AssertEqual(t, map[string]string{"*1": "im/a/cookie"}, c.AllParams()) 1849 return nil 1850 }) 1851 app.Get("/test3/*/blafasel/*", func(c *Ctx) error { 1852 utils.AssertEqual(t, map[string]string{"*1": "1111", "*2": "2222"}, c.AllParams()) 1853 return nil 1854 }) 1855 app.Get("/test4/:optional?", func(c *Ctx) error { 1856 utils.AssertEqual(t, map[string]string{"optional": ""}, c.AllParams()) 1857 return nil 1858 }) 1859 1860 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) 1861 utils.AssertEqual(t, nil, err, "app.Test(req)") 1862 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1863 1864 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/im/a/cookie", nil)) 1865 utils.AssertEqual(t, nil, err, "app.Test(req)") 1866 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1867 1868 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test3/1111/blafasel/2222", nil)) 1869 utils.AssertEqual(t, nil, err, "app.Test(req)") 1870 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1871 1872 resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil)) 1873 utils.AssertEqual(t, nil, err, "app.Test(req)") 1874 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1875 } 1876 1877 // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4 1878 func Benchmark_Ctx_Params(b *testing.B) { 1879 app := New() 1880 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1881 defer app.ReleaseCtx(c) 1882 c.route = &Route{ 1883 Params: []string{ 1884 "param1", "param2", "param3", "param4", 1885 }, 1886 } 1887 c.values = [maxParams]string{ 1888 "john", "doe", "is", "awesome", 1889 } 1890 var res string 1891 b.ReportAllocs() 1892 b.ResetTimer() 1893 for n := 0; n < b.N; n++ { 1894 _ = c.Params("param1") 1895 _ = c.Params("param2") 1896 _ = c.Params("param3") 1897 res = c.Params("param4") 1898 } 1899 utils.AssertEqual(b, "awesome", res) 1900 } 1901 1902 // go test -v -run=^$ -bench=Benchmark_Ctx_AllParams -benchmem -count=4 1903 func Benchmark_Ctx_AllParams(b *testing.B) { 1904 app := New() 1905 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1906 defer app.ReleaseCtx(c) 1907 c.route = &Route{ 1908 Params: []string{ 1909 "param1", "param2", "param3", "param4", 1910 }, 1911 } 1912 c.values = [maxParams]string{ 1913 "john", "doe", "is", "awesome", 1914 } 1915 var res map[string]string 1916 b.ReportAllocs() 1917 b.ResetTimer() 1918 for n := 0; n < b.N; n++ { 1919 res = c.AllParams() 1920 } 1921 utils.AssertEqual( 1922 b, 1923 map[string]string{ 1924 "param1": "john", 1925 "param2": "doe", 1926 "param3": "is", 1927 "param4": "awesome", 1928 }, 1929 res, 1930 ) 1931 } 1932 1933 // go test -v -run=^$ -bench=Benchmark_Ctx_ParamsParse -benchmem -count=4 1934 func Benchmark_Ctx_ParamsParse(b *testing.B) { 1935 app := New() 1936 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 1937 defer app.ReleaseCtx(c) 1938 c.route = &Route{ 1939 Params: []string{ 1940 "param1", "param2", "param3", "param4", 1941 }, 1942 } 1943 c.values = [maxParams]string{ 1944 "john", "doe", "is", "awesome", 1945 } 1946 var res struct { 1947 Param1 string `params:"param1"` 1948 Param2 string `params:"param2"` 1949 Param3 string `params:"param3"` 1950 Param4 string `params:"param4"` 1951 } 1952 b.ReportAllocs() 1953 b.ResetTimer() 1954 1955 var err error 1956 for n := 0; n < b.N; n++ { 1957 err = c.ParamsParser(&res) 1958 } 1959 1960 utils.AssertEqual(b, nil, err) 1961 utils.AssertEqual(b, "john", res.Param1) 1962 utils.AssertEqual(b, "doe", res.Param2) 1963 utils.AssertEqual(b, "is", res.Param3) 1964 utils.AssertEqual(b, "awesome", res.Param4) 1965 } 1966 1967 // go test -run Test_Ctx_Path 1968 func Test_Ctx_Path(t *testing.T) { 1969 t.Parallel() 1970 app := New(Config{UnescapePath: true}) 1971 app.Get("/test/:user", func(c *Ctx) error { 1972 utils.AssertEqual(t, "/Test/John", c.Path()) 1973 // not strict && case insensitive 1974 utils.AssertEqual(t, "/ABC/", c.Path("/ABC/")) 1975 utils.AssertEqual(t, "/test/john/", c.Path("/test/john/")) 1976 return nil 1977 }) 1978 1979 // test with special chars 1980 app.Get("/specialChars/:name", func(c *Ctx) error { 1981 utils.AssertEqual(t, "/specialChars/créer", c.Path()) 1982 // unescape is also working if you set the path afterwards 1983 utils.AssertEqual(t, "/اختبار/", c.Path("/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/")) 1984 return nil 1985 }) 1986 resp, err := app.Test(httptest.NewRequest(MethodGet, "/specialChars/cr%C3%A9er", nil)) 1987 utils.AssertEqual(t, nil, err, "app.Test(req)") 1988 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 1989 } 1990 1991 // go test -run Test_Ctx_Protocol 1992 func Test_Ctx_Protocol(t *testing.T) { 1993 t.Parallel() 1994 app := New() 1995 1996 freq := &fasthttp.RequestCtx{} 1997 freq.Request.Header.Set("X-Forwarded", "invalid") 1998 1999 c := app.AcquireCtx(freq) 2000 defer app.ReleaseCtx(c) 2001 c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) 2002 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2003 c.Request().Header.Reset() 2004 2005 c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) 2006 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2007 c.Request().Header.Reset() 2008 2009 c.Request().Header.Set(HeaderXForwardedProto, "https, http") 2010 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2011 c.Request().Header.Reset() 2012 2013 c.Request().Header.Set(HeaderXForwardedProtocol, "https, http") 2014 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2015 c.Request().Header.Reset() 2016 2017 c.Request().Header.Set(HeaderXForwardedSsl, "on") 2018 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2019 c.Request().Header.Reset() 2020 2021 c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) 2022 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2023 c.Request().Header.Reset() 2024 2025 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2026 } 2027 2028 // go test -v -run=^$ -bench=Benchmark_Ctx_Protocol -benchmem -count=4 2029 func Benchmark_Ctx_Protocol(b *testing.B) { 2030 app := New() 2031 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2032 defer app.ReleaseCtx(c) 2033 var res string 2034 b.ReportAllocs() 2035 b.ResetTimer() 2036 for n := 0; n < b.N; n++ { 2037 res = c.Protocol() 2038 } 2039 utils.AssertEqual(b, schemeHTTP, res) 2040 } 2041 2042 // go test -run Test_Ctx_Protocol_TrustedProxy 2043 func Test_Ctx_Protocol_TrustedProxy(t *testing.T) { 2044 t.Parallel() 2045 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}}) 2046 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2047 defer app.ReleaseCtx(c) 2048 2049 c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) 2050 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2051 c.Request().Header.Reset() 2052 2053 c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) 2054 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2055 c.Request().Header.Reset() 2056 2057 c.Request().Header.Set(HeaderXForwardedSsl, "on") 2058 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2059 c.Request().Header.Reset() 2060 2061 c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) 2062 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2063 c.Request().Header.Reset() 2064 2065 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2066 } 2067 2068 // go test -run Test_Ctx_Protocol_TrustedProxyRange 2069 func Test_Ctx_Protocol_TrustedProxyRange(t *testing.T) { 2070 t.Parallel() 2071 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) 2072 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2073 defer app.ReleaseCtx(c) 2074 2075 c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) 2076 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2077 c.Request().Header.Reset() 2078 2079 c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) 2080 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2081 c.Request().Header.Reset() 2082 2083 c.Request().Header.Set(HeaderXForwardedSsl, "on") 2084 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2085 c.Request().Header.Reset() 2086 2087 c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) 2088 utils.AssertEqual(t, schemeHTTPS, c.Protocol()) 2089 c.Request().Header.Reset() 2090 2091 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2092 } 2093 2094 // go test -run Test_Ctx_Protocol_UntrustedProxyRange 2095 func Test_Ctx_Protocol_UntrustedProxyRange(t *testing.T) { 2096 t.Parallel() 2097 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.1.1.1/30"}}) 2098 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2099 defer app.ReleaseCtx(c) 2100 2101 c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) 2102 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2103 c.Request().Header.Reset() 2104 2105 c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) 2106 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2107 c.Request().Header.Reset() 2108 2109 c.Request().Header.Set(HeaderXForwardedSsl, "on") 2110 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2111 c.Request().Header.Reset() 2112 2113 c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) 2114 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2115 c.Request().Header.Reset() 2116 2117 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2118 } 2119 2120 // go test -run Test_Ctx_Protocol_UnTrustedProxy 2121 func Test_Ctx_Protocol_UnTrustedProxy(t *testing.T) { 2122 t.Parallel() 2123 app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}}) 2124 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2125 defer app.ReleaseCtx(c) 2126 2127 c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) 2128 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2129 c.Request().Header.Reset() 2130 2131 c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) 2132 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2133 c.Request().Header.Reset() 2134 2135 c.Request().Header.Set(HeaderXForwardedSsl, "on") 2136 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2137 c.Request().Header.Reset() 2138 2139 c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) 2140 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2141 c.Request().Header.Reset() 2142 2143 utils.AssertEqual(t, schemeHTTP, c.Protocol()) 2144 } 2145 2146 // go test -run Test_Ctx_Query 2147 func Test_Ctx_Query(t *testing.T) { 2148 t.Parallel() 2149 app := New() 2150 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2151 defer app.ReleaseCtx(c) 2152 c.Request().URI().SetQueryString("search=john&age=20&id=") 2153 utils.AssertEqual(t, "john", c.Query("search")) 2154 utils.AssertEqual(t, "20", c.Query("age")) 2155 utils.AssertEqual(t, "default", c.Query("unknown", "default")) 2156 } 2157 2158 func Test_Ctx_QueryInt(t *testing.T) { 2159 t.Parallel() 2160 app := New() 2161 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2162 defer app.ReleaseCtx(c) 2163 c.Request().URI().SetQueryString("search=john&age=20&id=") 2164 2165 utils.AssertEqual(t, 0, c.QueryInt("foo")) 2166 utils.AssertEqual(t, 20, c.QueryInt("age", 12)) 2167 utils.AssertEqual(t, 0, c.QueryInt("search")) 2168 utils.AssertEqual(t, 1, c.QueryInt("search", 1)) 2169 utils.AssertEqual(t, 0, c.QueryInt("id")) 2170 utils.AssertEqual(t, 2, c.QueryInt("id", 2)) 2171 } 2172 2173 func Test_Ctx_QueryBool(t *testing.T) { 2174 t.Parallel() 2175 app := New() 2176 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2177 defer app.ReleaseCtx(c) 2178 c.Request().URI().SetQueryString("name=alex&want_pizza=false&id=") 2179 2180 utils.AssertEqual(t, false, c.QueryBool("want_pizza")) 2181 utils.AssertEqual(t, false, c.QueryBool("want_pizza", true)) 2182 utils.AssertEqual(t, false, c.QueryBool("name")) 2183 utils.AssertEqual(t, true, c.QueryBool("name", true)) 2184 utils.AssertEqual(t, false, c.QueryBool("id")) 2185 utils.AssertEqual(t, true, c.QueryBool("id", true)) 2186 } 2187 2188 func Test_Ctx_QueryFloat(t *testing.T) { 2189 t.Parallel() 2190 app := New() 2191 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2192 defer app.ReleaseCtx(c) 2193 c.Request().URI().SetQueryString("name=alex&amount=32.23&id=") 2194 2195 utils.AssertEqual(t, 32.23, c.QueryFloat("amount")) 2196 utils.AssertEqual(t, 32.23, c.QueryFloat("amount", 3.123)) 2197 utils.AssertEqual(t, 87.123, c.QueryFloat("name", 87.123)) 2198 utils.AssertEqual(t, float64(0), c.QueryFloat("name")) 2199 utils.AssertEqual(t, 12.87, c.QueryFloat("id", 12.87)) 2200 utils.AssertEqual(t, float64(0), c.QueryFloat("id")) 2201 } 2202 2203 // go test -run Test_Ctx_Range 2204 func Test_Ctx_Range(t *testing.T) { 2205 t.Parallel() 2206 app := New() 2207 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2208 defer app.ReleaseCtx(c) 2209 2210 var ( 2211 result Range 2212 err error 2213 ) 2214 2215 _, err = c.Range(1000) 2216 utils.AssertEqual(t, true, err != nil) 2217 2218 c.Request().Header.Set(HeaderRange, "bytes=500") 2219 _, err = c.Range(1000) 2220 utils.AssertEqual(t, true, err != nil) 2221 2222 c.Request().Header.Set(HeaderRange, "bytes=500=") 2223 _, err = c.Range(1000) 2224 utils.AssertEqual(t, true, err != nil) 2225 2226 c.Request().Header.Set(HeaderRange, "bytes=500-300") 2227 _, err = c.Range(1000) 2228 utils.AssertEqual(t, true, err != nil) 2229 2230 testRange := func(header string, start, end int) { 2231 c.Request().Header.Set(HeaderRange, header) 2232 result, err = c.Range(1000) 2233 utils.AssertEqual(t, nil, err) 2234 utils.AssertEqual(t, "bytes", result.Type) 2235 utils.AssertEqual(t, start, result.Ranges[0].Start) 2236 utils.AssertEqual(t, end, result.Ranges[0].End) 2237 } 2238 2239 testRange("bytes=a-700", 300, 999) 2240 testRange("bytes=500-b", 500, 999) 2241 testRange("bytes=500-1000", 500, 999) 2242 testRange("bytes=500-700", 500, 700) 2243 } 2244 2245 // go test -run Test_Ctx_Route 2246 func Test_Ctx_Route(t *testing.T) { 2247 t.Parallel() 2248 app := New() 2249 app.Get("/test", func(c *Ctx) error { 2250 utils.AssertEqual(t, "/test", c.Route().Path) 2251 return nil 2252 }) 2253 resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) 2254 utils.AssertEqual(t, nil, err, "app.Test(req)") 2255 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 2256 2257 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2258 defer app.ReleaseCtx(c) 2259 2260 utils.AssertEqual(t, "/", c.Route().Path) 2261 utils.AssertEqual(t, MethodGet, c.Route().Method) 2262 utils.AssertEqual(t, 0, len(c.Route().Handlers)) 2263 } 2264 2265 // go test -run Test_Ctx_RouteNormalized 2266 func Test_Ctx_RouteNormalized(t *testing.T) { 2267 t.Parallel() 2268 app := New() 2269 app.Get("/test", func(c *Ctx) error { 2270 utils.AssertEqual(t, "/test", c.Route().Path) 2271 return nil 2272 }) 2273 resp, err := app.Test(httptest.NewRequest(MethodGet, "//test", nil)) 2274 utils.AssertEqual(t, nil, err, "app.Test(req)") 2275 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 2276 } 2277 2278 // go test -run Test_Ctx_SaveFile 2279 func Test_Ctx_SaveFile(t *testing.T) { 2280 // TODO We should clean this up 2281 t.Parallel() 2282 app := New() 2283 2284 app.Post("/test", func(c *Ctx) error { 2285 fh, err := c.FormFile("file") 2286 utils.AssertEqual(t, nil, err) 2287 2288 tempFile, err := os.CreateTemp(os.TempDir(), "test-") 2289 utils.AssertEqual(t, nil, err) 2290 2291 defer func(file *os.File) { 2292 err := file.Close() 2293 utils.AssertEqual(t, nil, err) 2294 err = os.Remove(file.Name()) 2295 utils.AssertEqual(t, nil, err) 2296 }(tempFile) 2297 err = c.SaveFile(fh, tempFile.Name()) 2298 utils.AssertEqual(t, nil, err) 2299 2300 bs, err := os.ReadFile(tempFile.Name()) 2301 utils.AssertEqual(t, nil, err) 2302 utils.AssertEqual(t, "hello world", string(bs)) 2303 return nil 2304 }) 2305 2306 body := &bytes.Buffer{} 2307 writer := multipart.NewWriter(body) 2308 2309 ioWriter, err := writer.CreateFormFile("file", "test") 2310 utils.AssertEqual(t, nil, err) 2311 2312 _, err = ioWriter.Write([]byte("hello world")) 2313 utils.AssertEqual(t, nil, err) 2314 utils.AssertEqual(t, nil, writer.Close()) 2315 2316 req := httptest.NewRequest(MethodPost, "/test", body) 2317 req.Header.Set("Content-Type", writer.FormDataContentType()) 2318 req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) 2319 2320 resp, err := app.Test(req) 2321 utils.AssertEqual(t, nil, err, "app.Test(req)") 2322 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 2323 } 2324 2325 // go test -run Test_Ctx_SaveFileToStorage 2326 func Test_Ctx_SaveFileToStorage(t *testing.T) { 2327 t.Parallel() 2328 app := New() 2329 storage := memory.New() 2330 2331 app.Post("/test", func(c *Ctx) error { 2332 fh, err := c.FormFile("file") 2333 utils.AssertEqual(t, nil, err) 2334 2335 err = c.SaveFileToStorage(fh, "test", storage) 2336 utils.AssertEqual(t, nil, err) 2337 2338 file, err := storage.Get("test") 2339 utils.AssertEqual(t, []byte("hello world"), file) 2340 utils.AssertEqual(t, nil, err) 2341 2342 err = storage.Delete("test") 2343 utils.AssertEqual(t, nil, err) 2344 2345 return nil 2346 }) 2347 2348 body := &bytes.Buffer{} 2349 writer := multipart.NewWriter(body) 2350 2351 ioWriter, err := writer.CreateFormFile("file", "test") 2352 utils.AssertEqual(t, nil, err) 2353 2354 _, err = ioWriter.Write([]byte("hello world")) 2355 utils.AssertEqual(t, nil, err) 2356 utils.AssertEqual(t, nil, writer.Close()) 2357 2358 req := httptest.NewRequest(MethodPost, "/test", body) 2359 req.Header.Set("Content-Type", writer.FormDataContentType()) 2360 req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) 2361 2362 resp, err := app.Test(req) 2363 utils.AssertEqual(t, nil, err, "app.Test(req)") 2364 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 2365 } 2366 2367 // go test -run Test_Ctx_Secure 2368 func Test_Ctx_Secure(t *testing.T) { 2369 t.Parallel() 2370 app := New() 2371 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2372 defer app.ReleaseCtx(c) 2373 // TODO Add TLS conn 2374 utils.AssertEqual(t, false, c.Secure()) 2375 } 2376 2377 // go test -run Test_Ctx_Stale 2378 func Test_Ctx_Stale(t *testing.T) { 2379 t.Parallel() 2380 app := New() 2381 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2382 defer app.ReleaseCtx(c) 2383 utils.AssertEqual(t, true, c.Stale()) 2384 } 2385 2386 // go test -run Test_Ctx_Subdomains 2387 func Test_Ctx_Subdomains(t *testing.T) { 2388 t.Parallel() 2389 app := New() 2390 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2391 defer app.ReleaseCtx(c) 2392 c.Request().URI().SetHost("john.doe.is.awesome.google.com") 2393 utils.AssertEqual(t, []string{"john", "doe"}, c.Subdomains(4)) 2394 2395 c.Request().URI().SetHost("localhost:3000") 2396 utils.AssertEqual(t, []string{"localhost:3000"}, c.Subdomains()) 2397 } 2398 2399 // go test -v -run=^$ -bench=Benchmark_Ctx_Subdomains -benchmem -count=4 2400 func Benchmark_Ctx_Subdomains(b *testing.B) { 2401 app := New() 2402 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2403 defer app.ReleaseCtx(c) 2404 c.Request().SetRequestURI("http://john.doe.google.com") 2405 var res []string 2406 b.ReportAllocs() 2407 b.ResetTimer() 2408 for n := 0; n < b.N; n++ { 2409 res = c.Subdomains() 2410 } 2411 utils.AssertEqual(b, []string{"john", "doe"}, res) 2412 } 2413 2414 // go test -run Test_Ctx_ClearCookie 2415 func Test_Ctx_ClearCookie(t *testing.T) { 2416 t.Parallel() 2417 app := New() 2418 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2419 defer app.ReleaseCtx(c) 2420 c.Request().Header.Set(HeaderCookie, "john=doe") 2421 c.ClearCookie("john") 2422 utils.AssertEqual(t, true, strings.HasPrefix(string(c.Response().Header.Peek(HeaderSetCookie)), "john=; expires=")) 2423 2424 c.Request().Header.Set(HeaderCookie, "test1=dummy") 2425 c.Request().Header.Set(HeaderCookie, "test2=dummy") 2426 c.ClearCookie() 2427 utils.AssertEqual(t, true, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test1=; expires=")) 2428 utils.AssertEqual(t, true, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test2=; expires=")) 2429 } 2430 2431 // go test -race -run Test_Ctx_Download 2432 func Test_Ctx_Download(t *testing.T) { 2433 t.Parallel() 2434 app := New() 2435 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2436 defer app.ReleaseCtx(c) 2437 2438 utils.AssertEqual(t, nil, c.Download("ctx.go", "Awesome File!")) 2439 2440 f, err := os.Open("./ctx.go") 2441 utils.AssertEqual(t, nil, err) 2442 defer func() { 2443 utils.AssertEqual(t, nil, f.Close()) 2444 }() 2445 2446 expect, err := io.ReadAll(f) 2447 utils.AssertEqual(t, nil, err) 2448 utils.AssertEqual(t, expect, c.Response().Body()) 2449 utils.AssertEqual(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition))) 2450 2451 utils.AssertEqual(t, nil, c.Download("ctx.go")) 2452 utils.AssertEqual(t, `attachment; filename="ctx.go"`, string(c.Response().Header.Peek(HeaderContentDisposition))) 2453 } 2454 2455 // go test -race -run Test_Ctx_SendFile 2456 func Test_Ctx_SendFile(t *testing.T) { 2457 t.Parallel() 2458 app := New() 2459 2460 // fetch file content 2461 f, err := os.Open("./ctx.go") 2462 utils.AssertEqual(t, nil, err) 2463 defer func() { 2464 utils.AssertEqual(t, nil, f.Close()) 2465 }() 2466 expectFileContent, err := io.ReadAll(f) 2467 utils.AssertEqual(t, nil, err) 2468 // fetch file info for the not modified test case 2469 fI, err := os.Stat("./ctx.go") 2470 utils.AssertEqual(t, nil, err) 2471 2472 // simple test case 2473 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2474 err = c.SendFile("ctx.go") 2475 // check expectation 2476 utils.AssertEqual(t, nil, err) 2477 utils.AssertEqual(t, expectFileContent, c.Response().Body()) 2478 utils.AssertEqual(t, StatusOK, c.Response().StatusCode()) 2479 app.ReleaseCtx(c) 2480 2481 // test with custom error code 2482 c = app.AcquireCtx(&fasthttp.RequestCtx{}) 2483 err = c.Status(StatusInternalServerError).SendFile("ctx.go") 2484 // check expectation 2485 utils.AssertEqual(t, nil, err) 2486 utils.AssertEqual(t, expectFileContent, c.Response().Body()) 2487 utils.AssertEqual(t, StatusInternalServerError, c.Response().StatusCode()) 2488 app.ReleaseCtx(c) 2489 2490 // test not modified 2491 c = app.AcquireCtx(&fasthttp.RequestCtx{}) 2492 c.Request().Header.Set(HeaderIfModifiedSince, fI.ModTime().Format(time.RFC1123)) 2493 err = c.SendFile("ctx.go") 2494 // check expectation 2495 utils.AssertEqual(t, nil, err) 2496 utils.AssertEqual(t, StatusNotModified, c.Response().StatusCode()) 2497 utils.AssertEqual(t, []byte(nil), c.Response().Body()) 2498 app.ReleaseCtx(c) 2499 } 2500 2501 // go test -race -run Test_Ctx_SendFile_404 2502 func Test_Ctx_SendFile_404(t *testing.T) { 2503 t.Parallel() 2504 app := New() 2505 app.Get("/", func(c *Ctx) error { 2506 err := c.SendFile(filepath.FromSlash("john_dow.go/")) 2507 utils.AssertEqual(t, false, err == nil) 2508 return err 2509 }) 2510 2511 resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) 2512 utils.AssertEqual(t, nil, err) 2513 utils.AssertEqual(t, StatusNotFound, resp.StatusCode) 2514 } 2515 2516 // go test -race -run Test_Ctx_SendFile_Immutable 2517 func Test_Ctx_SendFile_Immutable(t *testing.T) { 2518 t.Parallel() 2519 app := New() 2520 var endpointsForTest []string 2521 addEndpoint := func(file, endpoint string) { 2522 endpointsForTest = append(endpointsForTest, endpoint) 2523 app.Get(endpoint, func(c *Ctx) error { 2524 if err := c.SendFile(file); err != nil { 2525 utils.AssertEqual(t, nil, err) 2526 return err 2527 } 2528 return c.SendStatus(200) 2529 }) 2530 } 2531 2532 // relative paths 2533 addEndpoint("./.github/index.html", "/relativeWithDot") 2534 addEndpoint(filepath.FromSlash("./.github/index.html"), "/relativeOSWithDot") 2535 addEndpoint(".github/index.html", "/relative") 2536 addEndpoint(filepath.FromSlash(".github/index.html"), "/relativeOS") 2537 2538 // absolute paths 2539 if path, err := filepath.Abs(".github/index.html"); err != nil { 2540 utils.AssertEqual(t, nil, err) 2541 } else { 2542 addEndpoint(path, "/absolute") 2543 addEndpoint(filepath.FromSlash(path), "/absoluteOS") // os related 2544 } 2545 2546 for _, endpoint := range endpointsForTest { 2547 t.Run(endpoint, func(t *testing.T) { 2548 // 1st try 2549 resp, err := app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) 2550 utils.AssertEqual(t, nil, err) 2551 utils.AssertEqual(t, StatusOK, resp.StatusCode) 2552 // 2nd try 2553 resp, err = app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) 2554 utils.AssertEqual(t, nil, err) 2555 utils.AssertEqual(t, StatusOK, resp.StatusCode) 2556 }) 2557 } 2558 } 2559 2560 // go test -race -run Test_Ctx_SendFile_RestoreOriginalURL 2561 func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { 2562 t.Parallel() 2563 app := New() 2564 app.Get("/", func(c *Ctx) error { 2565 originalURL := utils.CopyString(c.OriginalURL()) 2566 err := c.SendFile("ctx.go") 2567 utils.AssertEqual(t, originalURL, c.OriginalURL()) 2568 return err 2569 }) 2570 2571 _, err1 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) 2572 // second request required to confirm with zero allocation 2573 _, err2 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) 2574 2575 utils.AssertEqual(t, nil, err1) 2576 utils.AssertEqual(t, nil, err2) 2577 } 2578 2579 // go test -run Test_Ctx_JSON 2580 func Test_Ctx_JSON(t *testing.T) { 2581 t.Parallel() 2582 app := New() 2583 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2584 defer app.ReleaseCtx(c) 2585 2586 utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) 2587 2588 err := c.JSON(Map{ // map has no order 2589 "Name": "Grame", 2590 "Age": 20, 2591 }) 2592 utils.AssertEqual(t, nil, err) 2593 utils.AssertEqual(t, `{"Age":20,"Name":"Grame"}`, string(c.Response().Body())) 2594 utils.AssertEqual(t, "application/json", string(c.Response().Header.Peek("content-type"))) 2595 2596 testEmpty := func(v interface{}, r string) { 2597 err := c.JSON(v) 2598 utils.AssertEqual(t, nil, err) 2599 utils.AssertEqual(t, r, string(c.Response().Body())) 2600 } 2601 2602 testEmpty(nil, "null") 2603 testEmpty("", `""`) 2604 testEmpty(0, "0") 2605 testEmpty([]int{}, "[]") 2606 } 2607 2608 // go test -run=^$ -bench=Benchmark_Ctx_JSON -benchmem -count=4 2609 func Benchmark_Ctx_JSON(b *testing.B) { 2610 app := New() 2611 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2612 defer app.ReleaseCtx(c) 2613 type SomeStruct struct { 2614 Name string 2615 Age uint8 2616 } 2617 data := SomeStruct{ 2618 Name: "Grame", 2619 Age: 20, 2620 } 2621 var err error 2622 b.ReportAllocs() 2623 b.ResetTimer() 2624 for n := 0; n < b.N; n++ { 2625 err = c.JSON(data) 2626 } 2627 utils.AssertEqual(b, nil, err) 2628 utils.AssertEqual(b, `{"Name":"Grame","Age":20}`, string(c.Response().Body())) 2629 } 2630 2631 // go test -run Test_Ctx_JSONP 2632 func Test_Ctx_JSONP(t *testing.T) { 2633 t.Parallel() 2634 app := New() 2635 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2636 defer app.ReleaseCtx(c) 2637 2638 utils.AssertEqual(t, true, c.JSONP(complex(1, 1)) != nil) 2639 2640 err := c.JSONP(Map{ 2641 "Name": "Grame", 2642 "Age": 20, 2643 }) 2644 utils.AssertEqual(t, nil, err) 2645 utils.AssertEqual(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) 2646 utils.AssertEqual(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) 2647 2648 err = c.JSONP(Map{ 2649 "Name": "Grame", 2650 "Age": 20, 2651 }, "john") 2652 utils.AssertEqual(t, nil, err) 2653 utils.AssertEqual(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) 2654 utils.AssertEqual(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) 2655 } 2656 2657 // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 2658 func Benchmark_Ctx_JSONP(b *testing.B) { 2659 app := New() 2660 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2661 defer app.ReleaseCtx(c) 2662 type SomeStruct struct { 2663 Name string 2664 Age uint8 2665 } 2666 data := SomeStruct{ 2667 Name: "Grame", 2668 Age: 20, 2669 } 2670 callback := "emit" 2671 var err error 2672 b.ReportAllocs() 2673 b.ResetTimer() 2674 for n := 0; n < b.N; n++ { 2675 err = c.JSONP(data, callback) 2676 } 2677 utils.AssertEqual(b, nil, err) 2678 utils.AssertEqual(b, `emit({"Name":"Grame","Age":20});`, string(c.Response().Body())) 2679 } 2680 2681 // go test -run Test_Ctx_XML 2682 func Test_Ctx_XML(t *testing.T) { 2683 t.Parallel() 2684 app := New() 2685 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2686 defer app.ReleaseCtx(c) 2687 2688 utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) 2689 2690 type xmlResult struct { 2691 XMLName xml.Name `xml:"Users"` 2692 Names []string `xml:"Names"` 2693 Ages []int `xml:"Ages"` 2694 } 2695 2696 err := c.XML(xmlResult{ 2697 Names: []string{"Grame", "John"}, 2698 Ages: []int{1, 12, 20}, 2699 }) 2700 utils.AssertEqual(t, nil, err) 2701 utils.AssertEqual(t, `<Users><Names>Grame</Names><Names>John</Names><Ages>1</Ages><Ages>12</Ages><Ages>20</Ages></Users>`, string(c.Response().Body())) 2702 utils.AssertEqual(t, "application/xml", string(c.Response().Header.Peek("content-type"))) 2703 2704 testEmpty := func(v interface{}, r string) { 2705 err := c.XML(v) 2706 utils.AssertEqual(t, nil, err) 2707 utils.AssertEqual(t, r, string(c.Response().Body())) 2708 } 2709 2710 testEmpty(nil, "") 2711 testEmpty("", `<string></string>`) 2712 testEmpty(0, "<int>0</int>") 2713 testEmpty([]int{}, "") 2714 } 2715 2716 // go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 2717 func Benchmark_Ctx_XML(b *testing.B) { 2718 app := New() 2719 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2720 defer app.ReleaseCtx(c) 2721 type SomeStruct struct { 2722 Name string `xml:"Name"` 2723 Age uint8 `xml:"Age"` 2724 } 2725 data := SomeStruct{ 2726 Name: "Grame", 2727 Age: 20, 2728 } 2729 var err error 2730 b.ReportAllocs() 2731 b.ResetTimer() 2732 for n := 0; n < b.N; n++ { 2733 err = c.XML(data) 2734 } 2735 2736 utils.AssertEqual(b, nil, err) 2737 utils.AssertEqual(b, `<SomeStruct><Name>Grame</Name><Age>20</Age></SomeStruct>`, string(c.Response().Body())) 2738 } 2739 2740 // go test -run Test_Ctx_Links 2741 func Test_Ctx_Links(t *testing.T) { 2742 t.Parallel() 2743 app := New() 2744 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2745 defer app.ReleaseCtx(c) 2746 2747 c.Links() 2748 utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderLink))) 2749 2750 c.Links( 2751 "http://api.example.com/users?page=2", "next", 2752 "http://api.example.com/users?page=5", "last", 2753 ) 2754 utils.AssertEqual(t, `<http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"`, string(c.Response().Header.Peek(HeaderLink))) 2755 } 2756 2757 // go test -v -run=^$ -bench=Benchmark_Ctx_Links -benchmem -count=4 2758 func Benchmark_Ctx_Links(b *testing.B) { 2759 app := New() 2760 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2761 defer app.ReleaseCtx(c) 2762 b.ReportAllocs() 2763 b.ResetTimer() 2764 for n := 0; n < b.N; n++ { 2765 c.Links( 2766 "http://api.example.com/users?page=2", "next", 2767 "http://api.example.com/users?page=5", "last", 2768 ) 2769 } 2770 } 2771 2772 // go test -run Test_Ctx_Location 2773 func Test_Ctx_Location(t *testing.T) { 2774 t.Parallel() 2775 app := New() 2776 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2777 defer app.ReleaseCtx(c) 2778 c.Location("http://example.com") 2779 utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) 2780 } 2781 2782 // go test -run Test_Ctx_Next 2783 func Test_Ctx_Next(t *testing.T) { 2784 t.Parallel() 2785 app := New() 2786 app.Use("/", func(c *Ctx) error { 2787 return c.Next() 2788 }) 2789 app.Get("/test", func(c *Ctx) error { 2790 c.Set("X-Next-Result", "Works") 2791 return nil 2792 }) 2793 resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/test", nil)) 2794 utils.AssertEqual(t, nil, err, "app.Test(req)") 2795 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 2796 utils.AssertEqual(t, "Works", resp.Header.Get("X-Next-Result")) 2797 } 2798 2799 // go test -run Test_Ctx_Next_Error 2800 func Test_Ctx_Next_Error(t *testing.T) { 2801 t.Parallel() 2802 app := New() 2803 app.Use("/", func(c *Ctx) error { 2804 c.Set("X-Next-Result", "Works") 2805 return ErrNotFound 2806 }) 2807 2808 resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/test", nil)) 2809 utils.AssertEqual(t, nil, err, "app.Test(req)") 2810 utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") 2811 utils.AssertEqual(t, "Works", resp.Header.Get("X-Next-Result")) 2812 } 2813 2814 // go test -run Test_Ctx_Redirect 2815 func Test_Ctx_Redirect(t *testing.T) { 2816 t.Parallel() 2817 app := New() 2818 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2819 defer app.ReleaseCtx(c) 2820 2821 err := c.Redirect("http://default.com") 2822 utils.AssertEqual(t, nil, err) 2823 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2824 utils.AssertEqual(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) 2825 2826 err = c.Redirect("http://example.com", 301) 2827 utils.AssertEqual(t, nil, err) 2828 utils.AssertEqual(t, 301, c.Response().StatusCode()) 2829 utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) 2830 } 2831 2832 // go test -run Test_Ctx_RedirectToRouteWithParams 2833 func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { 2834 t.Parallel() 2835 app := New() 2836 app.Get("/user/:name", func(c *Ctx) error { 2837 return c.JSON(c.Params("name")) 2838 }).Name("user") 2839 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2840 defer app.ReleaseCtx(c) 2841 2842 err := c.RedirectToRoute("user", Map{ 2843 "name": "fiber", 2844 }) 2845 utils.AssertEqual(t, nil, err) 2846 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2847 utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) 2848 } 2849 2850 // go test -run Test_Ctx_RedirectToRouteWithParams 2851 func Test_Ctx_RedirectToRouteWithQueries(t *testing.T) { 2852 t.Parallel() 2853 app := New() 2854 app.Get("/user/:name", func(c *Ctx) error { 2855 return c.JSON(c.Params("name")) 2856 }).Name("user") 2857 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2858 defer app.ReleaseCtx(c) 2859 2860 err := c.RedirectToRoute("user", Map{ 2861 "name": "fiber", 2862 "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, 2863 }) 2864 utils.AssertEqual(t, nil, err) 2865 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2866 // analysis of query parameters with url parsing, since a map pass is always randomly ordered 2867 location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) 2868 utils.AssertEqual(t, nil, err, "url.Parse(location)") 2869 utils.AssertEqual(t, "/user/fiber", location.Path) 2870 utils.AssertEqual(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) 2871 } 2872 2873 // go test -run Test_Ctx_RedirectToRouteWithOptionalParams 2874 func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { 2875 t.Parallel() 2876 app := New() 2877 app.Get("/user/:name?", func(c *Ctx) error { 2878 return c.JSON(c.Params("name")) 2879 }).Name("user") 2880 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2881 defer app.ReleaseCtx(c) 2882 2883 err := c.RedirectToRoute("user", Map{ 2884 "name": "fiber", 2885 }) 2886 utils.AssertEqual(t, nil, err) 2887 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2888 utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) 2889 } 2890 2891 // go test -run Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue 2892 func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { 2893 t.Parallel() 2894 app := New() 2895 app.Get("/user/:name?", func(c *Ctx) error { 2896 return c.JSON(c.Params("name")) 2897 }).Name("user") 2898 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2899 defer app.ReleaseCtx(c) 2900 2901 err := c.RedirectToRoute("user", Map{}) 2902 utils.AssertEqual(t, nil, err) 2903 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2904 utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) 2905 } 2906 2907 // go test -run Test_Ctx_RedirectToRouteWithGreedyParameters 2908 func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { 2909 t.Parallel() 2910 app := New() 2911 app.Get("/user/+", func(c *Ctx) error { 2912 return c.JSON(c.Params("+")) 2913 }).Name("user") 2914 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2915 defer app.ReleaseCtx(c) 2916 2917 err := c.RedirectToRoute("user", Map{ 2918 "+": "test/routes", 2919 }) 2920 utils.AssertEqual(t, nil, err) 2921 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2922 utils.AssertEqual(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) 2923 } 2924 2925 // go test -run Test_Ctx_RedirectBack 2926 func Test_Ctx_RedirectBack(t *testing.T) { 2927 t.Parallel() 2928 app := New() 2929 app.Get("/", func(c *Ctx) error { 2930 return c.JSON("Home") 2931 }).Name("home") 2932 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2933 defer app.ReleaseCtx(c) 2934 err := c.RedirectBack("/") 2935 utils.AssertEqual(t, nil, err) 2936 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2937 utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) 2938 } 2939 2940 // go test -run Test_Ctx_RedirectBackWithReferer 2941 func Test_Ctx_RedirectBackWithReferer(t *testing.T) { 2942 t.Parallel() 2943 app := New() 2944 app.Get("/", func(c *Ctx) error { 2945 return c.JSON("Home") 2946 }).Name("home") 2947 app.Get("/back", func(c *Ctx) error { 2948 return c.JSON("Back") 2949 }).Name("back") 2950 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2951 defer app.ReleaseCtx(c) 2952 c.Request().Header.Set(HeaderReferer, "/back") 2953 err := c.RedirectBack("/") 2954 utils.AssertEqual(t, nil, err) 2955 utils.AssertEqual(t, 302, c.Response().StatusCode()) 2956 utils.AssertEqual(t, "/back", c.Get(HeaderReferer)) 2957 utils.AssertEqual(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) 2958 } 2959 2960 // go test -run Test_Ctx_Render 2961 func Test_Ctx_Render(t *testing.T) { 2962 t.Parallel() 2963 app := New() 2964 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2965 defer app.ReleaseCtx(c) 2966 err := c.Render("./.github/testdata/index.tmpl", Map{ 2967 "Title": "Hello, World!", 2968 }) 2969 utils.AssertEqual(t, nil, err) 2970 2971 buf := bytebufferpool.Get() 2972 _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail 2973 defer bytebufferpool.Put(buf) 2974 2975 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body())) 2976 2977 err = c.Render("./.github/testdata/template-non-exists.html", nil) 2978 utils.AssertEqual(t, false, err == nil) 2979 2980 err = c.Render("./.github/testdata/template-invalid.html", nil) 2981 utils.AssertEqual(t, false, err == nil) 2982 } 2983 2984 func Test_Ctx_RenderWithoutLocals(t *testing.T) { 2985 t.Parallel() 2986 app := New(Config{ 2987 PassLocalsToViews: false, 2988 }) 2989 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 2990 2991 c.Locals("Title", "Hello, World!") 2992 defer app.ReleaseCtx(c) 2993 err := c.Render("./.github/testdata/index.tmpl", Map{}) 2994 utils.AssertEqual(t, nil, err) 2995 2996 buf := bytebufferpool.Get() 2997 _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail 2998 defer bytebufferpool.Put(buf) 2999 3000 utils.AssertEqual(t, "<h1><no value></h1>", string(c.Response().Body())) 3001 } 3002 3003 func Test_Ctx_RenderWithLocals(t *testing.T) { 3004 t.Parallel() 3005 app := New(Config{ 3006 PassLocalsToViews: true, 3007 }) 3008 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3009 3010 c.Locals("Title", "Hello, World!") 3011 defer app.ReleaseCtx(c) 3012 err := c.Render("./.github/testdata/index.tmpl", Map{}) 3013 utils.AssertEqual(t, nil, err) 3014 3015 buf := bytebufferpool.Get() 3016 _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail 3017 defer bytebufferpool.Put(buf) 3018 3019 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3020 } 3021 3022 func Test_Ctx_RenderWithBind(t *testing.T) { 3023 t.Parallel() 3024 3025 app := New() 3026 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3027 3028 err := c.Bind(Map{ 3029 "Title": "Hello, World!", 3030 }) 3031 utils.AssertEqual(t, nil, err) 3032 defer app.ReleaseCtx(c) 3033 err = c.Render("./.github/testdata/index.tmpl", Map{}) 3034 utils.AssertEqual(t, nil, err) 3035 3036 buf := bytebufferpool.Get() 3037 _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail 3038 defer bytebufferpool.Put(buf) 3039 3040 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3041 } 3042 3043 func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { 3044 t.Parallel() 3045 app := New() 3046 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3047 3048 err := c.Bind(Map{ 3049 "Title": "Hello, World!", 3050 }) 3051 utils.AssertEqual(t, nil, err) 3052 defer app.ReleaseCtx(c) 3053 err = c.Render("./.github/testdata/index.tmpl", Map{ 3054 "Title": "Hello from Fiber!", 3055 }) 3056 utils.AssertEqual(t, nil, err) 3057 3058 buf := bytebufferpool.Get() 3059 _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail 3060 defer bytebufferpool.Put(buf) 3061 3062 utils.AssertEqual(t, "<h1>Hello from Fiber!</h1>", string(c.Response().Body())) 3063 } 3064 3065 func Test_Ctx_RenderWithBindLocals(t *testing.T) { 3066 t.Parallel() 3067 app := New(Config{ 3068 PassLocalsToViews: true, 3069 }) 3070 3071 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3072 3073 err := c.Bind(Map{ 3074 "Title": "Hello, World!", 3075 }) 3076 utils.AssertEqual(t, nil, err) 3077 3078 c.Locals("Summary", "Test") 3079 defer app.ReleaseCtx(c) 3080 3081 err = c.Render("./.github/testdata/template.tmpl", Map{}) 3082 utils.AssertEqual(t, nil, err) 3083 3084 utils.AssertEqual(t, "<h1>Hello, World! Test</h1>", string(c.Response().Body())) 3085 } 3086 3087 func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { 3088 t.Parallel() 3089 engine := &testTemplateEngine{} 3090 err := engine.Load() 3091 utils.AssertEqual(t, nil, err) 3092 app := New(Config{ 3093 PassLocalsToViews: true, 3094 Views: engine, 3095 }) 3096 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3097 3098 c.Locals("Title", "This is a test.") 3099 defer app.ReleaseCtx(c) 3100 3101 err = c.Render("index.tmpl", Map{ 3102 "Title": "Hello, World!", 3103 }) 3104 utils.AssertEqual(t, nil, err) 3105 3106 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3107 } 3108 3109 func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) { 3110 engine := &testTemplateEngine{} 3111 err := engine.Load() 3112 utils.AssertEqual(b, nil, err) 3113 utils.AssertEqual(b, nil, err) 3114 app := New(Config{ 3115 PassLocalsToViews: true, 3116 Views: engine, 3117 }) 3118 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3119 3120 err = c.Bind(Map{ 3121 "Title": "Hello, World!", 3122 }) 3123 utils.AssertEqual(b, nil, err) 3124 c.Locals("Summary", "Test") 3125 3126 defer app.ReleaseCtx(c) 3127 3128 b.ReportAllocs() 3129 b.ResetTimer() 3130 3131 for n := 0; n < b.N; n++ { 3132 err = c.Render("template.tmpl", Map{}) 3133 } 3134 utils.AssertEqual(b, nil, err) 3135 3136 utils.AssertEqual(b, "<h1>Hello, World! Test</h1>", string(c.Response().Body())) 3137 } 3138 3139 func Benchmark_Ctx_RedirectToRoute(b *testing.B) { 3140 app := New() 3141 app.Get("/user/:name", func(c *Ctx) error { 3142 return c.JSON(c.Params("name")) 3143 }).Name("user") 3144 3145 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3146 defer app.ReleaseCtx(c) 3147 3148 b.ReportAllocs() 3149 b.ResetTimer() 3150 3151 var err error 3152 for n := 0; n < b.N; n++ { 3153 err = c.RedirectToRoute("user", Map{ 3154 "name": "fiber", 3155 }) 3156 } 3157 utils.AssertEqual(b, nil, err) 3158 3159 utils.AssertEqual(b, 302, c.Response().StatusCode()) 3160 utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) 3161 } 3162 3163 func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { 3164 app := New() 3165 app.Get("/user/:name", func(c *Ctx) error { 3166 return c.JSON(c.Params("name")) 3167 }).Name("user") 3168 3169 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3170 defer app.ReleaseCtx(c) 3171 3172 b.ReportAllocs() 3173 b.ResetTimer() 3174 3175 var err error 3176 for n := 0; n < b.N; n++ { 3177 err = c.RedirectToRoute("user", Map{ 3178 "name": "fiber", 3179 "queries": map[string]string{"a": "a", "b": "b"}, 3180 }) 3181 } 3182 utils.AssertEqual(b, nil, err) 3183 3184 utils.AssertEqual(b, 302, c.Response().StatusCode()) 3185 // analysis of query parameters with url parsing, since a map pass is always randomly ordered 3186 location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) 3187 utils.AssertEqual(b, nil, err, "url.Parse(location)") 3188 utils.AssertEqual(b, "/user/fiber", location.Path) 3189 utils.AssertEqual(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) 3190 } 3191 3192 func Benchmark_Ctx_RenderLocals(b *testing.B) { 3193 engine := &testTemplateEngine{} 3194 err := engine.Load() 3195 utils.AssertEqual(b, nil, err) 3196 app := New(Config{ 3197 PassLocalsToViews: true, 3198 }) 3199 app.config.Views = engine 3200 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3201 3202 c.Locals("Title", "Hello, World!") 3203 3204 defer app.ReleaseCtx(c) 3205 3206 b.ReportAllocs() 3207 b.ResetTimer() 3208 3209 for n := 0; n < b.N; n++ { 3210 err = c.Render("index.tmpl", Map{}) 3211 } 3212 utils.AssertEqual(b, nil, err) 3213 3214 utils.AssertEqual(b, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3215 } 3216 3217 func Benchmark_Ctx_RenderBind(b *testing.B) { 3218 engine := &testTemplateEngine{} 3219 err := engine.Load() 3220 utils.AssertEqual(b, nil, err) 3221 app := New() 3222 app.config.Views = engine 3223 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3224 3225 err = c.Bind(Map{ 3226 "Title": "Hello, World!", 3227 }) 3228 utils.AssertEqual(b, nil, err) 3229 3230 defer app.ReleaseCtx(c) 3231 3232 b.ReportAllocs() 3233 b.ResetTimer() 3234 3235 for n := 0; n < b.N; n++ { 3236 err = c.Render("index.tmpl", Map{}) 3237 } 3238 utils.AssertEqual(b, nil, err) 3239 3240 utils.AssertEqual(b, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3241 } 3242 3243 // go test -run Test_Ctx_RestartRouting 3244 func Test_Ctx_RestartRouting(t *testing.T) { 3245 t.Parallel() 3246 app := New() 3247 calls := 0 3248 app.Get("/", func(c *Ctx) error { 3249 calls++ 3250 if calls < 3 { 3251 return c.RestartRouting() 3252 } 3253 return nil 3254 }) 3255 resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/", nil)) 3256 utils.AssertEqual(t, nil, err, "app.Test(req)") 3257 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 3258 utils.AssertEqual(t, 3, calls, "Number of calls") 3259 } 3260 3261 // go test -run Test_Ctx_RestartRoutingWithChangedPath 3262 func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { 3263 t.Parallel() 3264 app := New() 3265 var executedOldHandler, executedNewHandler bool 3266 3267 app.Get("/old", func(c *Ctx) error { 3268 c.Path("/new") 3269 return c.RestartRouting() 3270 }) 3271 app.Get("/old", func(c *Ctx) error { 3272 executedOldHandler = true 3273 return nil 3274 }) 3275 app.Get("/new", func(c *Ctx) error { 3276 executedNewHandler = true 3277 return nil 3278 }) 3279 3280 resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil)) 3281 utils.AssertEqual(t, nil, err, "app.Test(req)") 3282 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 3283 utils.AssertEqual(t, false, executedOldHandler, "Executed old handler") 3284 utils.AssertEqual(t, true, executedNewHandler, "Executed new handler") 3285 } 3286 3287 // go test -run Test_Ctx_RestartRoutingWithChangedPathAnd404 3288 func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) { 3289 t.Parallel() 3290 app := New() 3291 app.Get("/new", func(c *Ctx) error { 3292 return nil 3293 }) 3294 app.Use(func(c *Ctx) error { 3295 c.Path("/new") 3296 // c.Next() would fail this test as a 404 is returned from the next handler 3297 return c.RestartRouting() 3298 }) 3299 app.Use(func(c *Ctx) error { 3300 return ErrNotFound 3301 }) 3302 3303 resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil)) 3304 utils.AssertEqual(t, nil, err, "app.Test(req)") 3305 utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") 3306 } 3307 3308 type testTemplateEngine struct { 3309 templates *template.Template 3310 path string 3311 } 3312 3313 func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { 3314 if len(layout) == 0 { 3315 if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { 3316 return fmt.Errorf("failed to execute template without layout: %w", err) 3317 } 3318 return nil 3319 } 3320 if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { 3321 return fmt.Errorf("failed to execute template: %w", err) 3322 } 3323 if err := t.templates.ExecuteTemplate(w, layout[0], bind); err != nil { 3324 return fmt.Errorf("failed to execute template with layout: %w", err) 3325 } 3326 return nil 3327 } 3328 3329 func (t *testTemplateEngine) Load() error { 3330 if t.path == "" { 3331 t.path = "testdata" 3332 } 3333 t.templates = template.Must(template.ParseGlob("./.github/" + t.path + "/*.tmpl")) 3334 return nil 3335 } 3336 3337 // go test -run Test_Ctx_Render_Engine 3338 func Test_Ctx_Render_Engine(t *testing.T) { 3339 t.Parallel() 3340 engine := &testTemplateEngine{} 3341 utils.AssertEqual(t, nil, engine.Load()) 3342 app := New() 3343 app.config.Views = engine 3344 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3345 defer app.ReleaseCtx(c) 3346 err := c.Render("index.tmpl", Map{ 3347 "Title": "Hello, World!", 3348 }) 3349 utils.AssertEqual(t, nil, err) 3350 utils.AssertEqual(t, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3351 } 3352 3353 // go test -run Test_Ctx_Render_Engine_With_View_Layout 3354 func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { 3355 t.Parallel() 3356 engine := &testTemplateEngine{} 3357 utils.AssertEqual(t, nil, engine.Load()) 3358 app := New(Config{ViewsLayout: "main.tmpl"}) 3359 app.config.Views = engine 3360 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3361 defer app.ReleaseCtx(c) 3362 err := c.Render("index.tmpl", Map{ 3363 "Title": "Hello, World!", 3364 }) 3365 utils.AssertEqual(t, nil, err) 3366 utils.AssertEqual(t, "<h1>Hello, World!</h1><h1>I'm main</h1>", string(c.Response().Body())) 3367 } 3368 3369 // go test -v -run=^$ -bench=Benchmark_Ctx_Render_Engine -benchmem -count=4 3370 func Benchmark_Ctx_Render_Engine(b *testing.B) { 3371 engine := &testTemplateEngine{} 3372 err := engine.Load() 3373 utils.AssertEqual(b, nil, err) 3374 app := New() 3375 app.config.Views = engine 3376 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3377 defer app.ReleaseCtx(c) 3378 b.ReportAllocs() 3379 b.ResetTimer() 3380 for n := 0; n < b.N; n++ { 3381 err = c.Render("index.tmpl", Map{ 3382 "Title": "Hello, World!", 3383 }) 3384 } 3385 utils.AssertEqual(b, nil, err) 3386 utils.AssertEqual(b, "<h1>Hello, World!</h1>", string(c.Response().Body())) 3387 } 3388 3389 // go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4 3390 func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { 3391 app := New() 3392 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3393 defer app.ReleaseCtx(c) 3394 app.Get("/user/:name", func(c *Ctx) error { 3395 return c.SendString(c.Params("name")) 3396 }).Name("User") 3397 3398 var err error 3399 var location string 3400 for n := 0; n < b.N; n++ { 3401 location, err = c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"}) 3402 } 3403 utils.AssertEqual(b, "/user/fiber", location) 3404 utils.AssertEqual(b, nil, err) 3405 } 3406 3407 // go test -run Test_Ctx_Get_Location_From_Route_name 3408 func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { 3409 t.Parallel() 3410 3411 t.Run("case insensitive", func(t *testing.T) { 3412 t.Parallel() 3413 app := New() 3414 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3415 defer app.ReleaseCtx(c) 3416 app.Get("/user/:name", func(c *Ctx) error { 3417 return c.SendString(c.Params("name")) 3418 }).Name("User") 3419 3420 location, err := c.GetRouteURL("User", Map{"name": "fiber"}) 3421 utils.AssertEqual(t, nil, err) 3422 utils.AssertEqual(t, "/user/fiber", location) 3423 3424 location, err = c.GetRouteURL("User", Map{"Name": "fiber"}) 3425 utils.AssertEqual(t, nil, err) 3426 utils.AssertEqual(t, "/user/fiber", location) 3427 }) 3428 3429 t.Run("case sensitive", func(t *testing.T) { 3430 t.Parallel() 3431 app := New(Config{CaseSensitive: true}) 3432 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3433 defer app.ReleaseCtx(c) 3434 app.Get("/user/:name", func(c *Ctx) error { 3435 return c.SendString(c.Params("name")) 3436 }).Name("User") 3437 3438 location, err := c.GetRouteURL("User", Map{"name": "fiber"}) 3439 utils.AssertEqual(t, nil, err) 3440 utils.AssertEqual(t, "/user/fiber", location) 3441 3442 location, err = c.GetRouteURL("User", Map{"Name": "fiber"}) 3443 utils.AssertEqual(t, nil, err) 3444 utils.AssertEqual(t, "/user/", location) 3445 }) 3446 } 3447 3448 // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy 3449 func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { 3450 t.Parallel() 3451 app := New() 3452 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3453 defer app.ReleaseCtx(c) 3454 app.Get("/:phone/*/send/*", func(c *Ctx) error { 3455 return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1") + "\nSecond Param: " + c.Params("*2")) 3456 }).Name("SendSms") 3457 3458 location, err := c.GetRouteURL("SendSms", Map{ 3459 "phone": "23456789", 3460 "*1": "sms", 3461 "*2": "test-msg", 3462 }) 3463 utils.AssertEqual(t, nil, err) 3464 utils.AssertEqual(t, "/23456789/sms/send/test-msg", location) 3465 } 3466 3467 // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param 3468 func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing.T) { 3469 t.Parallel() 3470 app := New() 3471 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3472 defer app.ReleaseCtx(c) 3473 app.Get("/:phone/*/send", func(c *Ctx) error { 3474 return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1")) 3475 }).Name("SendSms") 3476 3477 location, err := c.GetRouteURL("SendSms", Map{ 3478 "phone": "23456789", 3479 "*": "sms", 3480 }) 3481 utils.AssertEqual(t, nil, err) 3482 utils.AssertEqual(t, "/23456789/sms/send", location) 3483 } 3484 3485 type errorTemplateEngine struct{} 3486 3487 func (errorTemplateEngine) Render(_ io.Writer, _ string, _ interface{}, _ ...string) error { 3488 return errors.New("errorTemplateEngine") 3489 } 3490 3491 func (errorTemplateEngine) Load() error { return nil } 3492 3493 // go test -run Test_Ctx_Render_Engine_Error 3494 func Test_Ctx_Render_Engine_Error(t *testing.T) { 3495 t.Parallel() 3496 app := New() 3497 app.config.Views = errorTemplateEngine{} 3498 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3499 defer app.ReleaseCtx(c) 3500 err := c.Render("index.tmpl", nil) 3501 utils.AssertEqual(t, false, err == nil) 3502 } 3503 3504 // go test -run Test_Ctx_Render_Go_Template 3505 func Test_Ctx_Render_Go_Template(t *testing.T) { 3506 t.Parallel() 3507 file, err := os.CreateTemp(os.TempDir(), "fiber") 3508 utils.AssertEqual(t, nil, err) 3509 defer func() { 3510 err := os.Remove(file.Name()) 3511 utils.AssertEqual(t, nil, err) 3512 }() 3513 3514 _, err = file.Write([]byte("template")) 3515 utils.AssertEqual(t, nil, err) 3516 3517 err = file.Close() 3518 utils.AssertEqual(t, nil, err) 3519 3520 app := New() 3521 3522 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3523 defer app.ReleaseCtx(c) 3524 3525 err = c.Render(file.Name(), nil) 3526 utils.AssertEqual(t, nil, err) 3527 utils.AssertEqual(t, "template", string(c.Response().Body())) 3528 } 3529 3530 // go test -run Test_Ctx_Send 3531 func Test_Ctx_Send(t *testing.T) { 3532 t.Parallel() 3533 app := New() 3534 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3535 defer app.ReleaseCtx(c) 3536 utils.AssertEqual(t, nil, c.Send([]byte("Hello, World"))) 3537 utils.AssertEqual(t, nil, c.Send([]byte("Don't crash please"))) 3538 utils.AssertEqual(t, nil, c.Send([]byte("1337"))) 3539 utils.AssertEqual(t, "1337", string(c.Response().Body())) 3540 } 3541 3542 // go test -v -run=^$ -bench=Benchmark_Ctx_Send -benchmem -count=4 3543 func Benchmark_Ctx_Send(b *testing.B) { 3544 app := New() 3545 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3546 defer app.ReleaseCtx(c) 3547 byt := []byte("Hello, World!") 3548 b.ReportAllocs() 3549 b.ResetTimer() 3550 3551 var err error 3552 for n := 0; n < b.N; n++ { 3553 err = c.Send(byt) 3554 } 3555 utils.AssertEqual(b, nil, err) 3556 utils.AssertEqual(b, "Hello, World!", string(c.Response().Body())) 3557 } 3558 3559 // go test -run Test_Ctx_SendStatus 3560 func Test_Ctx_SendStatus(t *testing.T) { 3561 t.Parallel() 3562 app := New() 3563 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3564 defer app.ReleaseCtx(c) 3565 err := c.SendStatus(415) 3566 utils.AssertEqual(t, nil, err) 3567 utils.AssertEqual(t, 415, c.Response().StatusCode()) 3568 utils.AssertEqual(t, "Unsupported Media Type", string(c.Response().Body())) 3569 } 3570 3571 // go test -run Test_Ctx_SendString 3572 func Test_Ctx_SendString(t *testing.T) { 3573 t.Parallel() 3574 app := New() 3575 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3576 defer app.ReleaseCtx(c) 3577 err := c.SendString("Don't crash please") 3578 utils.AssertEqual(t, nil, err) 3579 utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) 3580 } 3581 3582 // go test -run Test_Ctx_SendStream 3583 func Test_Ctx_SendStream(t *testing.T) { 3584 t.Parallel() 3585 app := New() 3586 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3587 defer app.ReleaseCtx(c) 3588 3589 err := c.SendStream(bytes.NewReader([]byte("Don't crash please"))) 3590 utils.AssertEqual(t, nil, err) 3591 utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) 3592 3593 err = c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) 3594 utils.AssertEqual(t, nil, err) 3595 utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) 3596 3597 err = c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) 3598 utils.AssertEqual(t, nil, err) 3599 utils.AssertEqual(t, "Hello bufio", string(c.Response().Body())) 3600 } 3601 3602 // go test -run Test_Ctx_Set 3603 func Test_Ctx_Set(t *testing.T) { 3604 t.Parallel() 3605 app := New() 3606 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3607 defer app.ReleaseCtx(c) 3608 c.Set("X-1", "1") 3609 c.Set("X-2", "2") 3610 c.Set("X-3", "3") 3611 c.Set("X-3", "1337") 3612 utils.AssertEqual(t, "1", string(c.Response().Header.Peek("x-1"))) 3613 utils.AssertEqual(t, "2", string(c.Response().Header.Peek("x-2"))) 3614 utils.AssertEqual(t, "1337", string(c.Response().Header.Peek("x-3"))) 3615 } 3616 3617 // go test -run Test_Ctx_Set_Splitter 3618 func Test_Ctx_Set_Splitter(t *testing.T) { 3619 t.Parallel() 3620 app := New() 3621 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3622 defer app.ReleaseCtx(c) 3623 3624 c.Set("Location", "foo\r\nSet-Cookie:%20SESSIONID=MaliciousValue\r\n") 3625 h := string(c.Response().Header.Peek("Location")) 3626 utils.AssertEqual(t, false, strings.Contains(h, "\r\n"), h) 3627 3628 c.Set("Location", "foo\nSet-Cookie:%20SESSIONID=MaliciousValue\n") 3629 h = string(c.Response().Header.Peek("Location")) 3630 utils.AssertEqual(t, false, strings.Contains(h, "\n"), h) 3631 } 3632 3633 // go test -v -run=^$ -bench=Benchmark_Ctx_Set -benchmem -count=4 3634 func Benchmark_Ctx_Set(b *testing.B) { 3635 app := New() 3636 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3637 defer app.ReleaseCtx(c) 3638 val := "1431-15132-3423" 3639 b.ReportAllocs() 3640 b.ResetTimer() 3641 for n := 0; n < b.N; n++ { 3642 c.Set(HeaderXRequestID, val) 3643 } 3644 } 3645 3646 // go test -run Test_Ctx_Status 3647 func Test_Ctx_Status(t *testing.T) { 3648 t.Parallel() 3649 app := New() 3650 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3651 defer app.ReleaseCtx(c) 3652 c.Status(400) 3653 utils.AssertEqual(t, 400, c.Response().StatusCode()) 3654 err := c.Status(415).Send([]byte("Hello, World")) 3655 utils.AssertEqual(t, nil, err) 3656 utils.AssertEqual(t, 415, c.Response().StatusCode()) 3657 utils.AssertEqual(t, "Hello, World", string(c.Response().Body())) 3658 } 3659 3660 // go test -run Test_Ctx_Type 3661 func Test_Ctx_Type(t *testing.T) { 3662 t.Parallel() 3663 app := New() 3664 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3665 defer app.ReleaseCtx(c) 3666 c.Type(".json") 3667 utils.AssertEqual(t, "application/json", string(c.Response().Header.Peek("Content-Type"))) 3668 3669 c.Type("json", "utf-8") 3670 utils.AssertEqual(t, "application/json; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) 3671 3672 c.Type(".html") 3673 utils.AssertEqual(t, "text/html", string(c.Response().Header.Peek("Content-Type"))) 3674 3675 c.Type("html", "utf-8") 3676 utils.AssertEqual(t, "text/html; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) 3677 } 3678 3679 // go test -v -run=^$ -bench=Benchmark_Ctx_Type -benchmem -count=4 3680 func Benchmark_Ctx_Type(b *testing.B) { 3681 app := New() 3682 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3683 defer app.ReleaseCtx(c) 3684 b.ReportAllocs() 3685 b.ResetTimer() 3686 for n := 0; n < b.N; n++ { 3687 c.Type(".json") 3688 c.Type("json") 3689 } 3690 } 3691 3692 // go test -v -run=^$ -bench=Benchmark_Ctx_Type_Charset -benchmem -count=4 3693 func Benchmark_Ctx_Type_Charset(b *testing.B) { 3694 app := New() 3695 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3696 defer app.ReleaseCtx(c) 3697 b.ReportAllocs() 3698 b.ResetTimer() 3699 for n := 0; n < b.N; n++ { 3700 c.Type(".json", "utf-8") 3701 c.Type("json", "utf-8") 3702 } 3703 } 3704 3705 // go test -run Test_Ctx_Vary 3706 func Test_Ctx_Vary(t *testing.T) { 3707 t.Parallel() 3708 app := New() 3709 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3710 defer app.ReleaseCtx(c) 3711 c.Vary("Origin") 3712 c.Vary("User-Agent") 3713 c.Vary("Accept-Encoding", "Accept") 3714 utils.AssertEqual(t, "Origin, User-Agent, Accept-Encoding, Accept", string(c.Response().Header.Peek("Vary"))) 3715 } 3716 3717 // go test -v -run=^$ -bench=Benchmark_Ctx_Vary -benchmem -count=4 3718 func Benchmark_Ctx_Vary(b *testing.B) { 3719 app := New() 3720 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3721 defer app.ReleaseCtx(c) 3722 b.ReportAllocs() 3723 b.ResetTimer() 3724 for n := 0; n < b.N; n++ { 3725 c.Vary("Origin", "User-Agent") 3726 } 3727 } 3728 3729 // go test -run Test_Ctx_Write 3730 func Test_Ctx_Write(t *testing.T) { 3731 t.Parallel() 3732 app := New() 3733 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3734 defer app.ReleaseCtx(c) 3735 _, err := c.Write([]byte("Hello, ")) 3736 utils.AssertEqual(t, nil, err) 3737 _, err = c.Write([]byte("World!")) 3738 utils.AssertEqual(t, nil, err) 3739 utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) 3740 } 3741 3742 // go test -v -run=^$ -bench=Benchmark_Ctx_Write -benchmem -count=4 3743 func Benchmark_Ctx_Write(b *testing.B) { 3744 app := New() 3745 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3746 defer app.ReleaseCtx(c) 3747 byt := []byte("Hello, World!") 3748 b.ReportAllocs() 3749 b.ResetTimer() 3750 3751 var err error 3752 for n := 0; n < b.N; n++ { 3753 _, err = c.Write(byt) 3754 } 3755 utils.AssertEqual(b, nil, err) 3756 } 3757 3758 // go test -run Test_Ctx_Writef 3759 func Test_Ctx_Writef(t *testing.T) { 3760 t.Parallel() 3761 app := New() 3762 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3763 defer app.ReleaseCtx(c) 3764 world := "World!" 3765 _, err := c.Writef("Hello, %s", world) 3766 utils.AssertEqual(t, nil, err) 3767 utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) 3768 } 3769 3770 // go test -v -run=^$ -bench=Benchmark_Ctx_Writef -benchmem -count=4 3771 func Benchmark_Ctx_Writef(b *testing.B) { 3772 app := New() 3773 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3774 defer app.ReleaseCtx(c) 3775 world := "World!" 3776 b.ReportAllocs() 3777 b.ResetTimer() 3778 3779 var err error 3780 for n := 0; n < b.N; n++ { 3781 _, err = c.Writef("Hello, %s", world) 3782 } 3783 utils.AssertEqual(b, nil, err) 3784 } 3785 3786 // go test -run Test_Ctx_WriteString 3787 func Test_Ctx_WriteString(t *testing.T) { 3788 t.Parallel() 3789 app := New() 3790 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3791 defer app.ReleaseCtx(c) 3792 _, err := c.WriteString("Hello, ") 3793 utils.AssertEqual(t, nil, err) 3794 _, err = c.WriteString("World!") 3795 utils.AssertEqual(t, nil, err) 3796 utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) 3797 } 3798 3799 // go test -run Test_Ctx_XHR 3800 func Test_Ctx_XHR(t *testing.T) { 3801 t.Parallel() 3802 app := New() 3803 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3804 defer app.ReleaseCtx(c) 3805 c.Request().Header.Set(HeaderXRequestedWith, "XMLHttpRequest") 3806 utils.AssertEqual(t, true, c.XHR()) 3807 } 3808 3809 // go test -run=^$ -bench=Benchmark_Ctx_XHR -benchmem -count=4 3810 func Benchmark_Ctx_XHR(b *testing.B) { 3811 app := New() 3812 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3813 defer app.ReleaseCtx(c) 3814 c.Request().Header.Set(HeaderXRequestedWith, "XMLHttpRequest") 3815 var equal bool 3816 b.ReportAllocs() 3817 b.ResetTimer() 3818 for n := 0; n < b.N; n++ { 3819 equal = c.XHR() 3820 } 3821 utils.AssertEqual(b, true, equal) 3822 } 3823 3824 // go test -v -run=^$ -bench=Benchmark_Ctx_SendString_B -benchmem -count=4 3825 func Benchmark_Ctx_SendString_B(b *testing.B) { 3826 app := New() 3827 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3828 defer app.ReleaseCtx(c) 3829 body := "Hello, world!" 3830 b.ReportAllocs() 3831 b.ResetTimer() 3832 3833 var err error 3834 for n := 0; n < b.N; n++ { 3835 err = c.SendString(body) 3836 } 3837 utils.AssertEqual(b, nil, err) 3838 utils.AssertEqual(b, []byte("Hello, world!"), c.Response().Body()) 3839 } 3840 3841 // go test -run Test_Ctx_QueryParser -v 3842 func Test_Ctx_QueryParser(t *testing.T) { 3843 t.Parallel() 3844 app := New() 3845 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3846 defer app.ReleaseCtx(c) 3847 type Query struct { 3848 ID int 3849 Name string 3850 Hobby []string 3851 } 3852 c.Request().SetBody([]byte(``)) 3853 c.Request().Header.SetContentType("") 3854 c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football") 3855 q := new(Query) 3856 utils.AssertEqual(t, nil, c.QueryParser(q)) 3857 utils.AssertEqual(t, 2, len(q.Hobby)) 3858 3859 c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football") 3860 q = new(Query) 3861 utils.AssertEqual(t, nil, c.QueryParser(q)) 3862 utils.AssertEqual(t, 2, len(q.Hobby)) 3863 3864 c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football") 3865 q = new(Query) 3866 utils.AssertEqual(t, nil, c.QueryParser(q)) 3867 utils.AssertEqual(t, 3, len(q.Hobby)) 3868 3869 empty := new(Query) 3870 c.Request().URI().SetQueryString("") 3871 utils.AssertEqual(t, nil, c.QueryParser(empty)) 3872 utils.AssertEqual(t, 0, len(empty.Hobby)) 3873 3874 type Query2 struct { 3875 Bool bool 3876 ID int 3877 Name string 3878 Hobby string 3879 FavouriteDrinks []string 3880 Empty []string 3881 Alloc []string 3882 No []int64 3883 } 3884 3885 c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1") 3886 q2 := new(Query2) 3887 q2.Bool = true 3888 q2.Name = "hello world" 3889 utils.AssertEqual(t, nil, c.QueryParser(q2)) 3890 utils.AssertEqual(t, "basketball,football", q2.Hobby) 3891 utils.AssertEqual(t, true, q2.Bool) 3892 utils.AssertEqual(t, "tom", q2.Name) // check value get overwritten 3893 utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, q2.FavouriteDrinks) 3894 var nilSlice []string 3895 utils.AssertEqual(t, nilSlice, q2.Empty) 3896 utils.AssertEqual(t, []string{""}, q2.Alloc) 3897 utils.AssertEqual(t, []int64{1}, q2.No) 3898 3899 type RequiredQuery struct { 3900 Name string `query:"name,required"` 3901 } 3902 rq := new(RequiredQuery) 3903 c.Request().URI().SetQueryString("") 3904 utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(rq).Error()) 3905 3906 type ArrayQuery struct { 3907 Data []string 3908 } 3909 aq := new(ArrayQuery) 3910 c.Request().URI().SetQueryString("data[]=john&data[]=doe") 3911 utils.AssertEqual(t, nil, c.QueryParser(aq)) 3912 utils.AssertEqual(t, 2, len(aq.Data)) 3913 } 3914 3915 // go test -run Test_Ctx_QueryParser_WithSetParserDecoder -v 3916 func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { 3917 t.Parallel() 3918 type NonRFCTime time.Time 3919 3920 nonRFCConverter := func(value string) reflect.Value { 3921 if v, err := time.Parse("2006-01-02", value); err == nil { 3922 return reflect.ValueOf(v) 3923 } 3924 return reflect.Value{} 3925 } 3926 3927 nonRFCTime := ParserType{ 3928 Customtype: NonRFCTime{}, 3929 Converter: nonRFCConverter, 3930 } 3931 3932 SetParserDecoder(ParserConfig{ 3933 IgnoreUnknownKeys: true, 3934 ParserType: []ParserType{nonRFCTime}, 3935 ZeroEmpty: true, 3936 SetAliasTag: "query", 3937 }) 3938 3939 app := New() 3940 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3941 defer app.ReleaseCtx(c) 3942 3943 type NonRFCTimeInput struct { 3944 Date NonRFCTime `query:"date"` 3945 Title string `query:"title"` 3946 Body string `query:"body"` 3947 } 3948 3949 c.Request().SetBody([]byte(``)) 3950 c.Request().Header.SetContentType("") 3951 q := new(NonRFCTimeInput) 3952 3953 c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October") 3954 utils.AssertEqual(t, nil, c.QueryParser(q)) 3955 utils.AssertEqual(t, "CustomDateTest", q.Title) 3956 date := fmt.Sprintf("%v", q.Date) 3957 utils.AssertEqual(t, "{0 63753609600 <nil>}", date) 3958 utils.AssertEqual(t, "October", q.Body) 3959 3960 c.Request().URI().SetQueryString("date=2021-04-10&title&Body=October") 3961 q = &NonRFCTimeInput{ 3962 Title: "Existing title", 3963 Body: "Existing Body", 3964 } 3965 utils.AssertEqual(t, nil, c.QueryParser(q)) 3966 utils.AssertEqual(t, "", q.Title) 3967 } 3968 3969 // go test -run Test_Ctx_QueryParser_Schema -v 3970 func Test_Ctx_QueryParser_Schema(t *testing.T) { 3971 t.Parallel() 3972 app := New() 3973 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 3974 defer app.ReleaseCtx(c) 3975 type Query1 struct { 3976 Name string `query:"name,required"` 3977 Nested struct { 3978 Age int `query:"age"` 3979 } `query:"nested,required"` 3980 } 3981 c.Request().SetBody([]byte(``)) 3982 c.Request().Header.SetContentType("") 3983 c.Request().URI().SetQueryString("name=tom&nested.age=10") 3984 q := new(Query1) 3985 utils.AssertEqual(t, nil, c.QueryParser(q)) 3986 3987 c.Request().URI().SetQueryString("namex=tom&nested.age=10") 3988 q = new(Query1) 3989 utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(q).Error()) 3990 3991 c.Request().URI().SetQueryString("name=tom&nested.agex=10") 3992 q = new(Query1) 3993 utils.AssertEqual(t, nil, c.QueryParser(q)) 3994 3995 c.Request().URI().SetQueryString("name=tom&test.age=10") 3996 q = new(Query1) 3997 utils.AssertEqual(t, "failed to decode: nested is empty", c.QueryParser(q).Error()) 3998 3999 type Query2 struct { 4000 Name string `query:"name"` 4001 Nested struct { 4002 Age int `query:"age,required"` 4003 } `query:"nested"` 4004 } 4005 c.Request().URI().SetQueryString("name=tom&nested.age=10") 4006 q2 := new(Query2) 4007 utils.AssertEqual(t, nil, c.QueryParser(q2)) 4008 4009 c.Request().URI().SetQueryString("nested.age=10") 4010 q2 = new(Query2) 4011 utils.AssertEqual(t, nil, c.QueryParser(q2)) 4012 4013 c.Request().URI().SetQueryString("nested.agex=10") 4014 q2 = new(Query2) 4015 utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) 4016 4017 c.Request().URI().SetQueryString("nested.agex=10") 4018 q2 = new(Query2) 4019 utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) 4020 4021 type Node struct { 4022 Value int `query:"val,required"` 4023 Next *Node `query:"next,required"` 4024 } 4025 c.Request().URI().SetQueryString("val=1&next.val=3") 4026 n := new(Node) 4027 utils.AssertEqual(t, nil, c.QueryParser(n)) 4028 utils.AssertEqual(t, 1, n.Value) 4029 utils.AssertEqual(t, 3, n.Next.Value) 4030 4031 c.Request().URI().SetQueryString("next.val=2") 4032 n = new(Node) 4033 utils.AssertEqual(t, "failed to decode: val is empty", c.QueryParser(n).Error()) 4034 4035 c.Request().URI().SetQueryString("val=3&next.value=2") 4036 n = new(Node) 4037 n.Next = new(Node) 4038 utils.AssertEqual(t, nil, c.QueryParser(n)) 4039 utils.AssertEqual(t, 3, n.Value) 4040 utils.AssertEqual(t, 0, n.Next.Value) 4041 4042 type Person struct { 4043 Name string `query:"name"` 4044 Age int `query:"age"` 4045 } 4046 4047 type CollectionQuery struct { 4048 Data []Person `query:"data"` 4049 } 4050 4051 c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10&data[1][name]=doe&data[1][age]=12") 4052 cq := new(CollectionQuery) 4053 utils.AssertEqual(t, nil, c.QueryParser(cq)) 4054 utils.AssertEqual(t, 2, len(cq.Data)) 4055 utils.AssertEqual(t, "john", cq.Data[0].Name) 4056 utils.AssertEqual(t, 10, cq.Data[0].Age) 4057 utils.AssertEqual(t, "doe", cq.Data[1].Name) 4058 utils.AssertEqual(t, 12, cq.Data[1].Age) 4059 4060 c.Request().URI().SetQueryString("data.0.name=john&data.0.age=10&data.1.name=doe&data.1.age=12") 4061 cq = new(CollectionQuery) 4062 utils.AssertEqual(t, nil, c.QueryParser(cq)) 4063 utils.AssertEqual(t, 2, len(cq.Data)) 4064 utils.AssertEqual(t, "john", cq.Data[0].Name) 4065 utils.AssertEqual(t, 10, cq.Data[0].Age) 4066 utils.AssertEqual(t, "doe", cq.Data[1].Name) 4067 utils.AssertEqual(t, 12, cq.Data[1].Age) 4068 } 4069 4070 // go test -run Test_Ctx_ReqHeaderParser -v 4071 func Test_Ctx_ReqHeaderParser(t *testing.T) { 4072 t.Parallel() 4073 app := New() 4074 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4075 defer app.ReleaseCtx(c) 4076 type Header struct { 4077 ID int 4078 Name string 4079 Hobby []string 4080 } 4081 c.Request().SetBody([]byte(``)) 4082 c.Request().Header.SetContentType("") 4083 4084 c.Request().Header.Add("id", "1") 4085 c.Request().Header.Add("Name", "John Doe") 4086 c.Request().Header.Add("Hobby", "golang,fiber") 4087 q := new(Header) 4088 utils.AssertEqual(t, nil, c.ReqHeaderParser(q)) 4089 utils.AssertEqual(t, 2, len(q.Hobby)) 4090 4091 c.Request().Header.Del("hobby") 4092 c.Request().Header.Add("Hobby", "golang,fiber,go") 4093 q = new(Header) 4094 utils.AssertEqual(t, nil, c.ReqHeaderParser(q)) 4095 utils.AssertEqual(t, 3, len(q.Hobby)) 4096 4097 empty := new(Header) 4098 c.Request().Header.Del("hobby") 4099 utils.AssertEqual(t, nil, c.QueryParser(empty)) 4100 utils.AssertEqual(t, 0, len(empty.Hobby)) 4101 4102 type Header2 struct { 4103 Bool bool 4104 ID int 4105 Name string 4106 Hobby string 4107 FavouriteDrinks []string 4108 Empty []string 4109 Alloc []string 4110 No []int64 4111 } 4112 4113 c.Request().Header.Add("id", "2") 4114 c.Request().Header.Add("Name", "Jane Doe") 4115 c.Request().Header.Del("hobby") 4116 c.Request().Header.Add("Hobby", "go,fiber") 4117 c.Request().Header.Add("favouriteDrinks", "milo,coke,pepsi") 4118 c.Request().Header.Add("alloc", "") 4119 c.Request().Header.Add("no", "1") 4120 4121 h2 := new(Header2) 4122 h2.Bool = true 4123 h2.Name = "hello world" 4124 utils.AssertEqual(t, nil, c.ReqHeaderParser(h2)) 4125 utils.AssertEqual(t, "go,fiber", h2.Hobby) 4126 utils.AssertEqual(t, true, h2.Bool) 4127 utils.AssertEqual(t, "Jane Doe", h2.Name) // check value get overwritten 4128 utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) 4129 var nilSlice []string 4130 utils.AssertEqual(t, nilSlice, h2.Empty) 4131 utils.AssertEqual(t, []string{""}, h2.Alloc) 4132 utils.AssertEqual(t, []int64{1}, h2.No) 4133 4134 type RequiredHeader struct { 4135 Name string `reqHeader:"name,required"` 4136 } 4137 rh := new(RequiredHeader) 4138 c.Request().Header.Del("name") 4139 utils.AssertEqual(t, "failed to decode: name is empty", c.ReqHeaderParser(rh).Error()) 4140 } 4141 4142 // go test -run Test_Ctx_ReqHeaderParser_WithSetParserDecoder -v 4143 func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { 4144 t.Parallel() 4145 type NonRFCTime time.Time 4146 4147 nonRFCConverter := func(value string) reflect.Value { 4148 if v, err := time.Parse("2006-01-02", value); err == nil { 4149 return reflect.ValueOf(v) 4150 } 4151 return reflect.Value{} 4152 } 4153 4154 nonRFCTime := ParserType{ 4155 Customtype: NonRFCTime{}, 4156 Converter: nonRFCConverter, 4157 } 4158 4159 SetParserDecoder(ParserConfig{ 4160 IgnoreUnknownKeys: true, 4161 ParserType: []ParserType{nonRFCTime}, 4162 ZeroEmpty: true, 4163 SetAliasTag: "req", 4164 }) 4165 4166 app := New() 4167 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4168 defer app.ReleaseCtx(c) 4169 4170 type NonRFCTimeInput struct { 4171 Date NonRFCTime `req:"date"` 4172 Title string `req:"title"` 4173 Body string `req:"body"` 4174 } 4175 4176 c.Request().SetBody([]byte(``)) 4177 c.Request().Header.SetContentType("") 4178 r := new(NonRFCTimeInput) 4179 4180 c.Request().Header.Add("Date", "2021-04-10") 4181 c.Request().Header.Add("Title", "CustomDateTest") 4182 c.Request().Header.Add("Body", "October") 4183 4184 utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) 4185 utils.AssertEqual(t, "CustomDateTest", r.Title) 4186 date := fmt.Sprintf("%v", r.Date) 4187 utils.AssertEqual(t, "{0 63753609600 <nil>}", date) 4188 utils.AssertEqual(t, "October", r.Body) 4189 4190 c.Request().Header.Add("Title", "") 4191 r = &NonRFCTimeInput{ 4192 Title: "Existing title", 4193 Body: "Existing Body", 4194 } 4195 utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) 4196 utils.AssertEqual(t, "", r.Title) 4197 } 4198 4199 // go test -run Test_Ctx_ReqHeaderParser_Schema -v 4200 func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { 4201 t.Parallel() 4202 app := New() 4203 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4204 defer app.ReleaseCtx(c) 4205 type Header1 struct { 4206 Name string `reqHeader:"Name,required"` 4207 Nested struct { 4208 Age int `reqHeader:"Age"` 4209 } `reqHeader:"Nested,required"` 4210 } 4211 c.Request().SetBody([]byte(``)) 4212 c.Request().Header.SetContentType("") 4213 4214 c.Request().Header.Add("Name", "tom") 4215 c.Request().Header.Add("Nested.Age", "10") 4216 q := new(Header1) 4217 utils.AssertEqual(t, nil, c.ReqHeaderParser(q)) 4218 4219 c.Request().Header.Del("Name") 4220 q = new(Header1) 4221 utils.AssertEqual(t, "failed to decode: Name is empty", c.ReqHeaderParser(q).Error()) 4222 4223 c.Request().Header.Add("Name", "tom") 4224 c.Request().Header.Del("Nested.Age") 4225 c.Request().Header.Add("Nested.Agex", "10") 4226 q = new(Header1) 4227 utils.AssertEqual(t, nil, c.ReqHeaderParser(q)) 4228 4229 c.Request().Header.Del("Nested.Agex") 4230 q = new(Header1) 4231 utils.AssertEqual(t, "failed to decode: Nested is empty", c.ReqHeaderParser(q).Error()) 4232 4233 c.Request().Header.Del("Nested.Agex") 4234 c.Request().Header.Del("Name") 4235 4236 type Header2 struct { 4237 Name string `reqHeader:"Name"` 4238 Nested struct { 4239 Age int `reqHeader:"age,required"` 4240 } `reqHeader:"Nested"` 4241 } 4242 4243 c.Request().Header.Add("Name", "tom") 4244 c.Request().Header.Add("Nested.Age", "10") 4245 4246 h2 := new(Header2) 4247 utils.AssertEqual(t, nil, c.ReqHeaderParser(h2)) 4248 4249 c.Request().Header.Del("Name") 4250 h2 = new(Header2) 4251 utils.AssertEqual(t, nil, c.ReqHeaderParser(h2)) 4252 4253 c.Request().Header.Del("Name") 4254 c.Request().Header.Del("Nested.Age") 4255 c.Request().Header.Add("Nested.Agex", "10") 4256 h2 = new(Header2) 4257 utils.AssertEqual(t, "failed to decode: Nested.age is empty", c.ReqHeaderParser(h2).Error()) 4258 4259 type Node struct { 4260 Value int `reqHeader:"Val,required"` 4261 Next *Node `reqHeader:"Next,required"` 4262 } 4263 c.Request().Header.Add("Val", "1") 4264 c.Request().Header.Add("Next.Val", "3") 4265 n := new(Node) 4266 utils.AssertEqual(t, nil, c.ReqHeaderParser(n)) 4267 utils.AssertEqual(t, 1, n.Value) 4268 utils.AssertEqual(t, 3, n.Next.Value) 4269 4270 c.Request().Header.Del("Val") 4271 n = new(Node) 4272 utils.AssertEqual(t, "failed to decode: Val is empty", c.ReqHeaderParser(n).Error()) 4273 4274 c.Request().Header.Add("Val", "3") 4275 c.Request().Header.Del("Next.Val") 4276 c.Request().Header.Add("Next.Value", "2") 4277 n = new(Node) 4278 n.Next = new(Node) 4279 utils.AssertEqual(t, nil, c.ReqHeaderParser(n)) 4280 utils.AssertEqual(t, 3, n.Value) 4281 utils.AssertEqual(t, 0, n.Next.Value) 4282 } 4283 4284 func Test_Ctx_EqualFieldType(t *testing.T) { 4285 t.Parallel() 4286 var out int 4287 utils.AssertEqual(t, false, equalFieldType(&out, reflect.Int, "key")) 4288 4289 var dummy struct{ f string } 4290 utils.AssertEqual(t, false, equalFieldType(&dummy, reflect.String, "key")) 4291 4292 var dummy2 struct{ f string } 4293 utils.AssertEqual(t, false, equalFieldType(&dummy2, reflect.String, "f")) 4294 4295 var user struct { 4296 Name string 4297 Address string `query:"address"` 4298 Age int `query:"AGE"` 4299 } 4300 utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "name")) 4301 utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "Name")) 4302 utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "address")) 4303 utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "Address")) 4304 utils.AssertEqual(t, true, equalFieldType(&user, reflect.Int, "AGE")) 4305 utils.AssertEqual(t, true, equalFieldType(&user, reflect.Int, "age")) 4306 } 4307 4308 // go test -v -run=^$ -bench=Benchmark_Ctx_QueryParser -benchmem -count=4 4309 func Benchmark_Ctx_QueryParser(b *testing.B) { 4310 app := New() 4311 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4312 defer app.ReleaseCtx(c) 4313 type Query struct { 4314 ID int 4315 Name string 4316 Hobby []string 4317 } 4318 c.Request().SetBody([]byte(``)) 4319 c.Request().Header.SetContentType("") 4320 c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football") 4321 q := new(Query) 4322 b.ReportAllocs() 4323 b.ResetTimer() 4324 4325 var err error 4326 for n := 0; n < b.N; n++ { 4327 err = c.QueryParser(q) 4328 } 4329 utils.AssertEqual(b, nil, err) 4330 utils.AssertEqual(b, nil, c.QueryParser(q)) 4331 } 4332 4333 // go test -v -run=^$ -bench=Benchmark_Ctx_parseQuery -benchmem -count=4 4334 func Benchmark_Ctx_parseQuery(b *testing.B) { 4335 app := New() 4336 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4337 defer app.ReleaseCtx(c) 4338 type Person struct { 4339 Name string `query:"name"` 4340 Age int `query:"age"` 4341 } 4342 4343 type CollectionQuery struct { 4344 Data []Person `query:"data"` 4345 } 4346 4347 c.Request().SetBody([]byte(``)) 4348 c.Request().Header.SetContentType("") 4349 c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10") 4350 cq := new(CollectionQuery) 4351 4352 b.ReportAllocs() 4353 b.ResetTimer() 4354 4355 var err error 4356 for n := 0; n < b.N; n++ { 4357 err = c.QueryParser(cq) 4358 } 4359 4360 utils.AssertEqual(b, nil, err) 4361 utils.AssertEqual(b, nil, c.QueryParser(cq)) 4362 } 4363 4364 // go test -v -run=^$ -bench=Benchmark_Ctx_QueryParser_Comma -benchmem -count=4 4365 func Benchmark_Ctx_QueryParser_Comma(b *testing.B) { 4366 app := New() 4367 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4368 defer app.ReleaseCtx(c) 4369 type Query struct { 4370 ID int 4371 Name string 4372 Hobby []string 4373 } 4374 c.Request().SetBody([]byte(``)) 4375 c.Request().Header.SetContentType("") 4376 // c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football") 4377 c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football") 4378 q := new(Query) 4379 b.ReportAllocs() 4380 b.ResetTimer() 4381 4382 var err error 4383 for n := 0; n < b.N; n++ { 4384 err = c.QueryParser(q) 4385 } 4386 utils.AssertEqual(b, nil, err) 4387 utils.AssertEqual(b, nil, c.QueryParser(q)) 4388 } 4389 4390 // go test -v -run=^$ -bench=Benchmark_Ctx_ReqHeaderParser -benchmem -count=4 4391 func Benchmark_Ctx_ReqHeaderParser(b *testing.B) { 4392 app := New() 4393 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4394 defer app.ReleaseCtx(c) 4395 type ReqHeader struct { 4396 ID int 4397 Name string 4398 Hobby []string 4399 } 4400 c.Request().SetBody([]byte(``)) 4401 c.Request().Header.SetContentType("") 4402 4403 c.Request().Header.Add("id", "1") 4404 c.Request().Header.Add("Name", "John Doe") 4405 c.Request().Header.Add("Hobby", "golang,fiber") 4406 4407 q := new(ReqHeader) 4408 b.ReportAllocs() 4409 b.ResetTimer() 4410 4411 var err error 4412 for n := 0; n < b.N; n++ { 4413 err = c.ReqHeaderParser(q) 4414 } 4415 utils.AssertEqual(b, nil, err) 4416 utils.AssertEqual(b, nil, c.ReqHeaderParser(q)) 4417 } 4418 4419 // go test -run Test_Ctx_BodyStreamWriter 4420 func Test_Ctx_BodyStreamWriter(t *testing.T) { 4421 t.Parallel() 4422 ctx := &fasthttp.RequestCtx{} 4423 4424 ctx.SetBodyStreamWriter(func(w *bufio.Writer) { 4425 fmt.Fprintf(w, "body writer line 1\n") 4426 if err := w.Flush(); err != nil { 4427 t.Errorf("unexpected error: %s", err) 4428 } 4429 fmt.Fprintf(w, "body writer line 2\n") 4430 }) 4431 if !ctx.IsBodyStream() { 4432 t.Fatal("IsBodyStream must return true") 4433 } 4434 4435 s := ctx.Response.String() 4436 br := bufio.NewReader(bytes.NewBufferString(s)) 4437 var resp fasthttp.Response 4438 if err := resp.Read(br); err != nil { 4439 t.Fatalf("Error when reading response: %s", err) 4440 } 4441 body := string(resp.Body()) 4442 expectedBody := "body writer line 1\nbody writer line 2\n" 4443 if body != expectedBody { 4444 t.Fatalf("unexpected body: %q. Expecting %q", body, expectedBody) 4445 } 4446 } 4447 4448 // go test -v -run=^$ -bench=Benchmark_Ctx_BodyStreamWriter -benchmem -count=4 4449 func Benchmark_Ctx_BodyStreamWriter(b *testing.B) { 4450 ctx := &fasthttp.RequestCtx{} 4451 user := []byte(`{"name":"john"}`) 4452 b.ReportAllocs() 4453 b.ResetTimer() 4454 4455 var err error 4456 for n := 0; n < b.N; n++ { 4457 ctx.ResetBody() 4458 ctx.SetBodyStreamWriter(func(w *bufio.Writer) { 4459 for i := 0; i < 10; i++ { 4460 _, err = w.Write(user) 4461 if err := w.Flush(); err != nil { 4462 return 4463 } 4464 } 4465 }) 4466 } 4467 utils.AssertEqual(b, nil, err) 4468 } 4469 4470 func Test_Ctx_String(t *testing.T) { 4471 t.Parallel() 4472 app := New() 4473 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4474 defer app.ReleaseCtx(c) 4475 4476 utils.AssertEqual(t, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", c.String()) 4477 } 4478 4479 func TestCtx_ParamsInt(t *testing.T) { 4480 // Create a test context and set some strings (or params) 4481 // create a fake app to be used within this test 4482 t.Parallel() 4483 app := New() 4484 4485 // Create some test endpoints 4486 4487 // For the user id I will use the number 1111, so I should be able to get the number 4488 // 1111 from the Ctx 4489 app.Get("/test/:user", func(c *Ctx) error { 4490 // utils.AssertEqual(t, "john", c.Params("user")) 4491 4492 num, err := c.ParamsInt("user") 4493 4494 // Check the number matches 4495 if num != 1111 { 4496 t.Fatalf("Expected number 1111 from the path, got %d", num) 4497 } 4498 4499 // Check no errors are returned, because we want NO errors in this one 4500 if err != nil { 4501 t.Fatalf("Expected nil error for 1111 test, got " + err.Error()) 4502 } 4503 4504 return nil 4505 }) 4506 4507 // In this test case, there will be a bad request where the expected number is NOT 4508 // a number in the path 4509 app.Get("/testnoint/:user", func(c *Ctx) error { 4510 // utils.AssertEqual(t, "john", c.Params("user")) 4511 4512 num, err := c.ParamsInt("user") 4513 4514 // Check the number matches 4515 if num != 0 { 4516 t.Fatalf("Expected number 0 from the path, got %d", num) 4517 } 4518 4519 // Check an error is returned, because we want NO errors in this one 4520 if err == nil { 4521 t.Fatal("Expected non nil error for bad req test, got nil") 4522 } 4523 4524 return nil 4525 }) 4526 4527 // For the user id I will use the number 2222, so I should be able to get the number 4528 // 2222 from the Ctx even when the default value is specified 4529 app.Get("/testignoredefault/:user", func(c *Ctx) error { 4530 // utils.AssertEqual(t, "john", c.Params("user")) 4531 4532 num, err := c.ParamsInt("user", 1111) 4533 4534 // Check the number matches 4535 if num != 2222 { 4536 t.Fatalf("Expected number 2222 from the path, got %d", num) 4537 } 4538 4539 // Check no errors are returned, because we want NO errors in this one 4540 if err != nil { 4541 t.Fatalf("Expected nil error for 2222 test, got " + err.Error()) 4542 } 4543 4544 return nil 4545 }) 4546 4547 // In this test case, there will be a bad request where the expected number is NOT 4548 // a number in the path, default value of 1111 should be used instead 4549 app.Get("/testdefault/:user", func(c *Ctx) error { 4550 // utils.AssertEqual(t, "john", c.Params("user")) 4551 4552 num, err := c.ParamsInt("user", 1111) 4553 4554 // Check the number matches 4555 if num != 1111 { 4556 t.Fatalf("Expected number 1111 from the path, got %d", num) 4557 } 4558 4559 // Check an error is returned, because we want NO errors in this one 4560 if err != nil { 4561 t.Fatalf("Expected nil error for 1111 test, got " + err.Error()) 4562 } 4563 4564 return nil 4565 }) 4566 4567 _, err := app.Test(httptest.NewRequest(MethodGet, "/test/1111", nil)) 4568 utils.AssertEqual(t, nil, err) 4569 4570 _, err = app.Test(httptest.NewRequest(MethodGet, "/testnoint/xd", nil)) 4571 utils.AssertEqual(t, nil, err) 4572 4573 _, err = app.Test(httptest.NewRequest(MethodGet, "/testignoredefault/2222", nil)) 4574 utils.AssertEqual(t, nil, err) 4575 4576 _, err = app.Test(httptest.NewRequest(MethodGet, "/testdefault/xd", nil)) 4577 utils.AssertEqual(t, nil, err) 4578 } 4579 4580 // go test -run Test_Ctx_GetRespHeader 4581 func Test_Ctx_GetRespHeader(t *testing.T) { 4582 t.Parallel() 4583 app := New() 4584 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4585 defer app.ReleaseCtx(c) 4586 4587 c.Set("test", "Hello, World 👋!") 4588 c.Response().Header.Set(HeaderContentType, "application/json") 4589 utils.AssertEqual(t, c.GetRespHeader("test"), "Hello, World 👋!") 4590 utils.AssertEqual(t, c.GetRespHeader(HeaderContentType), "application/json") 4591 } 4592 4593 // go test -run Test_Ctx_GetRespHeaders 4594 func Test_Ctx_GetRespHeaders(t *testing.T) { 4595 t.Parallel() 4596 app := New() 4597 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4598 defer app.ReleaseCtx(c) 4599 4600 c.Set("test", "Hello, World 👋!") 4601 c.Set("foo", "bar") 4602 c.Response().Header.Set(HeaderContentType, "application/json") 4603 4604 utils.AssertEqual(t, c.GetRespHeaders(), map[string]string{ 4605 "Content-Type": "application/json", 4606 "Foo": "bar", 4607 "Test": "Hello, World 👋!", 4608 }) 4609 } 4610 4611 // go test -run Test_Ctx_GetReqHeaders 4612 func Test_Ctx_GetReqHeaders(t *testing.T) { 4613 t.Parallel() 4614 app := New() 4615 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4616 defer app.ReleaseCtx(c) 4617 4618 c.Request().Header.Set("test", "Hello, World 👋!") 4619 c.Request().Header.Set("foo", "bar") 4620 c.Request().Header.Set(HeaderContentType, "application/json") 4621 4622 utils.AssertEqual(t, c.GetReqHeaders(), map[string]string{ 4623 "Content-Type": "application/json", 4624 "Foo": "bar", 4625 "Test": "Hello, World 👋!", 4626 }) 4627 } 4628 4629 // go test -run Test_Ctx_IsFromLocal 4630 func Test_Ctx_IsFromLocal(t *testing.T) { 4631 t.Parallel() 4632 // Test "0.0.0.0", "127.0.0.1" and "::1". 4633 { 4634 app := New() 4635 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4636 defer app.ReleaseCtx(c) 4637 utils.AssertEqual(t, true, c.IsFromLocal()) 4638 } 4639 // This is a test for "0.0.0.0" 4640 { 4641 app := New() 4642 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4643 c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.0") 4644 defer app.ReleaseCtx(c) 4645 utils.AssertEqual(t, true, c.IsFromLocal()) 4646 } 4647 4648 // This is a test for "127.0.0.1" 4649 { 4650 app := New() 4651 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4652 c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") 4653 defer app.ReleaseCtx(c) 4654 utils.AssertEqual(t, true, c.IsFromLocal()) 4655 } 4656 4657 // This is a test for "localhost" 4658 { 4659 app := New() 4660 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4661 defer app.ReleaseCtx(c) 4662 utils.AssertEqual(t, true, c.IsFromLocal()) 4663 } 4664 4665 // This is testing "::1", it is the compressed format IPV6 loopback address 0:0:0:0:0:0:0:1. 4666 // It is the equivalent of the IPV4 address 127.0.0.1. 4667 { 4668 app := New() 4669 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4670 c.Request().Header.Set(HeaderXForwardedFor, "::1") 4671 defer app.ReleaseCtx(c) 4672 utils.AssertEqual(t, true, c.IsFromLocal()) 4673 } 4674 4675 { 4676 app := New() 4677 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4678 c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90") 4679 defer app.ReleaseCtx(c) 4680 utils.AssertEqual(t, false, c.IsFromLocal()) 4681 } 4682 } 4683 4684 // go test -run Test_Ctx_RepeatParserWithSameStruct -v 4685 func Test_Ctx_RepeatParserWithSameStruct(t *testing.T) { 4686 t.Parallel() 4687 app := New() 4688 c := app.AcquireCtx(&fasthttp.RequestCtx{}) 4689 defer app.ReleaseCtx(c) 4690 4691 type Request struct { 4692 QueryParam string `query:"query_param"` 4693 HeaderParam string `reqHeader:"header_param"` 4694 BodyParam string `json:"body_param" xml:"body_param" form:"body_param"` 4695 } 4696 4697 r := new(Request) 4698 4699 c.Request().URI().SetQueryString("query_param=query_param") 4700 utils.AssertEqual(t, nil, c.QueryParser(r)) 4701 utils.AssertEqual(t, "query_param", r.QueryParam) 4702 4703 c.Request().Header.Add("header_param", "header_param") 4704 utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) 4705 utils.AssertEqual(t, "header_param", r.HeaderParam) 4706 4707 var gzipJSON bytes.Buffer 4708 w := gzip.NewWriter(&gzipJSON) 4709 _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) //nolint:errcheck // This will never fail 4710 err := w.Close() 4711 utils.AssertEqual(t, nil, err) 4712 c.Request().Header.SetContentType(MIMEApplicationJSON) 4713 c.Request().Header.Set(HeaderContentEncoding, "gzip") 4714 c.Request().SetBody(gzipJSON.Bytes()) 4715 c.Request().Header.SetContentLength(len(gzipJSON.Bytes())) 4716 utils.AssertEqual(t, nil, c.BodyParser(r)) 4717 utils.AssertEqual(t, "body_param", r.BodyParam) 4718 c.Request().Header.Del(HeaderContentEncoding) 4719 4720 testDecodeParser := func(contentType, body string) { 4721 c.Request().Header.SetContentType(contentType) 4722 c.Request().SetBody([]byte(body)) 4723 c.Request().Header.SetContentLength(len(body)) 4724 utils.AssertEqual(t, nil, c.BodyParser(r)) 4725 utils.AssertEqual(t, "body_param", r.BodyParam) 4726 } 4727 4728 testDecodeParser(MIMEApplicationJSON, `{"body_param":"body_param"}`) 4729 testDecodeParser(MIMEApplicationXML, `<Demo><body_param>body_param</body_param></Demo>`) 4730 testDecodeParser(MIMEApplicationForm, "body_param=body_param") 4731 testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"body_param\"\r\n\r\nbody_param\r\n--b--") 4732 }