ronantakizawa/codereview-bench huggingface dataset:
Repository: gofiber/fiber
File: router_test.go (Go)

Code before:

// go test -v ./... -run=^$ -bench=Benchmark_Router_Chain -benchmem -count=4
func Benchmark_Router_Chain(b *testing.B) {
	app := New()
	handler := func(c *Ctx) {
		c.Next()
	}
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)

	c := &fasthttp.RequestCtx{}

	c.Request.Header.SetMethod("GET")
	c.URI().SetPath("/")

	for n := 0; n < b.N; n++ {
		app.handler(c)
	}
}

// go test -v ./... -run=^$ -bench=Benchmark_Router_Next -benchmem -count=4
func Benchmark_Router_Next(b *testing.B) {
	app := New()
	registerDummyRoutes(app)

	request := &fasthttp.RequestCtx{}

	request.Request.Header.SetMethod("DELETE")
	request.URI().SetPath("/user/keys/1337")
	var res bool

	c := app.AcquireCtx(request)
	defer app.ReleaseCtx(c)

	for n := 0; n < b.N; n++ {
		c.indexRoute = -1
		res = app.next(c)
	}
	utils.AssertEqual(b, true, res)
	utils.AssertEqual(b, 31, c.indexRoute)
}

// go test -v ./... -run=^$ -bench=Benchmark_Route_Match -benchmem -count=4
func Benchmark_Route_Match(b *testing.B) {
	var match bool
	var params []string


Review comment (suggestion):
please don´t change the benchmark, in this bench the handlers should be in one line

to measure the compression of the route we should create an independent benchmark, if that was the idea behind the change

Code after:

// go test -v ./... -run=^$ -bench=Benchmark_Router_Chain -benchmem -count=4
func Benchmark_Router_Chain(b *testing.B) {
	app := New()
	handler := func(c *Ctx) {
		c.Next()
	}
	app.Get("/", handler, handler, handler, handler, handler, handler)

	c := &fasthttp.RequestCtx{}

	c.Request.Header.SetMethod("GET")
	c.URI().SetPath("/")

	for n := 0; n < b.N; n++ {
		app.handler(c)
	}
}

// go test -v ./... -run=^$ -bench=Benchmark_Router_WithCompression -benchmem -count=4
func Benchmark_Router_WithCompression(b *testing.B) {
	app := New()
	handler := func(c *Ctx) {
		c.Next()
	}
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)
	app.Get("/", handler)

	c := &fasthttp.RequestCtx{}

	c.Request.Header.SetMethod("GET")
	c.URI().SetPath("/")

	for n := 0; n < b.N; n++ {
		app.handler(c)
	}
}

