1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-19 04:27:53 +00:00
fiber/router.go

366 lines
9.7 KiB
Go
Raw Normal View History

2019-12-30 07:29:42 -05:00
package fiber
import (
"fmt"
2020-01-08 03:24:53 -05:00
"os"
"os/exec"
2020-01-06 18:38:39 -05:00
"path/filepath"
2019-12-30 07:29:42 -05:00
"regexp"
2020-01-08 03:24:53 -05:00
"runtime"
2020-01-08 03:12:05 -05:00
"strconv"
2020-01-06 18:38:39 -05:00
"strings"
2019-12-30 07:29:42 -05:00
"time"
2020-01-08 03:12:05 -05:00
"github.com/fatih/color"
2019-12-30 07:29:42 -05:00
"github.com/valyala/fasthttp"
)
2020-01-08 03:12:05 -05:00
const (
Version = "v0.2.0"
banner = ` _____ _ _
| __|_| |_ ___ ___
| __| | . | -_| _|
|__| |_|___|___|_|%s
2020-01-08 03:24:53 -05:00
2020-01-08 03:12:05 -05:00
`
)
2019-12-30 07:29:42 -05:00
type route struct {
method string
2020-01-06 18:38:39 -05:00
any bool
2019-12-30 07:29:42 -05:00
path string
regex *regexp.Regexp
params []string
2020-01-03 05:57:41 -05:00
handler func(*Ctx)
2019-12-30 07:29:42 -05:00
}
2019-12-30 13:33:50 +01:00
// Settings :
type Settings struct {
2020-01-08 03:12:05 -05:00
Name string
2020-01-08 03:24:53 -05:00
ClearTerminal bool
2020-01-08 03:12:05 -05:00
HideBanner bool
2019-12-30 13:33:50 +01:00
TLSEnable bool
CertKey string
CertFile string
Concurrency int
DisableKeepAlive bool
ReadBufferSize int
WriteBufferSize int
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxConnsPerIP int
MaxRequestsPerConn int
TCPKeepalive bool
TCPKeepalivePeriod time.Duration
MaxRequestBodySize int
ReduceMemoryUsage bool
GetOnly bool
DisableHeaderNamesNormalizing bool
SleepWhenConcurrencyLimitsExceeded time.Duration
NoDefaultServerHeader bool
NoDefaultContentType bool
KeepHijackedConns bool
}
// Fiber :
type Fiber struct {
routes []*route
methods []string
Settings *Settings
}
// New :
func New() *Fiber {
return &Fiber{
Settings: &Settings{
2020-01-08 03:12:05 -05:00
Name: "",
2020-01-08 03:24:53 -05:00
ClearTerminal: false,
2020-01-08 03:12:05 -05:00
HideBanner: false,
2019-12-30 13:33:50 +01:00
TLSEnable: false,
CertKey: "",
CertFile: "",
Concurrency: 256 * 1024,
DisableKeepAlive: false,
ReadBufferSize: 4096,
WriteBufferSize: 4096,
WriteTimeout: 0,
IdleTimeout: 0,
MaxConnsPerIP: 0,
MaxRequestsPerConn: 0,
TCPKeepalive: false,
TCPKeepalivePeriod: 0,
MaxRequestBodySize: 4 * 1024 * 1024,
ReduceMemoryUsage: false,
GetOnly: false,
DisableHeaderNamesNormalizing: false,
SleepWhenConcurrencyLimitsExceeded: 0,
NoDefaultServerHeader: true,
NoDefaultContentType: false,
KeepHijackedConns: false,
},
}
}
2020-01-06 18:38:39 -05:00
// Connect :
func (r *Fiber) Connect(args ...interface{}) {
r.register("CONNECT", args...)
2019-12-30 13:33:50 +01:00
}
// Put :
func (r *Fiber) Put(args ...interface{}) {
r.register("PUT", args...)
}
// Post :
func (r *Fiber) Post(args ...interface{}) {
r.register("POST", args...)
}
// Delete :
func (r *Fiber) Delete(args ...interface{}) {
r.register("DELETE", args...)
}
// Head :
func (r *Fiber) Head(args ...interface{}) {
r.register("HEAD", args...)
}
// Patch :
func (r *Fiber) Patch(args ...interface{}) {
r.register("PATCH", args...)
}
// Options :
func (r *Fiber) Options(args ...interface{}) {
r.register("OPTIONS", args...)
}
// Trace :
func (r *Fiber) Trace(args ...interface{}) {
r.register("TRACE", args...)
}
2020-01-06 18:38:39 -05:00
// Get :
func (r *Fiber) Get(args ...interface{}) {
r.register("GET", args...)
2019-12-30 13:33:50 +01:00
}
2020-01-07 11:28:06 -05:00
// Use :
func (r *Fiber) Use(args ...interface{}) {
r.register("*", args...)
}
2019-12-30 13:33:50 +01:00
// All :
func (r *Fiber) All(args ...interface{}) {
r.register("*", args...)
}
func (r *Fiber) register(method string, args ...interface{}) {
2020-01-06 18:38:39 -05:00
// Options
2019-12-30 13:33:50 +01:00
var path string
2020-01-06 18:38:39 -05:00
var static string
2020-01-03 05:57:41 -05:00
var handler func(*Ctx)
2020-01-06 18:38:39 -05:00
// app.Get(handler)
2019-12-30 13:33:50 +01:00
if len(args) == 1 {
2020-01-06 18:38:39 -05:00
switch arg := args[0].(type) {
case string:
static = arg
case func(*Ctx):
handler = arg
2019-12-30 13:33:50 +01:00
}
}
2020-01-06 18:38:39 -05:00
// app.Get(path, handler)
2019-12-30 13:33:50 +01:00
if len(args) == 2 {
2020-01-06 18:38:39 -05:00
path = args[0].(string)
2019-12-30 13:33:50 +01:00
if path[0] != '/' && path[0] != '*' {
panic("Invalid path, must begin with slash '/' or wildcard '*'")
}
2020-01-06 18:38:39 -05:00
switch arg := args[1].(type) {
case string:
static = arg
case func(*Ctx):
handler = arg
2019-12-30 13:33:50 +01:00
}
}
2020-01-06 18:38:39 -05:00
// Is this a static file handler?
if static != "" {
// static file route!!
r.registerStatic(method, path, static)
} else if handler != nil {
// function route!!
r.registerHandler(method, path, handler)
} else {
panic("Every route needs to contain either a dir/file path or callback function")
}
}
func (r *Fiber) registerStatic(method, prefix, root string) {
var any bool
if prefix == "*" || prefix == "/*" {
any = true
}
if prefix == "" {
prefix = "/"
}
files, _, err := walk(root)
if err != nil {
panic(err)
}
mount := filepath.Clean(root)
for _, file := range files {
path := filepath.Join(prefix, strings.Replace(file, mount, "", 1))
filePath := file
if filepath.Base(filePath) == "index.html" {
r.routes = append(r.routes, &route{method, any, prefix, nil, nil, func(c *Ctx) {
c.SendFile(filePath)
}})
}
r.routes = append(r.routes, &route{method, any, path, nil, nil, func(c *Ctx) {
c.SendFile(filePath)
}})
}
}
func (r *Fiber) registerHandler(method, path string, handler func(*Ctx)) {
2019-12-30 13:33:50 +01:00
if path == "" || path == "*" || path == "/*" {
2020-01-06 18:38:39 -05:00
r.routes = append(r.routes, &route{method, true, path, nil, nil, handler})
2019-12-30 13:33:50 +01:00
return
}
// Get params from path
params := getParams(path)
// If path has no params, we dont need regex
if len(params) == 0 {
2020-01-06 18:38:39 -05:00
r.routes = append(r.routes, &route{method, false, path, nil, nil, handler})
2019-12-30 13:33:50 +01:00
return
}
// Compile regix from path
regex, err := getRegex(path)
if err != nil {
panic("Invalid url pattern: " + path)
}
2020-01-06 18:38:39 -05:00
r.routes = append(r.routes, &route{method, false, path, regex, params, handler})
2019-12-30 13:33:50 +01:00
}
// handler :
func (r *Fiber) handler(fctx *fasthttp.RequestCtx) {
// get custom context from sync pool
ctx := acquireCtx(fctx)
// get path and method from main context
path := ctx.Path()
method := ctx.Method()
// loop trough routes
for _, route := range r.routes {
// Skip route if method is not allowed
if route.method != "*" && route.method != method {
continue
}
// First check if we match a static path or wildcard
2020-01-06 18:38:39 -05:00
if route.any || (route.path == path && route.params == nil) {
2019-12-30 13:33:50 +01:00
// If * always set the path to the wildcard parameter
2020-01-06 18:38:39 -05:00
if route.any {
2019-12-30 13:33:50 +01:00
ctx.params = &[]string{"*"}
ctx.values = []string{path}
}
// Execute handler with context
route.handler(ctx)
// if next is not set, leave loop and release ctx
if !ctx.next {
break
}
// set next to false for next iteration
ctx.next = false
// continue to go to the next route
continue
}
// Skip route if regex does not match
2019-12-30 07:29:42 -05:00
if route.regex == nil || !route.regex.MatchString(path) {
continue
}
// If we have parameters, lets find the matches
if route.params != nil && 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])]
}
}
// Execute handler with context
route.handler(ctx)
// if next is not set, leave loop and release ctx
if !ctx.next {
break
}
// set next to false for next iteration
ctx.next = false
}
// release context back into sync pool
releaseCtx(ctx)
}
// Listen :
2020-01-06 18:38:39 -05:00
func (r *Fiber) Listen(args ...interface{}) {
2020-01-08 03:12:05 -05:00
var port string
2020-01-06 18:38:39 -05:00
var addr string
if len(args) == 1 {
2020-01-08 03:12:05 -05:00
port = strconv.Itoa(args[0].(int))
2020-01-06 18:38:39 -05:00
}
if len(args) == 2 {
addr = args[0].(string)
2020-01-08 03:12:05 -05:00
port = strconv.Itoa(args[1].(int))
2020-01-06 18:38:39 -05:00
}
2019-12-30 07:29:42 -05:00
// Disable server header if server name is not given
if r.Settings.Name != "" {
r.Settings.NoDefaultServerHeader = false
}
server := &fasthttp.Server{
// Express custom handler
Handler: r.handler,
// Server settings
2020-01-03 05:57:41 -05:00
Name: r.Settings.Name,
Concurrency: r.Settings.Concurrency,
DisableKeepalive: r.Settings.DisableKeepAlive,
ReadBufferSize: r.Settings.ReadBufferSize,
WriteBufferSize: r.Settings.WriteBufferSize,
WriteTimeout: r.Settings.WriteTimeout,
IdleTimeout: r.Settings.IdleTimeout,
MaxConnsPerIP: r.Settings.MaxConnsPerIP,
MaxRequestsPerConn: r.Settings.MaxRequestsPerConn,
TCPKeepalive: r.Settings.TCPKeepalive,
TCPKeepalivePeriod: r.Settings.TCPKeepalivePeriod,
MaxRequestBodySize: r.Settings.MaxRequestBodySize,
ReduceMemoryUsage: r.Settings.ReduceMemoryUsage,
GetOnly: r.Settings.GetOnly,
DisableHeaderNamesNormalizing: r.Settings.DisableHeaderNamesNormalizing,
SleepWhenConcurrencyLimitsExceeded: r.Settings.SleepWhenConcurrencyLimitsExceeded,
NoDefaultServerHeader: r.Settings.NoDefaultServerHeader,
NoDefaultContentType: r.Settings.NoDefaultContentType,
KeepHijackedConns: r.Settings.KeepHijackedConns,
2019-12-30 07:29:42 -05:00
}
2020-01-08 03:24:53 -05:00
if r.Settings.ClearTerminal {
var cmd *exec.Cmd
goos := runtime.GOOS
if goos == "windows" {
cmd = exec.Command("cmd", "/c", "cls")
}
if goos == "linux" {
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
cmd.Run()
}
2020-01-08 03:12:05 -05:00
if !r.Settings.HideBanner {
2020-01-08 03:24:53 -05:00
fmt.Printf(color.HiCyanString(banner), color.GreenString(":"+port))
2020-01-08 03:12:05 -05:00
}
// fmt.Printf(banner, Version)
2019-12-30 07:29:42 -05:00
if r.Settings.TLSEnable {
2020-01-08 03:12:05 -05:00
if err := server.ListenAndServeTLS(fmt.Sprintf("%s:%s", addr, port), r.Settings.CertFile, r.Settings.CertKey); err != nil {
panic(err)
}
} else {
if err := server.ListenAndServe(fmt.Sprintf("%s:%s", addr, port)); err != nil {
2019-12-30 07:29:42 -05:00
panic(err)
}
}
}