More middlewares

pull/195/head
Fenny 2020-03-01 06:56:41 +01:00
parent 37583902cd
commit ca6aa02e7f
12 changed files with 492 additions and 116 deletions

121
app.go
View File

@ -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
View File

@ -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
View File

@ -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=

74
middleware/basic_auth.go Normal file
View File

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

View File

@ -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()
}
}

View File

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

View File

@ -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()
}
}

View File

@ -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()
}
}

63
middleware/request_id.go Normal file
View File

@ -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()
}
}

View File

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

View File

@ -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 {

View File

@ -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"