1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-16 02:52:45 +00:00
fiber/router.go
2019-12-30 07:29:42 -05:00

297 lines
8.4 KiB
Go

package fiber
import (
"fmt"
"regexp"
"time"
"github.com/valyala/fasthttp"
)
type route struct {
method string
path string
anyPath bool
regex *regexp.Regexp
params []string
handler func(*Context)
}
// Settings :
type Settings struct {
TLSEnable bool
CertKey string
CertFile string
Name 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{
methods: []string{"GET", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE", "CONNECT"},
Settings: &Settings{
TLSEnable: false,
CertKey: "",
CertFile: "",
Name: "",
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,
},
}
}
// Get :
func (r *Fiber) Get(args ...interface{}) {
r.register("GET", args...)
}
// 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...)
}
// Connect :
func (r *Fiber) Connect(args ...interface{}) {
r.register("CONNECT", args...)
}
// All :
func (r *Fiber) All(args ...interface{}) {
r.register("*", args...)
// for _, method := range r.methods {
// r.register(method, args...)
// }
}
// Use :
func (r *Fiber) Use(args ...interface{}) {
r.register("*", args...)
// for _, method := range r.methods {
// r.register(method, args...)
// }
}
// register :
func (r *Fiber) register(method string, args ...interface{}) {
// Pre-set variables for interface assertion
var ok bool
var path string
var handler func(*Context)
// Register only handler: app.Get(handler)
if len(args) == 1 {
// Convert interface to func(*Context)
handler, ok = args[0].(func(*Context))
if !ok {
panic("Invalid handler must be func(*express.Context)")
}
}
// Register path and handler: app.Get(path, handler)
if len(args) == 2 {
// Convert interface to path string
path, ok = args[0].(string)
if !ok {
panic("Invalid path")
}
// Panic if first char does not begins with / or *
if path[0] != '/' && path[0] != '*' {
panic("Invalid path, must begin with slash '/' or wildcard '*'")
}
// Convert interface to func(*Context)
handler, ok = args[1].(func(*Context))
if !ok {
panic("Invalid handler, must be func(*express.Context)")
}
}
// If its a simple wildcard ( aka match anything )
if path == "" || path == "*" || path == "/*" {
r.routes = append(r.routes, &route{method, path, true, nil, nil, handler})
fmt.Println(r.routes[0])
return
}
// Get params from path
params := getParams(path)
fmt.Println(params)
// If path has no params, we dont need regex
if len(params) == 0 {
r.routes = append(r.routes, &route{method, path, false, nil, nil, handler})
return
}
// Compile regix from path
regex, err := getRegex(path)
if err != nil {
panic("Invalid url pattern: " + path)
}
r.routes = append(r.routes, &route{method, path, false, regex, params, handler})
}
// 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
if route.anyPath || (route.path == path && route.params == nil) {
// If * always set the path to the wildcard parameter
if route.anyPath {
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
fmt.Println("We did regex -,-")
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 :
func (r *Fiber) Listen(port int) {
// 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
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,
}
if r.Settings.TLSEnable {
if err := server.ListenAndServeTLS(fmt.Sprintf(":%v", port), r.Settings.CertFile, r.Settings.CertKey); err != nil {
panic(err)
}
return
}
if err := server.ListenAndServe(fmt.Sprintf(":%v", port)); err != nil {
panic(err)
}
}