// go test -v ./... -run=^$ -bench=Benchmark_Router_Next -benchmem -count=4
func Benchmark_Router_Next(b *testing.B) {
	app := New()
	registerDummyRoutes(app)

	request := &fasthttp.RequestCtx{}

	request.Request.Header.SetMethod("DELETE")
	request.URI().SetPath("/user/keys/1337")
Repository: gofiber/fiber
File: middleware/logger.go (Go)

Code before:
func logger(config LoggerConfig) fiber.Handler {
	// Set config default values
	if config.Format == "" {
		config.Format = LoggerConfigDefault.Format
	}
	if config.TimeFormat == "" {
		config.TimeFormat = LoggerConfigDefault.TimeFormat
	}
	if config.Output == nil {
		// Check if colors should be disabled
		if os.Getenv("TERM") == "dumb" ||
			(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
			config.Output = LoggerConfigDefault.Output
		} else {
			config.enableColors = true
			config.Output = colorable.NewColorableStderr()

		}
	}
	// Middleware settings
	var mutex sync.RWMutex

	var tmpl loggerTemplate
	tmpl.new(config.Format, "${", "}")
	var location, err = time.LoadLocation(config.TimeZone)
	if err != nil {
		log.Fatalf("Logger: failed to load time zone: %e\n", err)
	}
	timestamp := time.Now().In(location).Format(config.TimeFormat)
	// Update date/time every second in a separate go routine
	if strings.Contains(config.Format, "${time}") {
		go func() {
			for {
				mutex.Lock()
				timestamp = time.Now().In(location).Format(config.TimeFormat)
				mutex.Unlock()
				time.Sleep(500 * time.Millisecond)
			}
		}()
	}
	pid := fmt.Sprintf("%-5s", strconv.Itoa(os.Getpid()))
	// Return handler
	return func(c *fiber.Ctx) {
		// Don't execute the middleware if Next returns true
		if config.Next != nil && config.Next(c) {
			c.Next()
			return
		}
		// Middleware logic...
		start := time.Now()
		// handle request

Review comment (suggestion):
I have my doubts about this integration.
It would be nice to have `Local` as default timezone and maybe as fallback.
@gofiber/maintainers, let me know what you think.
Thanks for your contribution! 🎉

Code after:
func logger(config LoggerConfig) fiber.Handler {
	// Set config default values
	if config.Format == "" {
		config.Format = LoggerConfigDefault.Format
	}
	if config.TimeZone == "" {
		config.TimeZone = LoggerConfigDefault.TimeZone
	}
	if config.TimeFormat == "" {
		config.TimeFormat = LoggerConfigDefault.TimeFormat
	}
	if config.Output == nil {
		// Check if colors should be disabled
		if os.Getenv("TERM") == "dumb" ||
			(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) {
			config.Output = LoggerConfigDefault.Output
		} else {
			config.enableColors = true
			config.Output = colorable.NewColorableStderr()

		}
	}
	// Middleware settings
	var mutex sync.RWMutex

	var tmpl loggerTemplate
	tmpl.new(config.Format, "${", "}")
	timestamp := nowTimeString(config.TimeZone, config.TimeFormat)
	// Update date/time every second in a separate go routine
	if strings.Contains(config.Format, "${time}") {
		go func() {
			for {
				mutex.Lock()
				timestamp = nowTimeString(config.TimeZone, config.TimeFormat)
				mutex.Unlock()
				time.Sleep(500 * time.Millisecond)
			}
		}()
	}
	pid := fmt.Sprintf("%-5s", strconv.Itoa(os.Getpid()))
	// Return handler
	return func(c *fiber.Ctx) {
		// Don't execute the middleware if Next returns true
		if config.Next != nil && config.Next(c) {
			c.Next()
			return
		}
		// Middleware logic...
		start := time.Now()
		// handle request
		c.Next()
Repository: gofiber/fiber
File: ctx.go (Go)

Code before:
			if match == etag || match == "W/"+etag || "W/"+match == etag {
				etagStale = false
				break
			}
		}
		if etagStale {
			return false
		}

		if modifiedSince != "" {
			var lastModified = getString(ctx.Fasthttp.Response.Header.Peek(HeaderLastModified))
			if lastModified != "" {
				lastModifiedTime, err := http.ParseTime(lastModified)
				if err != nil {
					return false
				}
				modifiedSinceTime, err := http.ParseTime(modifiedSince)
				if err != nil {
					return false
				}
				return lastModifiedTime.Before(modifiedSinceTime)
			}
		}
	}
	return true
}

// Get returns the HTTP request header specified by field.
// Field names are case-insensitive
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (ctx *Ctx) Get(key string, defaultValue ...string) string {
	return defaultString(getString(ctx.Fasthttp.Request.Header.Peek(key)), defaultValue)
}

// Hostname contains the hostname derived from the Host HTTP header.
// Returned value is only valid within the handler. Do not store any references.
// Make copies or use the Immutable setting instead.
func (ctx *Ctx) Hostname() string {
	return getString(ctx.Fasthttp.URI().Host())
}

// IP returns the remote IP address of the request.
func (ctx *Ctx) IP() string {
	return ctx.Fasthttp.RemoteIP().String()
}

// IPs returns an string slice of IP addresses specified in the X-Forwarded-For request header.
func (ctx *Ctx) IPs() (ips []string) {
	header := ctx.Fasthttp.Request.Header.Peek(HeaderXForwardedFor)
	if len(header) == 0 {

Review comment (performance):
to be clear, this function is implemented to replicate the previous regexp `(?:^|,)\s*?no-cache\s*?(?:,|$)`. If we think having random chars before `,no-cache,` is invalid too, happy to change it too.

Code after:
			}
		}
		if etagStale {
			return false
		}

		if modifiedSince != "" {
			var lastModified = getString(ctx.Fasthttp.Response.Header.Peek(HeaderLastModified))
			if lastModified != "" {
				lastModifiedTime, err := http.ParseTime(lastModified)
				if err != nil {
					return false
				}
				modifiedSinceTime, err := http.ParseTime(modifiedSince)
				if err != nil {
					return false
				}
				return lastModifiedTime.Before(modifiedSinceTime)
			}
		}
	}
	return true
}

