1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-19 13:47:54 +00:00
fiber/router.go

273 lines
7.3 KiB
Go
Raw Normal View History

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
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
}