mirror of https://github.com/gofiber/fiber.git
Make ctx pool accessible + ctx benchmarks (#355)
**🚀 Fiber `v1.9.6`** The Ctx pool is now accessible for third-party packages 🔥 New - `func AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx` - `func ReleaseCtx(ctx *Ctx)` 🩹 Fixes - Some `Ctx` methods didn't work correctly when called without an `*App` pointer. - `ctx.Vary` sometimes added duplicates to the response header 🧹 Updates - Add context benchmarks - Remove some unnecessary functions from `utils` 🧬 Middleware - [gofiber/adaptor](https://github.com/gofiber/adaptor) `v0.0.1` Converter for net/http handlers to/from Fiber handlerspull/366/head
parent
e2cc8106bb
commit
6e58bfcde3
22
app.go
22
app.go
|
@ -25,7 +25,7 @@ import (
|
|||
)
|
||||
|
||||
// Version of current package
|
||||
const Version = "1.9.5"
|
||||
const Version = "1.9.6"
|
||||
|
||||
// Map is a shortcut for map[string]interface{}
|
||||
type Map map[string]interface{}
|
||||
|
@ -33,13 +33,15 @@ type Map map[string]interface{}
|
|||
// App denotes the Fiber application.
|
||||
type App struct {
|
||||
// Internal fields
|
||||
mutex sync.Mutex // Mutual exclusion
|
||||
server *fasthttp.Server // FastHTTP server
|
||||
testconn *testConn // Test connection
|
||||
routes [][]*Route // Route stack
|
||||
testconn *testConn // Test connection
|
||||
routes [][]*Route // Route stack
|
||||
|
||||
// External fields
|
||||
Settings *Settings // Fiber settings
|
||||
|
||||
// Fasthttp server
|
||||
mutex sync.Mutex // Mutual exclusion
|
||||
server *fasthttp.Server // FastHTTP server
|
||||
}
|
||||
|
||||
// Settings holds is a struct holding the server settings
|
||||
|
@ -123,13 +125,13 @@ func New(settings ...*Settings) *App {
|
|||
// Create settings
|
||||
app.Settings = new(Settings)
|
||||
// Set default settings
|
||||
app.Settings.Prefork = isPrefork()
|
||||
app.Settings.Prefork = getArgument("-prefork")
|
||||
app.Settings.BodyLimit = 4 * 1024 * 1024
|
||||
// If settings exist, set defaults
|
||||
if len(settings) > 0 {
|
||||
app.Settings = settings[0] // Set custom settings
|
||||
if !app.Settings.Prefork { // Default to -prefork flag if false
|
||||
app.Settings.Prefork = isPrefork()
|
||||
app.Settings.Prefork = getArgument("-prefork")
|
||||
}
|
||||
if app.Settings.BodyLimit <= 0 { // Default MaxRequestBodySize
|
||||
app.Settings.BodyLimit = 4 * 1024 * 1024
|
||||
|
@ -142,7 +144,7 @@ func New(settings ...*Settings) *App {
|
|||
getBytes = getBytesImmutable
|
||||
}
|
||||
}
|
||||
// Setup test connection
|
||||
// Setup test listener
|
||||
app.testconn = new(testConn)
|
||||
// Setup server
|
||||
app.server = app.newServer()
|
||||
|
@ -403,7 +405,7 @@ func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error {
|
|||
ln = tls.NewListener(ln, tlsconfig[0])
|
||||
}
|
||||
// Print listening message
|
||||
if !app.Settings.DisableStartupMessage && !isChild() {
|
||||
if !app.Settings.DisableStartupMessage && !getArgument("-child") {
|
||||
fmt.Printf(" _______ __\n ____ / ____(_) /_ ___ _____\n_____ / /_ / / __ \\/ _ \\/ ___/\n __ / __/ / / /_/ / __/ /\n /_/ /_/_.___/\\___/_/ v%s\n", Version)
|
||||
fmt.Printf("Started listening on %s\n", ln.Addr().String())
|
||||
}
|
||||
|
@ -487,7 +489,7 @@ func (app *App) Test(request *http.Request, msTimeout ...int) (*http.Response, e
|
|||
// Sharding: https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
|
||||
func (app *App) prefork(address string) (ln net.Listener, err error) {
|
||||
// Master proc
|
||||
if !isChild() {
|
||||
if !getArgument("-child") {
|
||||
addr, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return ln, err
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||||
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||||
// 📌 API Documentation: https://docs.gofiber.io
|
||||
|
||||
package fiber
|
59
ctx.go
59
ctx.go
|
@ -69,30 +69,34 @@ var schemaDecoderForm = schema.NewDecoder()
|
|||
var schemaDecoderQuery = schema.NewDecoder()
|
||||
var cacheControlNoCacheRegexp, _ = regexp.Compile(`/(?:^|,)\s*?no-cache\s*?(?:,|$)/`)
|
||||
|
||||
// Ctx pool
|
||||
var poolCtx = sync.Pool{
|
||||
var ctxPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Ctx)
|
||||
},
|
||||
}
|
||||
|
||||
// Acquire Ctx from pool
|
||||
func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
|
||||
ctx := poolCtx.Get().(*Ctx)
|
||||
// AcquireCtx from pool
|
||||
func AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
|
||||
ctx := ctxPool.Get().(*Ctx)
|
||||
// Set stack index
|
||||
ctx.index = -1
|
||||
// Set path
|
||||
ctx.path = getString(fctx.URI().Path())
|
||||
// Set method
|
||||
ctx.method = getString(fctx.Request.Header.Method())
|
||||
// Attach fasthttp request to ctx
|
||||
ctx.Fasthttp = fctx
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Return Ctx to pool
|
||||
func releaseCtx(ctx *Ctx) {
|
||||
// ReleaseCtx to pool
|
||||
func ReleaseCtx(ctx *Ctx) {
|
||||
// Reset values
|
||||
ctx.route = nil
|
||||
ctx.values = nil
|
||||
ctx.Fasthttp = nil
|
||||
ctx.err = nil
|
||||
poolCtx.Put(ctx)
|
||||
ctxPool.Put(ctx)
|
||||
}
|
||||
|
||||
// Accepts checks if the specified extensions or content types are acceptable.
|
||||
|
@ -605,6 +609,9 @@ func (ctx *Ctx) MultipartForm() (*multipart.Form, error) {
|
|||
// Next executes the next method in the stack that matches the current route.
|
||||
// You can pass an optional error for custom error handling.
|
||||
func (ctx *Ctx) Next(err ...error) {
|
||||
if ctx.app == nil {
|
||||
return
|
||||
}
|
||||
ctx.route = nil
|
||||
ctx.values = nil
|
||||
if len(err) > 0 {
|
||||
|
@ -635,7 +642,7 @@ func (ctx *Ctx) Params(key string) (value string) {
|
|||
// Path returns the path part of the request URL.
|
||||
// Optionally, you could override the path.
|
||||
func (ctx *Ctx) Path(override ...string) string {
|
||||
if len(override) > 0 {
|
||||
if len(override) > 0 && ctx.app != nil {
|
||||
// Non strict routing
|
||||
if !ctx.app.Settings.StrictRouting && len(override[0]) > 1 {
|
||||
override[0] = strings.TrimRight(override[0], "/")
|
||||
|
@ -722,17 +729,18 @@ func (ctx *Ctx) Render(file string, bind interface{}) error {
|
|||
var err error
|
||||
var raw []byte
|
||||
var html string
|
||||
|
||||
if ctx.app.Settings.TemplateFolder != "" {
|
||||
file = filepath.Join(ctx.app.Settings.TemplateFolder, file)
|
||||
if ctx.app != nil {
|
||||
if ctx.app.Settings.TemplateFolder != "" {
|
||||
file = filepath.Join(ctx.app.Settings.TemplateFolder, file)
|
||||
}
|
||||
if ctx.app.Settings.TemplateExtension != "" {
|
||||
file = file + ctx.app.Settings.TemplateExtension
|
||||
}
|
||||
if raw, err = ioutil.ReadFile(filepath.Clean(file)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ctx.app.Settings.TemplateExtension != "" {
|
||||
file = file + ctx.app.Settings.TemplateExtension
|
||||
}
|
||||
if raw, err = ioutil.ReadFile(filepath.Clean(file)); err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.app.Settings.TemplateEngine != nil {
|
||||
if ctx.app != nil && ctx.app.Settings.TemplateEngine != nil {
|
||||
// Custom template engine
|
||||
// https://github.com/gofiber/template
|
||||
if html, err = ctx.app.Settings.TemplateEngine(getString(raw), bind); err != nil {
|
||||
|
@ -855,16 +863,19 @@ func (ctx *Ctx) Vary(fields ...string) {
|
|||
if len(fields) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
h := getString(ctx.Fasthttp.Response.Header.Peek(HeaderVary))
|
||||
h := strings.ToLower(getString(ctx.Fasthttp.Response.Header.Peek(HeaderVary)))
|
||||
for i := range fields {
|
||||
if h == "" {
|
||||
fields[i] = strings.ToLower(fields[i])
|
||||
if len(h) == 0 {
|
||||
h += fields[i]
|
||||
} else {
|
||||
} else if !strings.Contains(h, " "+fields[i]) && !strings.Contains(h, fields[i]+"") || !strings.Contains(h, fields[i]+",") {
|
||||
// Next developer, it's your job to optimize the following problem
|
||||
// Does the header value "Accept" exist in the following header value
|
||||
// "Origin, User-Agent, Accept-Encoding"
|
||||
// "Accept-Encoding" contains "Accept", false positive
|
||||
h += ", " + fields[i]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Set(HeaderVary, h)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
||||
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
||||
// 📌 API Documentation: https://docs.gofiber.io
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"mime/multipart"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
func Benchmark_Ctx_Accepts(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.Accepts(".xml")
|
||||
}
|
||||
|
||||
assertEqual(b, ".xml", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_AcceptsCharsets(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Accept-Charset", "utf-8, iso-8859-1;q=0.5")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.AcceptsCharsets("utf-8")
|
||||
}
|
||||
|
||||
assertEqual(b, "utf-8", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_AcceptsEncodings(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Accept-Encoding", "deflate, gzip;q=1.0, *;q=0.5")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.AcceptsEncodings("gzip")
|
||||
}
|
||||
|
||||
assertEqual(b, "gzip", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_AcceptsLanguages(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Accept-Language", "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.AcceptsEncodings("fr")
|
||||
}
|
||||
|
||||
assertEqual(b, "fr", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_BaseURL(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.SetHost("google.com:1337")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.BaseURL()
|
||||
}
|
||||
|
||||
assertEqual(b, "http://google.com:1337", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Body(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.SetBody([]byte("The best thing about a boolean is even if you are wrong, you are only off by a bit."))
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.Body()
|
||||
}
|
||||
|
||||
assertEqual(b, "The best thing about a boolean is even if you are wrong, you are only off by a bit.", res)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// func Benchmark_Ctx_BodyParser(b *testing.B) {
|
||||
|
||||
// }
|
||||
|
||||
func Benchmark_Ctx_Cookies(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Cookie", "john=doe")
|
||||
|
||||
var res string
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.Cookies("john")
|
||||
}
|
||||
|
||||
assertEqual(b, "doe", res)
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_FormFile(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
ioWriter, _ := writer.CreateFormFile("file", "test")
|
||||
_, _ = ioWriter.Write([]byte("hello world"))
|
||||
writer.Close()
|
||||
|
||||
c.Fasthttp.Request.Header.SetMethod("POST")
|
||||
c.Fasthttp.Request.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
c.Fasthttp.Request.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes())))
|
||||
c.Fasthttp.Request.SetBody(body.Bytes())
|
||||
|
||||
var res *multipart.FileHeader
|
||||
for n := 0; n < b.N; n++ {
|
||||
res, _ = c.FormFile("file")
|
||||
}
|
||||
|
||||
assertEqual(b, "test", res.Filename)
|
||||
assertEqual(b, "application/octet-stream", res.Header["Content-Type"][0])
|
||||
assertEqual(b, int64(11), res.Size)
|
||||
}
|
||||
|
||||
// func Benchmark_Ctx_FormValue(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Fresh(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Get(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Hostname(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_IP(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_IPs(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Is(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Locals(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Method(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_MultipartForm(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_OriginalURL(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Params(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Path(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Query(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Range(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Route(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_SaveFile(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Secure(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Stale(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Subdomains(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Append(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Attachment(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_ClearCookie(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Cookie(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// func Benchmark_Ctx_Download(b *testing.B) {
|
||||
// TODO
|
||||
// }
|
||||
|
||||
func Benchmark_Ctx_Format(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("Accept", "text/html")
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Format("Hello, World!")
|
||||
}
|
||||
|
||||
assertEqual(b, "<p>Hello, World!</p>", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_JSON(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.JSON(data)
|
||||
}
|
||||
|
||||
assertEqual(b, `{"Name":"Grame","Age":20}`, string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_JSONP(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.JSONP(data, "john")
|
||||
}
|
||||
|
||||
assertEqual(b, `john({"Name":"Grame","Age":20});`, string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Links(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Links(
|
||||
"http://api.example.com/users?page=2", "next",
|
||||
"http://api.example.com/users?page=5", "last",
|
||||
)
|
||||
}
|
||||
assertEqual(b, `<http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"`, string(c.Fasthttp.Response.Header.Peek("Link")))
|
||||
}
|
||||
|
||||
// TODO
|
||||
// func Benchmark_Ctx_Next(b *testing.B) {
|
||||
|
||||
// }
|
||||
|
||||
func Benchmark_Ctx_Redirect(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Redirect("http://example.com", 301)
|
||||
}
|
||||
assertEqual(b, 301, c.Fasthttp.Response.StatusCode())
|
||||
assertEqual(b, "http://example.com", string(c.Fasthttp.Response.Header.Peek("Location")))
|
||||
}
|
||||
|
||||
// TODO
|
||||
// func Benchmark_Ctx_Render(b *testing.B) {
|
||||
|
||||
// }
|
||||
|
||||
func Benchmark_Ctx_Send(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Send([]byte("Hello, World"))
|
||||
c.Send("Hello, World")
|
||||
c.Send(1337)
|
||||
}
|
||||
assertEqual(b, "1337", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_SendBytes(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.SendBytes([]byte("Hello, World"))
|
||||
}
|
||||
assertEqual(b, "Hello, World", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_SendStatus(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.SendStatus(415)
|
||||
}
|
||||
assertEqual(b, 415, c.Fasthttp.Response.StatusCode())
|
||||
assertEqual(b, "Unsupported Media Type", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_SendString(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Send("Hello, World")
|
||||
}
|
||||
assertEqual(b, "Hello, World", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Set(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Set("X-I'm-a-Dummy-HeAdEr", "1337")
|
||||
}
|
||||
|
||||
assertEqual(b, "1337", string(c.Fasthttp.Response.Header.Peek("X-I'm-a-Dummy-HeAdEr")))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Type(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Type(".json")
|
||||
c.Type("json")
|
||||
}
|
||||
|
||||
assertEqual(b, "application/json", string(c.Fasthttp.Response.Header.Peek("Content-Type")))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Vary(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Vary("Origin")
|
||||
}
|
||||
|
||||
assertEqual(b, "Origin", string(c.Fasthttp.Response.Header.Peek("Vary")))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_Write(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
c.Write("Hello, ")
|
||||
c.Write([]byte("World! "))
|
||||
c.Write(123)
|
||||
}
|
||||
c.Send("") // empty body
|
||||
c.Write("Hello, World!")
|
||||
assertEqual(b, "Hello, World!", string(c.Fasthttp.Response.Body()))
|
||||
}
|
||||
|
||||
func Benchmark_Ctx_XHR(b *testing.B) {
|
||||
c := AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer ReleaseCtx(c)
|
||||
|
||||
c.Fasthttp.Request.Header.Set("X-Requested-With", "xMlHtTpReQuEst")
|
||||
|
||||
var res bool
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = c.XHR()
|
||||
}
|
||||
|
||||
assertEqual(b, true, res)
|
||||
}
|
31
ctx_test.go
31
ctx_test.go
|
@ -569,20 +569,6 @@ func Test_Subdomains(t *testing.T) {
|
|||
assertEqual(t, nil, err, "app.Test(req)")
|
||||
assertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
}
|
||||
func Test_XHR(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
assertEqual(t, true, c.XHR())
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
|
||||
resp, err := app.Test(req)
|
||||
assertEqual(t, nil, err, "app.Test(req)")
|
||||
assertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
}
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
app := New()
|
||||
|
@ -979,7 +965,7 @@ func Test_Vary(t *testing.T) {
|
|||
resp, err := app.Test(req)
|
||||
assertEqual(t, nil, err, "app.Test(req)")
|
||||
assertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
assertEqual(t, "Origin, User-Agent, Accept-Encoding, Accept", resp.Header.Get("Vary"))
|
||||
assertEqual(t, "origin, user-agent, accept-encoding, accept", resp.Header.Get("Vary"))
|
||||
}
|
||||
func Test_Write(t *testing.T) {
|
||||
app := New()
|
||||
|
@ -1000,3 +986,18 @@ func Test_Write(t *testing.T) {
|
|||
assertEqual(t, nil, err)
|
||||
assertEqual(t, `Hello, World! 123`, string(body))
|
||||
}
|
||||
|
||||
func Test_XHR(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
assertEqual(t, true, c.XHR())
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||
|
||||
resp, err := app.Test(req)
|
||||
assertEqual(t, nil, err, "app.Test(req)")
|
||||
assertEqual(t, 200, resp.StatusCode, "Status code")
|
||||
}
|
||||
|
|
|
@ -106,11 +106,10 @@ func (r *Route) matchRoute(path string) (match bool, values []string) {
|
|||
|
||||
func (app *App) handler(fctx *fasthttp.RequestCtx) {
|
||||
// get fiber context from sync pool
|
||||
ctx := acquireCtx(fctx)
|
||||
defer releaseCtx(ctx)
|
||||
ctx := AcquireCtx(fctx)
|
||||
defer ReleaseCtx(ctx)
|
||||
// Attach app poiner to access the routes
|
||||
ctx.app = app
|
||||
|
||||
// Case sensitive routing
|
||||
if !app.Settings.CaseSensitive {
|
||||
ctx.path = strings.ToLower(ctx.path)
|
||||
|
@ -263,7 +262,6 @@ func (app *App) registerStatic(prefix, root string, config ...Static) {
|
|||
}
|
||||
// Serve file
|
||||
fileHandler(c.Fasthttp)
|
||||
|
||||
// Finish request if found and not forbidden
|
||||
status := c.Fasthttp.Response.StatusCode()
|
||||
if status != 404 && status != 403 {
|
||||
|
|
98
utils.go
98
utils.go
|
@ -100,67 +100,16 @@ func getMIME(extension string) (mime string) {
|
|||
return mime
|
||||
}
|
||||
|
||||
// #nosec G103
|
||||
// getString converts byte slice to a string without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
var getString = func(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
var getStringImmutable = func(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// #nosec G103
|
||||
// getBytes converts string to a byte slice without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
var getBytes = func(s string) (b []byte) {
|
||||
return *(*[]byte)(unsafe.Pointer(&s))
|
||||
}
|
||||
var getBytesImmutable = func(s string) (b []byte) {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
// Check if -prefork is in arguments
|
||||
func isPrefork() bool {
|
||||
// Check if key is in arguments
|
||||
func getArgument(arg string) bool {
|
||||
for i := range os.Args[1:] {
|
||||
if os.Args[1:][i] == "-prefork" {
|
||||
if os.Args[1:][i] == arg {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if -child is in arguments
|
||||
func isChild() bool {
|
||||
for i := range os.Args[1:] {
|
||||
if os.Args[1:][i] == "-child" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// https://golang.org/src/net/net.go#L113
|
||||
// Helper methods for application#test
|
||||
type testConn struct {
|
||||
net.Conn
|
||||
r bytes.Buffer
|
||||
w bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *testConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(0, 0, 0, 0),
|
||||
}
|
||||
}
|
||||
func (c *testConn) LocalAddr() net.Addr { return c.RemoteAddr() }
|
||||
func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) }
|
||||
func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) }
|
||||
func (c *testConn) Close() error { return nil }
|
||||
func (c *testConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110
|
||||
func parseTokenList(noneMatchBytes []byte) []string {
|
||||
|
@ -189,6 +138,47 @@ func parseTokenList(noneMatchBytes []byte) []string {
|
|||
return list
|
||||
}
|
||||
|
||||
// https://golang.org/src/net/net.go#L113
|
||||
// Helper methods for application#test
|
||||
type testConn struct {
|
||||
net.Conn
|
||||
r bytes.Buffer
|
||||
w bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *testConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(0, 0, 0, 0),
|
||||
}
|
||||
}
|
||||
func (c *testConn) LocalAddr() net.Addr { return c.RemoteAddr() }
|
||||
func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) }
|
||||
func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) }
|
||||
func (c *testConn) Close() error { return nil }
|
||||
func (c *testConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
// #nosec G103
|
||||
// getString converts byte slice to a string without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
var getString = func(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
var getStringImmutable = func(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// #nosec G103
|
||||
// getBytes converts string to a byte slice without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
var getBytes = func(s string) (b []byte) {
|
||||
return *(*[]byte)(unsafe.Pointer(&s))
|
||||
}
|
||||
var getBytesImmutable = func(s string) (b []byte) {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
// HTTP methods and their unique INTs
|
||||
var methodINT = map[string]int{
|
||||
MethodGet: 0,
|
||||
|
|
Loading…
Reference in New Issue