// isNoCache checks if the cacheControl header value is a `no-cache`.
func isNoCache(cacheControl string) bool {
	noCacheStrIdx := strings.Index(cacheControl, HeaderCacheControlNoCacheValue)
	if noCacheStrIdx == -1 {
		return false
	}

	for i := noCacheStrIdx - 1; i >= 0; i-- {
		if cacheControl[i] != ' ' {
			// before `no-cache`, if the first non space character is `,`, then
			// it's a valid no-cache value.
			if cacheControl[i] != ',' {
				return false
			}
			break
		}
	}

	for i := noCacheStrIdx + len(HeaderCacheControlNoCacheValue); i < len(cacheControl); i++ {
		if cacheControl[i] != ' ' {
			// after `no-cache`, if the first non space character is `,`, then
			// it's a valid no-cache value.
			if cacheControl[i] != ',' {
				return false
			}
			break
		}
Repository: gofiber/fiber
File: router.go (Go)

Code before:
			return path
		},
		PathNotFound: func(fctx *fasthttp.RequestCtx) {
			fctx.Response.SetStatusCode(StatusNotFound)
		},
	}

	// Set config if provided
	var maxAge = 0
	if len(config) > 0 {
		maxAge = config[0].MaxAge
		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()
	handler := func(c *Ctx) error {
		// Serve file
		fileHandler(c.fasthttp)
		// Return request if found and not forbidden
		status := c.fasthttp.Response.StatusCode()
		if status != StatusNotFound && status != StatusForbidden {
			c.fasthttp.Response.Header.Set(HeaderCacheControl, "public, max-age="+strconv.Itoa(maxAge))
			return nil
		}
		// Reset response to default
		c.fasthttp.SetContentType("") // Issue #420
		c.fasthttp.Response.SetStatusCode(StatusOK)
		c.fasthttp.Response.SetBodyString("")
		// Next middleware
		return c.Next()
	}

	// Create route metadata without pointer
	route := Route{
		// Router booleans
		use:  true,
		root: isRoot,
		path: prefix,
		// Public data
		Method:   MethodGet,
		Path:     prefix,
		Handlers: []Handler{handler},
	}
	// Increment global handler count
	app.mutex.Lock()
	app.handlerCount++
	app.mutex.Unlock()

Review comment (performance):
We should build the `CacheControl` value once outside of the handler value.
```go
cacheControlValue:= "public, max-age="+strconv.Itoa(maxAge)
```

And check if the value is higher than 1 before setting it
```go
if maxAge > 0 {
  c.fasthttp.Response.Header.Set(HeaderCacheControl, cacheControlValue)
}
```

Code after:
			return path
		},
		PathNotFound: func(fctx *fasthttp.RequestCtx) {
			fctx.Response.SetStatusCode(StatusNotFound)
		},
	}

	// Set config if provided
	var cacheControlValue string
	if len(config) > 0 {
		maxAge := config[0].MaxAge
		if maxAge > 0 {
			cacheControlValue = "public, max-age=" + strconv.Itoa(maxAge)
		}

		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()
	handler := func(c *Ctx) error {
		// Serve file
		fileHandler(c.fasthttp)
		// Return request if found and not forbidden
		status := c.fasthttp.Response.StatusCode()
		if status != StatusNotFound && status != StatusForbidden {
			if len(cacheControlValue) > 0 {
				c.fasthttp.Response.Header.Set(HeaderCacheControl, cacheControlValue)
			}
			return nil
		}
		// Reset response to default
		c.fasthttp.SetContentType("") // Issue #420
		c.fasthttp.Response.SetStatusCode(StatusOK)
		c.fasthttp.Response.SetBodyString("")
		// Next middleware
		return c.Next()
	}

	// Create route metadata without pointer
	route := Route{
		// Router booleans
		use:  true,
		root: isRoot,
		path: prefix,
		// Public data
		Method:   MethodGet,
		Path:     prefix,
Repository: gofiber/fiber
File: middleware/csrf/config.go (Go)

Code before:
	CookieExpires time.Duration

	// Deprecated, please use Cookie* related fields
	Cookie *fiber.Cookie

	// Deprecated, please use KeyLookup
	TokenLookup string

	// ErrorHandler is executed when an error is returned from fiber.Handler.
	//
	// Optional. Default: DefaultErrorHandler
	ErrorHandler fiber.ErrorHandler
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	KeyLookup:      "header:X-Csrf-Token",
	CookieName:     "csrf_",
	CookieSameSite: "Strict",
	Expiration:     1 * time.Hour,
	KeyGenerator:   utils.UUID,
	ErrorHandler:   DefaultErrorHandler,
}

// Default ErrorHandler that process return error from fiber.Handler
var DefaultErrorHandler = func(c *fiber.Ctx, err error) error {
	return fiber.ErrForbidden
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Return default config if nothing provided
	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	// Set default values
	if cfg.TokenLookup != "" {
		fmt.Println("[CSRF] TokenLookup is deprecated, please use KeyLookup")
		cfg.KeyLookup = cfg.TokenLookup
	}
	if int(cfg.CookieExpires.Seconds()) > 0 {
		fmt.Println("[CSRF] CookieExpires is deprecated, please use Expiration")
		cfg.Expiration = cfg.CookieExpires
	}
	if cfg.Cookie != nil {
		fmt.Println("[CSRF] Cookie is deprecated, please use Cookie* related fields")
		if cfg.Cookie.Name != "" {

Review comment (suggestion):
It's better to make `DefaultErrorHandler` private.

Code after:
	CookieExpires time.Duration

	// Deprecated, please use Cookie* related fields
	Cookie *fiber.Cookie

	// Deprecated, please use KeyLookup
	TokenLookup string

	// ErrorHandler is executed when an error is returned from fiber.Handler.
	//
	// Optional. Default: DefaultErrorHandler
	ErrorHandler fiber.ErrorHandler
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	KeyLookup:      "header:X-Csrf-Token",
	CookieName:     "csrf_",
	CookieSameSite: "Strict",
	Expiration:     1 * time.Hour,
	KeyGenerator:   utils.UUID,
	ErrorHandler:   defaultErrorHandler,
}

// default ErrorHandler that process return error from fiber.Handler
var defaultErrorHandler = func(c *fiber.Ctx, err error) error {
	return fiber.ErrForbidden
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Return default config if nothing provided
	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	// Set default values
	if cfg.TokenLookup != "" {
		fmt.Println("[CSRF] TokenLookup is deprecated, please use KeyLookup")
		cfg.KeyLookup = cfg.TokenLookup
	}
	if int(cfg.CookieExpires.Seconds()) > 0 {
		fmt.Println("[CSRF] CookieExpires is deprecated, please use Expiration")
		cfg.Expiration = cfg.CookieExpires
	}
	if cfg.Cookie != nil {
		fmt.Println("[CSRF] Cookie is deprecated, please use Cookie* related fields")
		if cfg.Cookie.Name != "" {
Repository: gofiber/fiber
File: middleware/recover/config.go (Go)

Code before:

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// EnableStackTrace enables printing stack trace
	//
	// Optional. Default: false
	EnableStackTrace bool

	// PrintHandler defines a function to print stack trace
	//
	// Optional. Default: defaultPrintHandler
	PrintHandler func(e interface{})
}

var defaultStackTraceBufLen = 1024

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:             nil,
	EnableStackTrace: false,
	PrintHandler:     defaultPrintHandler,
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Return default config if nothing provided
	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	if cfg.EnableStackTrace && cfg.PrintHandler == nil {
		cfg.PrintHandler = defaultPrintHandler
	}

	return cfg
}


Review comment (suggestion):
I'm wondering if `StackTraceHandler` is more appropriate? It maybe does not just print the stack trace, but also can be stored or sent to remote hosts.

Code after:

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// EnableStackTrace enables handling stack trace
	//
	// Optional. Default: false
	EnableStackTrace bool

	// StackTraceHandler defines a function to handle stack trace
	//
	// Optional. Default: defaultStackTraceHandler
	StackTraceHandler func(e interface{})
}

var defaultStackTraceBufLen = 1024

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:              nil,
	EnableStackTrace:  false,
	StackTraceHandler: defaultStackTraceHandler,
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Return default config if nothing provided
	if len(config) < 1 {
		return ConfigDefault
	}

	// Override default config
	cfg := config[0]

	if cfg.EnableStackTrace && cfg.StackTraceHandler == nil {
		cfg.StackTraceHandler = defaultStackTraceHandler
	}

	return cfg
}
Repository: gofiber/fiber
File: middleware/session/store.go (Go)

Code before:
	sess := acquireSession()
	sess.ctx = c
	sess.config = s
	sess.id = id
	sess.fresh = fresh

	// Fetch existing data
	if !fresh {
		raw, err := s.Storage.Get(id)
		// Unmashal if we found data
		if raw != nil && err == nil {
			mux.Lock()
			gotiny.Unmarshal(raw, &sess.data)
			mux.Unlock()
			sess.fresh = false
		} else if err != nil {
			return nil, err
		} else {
			sess.fresh = true
		}
	}

	return sess, nil
}

// getSessionID will returns the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.CookieName)
	if len(id) > 0 {
		return id
	}

	if s.Source == SourceHeader {
		id = string(c.Request().Header.Peek(s.CookieName))
		if len(id) > 0 {
			return id
		}
	}

	if s.Source == SourceURLQuery {
		id = c.FormValue(s.CookieName)
		if len(id) > 0 {
			return id
		}
	}

	return ""
}

