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