2020-02-22 17:03:30 -05:00
|
|
|
// 🚀 Fiber is an Express inspired web framework written in Go with 💖
|
|
|
|
// 📌 API Documentation: https://fiber.wiki
|
|
|
|
// 📝 Github Repository: https://github.com/gofiber/fiber
|
2020-02-21 18:06:08 +01:00
|
|
|
|
2019-12-30 07:29:42 -05:00
|
|
|
package fiber
|
|
|
|
|
|
|
|
import (
|
2020-03-24 12:19:22 +01:00
|
|
|
"log"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2019-12-30 07:29:42 -05:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
fasthttp "github.com/valyala/fasthttp"
|
2019-12-30 07:29:42 -05:00
|
|
|
)
|
|
|
|
|
2020-02-11 01:26:09 +01:00
|
|
|
// Route struct
|
2020-02-01 19:42:40 +03:00
|
|
|
type Route struct {
|
2020-03-24 12:19:22 +01:00
|
|
|
isGet bool // allows HEAD requests if GET
|
2020-03-14 12:30:21 +01:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
isMiddleware bool // is middleware route
|
2020-02-21 16:54:14 +01:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
isStar bool // path == '*'
|
|
|
|
isSlash bool // path == '/'
|
|
|
|
isRegex bool // needs regex parsing
|
2020-02-21 16:54:14 +01:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
Method string // http method
|
|
|
|
Path string // orginal path
|
|
|
|
Params []string // path params
|
|
|
|
Regexp *regexp.Regexp // regexp matcher
|
2020-03-04 12:30:29 +01:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
Handler func(*Ctx) // ctx handler
|
2020-03-03 12:21:34 -05:00
|
|
|
}
|
2020-02-21 18:07:43 +01:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
func (app *Fiber) nextRoute(ctx *Ctx) {
|
2020-03-24 12:19:22 +01:00
|
|
|
// Keep track of head matches
|
|
|
|
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
|
|
|
|
ctx.values = values
|
|
|
|
route.Handler(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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-03-04 12:30:29 +01:00
|
|
|
func (r *Route) matchRoute(method, path string) (match bool, values []string) {
|
2020-03-24 12:19:22 +01:00
|
|
|
// is route middleware? matches all http methods
|
|
|
|
if r.isMiddleware {
|
|
|
|
// '*' or '/' means its a valid match
|
|
|
|
if r.isStar || r.isSlash {
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
// if midware path starts with req.path
|
|
|
|
if strings.HasPrefix(path, r.Path) {
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
// middlewares dont support regex so bye!
|
|
|
|
return false, values
|
|
|
|
}
|
|
|
|
// non-middleware route, http method must match!
|
|
|
|
// the wildcard method is for .All() & .Use() methods
|
|
|
|
// If route is GET, also match HEAD requests
|
|
|
|
if r.Method == method || r.Method[0] == '*' || (r.isGet && len(method) == 4 && method == "HEAD") {
|
|
|
|
// '*' means we match anything
|
|
|
|
if r.isStar {
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
// simple '/' bool, so you avoid unnecessary comparison for long paths
|
|
|
|
if r.isSlash && path == "/" {
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
// does this route need regex matching?
|
|
|
|
// does req.path match regex pattern?
|
|
|
|
if r.isRegex && 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, values
|
|
|
|
}
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
// last thing to do is to check for a simple path match
|
|
|
|
if len(r.Path) == len(path) && r.Path == path {
|
|
|
|
return true, values
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Nothing match
|
|
|
|
return false, values
|
2020-03-04 12:30:29 +01:00
|
|
|
}
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
func (app *Fiber) handler(fctx *fasthttp.RequestCtx) {
|
2020-03-24 12:19:22 +01:00
|
|
|
// get fiber context from sync pool
|
|
|
|
ctx := acquireCtx(fctx)
|
|
|
|
defer releaseCtx(ctx)
|
|
|
|
// attach app poiner and compress settings
|
|
|
|
ctx.app = app
|
2020-03-04 12:30:29 +01:00
|
|
|
|
2020-03-24 12:19:22 +01:00
|
|
|
// 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-24 05:31:51 +01:00
|
|
|
func (app *Fiber) registerMethod(method, path string, handlers ...func(*Ctx)) {
|
2020-03-24 12:19:22 +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 '*'
|
|
|
|
if path[0] != '/' && path[0] != '*' {
|
|
|
|
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
|
|
|
|
var isGet = method == "GET"
|
|
|
|
var isMiddleware = method == "USE"
|
|
|
|
// Middleware / All allows all HTTP methods
|
|
|
|
if isMiddleware || method == "ALL" {
|
|
|
|
method = "*"
|
|
|
|
}
|
|
|
|
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(original)
|
|
|
|
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
|
|
|
|
}
|
|
|
|
for i := range handlers {
|
|
|
|
app.routes = append(app.routes, &Route{
|
|
|
|
isGet: isGet,
|
|
|
|
isMiddleware: isMiddleware,
|
|
|
|
isStar: isStar,
|
|
|
|
isSlash: isSlash,
|
|
|
|
isRegex: isRegex,
|
|
|
|
Method: method,
|
|
|
|
Path: path,
|
|
|
|
Params: Params,
|
|
|
|
Regexp: Regexp,
|
|
|
|
Handler: handlers[i],
|
|
|
|
})
|
|
|
|
}
|
2019-12-30 13:33:50 +01:00
|
|
|
}
|
2020-03-03 12:21:34 -05:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
func (app *Fiber) registerStatic(prefix, root string, config ...Static) {
|
2020-03-24 12:19:22 +01:00
|
|
|
// Cannot have an empty prefix
|
|
|
|
if prefix == "" {
|
|
|
|
prefix = "/"
|
|
|
|
}
|
|
|
|
// Prefix always start with a '/' or '*'
|
|
|
|
if prefix[0] != '/' && prefix[0] != '*' {
|
|
|
|
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 ?
|
|
|
|
var isSlash = prefix == "/"
|
|
|
|
if strings.Contains(prefix, "*") {
|
|
|
|
wildcard = true
|
|
|
|
prefix = strings.Split(prefix, "*")[0]
|
|
|
|
}
|
|
|
|
var stripper = len(prefix)
|
|
|
|
if isSlash {
|
|
|
|
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()
|
|
|
|
app.routes = append(app.routes, &Route{
|
|
|
|
isMiddleware: true,
|
|
|
|
isSlash: isSlash,
|
|
|
|
Method: "*",
|
|
|
|
Path: prefix,
|
|
|
|
Handler: func(c *Ctx) {
|
|
|
|
// Only handle GET & HEAD methods
|
|
|
|
if c.method == "GET" || c.method == "HEAD" {
|
|
|
|
// Do stuff
|
|
|
|
if wildcard {
|
|
|
|
c.Fasthttp.Request.SetRequestURI(prefix)
|
|
|
|
}
|
|
|
|
// Serve file
|
|
|
|
fileHandler(c.Fasthttp)
|
|
|
|
// End response when file is found
|
|
|
|
if c.Fasthttp.Response.StatusCode() != 404 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Next()
|
|
|
|
},
|
|
|
|
})
|
2019-12-30 07:29:42 -05:00
|
|
|
}
|