Review comment (suggestion):
```suggestion
// getSessionID will return the session id from:
```

Code after:
		}
	}

	// If no key exist, create new one
	if len(id) == 0 {
		loadDada = false
		id = s.KeyGenerator()
	}

	// Create session object
	sess := acquireSession()
	sess.ctx = c
	sess.config = s
	sess.id = id
	sess.fresh = fresh

	// Fetch existing data
	if loadDada {
		raw, err := s.Storage.Get(id)
		// Unmashal if we found data
		if raw != nil && err == nil {
			mux.Lock()
			defer mux.Unlock()
			_, _ = sess.byteBuffer.Write(raw)
			encCache := gob.NewDecoder(sess.byteBuffer)
			err := encCache.Decode(&sess.data.Data)
			if err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		} else {
			sess.fresh = true
		}
	}

	return sess, nil
}

// getSessionID will return the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.sessionName)
	if len(id) > 0 {
		return id
	}

	if s.source == SourceHeader {
		id = string(c.Request().Header.Peek(s.sessionName))
Repository: gofiber/fiber
File: middleware/session/store.go (Go)

Code before:
			mux.Lock()
			gotiny.Unmarshal(raw, &sess.data)
			mux.Unlock()
			sess.fresh = false
		} else if err != nil {
			return nil, err
		} else {
			sess.fresh = true
		}
	}

	return sess, nil
}

