2020-05-07 19:28:21 +02:00
|
|
|
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
2020-05-07 21:02:24 +02:00
|
|
|
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
2020-05-07 19:28:21 +02:00
|
|
|
// 📌 API Documentation: https://docs.gofiber.io
|
2020-02-21 18:06:08 +01:00
|
|
|
|
2019-12-30 07:29:42 -05:00
|
|
|
package fiber
|
|
|
|
|
|
|
|
import (
|
2020-03-24 12:20:07 +01:00
|
|
|
"log"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2019-12-30 07:29:42 -05:00
|
|
|
|
2020-03-24 12:20:07 +01:00
|
|
|
fasthttp "github.com/valyala/fasthttp"
|
2019-12-30 07:29:42 -05:00
|
|
|
)
|
|
|
|
|
2020-05-07 19:28:21 +02:00
|
|
|
// Route metadata
|
2020-02-01 19:42:40 +03:00
|
|
|
type Route struct {
|
2020-05-07 19:28:21 +02:00
|
|
|
// Internal fields
|
|
|
|
use bool // USE matches path prefixes
|
|
|
|
star bool // Path equals '*' or '/*'
|
|
|
|
root bool // Path equals '/'
|
|
|
|
parsed parsedParams // parsed contains parsed params segments
|
2020-02-21 16:54:14 +01:00
|
|
|
|
2020-05-07 19:28:21 +02:00
|
|
|
// External fields for ctx.Route() method
|
|
|
|
Path string // Registered route path
|
|
|
|
Method string // HTTP method
|
|
|
|
Params []string // Slice containing the params names
|
|
|
|
Handler func(*Ctx) // Ctx handler
|
2020-03-03 12:21:34 -05:00
|
|
|
}
|
2020-02-21 18:07:43 +01:00
|
|
|
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) nextRoute(ctx *Ctx) {
|
2020-05-07 19:28:21 +02:00
|
|
|
mINT := methodINT[ctx.method]
|
|
|
|
// Get stack length
|
|
|
|
lenr := len(app.routes[mINT]) - 1
|
|
|
|
// Loop over stack starting from previous index
|
2020-03-24 12:20:07 +01:00
|
|
|
for ctx.index < lenr {
|
2020-05-07 19:28:21 +02:00
|
|
|
// Increment stack index
|
2020-03-24 12:20:07 +01:00
|
|
|
ctx.index++
|
2020-05-07 19:28:21 +02:00
|
|
|
// Get *Route
|
|
|
|
route := app.routes[mINT][ctx.index]
|
|
|
|
// Check if it matches the request path
|
|
|
|
match, values := route.matchRoute(ctx.path)
|
|
|
|
// No match, continue
|
|
|
|
if !match {
|
|
|
|
continue
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
2020-05-07 19:28:21 +02:00
|
|
|
// Match! Set route and param values to Ctx
|
|
|
|
ctx.route = route
|
|
|
|
ctx.values = values
|
|
|
|
// Execute handler
|
|
|
|
route.Handler(ctx)
|
|
|
|
// Generate ETag if enabled
|
|
|
|
if app.Settings.ETag {
|
|
|
|
setETag(ctx, false)
|
|
|
|
}
|
|
|
|
return
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
2020-05-07 19:28:21 +02:00
|
|
|
// Send a 404 by default if no route is matched
|
2020-03-24 12:20:07 +01:00
|
|
|
if len(ctx.Fasthttp.Response.Body()) == 0 {
|
|
|
|
ctx.SendStatus(404)
|
|
|
|
}
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
2020-03-03 12:21:34 -05:00
|
|
|
|
2020-05-07 19:28:21 +02:00
|
|
|
func (r *Route) matchRoute(path string) (match bool, values []string) {
|
|
|
|
if r.use {
|
2020-05-12 23:24:04 +02:00
|
|
|
if r.root || strings.HasPrefix(path, r.Path) {
|
2020-03-24 12:20:07 +01:00
|
|
|
return true, values
|
|
|
|
}
|
2020-05-12 19:24:04 +02:00
|
|
|
// Check for a simple path match
|
|
|
|
} else if len(r.Path) == len(path) && r.Path == path {
|
|
|
|
return true, values
|
|
|
|
// Middleware routes allow prefix matches
|
2020-05-12 23:24:04 +02:00
|
|
|
} else if r.root && path == "/" {
|
2020-05-12 19:24:04 +02:00
|
|
|
return true, values
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
2020-05-07 19:28:21 +02:00
|
|
|
// '*' wildcard matches any path
|
2020-05-12 23:24:04 +02:00
|
|
|
if r.star {
|
2020-05-07 20:22:26 +02:00
|
|
|
return true, []string{path}
|
2020-05-07 19:28:21 +02:00
|
|
|
}
|
|
|
|
// Does this route have parameters
|
|
|
|
if len(r.Params) > 0 {
|
2020-05-12 19:24:04 +02:00
|
|
|
// Match params
|
|
|
|
if values, match = r.parsed.getMatch(path, r.use); match {
|
|
|
|
return
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-12 19:24:04 +02:00
|
|
|
// No match
|
2020-03-24 12:20:07 +01:00
|
|
|
return false, values
|
2020-03-04 12:30:29 +01:00
|
|
|
}
|
|
|
|
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) handler(fctx *fasthttp.RequestCtx) {
|
2020-03-24 12:20:07 +01:00
|
|
|
// get fiber context from sync pool
|
2020-05-11 04:30:31 +02:00
|
|
|
ctx := AcquireCtx(fctx)
|
|
|
|
defer ReleaseCtx(ctx)
|
2020-05-07 19:28:21 +02:00
|
|
|
// Attach app poiner to access the routes
|
2020-03-24 12:20:07 +01:00
|
|
|
ctx.app = app
|
|
|
|
// Case sensitive routing
|
|
|
|
if !app.Settings.CaseSensitive {
|
|
|
|
ctx.path = strings.ToLower(ctx.path)
|
|
|
|
}
|
|
|
|
// Strict routing
|
|
|
|
if !app.Settings.StrictRouting && len(ctx.path) > 1 {
|
|
|
|
ctx.path = strings.TrimRight(ctx.path, "/")
|
|
|
|
}
|
|
|
|
// Find route
|
|
|
|
app.nextRoute(ctx)
|
2020-02-27 04:10:26 -05:00
|
|
|
}
|
2020-03-03 12:21:34 -05:00
|
|
|
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) registerMethod(method, path string, handlers ...func(*Ctx)) {
|
2020-03-24 12:20:07 +01:00
|
|
|
// Route requires atleast one handler
|
|
|
|
if len(handlers) == 0 {
|
|
|
|
log.Fatalf("Missing handler in route")
|
|
|
|
}
|
|
|
|
// Cannot have an empty path
|
|
|
|
if path == "" {
|
|
|
|
path = "/"
|
|
|
|
}
|
|
|
|
// Path always start with a '/' or '*'
|
2020-05-07 19:43:25 +02:00
|
|
|
if path[0] != '/' {
|
2020-03-24 12:20:07 +01:00
|
|
|
path = "/" + path
|
|
|
|
}
|
|
|
|
// Store original path to strip case sensitive params
|
|
|
|
original := 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, "/")
|
|
|
|
}
|
|
|
|
// Set route booleans
|
2020-05-07 19:28:21 +02:00
|
|
|
var isUse = method == "USE"
|
2020-03-24 12:20:07 +01:00
|
|
|
// Middleware / All allows all HTTP methods
|
2020-05-07 19:28:21 +02:00
|
|
|
if isUse || method == "ALL" {
|
2020-03-24 12:20:07 +01:00
|
|
|
method = "*"
|
|
|
|
}
|
2020-05-07 19:43:25 +02:00
|
|
|
var isStar = path == "/*"
|
2020-03-24 12:20:07 +01:00
|
|
|
// Middleware containing only a `/` equals wildcard
|
2020-05-12 19:24:04 +02:00
|
|
|
// if isUse && path == "/" {
|
|
|
|
// isStar = true
|
|
|
|
// }
|
2020-05-07 19:28:21 +02:00
|
|
|
var isRoot = path == "/"
|
2020-03-24 12:20:07 +01:00
|
|
|
// Route properties
|
2020-05-12 19:24:04 +02:00
|
|
|
var isParsed = getParams(original)
|
2020-03-24 12:20:07 +01:00
|
|
|
for i := range handlers {
|
2020-05-07 19:28:21 +02:00
|
|
|
route := &Route{
|
|
|
|
use: isUse,
|
|
|
|
star: isStar,
|
|
|
|
root: isRoot,
|
|
|
|
parsed: isParsed,
|
|
|
|
|
|
|
|
Path: path,
|
|
|
|
Method: method,
|
2020-05-12 19:24:04 +02:00
|
|
|
Params: isParsed.params,
|
2020-05-07 19:28:21 +02:00
|
|
|
Handler: handlers[i],
|
|
|
|
}
|
|
|
|
if method == "*" {
|
|
|
|
// Add handler to all HTTP methods
|
|
|
|
for m := range methodINT {
|
|
|
|
app.addRoute(m, route)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Add route to stack
|
|
|
|
app.addRoute(method, route)
|
|
|
|
// Add route to HEAD method if GET
|
|
|
|
if method == MethodGet {
|
|
|
|
app.addRoute(MethodHead, route)
|
|
|
|
}
|
|
|
|
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
2019-12-30 13:33:50 +01:00
|
|
|
}
|
2020-03-03 12:21:34 -05:00
|
|
|
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) registerStatic(prefix, root string, config ...Static) {
|
2020-03-24 12:20:07 +01:00
|
|
|
// Cannot have an empty prefix
|
|
|
|
if prefix == "" {
|
|
|
|
prefix = "/"
|
|
|
|
}
|
|
|
|
// Prefix always start with a '/' or '*'
|
2020-05-07 20:22:26 +02:00
|
|
|
if prefix[0] != '/' {
|
2020-03-24 12:20:07 +01:00
|
|
|
prefix = "/" + prefix
|
|
|
|
}
|
|
|
|
// Match anything
|
|
|
|
var wildcard = false
|
|
|
|
if prefix == "*" || prefix == "/*" {
|
|
|
|
wildcard = true
|
|
|
|
prefix = "/"
|
|
|
|
}
|
|
|
|
// Case sensitive routing, all to lowercase
|
|
|
|
if !app.Settings.CaseSensitive {
|
|
|
|
prefix = strings.ToLower(prefix)
|
|
|
|
}
|
|
|
|
// For security we want to restrict to the current work directory.
|
|
|
|
if len(root) == 0 {
|
|
|
|
root = "."
|
|
|
|
}
|
|
|
|
// Strip trailing slashes from the root path
|
|
|
|
if len(root) > 0 && root[len(root)-1] == '/' {
|
|
|
|
root = root[:len(root)-1]
|
|
|
|
}
|
|
|
|
// isSlash ?
|
2020-05-07 19:28:21 +02:00
|
|
|
var isRoot = prefix == "/"
|
2020-03-24 12:20:07 +01:00
|
|
|
if strings.Contains(prefix, "*") {
|
|
|
|
wildcard = true
|
|
|
|
prefix = strings.Split(prefix, "*")[0]
|
|
|
|
}
|
|
|
|
var stripper = len(prefix)
|
2020-05-07 19:28:21 +02:00
|
|
|
if isRoot {
|
2020-03-24 12:20:07 +01:00
|
|
|
stripper = 0
|
|
|
|
}
|
|
|
|
// Fileserver settings
|
|
|
|
fs := &fasthttp.FS{
|
|
|
|
Root: root,
|
|
|
|
GenerateIndexPages: false,
|
|
|
|
AcceptByteRange: false,
|
|
|
|
Compress: false,
|
|
|
|
CompressedFileSuffix: ".fiber.gz",
|
|
|
|
CacheDuration: 10 * time.Second,
|
|
|
|
IndexNames: []string{"index.html"},
|
|
|
|
PathRewrite: fasthttp.NewPathPrefixStripper(stripper),
|
|
|
|
PathNotFound: func(ctx *fasthttp.RequestCtx) {
|
|
|
|
ctx.Response.SetStatusCode(404)
|
|
|
|
ctx.Response.SetBodyString("Not Found")
|
|
|
|
},
|
|
|
|
}
|
|
|
|
// Set config if provided
|
|
|
|
if len(config) > 0 {
|
|
|
|
fs.Compress = config[0].Compress
|
|
|
|
fs.AcceptByteRange = config[0].ByteRange
|
|
|
|
fs.GenerateIndexPages = config[0].Browse
|
|
|
|
if config[0].Index != "" {
|
|
|
|
fs.IndexNames = []string{config[0].Index}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fileHandler := fs.NewRequestHandler()
|
2020-05-07 19:28:21 +02:00
|
|
|
route := &Route{
|
|
|
|
use: true,
|
|
|
|
root: isRoot,
|
|
|
|
Method: "*",
|
|
|
|
Path: prefix,
|
2020-03-24 12:20:07 +01:00
|
|
|
Handler: func(c *Ctx) {
|
2020-05-07 19:28:21 +02:00
|
|
|
// Do stuff
|
|
|
|
if wildcard {
|
|
|
|
c.Fasthttp.Request.SetRequestURI(prefix)
|
|
|
|
}
|
|
|
|
// Serve file
|
|
|
|
fileHandler(c.Fasthttp)
|
|
|
|
// Finish request if found and not forbidden
|
|
|
|
status := c.Fasthttp.Response.StatusCode()
|
|
|
|
if status != 404 && status != 403 {
|
|
|
|
return
|
2020-03-24 12:20:07 +01:00
|
|
|
}
|
2020-05-07 19:28:21 +02:00
|
|
|
// Reset response
|
|
|
|
c.Fasthttp.Response.Reset()
|
|
|
|
// Next middleware
|
2020-03-24 12:20:07 +01:00
|
|
|
c.Next()
|
|
|
|
},
|
2020-05-07 19:28:21 +02:00
|
|
|
}
|
|
|
|
// Add route to stack
|
|
|
|
app.addRoute(MethodGet, route)
|
|
|
|
app.addRoute(MethodHead, route)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (app *App) addRoute(method string, route *Route) {
|
|
|
|
m := methodINT[method]
|
|
|
|
app.routes[m] = append(app.routes[m], route)
|
2019-12-30 07:29:42 -05:00
|
|
|
}
|