mirror of https://github.com/gofiber/fiber.git
More middlewares
parent
37583902cd
commit
ca6aa02e7f
121
app.go
121
app.go
|
@ -54,23 +54,23 @@ type (
|
|||
Compression bool `default:"false"`
|
||||
// CompressionLevel int `default:"1"`
|
||||
// fasthttp settings
|
||||
GETOnly bool `default:"false"`
|
||||
IdleTimeout time.Duration `default:"0"`
|
||||
Concurrency int `default:"256 * 1024"`
|
||||
ReadTimeout time.Duration `default:"0"`
|
||||
WriteTimeout time.Duration `default:"0"`
|
||||
TCPKeepalive bool `default:"false"`
|
||||
MaxConnsPerIP int `default:"0"`
|
||||
ReadBufferSize int `default:"4096"`
|
||||
WriteBufferSize int `default:"4096"`
|
||||
ConcurrencySleep time.Duration `default:"0"`
|
||||
DisableKeepAlive bool `default:"false"`
|
||||
ReduceMemoryUsage bool `default:"false"`
|
||||
MaxRequestsPerConn int `default:"0"`
|
||||
TCPKeepalivePeriod time.Duration `default:"0"`
|
||||
MaxRequestBodySize int `default:"4 * 1024 * 1024"`
|
||||
NoHeaderNormalizing bool `default:"false"`
|
||||
NoDefaultContentType bool `default:"false"`
|
||||
// GETOnly bool `default:"false"`
|
||||
// IdleTimeout time.Duration `default:"0"`
|
||||
// Concurrency int `default:"256 * 1024"`
|
||||
// ReadTimeout time.Duration `default:"0"`
|
||||
// WriteTimeout time.Duration `default:"0"`
|
||||
// TCPKeepalive bool `default:"false"`
|
||||
// MaxConnsPerIP int `default:"0"`
|
||||
// ReadBufferSize int `default:"4096"`
|
||||
// WriteBufferSize int `default:"4096"`
|
||||
// ConcurrencySleep time.Duration `default:"0"`
|
||||
// DisableKeepAlive bool `default:"false"`
|
||||
// ReduceMemoryUsage bool `default:"false"`
|
||||
// MaxRequestsPerConn int `default:"0"`
|
||||
// TCPKeepalivePeriod time.Duration `default:"0"`
|
||||
BodyLimit int `default:"4 * 1024 * 1024"`
|
||||
// NoHeaderNormalizing bool `default:"false"`
|
||||
// NoDefaultContentType bool `default:"false"`
|
||||
// template settings
|
||||
TemplateFolder string `default:""`
|
||||
TemplateEngine string `default:""`
|
||||
|
@ -107,17 +107,17 @@ func New(settings ...*Settings) (app *App) {
|
|||
return string(b)
|
||||
}
|
||||
}
|
||||
if opt.Concurrency == 0 {
|
||||
opt.Concurrency = 256 * 1024
|
||||
}
|
||||
if opt.ReadBufferSize == 0 {
|
||||
opt.ReadBufferSize = 4096
|
||||
}
|
||||
if opt.WriteBufferSize == 0 {
|
||||
opt.WriteBufferSize = 4096
|
||||
}
|
||||
if opt.MaxRequestBodySize == 0 {
|
||||
opt.MaxRequestBodySize = 4 * 1024 * 1024
|
||||
// if opt.Concurrency == 0 {
|
||||
// opt.Concurrency = 256 * 1024
|
||||
// }
|
||||
// if opt.ReadBufferSize == 0 {
|
||||
// opt.ReadBufferSize = 4096
|
||||
// }
|
||||
// if opt.WriteBufferSize == 0 {
|
||||
// opt.WriteBufferSize = 4096
|
||||
// }
|
||||
if opt.BodyLimit == 0 {
|
||||
opt.BodyLimit = 4 * 1024 * 1024
|
||||
}
|
||||
// if opt.CompressionLevel == 0 {
|
||||
// opt.CompressionLevel = 1
|
||||
|
@ -126,11 +126,11 @@ func New(settings ...*Settings) (app *App) {
|
|||
return
|
||||
}
|
||||
app.Settings = &Settings{
|
||||
Prefork: prefork,
|
||||
Concurrency: 256 * 1024,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
MaxRequestBodySize: 4 * 1024 * 1024,
|
||||
Prefork: prefork,
|
||||
// Concurrency: 256 * 1024,
|
||||
// ReadBufferSize: 4096,
|
||||
// WriteBufferSize: 4096,
|
||||
BodyLimit: 4 * 1024 * 1024,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -379,31 +379,44 @@ func (app *App) prefork(address string) (ln net.Listener, err error) {
|
|||
return ln, err
|
||||
}
|
||||
|
||||
type disableLogger struct{}
|
||||
|
||||
func (dl *disableLogger) Printf(format string, args ...interface{}) {
|
||||
// fmt.Println(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (app *App) newServer() *fasthttp.Server {
|
||||
return &fasthttp.Server{
|
||||
Handler: app.handler,
|
||||
Name: app.Settings.ServerHeader,
|
||||
// Concurrency: app.Settings.Concurrency,
|
||||
// SleepWhenConcurrencyLimitsExceeded: app.Settings.ConcurrencySleep,
|
||||
// DisableKeepalive: app.Settings.DisableKeepAlive,
|
||||
// ReadBufferSize: app.Settings.ReadBufferSize,
|
||||
// WriteBufferSize: app.Settings.WriteBufferSize,
|
||||
// ReadTimeout: app.Settings.ReadTimeout,
|
||||
// WriteTimeout: app.Settings.WriteTimeout,
|
||||
// IdleTimeout: app.Settings.IdleTimeout,
|
||||
// MaxConnsPerIP: app.Settings.MaxConnsPerIP,
|
||||
// MaxRequestsPerConn: app.Settings.MaxRequestsPerConn,
|
||||
// TCPKeepalive: app.Settings.TCPKeepalive,
|
||||
// TCPKeepalivePeriod: app.Settings.TCPKeepalivePeriod,
|
||||
MaxRequestBodySize: app.Settings.BodyLimit,
|
||||
// ReduceMemoryUsage: app.Settings.ReduceMemoryUsage,
|
||||
// GetOnly: app.Settings.GETOnly,
|
||||
// DisableHeaderNamesNormalizing: app.Settings.NoHeaderNormalizing,
|
||||
NoDefaultServerHeader: app.Settings.ServerHeader == "",
|
||||
// NoDefaultContentType: app.Settings.NoDefaultContentType,
|
||||
Logger: &disableLogger{},
|
||||
LogAllErrors: false,
|
||||
ErrorHandler: func(ctx *fasthttp.RequestCtx, err error) {
|
||||
ctx.Response.SetStatusCode(400)
|
||||
ctx.Response.SetBodyString("Bad Request")
|
||||
if err.Error() == "body size exceeds the given limit" {
|
||||
ctx.Response.SetStatusCode(413)
|
||||
ctx.Response.SetBodyString("Request Entity Too Large")
|
||||
} else {
|
||||
ctx.Response.SetStatusCode(400)
|
||||
ctx.Response.SetBodyString("Bad Request")
|
||||
}
|
||||
},
|
||||
Name: app.Settings.ServerHeader,
|
||||
Concurrency: app.Settings.Concurrency,
|
||||
SleepWhenConcurrencyLimitsExceeded: app.Settings.ConcurrencySleep,
|
||||
DisableKeepalive: app.Settings.DisableKeepAlive,
|
||||
ReadBufferSize: app.Settings.ReadBufferSize,
|
||||
WriteBufferSize: app.Settings.WriteBufferSize,
|
||||
ReadTimeout: app.Settings.ReadTimeout,
|
||||
WriteTimeout: app.Settings.WriteTimeout,
|
||||
IdleTimeout: app.Settings.IdleTimeout,
|
||||
MaxConnsPerIP: app.Settings.MaxConnsPerIP,
|
||||
MaxRequestsPerConn: app.Settings.MaxRequestsPerConn,
|
||||
TCPKeepalive: app.Settings.TCPKeepalive,
|
||||
TCPKeepalivePeriod: app.Settings.TCPKeepalivePeriod,
|
||||
MaxRequestBodySize: app.Settings.MaxRequestBodySize,
|
||||
ReduceMemoryUsage: app.Settings.ReduceMemoryUsage,
|
||||
GetOnly: app.Settings.GETOnly,
|
||||
DisableHeaderNamesNormalizing: app.Settings.NoHeaderNormalizing,
|
||||
NoDefaultServerHeader: app.Settings.ServerHeader == "",
|
||||
NoDefaultContentType: app.Settings.NoDefaultContentType,
|
||||
}
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -5,7 +5,9 @@ go 1.11
|
|||
require (
|
||||
github.com/fasthttp/websocket v1.4.2
|
||||
github.com/gofiber/template v1.0.0
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/schema v1.1.0
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/valyala/fasthttp v1.9.0
|
||||
github.com/valyala/fasttemplate v1.1.0
|
||||
)
|
||||
|
|
18
go.sum
18
go.sum
|
@ -1,40 +1,30 @@
|
|||
github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE=
|
||||
github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc=
|
||||
github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8=
|
||||
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
|
||||
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
||||
github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw=
|
||||
github.com/cbroglie/mustache v1.0.1/go.mod h1:R/RUa+SobQ14qkP4jtx5Vke5sDytONDQXNLPY/PO69g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/fasthttp/websocket v1.4.2 h1:AU/zSiIIAuJjBMf5o+vO0syGOnEfvZRu40xIhW/3RuM=
|
||||
github.com/fasthttp/websocket v1.4.2/go.mod h1:smsv/h4PBEBaU0XDTY5UwJTpZv69fQ0FfcLJr21mA6Y=
|
||||
github.com/gofiber/template v1.0.0 h1:Vf4Fby9zUWVQyY2y69KKyRHsEYlIE+Pxb25M+jiaEL0=
|
||||
github.com/gofiber/template v1.0.0/go.mod h1:+bij+R0NI6urTg2jtQvPj5wb2uWMxW9eYGsAN3QhnP0=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f h1:PgA+Olipyj258EIEYnpFFONrrCcAIWNUNoFhUfMqAGY=
|
||||
github.com/savsgio/gotils v0.0.0-20200117113501-90175b0fbe3f/go.mod h1:lHhJedqxCoHN+zMtwGNTXWmF0u9Jt363FYRhV6g0CdY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
|
||||
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
".."
|
||||
)
|
||||
|
||||
// BasicAuthConfig defines the config for BasicAuth middleware
|
||||
type BasicAuthConfig struct {
|
||||
// Skip defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// Users
|
||||
// Required.
|
||||
Users map[string]string
|
||||
// Realm is a string to define realm attribute of BasicAuth.
|
||||
// Optional. Default: "Restricted".
|
||||
Realm string
|
||||
}
|
||||
|
||||
// BasicAuthConfigDefault is the default BasicAuth middleware config.
|
||||
var BasicAuthConfigDefault = BasicAuthConfig{
|
||||
Skip: nil,
|
||||
Realm: "Restricted",
|
||||
}
|
||||
|
||||
// BasicAuth ...
|
||||
func BasicAuth(config ...BasicAuthConfig) func(*fiber.Ctx) {
|
||||
// Init config
|
||||
var cfg BasicAuthConfig
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Set config default values
|
||||
if cfg.Realm == "" {
|
||||
cfg.Realm = BasicAuthConfigDefault.Realm
|
||||
}
|
||||
// Return middleware handler
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// Get authorization header
|
||||
auth := c.Get(fiber.HeaderAuthorization)
|
||||
// Check if characters are provided
|
||||
if len(auth) > 6 && strings.ToLower(auth[:5]) == "basic" {
|
||||
// Try to decode
|
||||
if raw, err := base64.StdEncoding.DecodeString(auth[6:]); err == nil {
|
||||
// Convert to string
|
||||
cred := string(raw)
|
||||
// Find semicolumn
|
||||
for i := 0; i < len(cred); i++ {
|
||||
if cred[i] == ':' {
|
||||
// Split into user & pass
|
||||
user := cred[:i]
|
||||
pass := cred[i+1:]
|
||||
// If exist & match in Users, we let him pass
|
||||
if cfg.Users[user] == pass {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Authentication required
|
||||
c.Set(fiber.HeaderWWWAuthenticate, "basic realm="+cfg.Realm)
|
||||
c.SendStatus(401)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package middleware
|
||||
|
||||
import "github.com/gofiber/fiber"
|
||||
|
||||
// BasicAuthConfig ...
|
||||
type BasicAuthConfig struct {
|
||||
}
|
||||
|
||||
// BasicAuth ...
|
||||
func BasicAuth(config ...BasicAuthConfig) func(*fiber.Ctx) {
|
||||
// Init config
|
||||
var cfg BasicAuthConfig
|
||||
// Set config if provided
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Don't forget to remove this
|
||||
_ = cfg
|
||||
// Set config default values
|
||||
return func(c *fiber.Ctx) {
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -5,11 +5,12 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber"
|
||||
".."
|
||||
)
|
||||
|
||||
// CORSConfig ...
|
||||
type CORSConfig struct {
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// Optional. Default value []string{"*"}.
|
||||
AllowOrigins []string
|
||||
// Optional. Default value []string{"GET","POST","HEAD","PUT","DELETE","PATCH"}
|
||||
|
@ -24,6 +25,20 @@ type CORSConfig struct {
|
|||
MaxAge int
|
||||
}
|
||||
|
||||
// CorsConfigDefault is the defaul Cors middleware config.
|
||||
var CorsConfigDefault = CORSConfig{
|
||||
Skip: nil,
|
||||
AllowOrigins: []string{"*"},
|
||||
AllowMethods: []string{
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodHead,
|
||||
http.MethodPut,
|
||||
http.MethodDelete,
|
||||
http.MethodPatch,
|
||||
},
|
||||
}
|
||||
|
||||
// Cors ...
|
||||
func Cors(config ...CORSConfig) func(*fiber.Ctx) {
|
||||
// Init config
|
||||
|
@ -32,19 +47,12 @@ func Cors(config ...CORSConfig) func(*fiber.Ctx) {
|
|||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Set config default vvalues
|
||||
// Set config default values
|
||||
if len(cfg.AllowOrigins) == 0 {
|
||||
cfg.AllowOrigins = []string{"*"}
|
||||
cfg.AllowOrigins = CorsConfigDefault.AllowOrigins
|
||||
}
|
||||
if len(cfg.AllowMethods) == 0 {
|
||||
cfg.AllowMethods = []string{
|
||||
http.MethodGet,
|
||||
http.MethodPost,
|
||||
http.MethodHead,
|
||||
http.MethodPut,
|
||||
http.MethodDelete,
|
||||
http.MethodPatch,
|
||||
}
|
||||
cfg.AllowMethods = CorsConfigDefault.AllowMethods
|
||||
}
|
||||
// Middleware settings
|
||||
allowMethods := strings.Join(cfg.AllowMethods, ",")
|
||||
|
@ -53,6 +61,11 @@ func Cors(config ...CORSConfig) func(*fiber.Ctx) {
|
|||
maxAge := strconv.Itoa(cfg.MaxAge)
|
||||
// Middleware function
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
origin := c.Get(fiber.HeaderOrigin)
|
||||
allowOrigin := ""
|
||||
// Check allowed origins
|
||||
|
|
|
@ -1,9 +1,49 @@
|
|||
package middleware
|
||||
|
||||
import "github.com/gofiber/fiber"
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
".."
|
||||
)
|
||||
|
||||
// LimiterConfig ...
|
||||
type LimiterConfig struct {
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// Timeout in seconds on how long to keep records of requests in memory
|
||||
// Default: 60
|
||||
Timeout int
|
||||
// Max number of recent connections during `Timeout` seconds before sending a 429 response
|
||||
// Default: 10
|
||||
Max int
|
||||
// Message
|
||||
// default: "Too many requests, please try again later."
|
||||
Message string
|
||||
// StatusCode
|
||||
// Default: 429 Too Many Requests
|
||||
StatusCode int
|
||||
// Key allows to use a custom handler to create custom keys
|
||||
// Default: func(c *fiber.Ctx) string {
|
||||
// return c.IP()
|
||||
// }
|
||||
Key func(*fiber.Ctx) string
|
||||
// Handler is called when a request hits the limit
|
||||
// Default: func(c *fiber.Ctx) {
|
||||
// c.Status(cfg.StatusCode).SendString(cfg.Message)
|
||||
// }
|
||||
Handler func(*fiber.Ctx)
|
||||
}
|
||||
|
||||
// LimiterConfigDefault is the defaul Limiter middleware config.
|
||||
var LimiterConfigDefault = LimiterConfig{
|
||||
Skip: nil,
|
||||
Timeout: 60,
|
||||
Max: 10,
|
||||
Message: "Too many requests, please try again later.",
|
||||
StatusCode: 429,
|
||||
Key: func(c *fiber.Ctx) string {
|
||||
return c.IP()
|
||||
},
|
||||
}
|
||||
|
||||
// Limiter ...
|
||||
|
@ -14,10 +54,88 @@ func Limiter(config ...LimiterConfig) func(*fiber.Ctx) {
|
|||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Don't forget to remove this
|
||||
_ = cfg
|
||||
// Set config default values
|
||||
if cfg.Timeout == 0 {
|
||||
cfg.Timeout = LimiterConfigDefault.Timeout
|
||||
}
|
||||
if cfg.Max == 0 {
|
||||
cfg.Max = LimiterConfigDefault.Max
|
||||
}
|
||||
if cfg.Message == "" {
|
||||
cfg.Message = LimiterConfigDefault.Message
|
||||
}
|
||||
if cfg.StatusCode == 0 {
|
||||
cfg.StatusCode = LimiterConfigDefault.StatusCode
|
||||
}
|
||||
if cfg.Key == nil {
|
||||
cfg.Key = LimiterConfigDefault.Key
|
||||
}
|
||||
if cfg.Handler == nil {
|
||||
cfg.Handler = func(c *fiber.Ctx) {
|
||||
c.Status(cfg.StatusCode).SendString(cfg.Message)
|
||||
}
|
||||
}
|
||||
// Limiter settings
|
||||
var hits = map[string]int{}
|
||||
var reset = map[string]int{}
|
||||
var timestamp = int(time.Now().Unix())
|
||||
// Update timestamp every second
|
||||
go func() {
|
||||
for {
|
||||
timestamp = int(time.Now().Unix())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}()
|
||||
// Reset hits every cfg.Timeout
|
||||
go func() {
|
||||
for {
|
||||
// For every key in reset
|
||||
for key := range reset {
|
||||
// If resetTime exist and current time is equal or bigger
|
||||
if reset[key] != 0 && timestamp >= reset[key] {
|
||||
// Reset hits and resetTime
|
||||
hits[key] = 0
|
||||
reset[key] = 0
|
||||
}
|
||||
}
|
||||
// Wait cfg.Timeout
|
||||
time.Sleep(time.Duration(cfg.Timeout) * time.Second)
|
||||
}
|
||||
}()
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// Get key (default is the remote IP)
|
||||
key := cfg.Key(c)
|
||||
// Increment key hits
|
||||
hits[key]++
|
||||
// Set unix timestamp if not exist
|
||||
if reset[key] == 0 {
|
||||
reset[key] = timestamp + cfg.Timeout
|
||||
}
|
||||
// Get current hits
|
||||
hitCount := hits[key]
|
||||
// Set how many hits we have left
|
||||
remaining := cfg.Max - hitCount
|
||||
// Calculate when it resets in seconds
|
||||
resetTime := reset[key] - timestamp
|
||||
// Check if hits exceed the cfg.Max
|
||||
if remaining < 1 {
|
||||
// Call Handler func
|
||||
cfg.Handler(c)
|
||||
// Return response with Retry-After header
|
||||
// https://tools.ietf.org/html/rfc6584
|
||||
c.Set("Retry-After", strconv.Itoa(resetTime))
|
||||
return
|
||||
}
|
||||
// We can continue, update RateLimit headers
|
||||
c.Set("X-RateLimit-Limit", strconv.Itoa(cfg.Max))
|
||||
c.Set("X-RateLimit-Remaining", strconv.Itoa(remaining))
|
||||
c.Set("X-RateLimit-Reset", strconv.Itoa(resetTime))
|
||||
// Bye!
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,41 @@
|
|||
package middleware
|
||||
|
||||
import "github.com/gofiber/fiber"
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
".."
|
||||
"github.com/valyala/fasttemplate"
|
||||
)
|
||||
|
||||
// LoggerConfig ...
|
||||
type LoggerConfig struct {
|
||||
// Skip defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// Format defines the logging format with defined variables
|
||||
// Optional. Default: "${time} - ${ip} - ${method} ${path}\t${ua}\n"
|
||||
// Possible values: time, ip, url, host, method, path, protocol
|
||||
// referer, ua, header:<key>, query:<key>, formform:<key>, cookie:<key>
|
||||
Format string
|
||||
// TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html
|
||||
// Optional. Default: 15:04:05
|
||||
TimeFormat string
|
||||
// Output is a writter where logs are written
|
||||
// Default: os.Stderr
|
||||
Output io.Writer
|
||||
}
|
||||
|
||||
// LoggerConfigDefault is the defaul Logger middleware config.
|
||||
var LoggerConfigDefault = LoggerConfig{
|
||||
Skip: nil,
|
||||
Format: "${time} - ${ip} - ${method} ${path}\t${ua}\n",
|
||||
TimeFormat: "15:04:05",
|
||||
Output: os.Stderr,
|
||||
}
|
||||
|
||||
// Logger ...
|
||||
|
@ -14,10 +46,78 @@ func Logger(config ...LoggerConfig) func(*fiber.Ctx) {
|
|||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Don't forget to remove this
|
||||
_ = cfg
|
||||
// Set config default values
|
||||
if cfg.Format == "" {
|
||||
cfg.Format = LoggerConfigDefault.Format
|
||||
}
|
||||
if cfg.TimeFormat == "" {
|
||||
cfg.TimeFormat = LoggerConfigDefault.TimeFormat
|
||||
}
|
||||
if cfg.Output == nil {
|
||||
cfg.Output = LoggerConfigDefault.Output
|
||||
}
|
||||
// Middleware settings
|
||||
tmpl := fasttemplate.New(cfg.Format, "${", "}")
|
||||
pool := &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bytes.NewBuffer(make([]byte, 256))
|
||||
},
|
||||
}
|
||||
timestamp := time.Now().Format(cfg.TimeFormat)
|
||||
// Update date/time every second in a seperate go routine
|
||||
if strings.Contains(cfg.Format, "${time}") {
|
||||
go func() {
|
||||
for {
|
||||
timestamp = time.Now().Format(cfg.TimeFormat)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
// Middleware function
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
buf := pool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer pool.Put(buf)
|
||||
tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) {
|
||||
switch tag {
|
||||
case "time":
|
||||
return buf.WriteString(timestamp)
|
||||
case "referer":
|
||||
return buf.WriteString(c.Get(fiber.HeaderReferer))
|
||||
case "protocol":
|
||||
return buf.WriteString(c.Protocol())
|
||||
case "ip":
|
||||
return buf.WriteString(c.IP())
|
||||
case "host":
|
||||
return buf.WriteString(c.Hostname())
|
||||
case "method":
|
||||
return buf.WriteString(c.Method())
|
||||
case "path":
|
||||
return buf.WriteString(c.Path())
|
||||
case "url":
|
||||
return buf.WriteString(c.OriginalURL())
|
||||
case "ua":
|
||||
return buf.WriteString(c.Get(fiber.HeaderUserAgent))
|
||||
default:
|
||||
switch {
|
||||
case strings.HasPrefix(tag, "header:"):
|
||||
return buf.WriteString(c.Get(tag[7:]))
|
||||
case strings.HasPrefix(tag, "query:"):
|
||||
return buf.WriteString(c.Query(tag[6:]))
|
||||
case strings.HasPrefix(tag, "form:"):
|
||||
return buf.WriteString(c.FormValue(tag[5:]))
|
||||
case strings.HasPrefix(tag, "cookie:"):
|
||||
return buf.WriteString(c.Cookies(tag[7:]))
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
})
|
||||
cfg.Output.Write(buf.Bytes())
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
".."
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// RequestIDConfig defines the config for RequestID middleware
|
||||
type RequestIDConfig struct {
|
||||
// Skip defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// Generator defines a function to generate an ID.
|
||||
// Optional. Default: func() string {
|
||||
// return uuid.New().String()
|
||||
// }
|
||||
Generator func() string
|
||||
}
|
||||
|
||||
// RequestIDConfigDefault is the default RequestID middleware config.
|
||||
var RequestIDConfigDefault = RequestIDConfig{
|
||||
Skip: nil,
|
||||
Generator: func() string {
|
||||
return uuid.New().String()
|
||||
},
|
||||
}
|
||||
|
||||
// RequestID adds an indentifier to the request using the `X-Request-ID` header
|
||||
func RequestID(config ...RequestIDConfig) func(*fiber.Ctx) {
|
||||
// Init config
|
||||
var cfg RequestIDConfig
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Set config default values
|
||||
if cfg.Generator == nil {
|
||||
cfg.Skip = RequestIDConfigDefault.Skip
|
||||
}
|
||||
if cfg.Generator == nil {
|
||||
cfg.Generator = RequestIDConfigDefault.Generator
|
||||
}
|
||||
// Return middleware handler
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// Get value from RequestID
|
||||
rid := c.Get(fiber.HeaderXRequestID)
|
||||
fmt.Println(rid)
|
||||
// Create new ID
|
||||
if rid == "" {
|
||||
rid = cfg.Generator()
|
||||
}
|
||||
// Set X-Request-ID
|
||||
c.Set(fiber.HeaderXRequestID, rid)
|
||||
// Bye!
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -3,51 +3,76 @@ package middleware
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber"
|
||||
".."
|
||||
)
|
||||
|
||||
// SecureConfig ...
|
||||
type SecureConfig struct {
|
||||
// Skip defines a function to skip middleware.
|
||||
// Optional. Default: nil
|
||||
Skip func(*fiber.Ctx) bool
|
||||
// XSSProtection
|
||||
// Optional. Default value "1; mode=block".
|
||||
XSSProtection string
|
||||
// ContentTypeNosniff
|
||||
// Optional. Default value "nosniff".
|
||||
ContentTypeNosniff string
|
||||
// Optional. Default value "SAMEORIGIN". Possible values: "SAMEORIGIN", "DENY", "ALLOW-FROM uri"
|
||||
// XFrameOptions
|
||||
// Optional. Default value "SAMEORIGIN".
|
||||
// Possible values: "SAMEORIGIN", "DENY", "ALLOW-FROM uri"
|
||||
XFrameOptions string
|
||||
// HSTSMaxAge
|
||||
// Optional. Default value 0.
|
||||
HSTSMaxAge int
|
||||
// HSTSExcludeSubdomains
|
||||
// Optional. Default value false.
|
||||
HSTSExcludeSubdomains bool
|
||||
// ContentSecurityPolicy
|
||||
// Optional. Default value "".
|
||||
ContentSecurityPolicy string
|
||||
// CSPReportOnly
|
||||
// Optional. Default value false.
|
||||
CSPReportOnly bool
|
||||
// HSTSPreloadEnabled
|
||||
// Optional. Default value false.
|
||||
HSTSPreloadEnabled bool
|
||||
// ReferrerPolicy
|
||||
// Optional. Default value "".
|
||||
ReferrerPolicy string
|
||||
}
|
||||
|
||||
// SecureConfigDefault is the defaul Secure middleware config.
|
||||
var SecureConfigDefault = SecureConfig{
|
||||
Skip: nil,
|
||||
XSSProtection: "1; mode=block",
|
||||
ContentTypeNosniff: "nosniff",
|
||||
XFrameOptions: "SAMEORIGIN",
|
||||
}
|
||||
|
||||
// Secure ...
|
||||
func Secure(config ...SecureConfig) func(*fiber.Ctx) {
|
||||
// Init config
|
||||
var cfg SecureConfig
|
||||
// Set config if provided
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
}
|
||||
// Set config default options
|
||||
// Set config default values
|
||||
if cfg.XSSProtection == "" {
|
||||
cfg.XSSProtection = "1; mode=block"
|
||||
cfg.XSSProtection = SecureConfigDefault.XSSProtection
|
||||
}
|
||||
if cfg.ContentTypeNosniff == "" {
|
||||
cfg.ContentTypeNosniff = "nosniff"
|
||||
cfg.ContentTypeNosniff = SecureConfigDefault.ContentTypeNosniff
|
||||
}
|
||||
if cfg.XFrameOptions == "" {
|
||||
cfg.XFrameOptions = "SAMEORIGIN"
|
||||
cfg.XFrameOptions = SecureConfigDefault.XFrameOptions
|
||||
}
|
||||
// Return middleware handler
|
||||
return func(c *fiber.Ctx) {
|
||||
// Skip middleware if Skip returns true
|
||||
if cfg.Skip != nil && cfg.Skip(c) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if cfg.XSSProtection != "" {
|
||||
c.Set(fiber.HeaderXXSSProtection, cfg.XSSProtection)
|
||||
}
|
||||
|
|
|
@ -260,8 +260,8 @@ func (app *App) handler(fctx *fasthttp.RequestCtx) {
|
|||
ctx := acquireCtx()
|
||||
defer releaseCtx(ctx)
|
||||
ctx.app = app
|
||||
ctx.Fasthttp = fctx
|
||||
ctx.compress = app.Settings.Compression
|
||||
ctx.Fasthttp = fctx
|
||||
// get path and method
|
||||
path := ctx.Path()
|
||||
if !app.Settings.CaseSensitive {
|
||||
|
|
1
utils.go
1
utils.go
|
@ -493,6 +493,7 @@ const (
|
|||
HeaderUpgrade = "Upgrade"
|
||||
HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control"
|
||||
HeaderXPingback = "X-Pingback"
|
||||
HeaderXRequestID = "X-Request-ID"
|
||||
HeaderXRequestedWith = "X-Requested-With"
|
||||
HeaderXRobotsTag = "X-Robots-Tag"
|
||||
HeaderXUACompatible = "X-UA-Compatible"
|
||||
|
|
Loading…
Reference in New Issue