// getSessionID will returns the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.CookieName)
	if len(id) > 0 {
		return id
	}

	if s.Source == SourceHeader {
		id = string(c.Request().Header.Peek(s.CookieName))
		if len(id) > 0 {
			return id
		}
	}

	if s.Source == SourceURLQuery {
		id = c.FormValue(s.CookieName)
		if len(id) > 0 {
			return id
		}
	}

	return ""
}

// Reset will delete all session from the storage
func (s *Store) Reset() error {
	return s.Storage.Reset()
}


Review comment (security):
We shouldn't use the cookie name as the header name, this will be confusing to users. Instead we should use a key-value syntax, see our csrf middleware for an example: https://github.com/gofiber/fiber/blob/master/middleware/csrf/csrf.go#L30-L40

Code after:
	sess.ctx = c
	sess.config = s
	sess.id = id
	sess.fresh = fresh

	// Fetch existing data
	if loadDada {
		raw, err := s.Storage.Get(id)
		// Unmashal if we found data
		if raw != nil && err == nil {
			mux.Lock()
			defer mux.Unlock()
			_, _ = sess.byteBuffer.Write(raw)
			encCache := gob.NewDecoder(sess.byteBuffer)
			err := encCache.Decode(&sess.data.Data)
			if err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		} else {
			sess.fresh = true
		}
	}

	return sess, nil
}

