+
+
+
+ JustDave
+
+ |
@@ -295,7 +461,7 @@ Eğer **teşekkür etmek** ve/veya `Fiber` ın aktif geliştirilmesini destekle
|
- Vic Shóstak
+ Vic Shóstak
|
diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md
index 33292c094a..6a20c30860 100644
--- a/.github/README_zh-CN.md
+++ b/.github/README_zh-CN.md
@@ -118,7 +118,7 @@ Fiber **受** Internet上最流行的Web框架Expressjs的**启发** 。我们
下面列出了一些常见示例。如果您想查看更多代码示例,请访问我们的[Recipes存储库](https://github.com/gofiber/recipes)或访问我们的[API文档](https://fiber.wiki) 。
-### 路由
+### Routing
```go
func main() {
@@ -146,13 +146,13 @@ func main() {
}
```
-### 静态文件
-
+### Serve static files
+https://fiber.wiki/application#static
```go
func main() {
app := fiber.New()
- app.Static("/public")
+ app.Static("/", "/public")
// => http://localhost:3000/js/script.js
// => http://localhost:3000/css/style.css
@@ -167,8 +167,9 @@ func main() {
}
```
-### 中间件
-
+### Middleware & Next
+https://fiber.wiki/routing#middleware
+https://fiber.wiki/context#next
```go
func main() {
app := fiber.New()
@@ -196,12 +197,13 @@ func main() {
```
- 📚 显示更多代码示例
-
-### 模板引擎
+ 📚 Show more code examples
-已经支持的:
+### Template engines
+https://fiber.wiki/application#settings
+https://fiber.wiki/context#render
+Supported engines:
- [html](https://golang.org/pkg/html/template/)
- [amber](https://github.com/eknkc/amber)
- [handlebars](https://github.com/aymerick/raymond)
@@ -234,8 +236,8 @@ func main() {
}
```
-### 组路由
-
+### Grouping routes into chains
+https://fiber.wiki/application#group
```go
func main() {
app := fiber.New()
@@ -257,16 +259,73 @@ func main() {
}
```
-### 自定义 404 响应
+### Middleware logger
+https://fiber.wiki/middleware#logger
+```go
+import (
+ "github.com/gofiber/fiber"
+ "github.com/gofiber/fiber/middleware"
+)
+
+func main() {
+ app := fiber.New()
+
+ // If you want to change default Logger config
+ loggerConfig := middleware.LoggerConfig{
+ Format: "${time} - ${method} ${path}\n",
+ TimeFormat: "Mon, 2 Jan 2006 15:04:05 MST",
+ }
+
+ // Middleware for Logger with config
+ app.Use(middleware.Logger(loggerConfig))
+
+ // ...
+}
+```
+
+### Cross-Origin Resource Sharing (CORS)
+https://fiber.wiki/middleware#cors
+
+[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is a mechanism that uses additional HTTP headers to tell browsers to give a web application running at one origin, access to selected resources from a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.
+
+```go
+import (
+ "github.com/gofiber/fiber"
+ "github.com/gofiber/fiber/middleware"
+)
+
+func main() {
+ app := fiber.New()
+
+ // Connect CORS for each route as middleware
+ app.Use(middleware.CORS())
+
+ app.Get("/", func(c *fiber.Ctx) {
+ c.Send("CORS is enabled!")
+ })
+
+ app.Listen(3000)
+}
+```
+
+Check CORS by passing any domain in `Origin` header:
+
+```bash
+curl -H "Origin: http://example.com" --verbose http://localhost:3000
+```
+
+### Custom 404 response
```go
func main() {
app := fiber.New()
app.Static("/public")
+
app.Get("/demo", func(c *fiber.Ctx) {
c.Send("This is a demo!")
})
+
app.Post("/register", func(c *fiber.Ctx) {
c.Send("Welcome!")
})
@@ -280,36 +339,41 @@ func main() {
}
```
-### JSON
-
+### JSON Response
+https://fiber.wiki/context#json
```go
+type User struct {
+ Name string `json:"name"`
+ Age int `json:"age"`
+}
+
func main() {
app := fiber.New()
- type User struct {
- Name string `json:"name"`
- Age int `json:"age"`
- }
+ app.Get("/user", func(c *fiber.Ctx) {
+ c.JSON(&User{"John", 20})
+ // {"name":"John", "age":20}
+ })
- // Serialize JSON
app.Get("/json", func(c *fiber.Ctx) {
- c.JSON(&User{"John", 20})
- // => {"name":"John", "age":20}
+ c.JSON(&fiber.Map{
+ "success": true,
+ "message": "Hi John!",
+ })
+ // {"success":true, "message":"Hi John!"}
})
app.Listen(3000)
}
```
-### WebSocket
-
+### WebSocket support
+https://fiber.wiki/application#websocket
```go
func main() {
app := fiber.New()
- app.WebSocket("/ws/:name", func(c *fiber.Conn) {
- log.Println(c.Params("name"))
-
+ app.WebSocket("/ws", func(c *fiber.Conn) {
for {
mt, msg, err := c.ReadMessage()
if err != nil {
@@ -327,26 +391,33 @@ func main() {
}
})
- // Listen on ws://localhost:3000/ws/john
+ // Listen on ws://localhost:3000/ws
app.Listen(3000)
}
```
-### 从 panic 中恢复
-
+### Recover middleware
+https://fiber.wiki/middleware#recover
```go
+package main
+
+import (
+ "github.com/gofiber/fiber"
+ "github.com/gofiber/fiber/middleware"
+)
+
func main() {
app := fiber.New()
+ app.Use(middleware.Recover(func(c *fiber.Ctx, err error) {
+ log.Println(err) // "Something went wrong!"
+ c.SendStatus(500) // Internal Server Error
+ })))
+
app.Get("/", func(c *fiber.Ctx) {
panic("Something went wrong!")
})
- app.Recover(func(c *fiber.Ctx) {
- c.Status(500).Send(c.Error())
- // => 500 "Something went wrong!"
- })
-
app.Listen(3000)
}
```
@@ -374,6 +445,12 @@ func main() {
+
+
+
+ JustDave
+
+ |
@@ -383,7 +460,7 @@ func main() {
|
- koddr
+ Vic Shóstak
|
diff --git a/app.go b/app.go
index 1f38994446..257cafc7c6 100644
--- a/app.go
+++ b/app.go
@@ -6,6 +6,7 @@ package fiber
import (
"bufio"
+ "crypto/tls"
"flag"
"fmt"
"io/ioutil"
@@ -25,26 +26,26 @@ import (
)
// Version of Fiber
-const Version = "1.8.0"
+const Version = "1.8.2"
type (
// App denotes the Fiber application.
App struct {
- server *fasthttp.Server
- routes []*Route
- child bool
- recover func(*Ctx)
- Settings *Settings
+ server *fasthttp.Server // Fasthttp server settings
+ routes []*Route // Route stack
+ child bool // If current process is a child ( for prefork )
+ recover func(*Ctx) // Deprecated, use middleware.Recover
+ Settings *Settings // Fiber settings
}
// Map defines a generic map of type `map[string]interface{}`.
Map map[string]interface{}
// Settings is a struct holding the server settings
Settings struct {
- // fiber settings
+ // This will spawn multiple Go processes listening on the same port
Prefork bool `default:"false"`
- // Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different. Otherwise, the router treats "/foo" and "/foo/" as the same.
+ // Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different.
StrictRouting bool `default:"false"`
- // Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes. When disabled, "/Foo" and "/foo" are treated the same.
+ // Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes.
CaseSensitive bool `default:"false"`
// Enables the "Server: value" HTTP header.
ServerHeader string `default:""`
@@ -52,28 +53,13 @@ type (
Immutable bool `default:"false"`
// Enables GZip / Deflate compression for all responses
Compression bool `default:"false"`
- // CompressionLevel int `default:"1"`
- // fasthttp settings
- // GETOnly bool `default:"false"`
- // IdleTimeout time.Duration `default:"0"`
- // Concurrency int `default:"256 * 1024"`
- // ReadTimeout time.Duration `default:"0"`
- // WriteTimeout time.Duration `default:"0"`
- // TCPKeepalive bool `default:"false"`
- // MaxConnsPerIP int `default:"0"`
- // ReadBufferSize int `default:"4096"`
- // WriteBufferSize int `default:"4096"`
- // ConcurrencySleep time.Duration `default:"0"`
- // DisableKeepAlive bool `default:"false"`
- // ReduceMemoryUsage bool `default:"false"`
- // MaxRequestsPerConn int `default:"0"`
- // TCPKeepalivePeriod time.Duration `default:"0"`
+ // Max body size that the server accepts
BodyLimit int `default:"4 * 1024 * 1024"`
- // NoHeaderNormalizing bool `default:"false"`
- // NoDefaultContentType bool `default:"false"`
- // template settings
- TemplateFolder string `default:""`
- TemplateEngine string `default:""`
+ // Folder containing template files
+ TemplateFolder string `default:""`
+ // Template engine: html, amber, handlebars , mustache or pug
+ TemplateEngine string `default:""`
+ // Extension for the template files
TemplateExtension string `default:""`
}
)
@@ -84,61 +70,45 @@ func init() {
}
// New : https://fiber.wiki/application#new
-func New(settings ...*Settings) (app *App) {
- var prefork bool
- var child bool
- for _, arg := range os.Args[1:] {
- if arg == "-prefork" {
+func New(settings ...*Settings) *App {
+ var prefork, child bool
+ // Loop trought args without using flag.Parse()
+ for i := range os.Args[1:] {
+ if os.Args[i] == "-prefork" {
prefork = true
- } else if arg == "-child" {
+ } else if os.Args[i] == "-child" {
child = true
}
}
- app = &App{
+ // Create default app
+ app := &App{
child: child,
+ Settings: &Settings{
+ Prefork: prefork,
+ BodyLimit: 4 * 1024 * 1024,
+ },
}
+ // If settings exist, set some defaults
if len(settings) > 0 {
- opt := settings[0]
- if !opt.Prefork {
- opt.Prefork = prefork
+ if !settings[0].Prefork { // Default to -prefork flag if false
+ settings[0].Prefork = prefork
}
- if opt.Immutable {
- getString = func(b []byte) string {
- return string(b)
- }
+ if settings[0].BodyLimit == 0 { // Default MaxRequestBodySize
+ settings[0].BodyLimit = 4 * 1024 * 1024
}
- // if opt.Concurrency == 0 {
- // opt.Concurrency = 256 * 1024
- // }
- // if opt.ReadBufferSize == 0 {
- // opt.ReadBufferSize = 4096
- // }
- // if opt.WriteBufferSize == 0 {
- // opt.WriteBufferSize = 4096
- // }
- if opt.BodyLimit == 0 {
- opt.BodyLimit = 4 * 1024 * 1024
+ if settings[0].Immutable { // Replace unsafe conversion funcs
+ getString = func(b []byte) string { return string(b) }
+ getBytes = func(s string) []byte { return []byte(s) }
}
- // if opt.CompressionLevel == 0 {
- // opt.CompressionLevel = 1
- // }
- app.Settings = opt
- return
- }
- app.Settings = &Settings{
- Prefork: prefork,
- // Concurrency: 256 * 1024,
- // ReadBufferSize: 4096,
- // WriteBufferSize: 4096,
- BodyLimit: 4 * 1024 * 1024,
+ app.Settings = settings[0] // Set custom settings
}
- return
+ return app
}
// Group : https://fiber.wiki/application#group
func (app *App) Group(prefix string, handlers ...func(*Ctx)) *Group {
if len(handlers) > 0 {
- app.registerMethod("USE", prefix, "", handlers...)
+ app.registerMethod("USE", prefix, handlers...)
}
return &Group{
prefix: prefix,
@@ -147,8 +117,8 @@ func (app *App) Group(prefix string, handlers ...func(*Ctx)) *Group {
}
// Static : https://fiber.wiki/application#static
-func (app *App) Static(args ...string) *App {
- app.registerStatic("/", args...)
+func (app *App) Static(prefix, root string) *App {
+ app.registerStatic(prefix, root)
return app
}
@@ -166,83 +136,84 @@ func (app *App) Use(args ...interface{}) *App {
log.Fatalf("Invalid handler: %v", reflect.TypeOf(arg))
}
}
- app.registerMethod("USE", "", path, handlers...)
+ app.registerMethod("USE", path, handlers...)
return app
}
// Connect : https://fiber.wiki/application#http-methods
func (app *App) Connect(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodConnect, "", path, handlers...)
+ app.registerMethod(http.MethodConnect, path, handlers...)
return app
}
// Put : https://fiber.wiki/application#http-methods
func (app *App) Put(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodPut, "", path, handlers...)
+ app.registerMethod(http.MethodPut, path, handlers...)
return app
}
// Post : https://fiber.wiki/application#http-methods
func (app *App) Post(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodPost, "", path, handlers...)
+ app.registerMethod(http.MethodPost, path, handlers...)
return app
}
// Delete : https://fiber.wiki/application#http-methods
func (app *App) Delete(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodDelete, "", path, handlers...)
+ app.registerMethod(http.MethodDelete, path, handlers...)
return app
}
// Head : https://fiber.wiki/application#http-methods
func (app *App) Head(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodHead, "", path, handlers...)
+ app.registerMethod(http.MethodHead, path, handlers...)
return app
}
// Patch : https://fiber.wiki/application#http-methods
func (app *App) Patch(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodPatch, "", path, handlers...)
+ app.registerMethod(http.MethodPatch, path, handlers...)
return app
}
// Options : https://fiber.wiki/application#http-methods
func (app *App) Options(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodOptions, "", path, handlers...)
+ app.registerMethod(http.MethodOptions, path, handlers...)
return app
}
// Trace : https://fiber.wiki/application#http-methods
func (app *App) Trace(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodTrace, "", path, handlers...)
+ app.registerMethod(http.MethodTrace, path, handlers...)
return app
}
// Get : https://fiber.wiki/application#http-methods
func (app *App) Get(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod(http.MethodGet, "", path, handlers...)
+ app.registerMethod(http.MethodGet, path, handlers...)
return app
}
// All : https://fiber.wiki/application#http-methods
func (app *App) All(path string, handlers ...func(*Ctx)) *App {
- app.registerMethod("ALL", "", path, handlers...)
+ app.registerMethod("ALL", path, handlers...)
return app
}
// WebSocket : https://fiber.wiki/application#websocket
-func (app *App) WebSocket(path string, handler func(*Conn)) *App {
- app.registerWebSocket(http.MethodGet, "", path, handler)
+func (app *App) WebSocket(path string, handle func(*Conn)) *App {
+ app.registerWebSocket(http.MethodGet, path, handle)
return app
}
// Recover : https://fiber.wiki/application#recover
func (app *App) Recover(handler func(*Ctx)) {
+ log.Println("Warning: Recover(handler) is deprecated since v1.8.2, please use middleware.Recover(handler, error) instead.")
app.recover = handler
}
// Listen : https://fiber.wiki/application#listen
-func (app *App) Listen(address interface{}, tls ...string) error {
+func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error {
addr, ok := address.(string)
if !ok {
port, ok := address.(int)
@@ -273,9 +244,9 @@ func (app *App) Listen(address interface{}, tls ...string) error {
}
}
- // enable TLS/HTTPS
- if len(tls) > 1 {
- return app.server.ServeTLS(ln, tls[0], tls[1])
+ // TLS config
+ if len(tlsconfig) > 0 {
+ ln = tls.NewListener(ln, tlsconfig[0])
}
return app.server.Serve(ln)
}
@@ -387,26 +358,11 @@ func (dl *disableLogger) Printf(format string, args ...interface{}) {
func (app *App) newServer() *fasthttp.Server {
return &fasthttp.Server{
- Handler: app.handler,
- Name: app.Settings.ServerHeader,
- // Concurrency: app.Settings.Concurrency,
- // SleepWhenConcurrencyLimitsExceeded: app.Settings.ConcurrencySleep,
- // DisableKeepalive: app.Settings.DisableKeepAlive,
- // ReadBufferSize: app.Settings.ReadBufferSize,
- // WriteBufferSize: app.Settings.WriteBufferSize,
- // ReadTimeout: app.Settings.ReadTimeout,
- // WriteTimeout: app.Settings.WriteTimeout,
- // IdleTimeout: app.Settings.IdleTimeout,
- // MaxConnsPerIP: app.Settings.MaxConnsPerIP,
- // MaxRequestsPerConn: app.Settings.MaxRequestsPerConn,
- // TCPKeepalive: app.Settings.TCPKeepalive,
- // TCPKeepalivePeriod: app.Settings.TCPKeepalivePeriod,
- MaxRequestBodySize: app.Settings.BodyLimit,
- // ReduceMemoryUsage: app.Settings.ReduceMemoryUsage,
- // GetOnly: app.Settings.GETOnly,
- // DisableHeaderNamesNormalizing: app.Settings.NoHeaderNormalizing,
+ Handler: app.handler,
+ Name: app.Settings.ServerHeader,
+ MaxRequestBodySize: app.Settings.BodyLimit,
NoDefaultServerHeader: app.Settings.ServerHeader == "",
- // NoDefaultContentType: app.Settings.NoDefaultContentType,
+
Logger: &disableLogger{},
LogAllErrors: false,
ErrorHandler: func(ctx *fasthttp.RequestCtx, err error) {
diff --git a/app_test.go b/app_test.go
index 43807f149f..a5744b70e7 100644
--- a/app_test.go
+++ b/app_test.go
@@ -70,10 +70,9 @@ func Test_Static(t *testing.T) {
app := New()
grp := app.Group("/v1")
grp.Static("/v2", ".travis.yml")
- app.Static("/yesyes*", ".github/FUNDING.yml")
- app.Static("./.github")
+ app.Static("/*", ".github/FUNDING.yml")
app.Static("/john", "./.github")
- req, _ := http.NewRequest("GET", "/stale.yml", nil)
+ req, _ := http.NewRequest("GET", "/john/stale.yml", nil)
resp, err := app.Test(req)
if err != nil {
t.Fatalf(`%s: %s`, t.Name(), err)
diff --git a/context.go b/context.go
index 676a69bfd9..dd3e16b165 100644
--- a/context.go
+++ b/context.go
@@ -28,17 +28,19 @@ import (
// It has methods for the request query string, parameters, body, HTTP headers and so on.
// For more information please visit our documentation: https://fiber.wiki/context
type Ctx struct {
- app *App
- route *Route
- next bool
- error error
- params *[]string
- values []string
- compress bool
- Fasthttp *fasthttp.RequestCtx
-}
-
-// Range info of range header
+ app *App // Reference to *App
+ route *Route // Reference to *Route
+ index int // Index of the current stack
+ matched bool // If the context found a match in stack
+ method string // HTTP method
+ path string // HTTP path
+ values []string // Route parameter values
+ compress bool // If the response needs to be compressed
+ Fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
+ err error // Contains error if catched
+}
+
+// Range struct
type Range struct {
Type string
Ranges []struct {
@@ -47,6 +49,17 @@ type Range struct {
}
}
+// Cookie struct
+type Cookie struct {
+ Name string
+ Value string
+ Path string
+ Domain string
+ Expires time.Time
+ Secure bool
+ HTTPOnly bool
+}
+
// Ctx pool
var poolCtx = sync.Pool{
New: func() interface{} {
@@ -55,20 +68,23 @@ var poolCtx = sync.Pool{
}
// Acquire Ctx from pool
-func acquireCtx() *Ctx {
+func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
ctx := poolCtx.Get().(*Ctx)
+ ctx.index = -1
+ ctx.path = getString(fctx.URI().Path())
+ ctx.method = getString(fctx.Request.Header.Method())
+ ctx.Fasthttp = fctx
return ctx
}
// Return Ctx to pool
func releaseCtx(ctx *Ctx) {
ctx.route = nil
- ctx.next = false
- ctx.error = nil
- ctx.params = nil
ctx.values = nil
ctx.compress = false
+ ctx.matched = false
ctx.Fasthttp = nil
+ ctx.err = nil
poolCtx.Put(ctx)
}
@@ -97,17 +113,6 @@ func releaseConn(conn *Conn) {
poolConn.Put(conn)
}
-// Cookie : struct
-type Cookie struct {
- Name string
- Value string
- Path string
- Domain string
- Expires time.Time
- Secure bool
- HTTPOnly bool
-}
-
// Accepts : https://fiber.wiki/context#accepts
func (ctx *Ctx) Accepts(offers ...string) (offer string) {
if len(offers) == 0 {
@@ -358,7 +363,7 @@ func (ctx *Ctx) Download(file string, name ...string) {
// Error returns err that is passed via Next(err)
func (ctx *Ctx) Error() error {
- return ctx.error
+ return ctx.err
}
// Format : https://fiber.wiki/context#format
@@ -522,12 +527,11 @@ func (ctx *Ctx) MultipartForm() (*multipart.Form, error) {
// Next : https://fiber.wiki/context#next
func (ctx *Ctx) Next(err ...error) {
ctx.route = nil
- ctx.next = true
- ctx.params = nil
ctx.values = nil
if len(err) > 0 {
- ctx.error = err[0]
+ ctx.err = err[0]
}
+ ctx.app.nextRoute(ctx)
}
// OriginalURL : https://fiber.wiki/context#originalurl
@@ -537,11 +541,11 @@ func (ctx *Ctx) OriginalURL() string {
// Params : https://fiber.wiki/context#params
func (ctx *Ctx) Params(key string) (value string) {
- if ctx.params == nil {
+ if ctx.route.Params == nil {
return
}
- for i := 0; i < len(*ctx.params); i++ {
- if (*ctx.params)[i] == key {
+ for i := 0; i < len(ctx.route.Params); i++ {
+ if (ctx.route.Params)[i] == key {
return ctx.values[i]
}
}
@@ -725,7 +729,7 @@ func (ctx *Ctx) SendStatus(status int) {
ctx.Fasthttp.Response.SetStatusCode(status)
// Only set status body when there is no response body
if len(ctx.Fasthttp.Response.Body()) == 0 {
- ctx.Fasthttp.Response.SetBodyString(getStatus(status))
+ ctx.Fasthttp.Response.SetBodyString(statusMessages[status])
}
}
diff --git a/group.go b/group.go
index edc84c37d2..c41ccdc3cd 100644
--- a/group.go
+++ b/group.go
@@ -8,7 +8,6 @@ import (
"log"
"net/http"
"reflect"
- "strings"
)
// Group ...
@@ -19,19 +18,9 @@ type Group struct {
// Group : https://fiber.wiki/application#group
func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group {
- if len(prefix) > 0 && prefix[0] != '/' && prefix[0] != '*' {
- prefix = "/" + prefix
- }
- // When grouping, always remove single slash
- if len(grp.prefix) > 0 && prefix == "/" {
- prefix = ""
- }
- // Prepent group prefix if exist
- prefix = grp.prefix + prefix
- // Clean path by removing double "//" => "/"
- prefix = strings.Replace(prefix, "//", "/", -1)
+ prefix = groupPaths(grp.prefix, prefix)
if len(handlers) > 0 {
- grp.app.registerMethod("USE", prefix, "", handlers...)
+ grp.app.registerMethod("USE", prefix, handlers...)
}
return &Group{
prefix: prefix,
@@ -40,8 +29,9 @@ func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group {
}
// Static : https://fiber.wiki/application#static
-func (grp *Group) Static(args ...string) *Group {
- grp.app.registerStatic(grp.prefix, args...)
+func (grp *Group) Static(prefix, root string) *Group {
+ prefix = groupPaths(grp.prefix, prefix)
+ grp.app.registerStatic(prefix, root)
return grp
}
@@ -56,80 +46,93 @@ func (grp *Group) Use(args ...interface{}) *Group {
case func(*Ctx):
handlers = append(handlers, arg)
default:
- log.Fatalf("Invalid handlerrrr: %v", reflect.TypeOf(arg))
+ log.Fatalf("Invalid handler: %v", reflect.TypeOf(arg))
}
}
- grp.app.registerMethod("USE", grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod("USE", path, handlers...)
return grp
}
// Connect : https://fiber.wiki/application#http-methods
func (grp *Group) Connect(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodConnect, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodConnect, path, handlers...)
return grp
}
// Put : https://fiber.wiki/application#http-methods
func (grp *Group) Put(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodPut, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodPut, path, handlers...)
return grp
}
// Post : https://fiber.wiki/application#http-methods
func (grp *Group) Post(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodPost, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodPost, path, handlers...)
return grp
}
// Delete : https://fiber.wiki/application#http-methods
func (grp *Group) Delete(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodDelete, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodDelete, path, handlers...)
return grp
}
// Head : https://fiber.wiki/application#http-methods
func (grp *Group) Head(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodHead, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodHead, path, handlers...)
return grp
}
// Patch : https://fiber.wiki/application#http-methods
func (grp *Group) Patch(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodPatch, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodPatch, path, handlers...)
return grp
}
// Options : https://fiber.wiki/application#http-methods
func (grp *Group) Options(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodOptions, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodOptions, path, handlers...)
return grp
}
// Trace : https://fiber.wiki/application#http-methods
func (grp *Group) Trace(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodTrace, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodTrace, path, handlers...)
return grp
}
// Get : https://fiber.wiki/application#http-methods
func (grp *Group) Get(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod(http.MethodGet, grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod(http.MethodGet, path, handlers...)
return grp
}
// All : https://fiber.wiki/application#http-methods
func (grp *Group) All(path string, handlers ...func(*Ctx)) *Group {
- grp.app.registerMethod("ALL", grp.prefix, path, handlers...)
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerMethod("ALL", path, handlers...)
return grp
}
// WebSocket : https://fiber.wiki/application#websocket
-func (grp *Group) WebSocket(path string, handler func(*Conn)) *Group {
- grp.app.registerWebSocket(http.MethodGet, grp.prefix, path, handler)
+func (grp *Group) WebSocket(path string, handle func(*Conn)) *Group {
+ path = groupPaths(grp.prefix, path)
+ grp.app.registerWebSocket(http.MethodGet, path, handle)
return grp
}
// Recover : https://fiber.wiki/application#recover
func (grp *Group) Recover(handler func(*Ctx)) {
+ log.Println("Warning: Recover(handler) is deprecated since v1.8.2, please use middleware.Recover(handler, error) instead.")
grp.app.recover = handler
}
diff --git a/middleware/helmet.go b/middleware/helmet.go
index d92d1b89ce..ecb8ce5ea6 100644
--- a/middleware/helmet.go
+++ b/middleware/helmet.go
@@ -49,8 +49,8 @@ var HelmetConfigDefault = HelmetConfig{
XFrameOptions: "SAMEORIGIN",
}
-// Secure ...
-func Secure(config ...HelmetConfig) func(*fiber.Ctx) {
+// Helmet ...
+func Helmet(config ...HelmetConfig) func(*fiber.Ctx) {
// Init config
var cfg HelmetConfig
if len(config) > 0 {
diff --git a/middleware/logger.go b/middleware/logger.go
index 4f2aca7099..2c09ff071d 100644
--- a/middleware/logger.go
+++ b/middleware/logger.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
+ "strconv"
"strings"
"sync"
"time"
@@ -34,7 +35,7 @@ type LoggerConfig struct {
// LoggerConfigDefault is the defaul Logger middleware config.
var LoggerConfigDefault = LoggerConfig{
Skip: nil,
- Format: "${time} - ${ip} - ${method} ${path}\t${ua}\n",
+ Format: "${time} ${method} ${path} - ${ip} - ${status} - ${latency}ms\n",
TimeFormat: "15:04:05",
Output: os.Stderr,
}
@@ -81,6 +82,11 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) {
c.Next()
return
}
+ start := time.Now()
+ // handle request
+ c.Next()
+ // build log
+ stop := time.Now()
buf := pool.Get().(*bytes.Buffer)
buf.Reset()
defer pool.Put(buf)
@@ -104,6 +110,10 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) {
return buf.WriteString(c.OriginalURL())
case "ua":
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
+ case "latency":
+ return buf.WriteString(stop.Sub(start).String())
+ case "status":
+ return buf.WriteString(strconv.Itoa(c.Fasthttp.Response.StatusCode()))
default:
switch {
case strings.HasPrefix(tag, "header:"):
@@ -124,6 +134,5 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) {
if _, err := cfg.Output.Write(buf.Bytes()); err != nil {
fmt.Println(err)
}
- c.Next()
}
}
diff --git a/middleware/logger.go.fasthttp.gz b/middleware/logger.go.fasthttp.gz
new file mode 100644
index 0000000000..8767ca83f8
Binary files /dev/null and b/middleware/logger.go.fasthttp.gz differ
diff --git a/middleware/recover.go b/middleware/recover.go
new file mode 100644
index 0000000000..dc7c204126
--- /dev/null
+++ b/middleware/recover.go
@@ -0,0 +1,33 @@
+package middleware
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/gofiber/fiber"
+)
+
+// Recover ...
+func Recover(handle ...func(*fiber.Ctx, error)) func(*fiber.Ctx) {
+ h := func(c *fiber.Ctx, err error) {
+ log.Println(err)
+ c.SendStatus(500)
+ }
+ // Init custom error handler if exist
+ if len(handle) > 0 {
+ h = handle[0]
+ }
+ // Return middleware handle
+ return func(c *fiber.Ctx) {
+ defer func() {
+ if r := recover(); r != nil {
+ err, ok := r.(error)
+ if !ok {
+ err = fmt.Errorf("%v", r)
+ }
+ h(c, err)
+ }
+ }()
+ c.Next()
+ }
+}
diff --git a/router.go b/router.go
index 2cd26692d0..aebf276758 100644
--- a/router.go
+++ b/router.go
@@ -5,8 +5,8 @@
package fiber
import (
- "fmt"
"log"
+ "os"
"path/filepath"
"regexp"
"strings"
@@ -17,403 +17,311 @@ import (
// Route struct
type Route struct {
- // HTTP method in uppercase, can be a * for "Use" & "All" routes
- Method string
- // Stores the original path
- Path string
- // Prefix is for ending wildcards or middleware
- Prefix string
- // Stores regex for complex paths
- Regex *regexp.Regexp
- // Stores params keys for :params / :optional? / *
- Params []string
- // Handler for context
- HandlerCtx func(*Ctx)
- // Handler for websockets
- HandlerConn func(*Conn)
-}
-
-func (app *App) registerStatic(grpPrefix string, args ...string) {
- var prefix = "/"
- var root = "./"
- // enable / disable gzipping somewhere?
- // todo v2.0.0
- gzip := true
+ isMiddleware bool // is middleware route
+ isWebSocket bool // is websocket route
- if len(args) == 1 {
- root = args[0]
- }
- if len(args) == 2 {
- prefix = args[0]
- root = args[1]
- }
+ isStar bool // path == '*'
+ isSlash bool // path == '/'
+ isRegex bool // needs regex parsing
- // A non wildcard path must start with a '/'
- if prefix != "*" && len(prefix) > 0 && prefix[0] != '/' {
- prefix = "/" + prefix
- }
- // Prepend group prefix
- if len(grpPrefix) > 0 {
- // `/v1`+`/` => `/v1`+``
- if prefix == "/" {
- prefix = grpPrefix
- } else {
- prefix = grpPrefix + prefix
- }
- // Remove duplicate slashes `//`
- prefix = strings.Replace(prefix, "//", "/", -1)
- }
- // Empty or '/*' path equals "match anything"
- // TODO fix * for paths with grpprefix
- if prefix == "/*" {
- prefix = "*"
- }
- // Lets get all files from root
- files, _, err := getFiles(root)
- if err != nil {
- log.Fatal("Static: ", err)
- }
- // ./static/compiled => static/compiled
- mount := filepath.Clean(root)
+ Method string // http method
+ Path string // orginal path
+ Params []string // path params
+ Regexp *regexp.Regexp // regexp matcher
- if !app.Settings.CaseSensitive {
- prefix = strings.ToLower(prefix)
- }
- if !app.Settings.StrictRouting && len(prefix) > 1 {
- prefix = strings.TrimRight(prefix, "/")
- }
+ HandleCtx func(*Ctx) // ctx handler
+ HandleConn func(*Conn) // conn handler
- // Loop over all files
- for _, file := range files {
- // Ignore the .gzipped files by fasthttp
- if strings.Contains(file, ".fasthttp.gz") {
- continue
- }
- // Time to create a fake path for the route match
- // static/index.html => /index.html
- path := filepath.Join(prefix, strings.Replace(file, mount, "", 1))
- // for windows: static\index.html => /index.html
- path = filepath.ToSlash(path)
- // Store file path to use in ctx handler
- filePath := file
+}
- if len(prefix) > 1 && strings.Contains(prefix, "*") {
- app.routes = append(app.routes, &Route{
- Method: "GET",
- Path: path,
- Prefix: strings.Split(prefix, "*")[0],
- HandlerCtx: func(c *Ctx) {
- c.SendFile(filePath, gzip)
- },
- })
+func (app *App) nextRoute(ctx *Ctx) {
+ lenr := len(app.routes) - 1
+ for ctx.index < lenr {
+ ctx.index++
+ route := app.routes[ctx.index]
+ match, values := route.matchRoute(ctx.method, ctx.path)
+ if match {
+ ctx.route = route
+ if !ctx.matched {
+ ctx.matched = true
+ }
+ if len(values) > 0 {
+ ctx.values = values
+ }
+ if route.isWebSocket {
+ if err := websocketUpgrader.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) {
+ conn := acquireConn(fconn)
+ defer releaseConn(conn)
+ route.HandleConn(conn)
+ }); err != nil { // Upgrading failed
+ ctx.SendStatus(400)
+ }
+ } else {
+ route.HandleCtx(ctx)
+ }
return
}
- // If the file is an index.html, bind the prefix to index.html directly
- if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" {
- app.routes = append(app.routes, &Route{
- Method: "GET",
- Path: prefix,
- HandlerCtx: func(c *Ctx) {
- c.SendFile(filePath, gzip)
- },
- })
+ }
+ if !ctx.matched {
+ ctx.SendStatus(404)
+ }
+}
+
+func (r *Route) matchRoute(method, path string) (match bool, values []string) {
+ // is route middleware? matches all http methods
+ if r.isMiddleware {
+ // '*' or '/' means its a valid match
+ if r.isStar || r.isSlash {
+ return true, nil
}
- if !app.Settings.CaseSensitive {
- path = strings.ToLower(path)
+ // if midware path starts with req.path
+ if strings.HasPrefix(path, r.Path) {
+ return true, nil
}
- if !app.Settings.StrictRouting && len(prefix) > 1 {
- path = strings.TrimRight(path, "/")
+ // middlewares dont support regex so bye!
+ return false, nil
+ }
+ // non-middleware route, http method must match!
+ // the wildcard method is for .All() method
+ if r.Method != method && r.Method[0] != '*' {
+ return false, nil
+ }
+ // '*' means we match anything
+ if r.isStar {
+ return true, nil
+ }
+ // simple '/' bool, so you avoid unnecessary comparison for long paths
+ if r.isSlash && path == "/" {
+ return true, nil
+ }
+ // does this route need regex matching?
+ if r.isRegex {
+ // req.path match regex pattern
+ if r.Regexp.MatchString(path) {
+ // do we have parameters
+ if len(r.Params) > 0 {
+ // get values for parameters
+ matches := r.Regexp.FindAllStringSubmatch(path, -1)
+ // did we get the values?
+ if len(matches) > 0 && len(matches[0]) > 1 {
+ values = matches[0][1:len(matches[0])]
+ return true, values
+ }
+ return false, nil
+ }
+ return true, nil
}
- // Add the route + SendFile(filepath) to routes
- app.routes = append(app.routes, &Route{
- Method: "GET",
- Path: path,
- HandlerCtx: func(c *Ctx) {
- c.SendFile(filePath, gzip)
- },
- })
- }
-}
-func (app *App) registerWebSocket(method, group, path string, handler func(*Conn)) {
- if len(path) > 0 && path[0] != '/' {
- path = "/" + path
+ return false, nil
}
- if len(group) > 0 {
- // `/v1`+`/` => `/v1`+``
- if path == "/" {
- path = group
- } else {
- path = group + path
- }
- // Remove duplicate slashes `//`
- path = strings.Replace(path, "//", "/", -1)
+ // last thing to do is to check for a simple path match
+ if r.Path == path {
+ return true, nil
}
- // Routes are case insensitive by default
+ // Nothing match
+ return false, nil
+}
+
+func (app *App) handler(fctx *fasthttp.RequestCtx) {
+ // get fiber context from sync pool
+ ctx := acquireCtx(fctx)
+ defer releaseCtx(ctx)
+
+ // attach app poiner and compress settings
+ ctx.app = app
+ ctx.compress = app.Settings.Compression
+
+ // Case sensitive routing
if !app.Settings.CaseSensitive {
- path = strings.ToLower(path)
+ ctx.path = strings.ToLower(ctx.path)
}
- if !app.Settings.StrictRouting && len(path) > 1 {
- path = strings.TrimRight(path, "/")
+ // Strict routing
+ if !app.Settings.StrictRouting && len(ctx.path) > 1 {
+ ctx.path = strings.TrimRight(ctx.path, "/")
}
- // Get ':param' & ':optional?' & '*' from path
- params := getParams(path)
- if len(params) > 0 {
- log.Fatal("WebSocket routes do not support path parameters: `:param, :optional?, *`")
+
+ app.nextRoute(ctx)
+
+ if ctx.compress {
+ compressResponse(fctx)
}
- app.routes = append(app.routes, &Route{
- Method: method, Path: path, HandlerConn: handler,
- })
}
-func (app *App) registerMethod(method, group, path string, handlers ...func(*Ctx)) {
- // No special paths for websockets
+
+func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) {
+ // Route requires atleast one handler
if len(handlers) == 0 {
log.Fatalf("Missing handler in route")
}
- // Set variables
- var prefix string
- var middleware = method == "USE"
- // A non wildcard path must start with a '/'
- if path != "*" && len(path) > 0 && path[0] != '/' {
- path = "/" + path
- }
- // Prepend group prefix
- if len(group) > 0 {
- // `/v1`+`/` => `/v1`+``
- if path == "/" {
- path = group
- } else {
- path = group + path
- }
- // Remove duplicate slashes `//`
- path = strings.Replace(path, "//", "/", -1)
- }
- // Empty or '/*' path equals "match anything"
- // TODO fix * for paths with grpprefix
- if path == "/*" || (middleware && path == "") {
- path = "*"
+ // Cannot have an empty path
+ if path == "" {
+ path = "/"
}
- if method == "ALL" || middleware {
- method = "*"
+ // Path always start with a '/' or '*'
+ if path[0] != '/' && path[0] != '*' {
+ path = "/" + path
}
- // Routes are case insensitive by default
+
+ // Case sensitive routing, all to lowercase
if !app.Settings.CaseSensitive {
path = strings.ToLower(path)
}
+ // Strict routing, remove last `/`
if !app.Settings.StrictRouting && len(path) > 1 {
path = strings.TrimRight(path, "/")
}
- // If the route can match anything
- if path == "*" {
- for i := range handlers {
- app.routes = append(app.routes, &Route{
- Method: method, Path: path, HandlerCtx: handlers[i],
- })
- }
- return
- }
- // Get ':param' & ':optional?' & '*' from path
- params := getParams(path)
- // Enable prefix for midware
- if len(params) == 0 && middleware {
- prefix = path
- }
- // If path has no params (simple path)
- if len(params) == 0 {
- for i := range handlers {
- app.routes = append(app.routes, &Route{
- Method: method, Path: path, Prefix: prefix, HandlerCtx: handlers[i],
- })
- }
- return
+ // Set route booleans
+ var isMiddleware = method == "USE"
+ // Middleware / All allows all HTTP methods
+ if isMiddleware || method == "ALL" {
+ method = "*"
}
-
- // If path only contains 1 wildcard, we can create a prefix
- // If its a middleware, we also create a prefix
- if len(params) == 1 && params[0] == "*" {
- prefix = strings.Split(path, "*")[0]
- for i := range handlers {
- app.routes = append(app.routes, &Route{
- Method: method, Path: path, Prefix: prefix,
- Params: params, HandlerCtx: handlers[i],
- })
+ var isStar = path == "*" || path == "/*"
+ // Middleware containing only a `/` equals wildcard
+ if isMiddleware && path == "/" {
+ isStar = true
+ }
+ var isSlash = path == "/"
+ var isRegex = false
+ // Route properties
+ var Params = getParams(path)
+ var Regexp *regexp.Regexp
+ // Params requires regex pattern
+ if len(Params) > 0 {
+ regex, err := getRegex(path)
+ if err != nil {
+ log.Fatal("Router: Invalid path pattern: " + path)
}
- return
+ isRegex = true
+ Regexp = regex
}
- // We have an :param or :optional? and need to compile a regex struct
- regex, err := getRegex(path)
- if err != nil {
- log.Fatal("Router: Invalid path pattern: " + path)
- }
- // Add route with regex
for i := range handlers {
app.routes = append(app.routes, &Route{
- Method: method, Path: path, Regex: regex,
- Params: params, HandlerCtx: handlers[i],
+ isMiddleware: isMiddleware,
+ isStar: isStar,
+ isSlash: isSlash,
+ isRegex: isRegex,
+ Method: method,
+ Path: path,
+ Params: Params,
+ Regexp: Regexp,
+ HandleCtx: handlers[i],
})
}
}
-func (app *App) handler(fctx *fasthttp.RequestCtx) {
- // Use this boolean to perform 404 not found at the end
- var match = false
- // get custom context from sync pool
- ctx := acquireCtx()
- defer releaseCtx(ctx)
- ctx.app = app
- ctx.compress = app.Settings.Compression
- ctx.Fasthttp = fctx
- // get path and method
- path := ctx.Path()
+
+func (app *App) registerWebSocket(method, path string, handle func(*Conn)) {
+ // Cannot have an empty path
+ if path == "" {
+ path = "/"
+ }
+ // Path always start with a '/' or '*'
+ if path[0] != '/' && path[0] != '*' {
+ path = "/" + path
+ }
+
+ // Case sensitive routing, all to lowercase
if !app.Settings.CaseSensitive {
path = strings.ToLower(path)
}
+ // Strict routing, remove last `/`
if !app.Settings.StrictRouting && len(path) > 1 {
path = strings.TrimRight(path, "/")
}
- method := ctx.Method()
- // enable recovery
- if app.recover != nil {
- defer func() {
- if r := recover(); r != nil {
- err, ok := r.(error)
- if !ok {
- err = fmt.Errorf("%v", r)
- }
- ctx.error = err
- app.recover(ctx)
- }
- }()
- }
- // loop trough routes
- for _, route := range app.routes {
- // Skip route if method does not match
- if route.Method != "*" && route.Method != method {
- continue
- }
- // Set route pointer if user wants to call .Route()
- ctx.route = route
- // wilcard or exact same path
- // TODO v2: enable or disable case insensitive match
- if route.Path == "*" || route.Path == path {
- // if * always set the path to the wildcard parameter
- if route.Path == "*" {
- ctx.params = &[]string{"*"}
- ctx.values = []string{path}
- }
- // ctx.Fasthttp.Request.Header.ConnectionUpgrade()
- // Websocket request
- if route.HandlerConn != nil && websocket.FastHTTPIsWebSocketUpgrade(fctx) {
- // Try to upgrade
- err := socketUpgrade.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) {
- conn := acquireConn(fconn)
- defer releaseConn(conn)
- route.HandlerConn(conn)
- })
- // Upgrading failed
- if err != nil {
- log.Fatalf("Failed to upgrade websocket connection")
- }
- return
- }
- // No handler for HTTP nor websocket
- if route.HandlerCtx == nil {
- continue
- }
- // Match found, 404 not needed
- match = true
- route.HandlerCtx(ctx)
- // if next is not set, leave loop and release ctx
- if !ctx.next {
- break
- } else {
- // reset match to false
- match = false
- }
- // set next to false for next iteration
- ctx.next = false
- // continue to go to the next route
- continue
- }
- if route.Prefix != "" && strings.HasPrefix(path, route.Prefix) {
- ctx.route = route
- if strings.Contains(route.Path, "*") {
- ctx.params = &[]string{"*"}
- // ctx.values = matches[0][1:len(matches[0])]
- // parse query source
- ctx.values = []string{strings.Replace(path, route.Prefix, "", 1)}
- }
- // No handler for HTTP nor websocket
- if route.HandlerCtx == nil {
- continue
- }
- // Match found, 404 not needed
- match = true
- route.HandlerCtx(ctx)
- // if next is not set, leave loop and release ctx
- if !ctx.next {
- break
- } else {
- // reset match to false
- match = false
- }
- // set next to false for next iteration
- ctx.next = false
- // continue to go to the next route
- continue
- }
- // Skip route if regex does not exist
- if route.Regex == nil {
- continue
- }
+ var isWebSocket = true
- // Skip route if regex does not match
- if !route.Regex.MatchString(path) {
- continue
+ var isStar = path == "*" || path == "/*"
+ var isSlash = path == "/"
+ var isRegex = false
+ // Route properties
+ var Params = getParams(path)
+ var Regexp *regexp.Regexp
+ // Params requires regex pattern
+ if len(Params) > 0 {
+ regex, err := getRegex(path)
+ if err != nil {
+ log.Fatal("Router: Invalid path pattern: " + path)
}
+ isRegex = true
+ Regexp = regex
+ }
+ app.routes = append(app.routes, &Route{
+ isWebSocket: isWebSocket,
+ isStar: isStar,
+ isSlash: isSlash,
+ isRegex: isRegex,
- // If we have parameters, lets find the matches
- if len(route.Params) > 0 {
- matches := route.Regex.FindAllStringSubmatch(path, -1)
- // If we have matches, add params and values to context
- if len(matches) > 0 && len(matches[0]) > 1 {
- ctx.params = &route.Params
- ctx.values = matches[0][1:len(matches[0])]
- }
- }
- // No handler for HTTP nor websocket
- if route.HandlerCtx == nil {
- continue
- }
- // Match found, 404 not needed
- match = true
- route.HandlerCtx(ctx)
- // if next is not set, leave loop and release ctx
- if !ctx.next {
- break
- } else {
- // reset match to false
- match = false
- }
- // set next to false for next iteration
- ctx.next = false
+ Method: method,
+ Path: path,
+ Params: Params,
+ Regexp: Regexp,
+ HandleConn: handle,
+ })
+}
+
+func (app *App) registerStatic(prefix, root string) {
+ // Cannot have an empty prefix
+ if prefix == "" {
+ prefix = "/"
}
- // Do we need to compress?
- if ctx.compress {
- compressDefaultCompression(fctx)
- // switch app.Settings.CompressionLevel {
- // case 2:
- // compressBestSpeed(fctx)
- // case 3:
- // compressBestCompression(fctx)
- // case 4:
- // compressHuffmanOnly(fctx)
- // default: // 1
- // compressDefaultCompression(fctx)
- // }
+ // prefix always start with a '/' or '*'
+ if prefix[0] != '/' && prefix[0] != '*' {
+ prefix = "/" + prefix
}
- // No match, send default 404 Not Found
- if !match {
- ctx.SendStatus(404)
+ // Case sensitive routing, all to lowercase
+ if !app.Settings.CaseSensitive {
+ prefix = strings.ToLower(prefix)
}
+
+ var isStar = prefix == "*" || prefix == "/*"
+
+ files := map[string]string{}
+ // Clean root path
+ root = filepath.Clean(root)
+ // Check if root exist and is accessible
+ if _, err := os.Stat(root); err != nil {
+ log.Fatalf("%s", err)
+ }
+ // Store path url and file paths in map
+ if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+ if !info.IsDir() {
+ url := "*"
+ if !isStar {
+ // /css/style.css: static/css/style.css
+ url = filepath.Join(prefix, strings.Replace(path, root, "", 1))
+ }
+ // \static\css: /static/css
+ url = filepath.ToSlash(url)
+ files[url] = path
+ if filepath.Base(path) == "index.html" {
+ files[url] = path
+ }
+ }
+ return err
+ }); err != nil {
+ log.Fatalf("%s", err)
+ }
+ compress := app.Settings.Compression
+ app.routes = append(app.routes, &Route{
+ isMiddleware: true,
+ isStar: isStar,
+ Method: "*",
+ Path: prefix,
+ HandleCtx: func(c *Ctx) {
+ // Only allow GET & HEAD methods
+ if c.method == "GET" || c.method == "HEAD" {
+ path := "*"
+ if !isStar {
+ path = c.path
+ }
+ file := files[path]
+ if file != "" {
+ c.SendFile(file, compress)
+ return
+ }
+ }
+ c.matched = false
+ c.Next()
+ },
+ })
}
diff --git a/utils.go b/utils.go
index ba980e06c5..dbf3e39a89 100644
--- a/utils.go
+++ b/utils.go
@@ -20,13 +20,9 @@ import (
fasthttp "github.com/valyala/fasthttp"
)
-var compressDefaultCompression = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressDefaultCompression)
-
-// var compressBestSpeed = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressBestSpeed)
-// var compressBestCompression = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressBestCompression)
-// var compressHuffmanOnly = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressHuffmanOnly)
var schemaDecoder = schema.NewDecoder()
-var socketUpgrade = websocket.FastHTTPUpgrader{
+var compressResponse = fasthttp.CompressHandlerLevel(func(c *fasthttp.RequestCtx) {}, fasthttp.CompressDefaultCompression)
+var websocketUpgrader = websocket.FastHTTPUpgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(fctx *fasthttp.RequestCtx) bool {
@@ -34,6 +30,15 @@ var socketUpgrade = websocket.FastHTTPUpgrader{
},
}
+func groupPaths(prefix, path string) string {
+ if path == "/" {
+ path = ""
+ }
+ path = prefix + path
+ path = strings.Replace(path, "//", "/", -1)
+ return path
+}
+
func getParams(path string) (params []string) {
if len(path) < 1 {
return
@@ -108,10 +113,6 @@ func getType(ext string) (mime string) {
return mime
}
-func getStatus(status int) (msg string) {
- return statusMessage[status]
-}
-
// #nosec G103
// getString converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
@@ -122,7 +123,7 @@ var getString = func(b []byte) string {
// #nosec G103
// getBytes converts string to a byte slice without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
-func getBytes(s string) (b []byte) {
+var getBytes = func(s string) (b []byte) {
return *(*[]byte)(unsafe.Pointer(&s))
}
@@ -162,8 +163,8 @@ const (
MIMEOctetStream = "application/octet-stream"
)
-// HTTP status codes
-var statusMessage = map[int]string{
+// HTTP status codes with messages
+var statusMessages = map[int]string{
100: "Continue",
101: "Switching Protocols",
102: "Processing",
| |