// getSessionID will return the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.sessionName)
	if len(id) > 0 {
		return id
	}

	if s.source == SourceHeader {
		id = string(c.Request().Header.Peek(s.sessionName))
		if len(id) > 0 {
			return id
		}
	}

	if s.source == SourceURLQuery {
		id = c.Query(s.sessionName)
		if len(id) > 0 {
			return id
		}
	}
Repository: gofiber/fiber
File: middleware/csrf/csrf.go (Go)

Code before:
			token, err = extractor(c)
			if err != nil {
				return cfg.ErrorHandler(c, err)
			}

			// if token does not exist in Storage
			if manager.getRaw(token) == nil {
				// Expire cookie
				c.Cookie(&fiber.Cookie{
					Name:     cfg.CookieName,
					Domain:   cfg.CookieDomain,
					Path:     cfg.CookiePath,
					Expires:  time.Now().Add(-1 * time.Minute),
					Secure:   cfg.CookieSecure,
					HTTPOnly: cfg.CookieHTTPOnly,
					SameSite: cfg.CookieSameSite,
				})
				return cfg.ErrorHandler(c, err)
			}

			// The token is validated, time to delete it
			manager.delete(token)
			generateToken = true
		}

		if generateToken {
			// And generate a new token
			token = cfg.KeyGenerator()
			// Add token to Storage
			manager.setRaw(token, dummyValue, cfg.Expiration)

			// Create cookie to pass token to client
			cookie := &fiber.Cookie{
				Name:     cfg.CookieName,
				Value:    token,
				Domain:   cfg.CookieDomain,
				Path:     cfg.CookiePath,
				Expires:  time.Now().Add(cfg.Expiration),
				Secure:   cfg.CookieSecure,
				HTTPOnly: cfg.CookieHTTPOnly,
				SameSite: cfg.CookieSameSite,
			}
			// Set cookie to response
			c.Cookie(cookie)
		}

		// Protect clients from caching the response by telling the browser
		// a new header value is generated
		c.Vary(fiber.HeaderCookie)

		// Store token in context if set

Review comment (suggestion):
I think you change the original logic of this middleware. When using safe methods, and we already get token, we just update cookie with new expiration, And then we call `c.Vary(fiber.HeaderCookie)`.

Code after:
			if manager.getRaw(token) == nil {
				// Expire cookie
				c.Cookie(&fiber.Cookie{
					Name:     cfg.CookieName,
					Domain:   cfg.CookieDomain,
					Path:     cfg.CookiePath,
					Expires:  time.Now().Add(-1 * time.Minute),
					Secure:   cfg.CookieSecure,
					HTTPOnly: cfg.CookieHTTPOnly,
					SameSite: cfg.CookieSameSite,
				})
				return cfg.ErrorHandler(c, err)
			}
		}

		// Generate CSRF token if not exist
		if token == "" {
			// And generate a new token
			token = cfg.KeyGenerator()
		}

		// Add/update token to Storage
		manager.setRaw(token, dummyValue, cfg.Expiration)

		// Create cookie to pass token to client
		cookie := &fiber.Cookie{
			Name:     cfg.CookieName,
			Value:    token,
			Domain:   cfg.CookieDomain,
			Path:     cfg.CookiePath,
			Expires:  time.Now().Add(cfg.Expiration),
			Secure:   cfg.CookieSecure,
			HTTPOnly: cfg.CookieHTTPOnly,
			SameSite: cfg.CookieSameSite,
		}
		// Set cookie to response
		c.Cookie(cookie)

		// Protect clients from caching the response by telling the browser
		// a new header value is generated
		c.Vary(fiber.HeaderCookie)

		// Store token in context if set
		if cfg.ContextKey != "" {
			c.Locals(cfg.ContextKey, token)
		}

		// Continue stack
		return c.Next()
	}
}