mirror of
https://github.com/gofiber/fiber.git
synced 2025-02-22 17:04:26 +00:00
🐛 Fix expiration time in cache middleware (#1881)
* 🐛 Fix: Expiration time in cache middleware * Custom expiration time using ExpirationGenerator is also functional now instead of default Expiration only * 🚨 Improve Test_CustomExpiration * - stabilization of the tests - speed up the cache tests - fix race conditions in client and client tests Co-authored-by: wernerr <rene@gofiber.io>
This commit is contained in:
parent
2326297bb8
commit
bb9ac8feaf
15
client.go
15
client.go
@ -3,6 +3,7 @@ package fiber
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -16,8 +17,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/utils"
|
"github.com/gofiber/fiber/v2/utils"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
)
|
)
|
||||||
@ -60,6 +59,7 @@ var defaultClient Client
|
|||||||
//
|
//
|
||||||
// It is safe calling Client methods from concurrently running goroutines.
|
// It is safe calling Client methods from concurrently running goroutines.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
// UserAgent is used in User-Agent request header.
|
// UserAgent is used in User-Agent request header.
|
||||||
UserAgent string
|
UserAgent string
|
||||||
|
|
||||||
@ -133,10 +133,15 @@ func (c *Client) createAgent(method, url string) *Agent {
|
|||||||
a.req.Header.SetMethod(method)
|
a.req.Header.SetMethod(method)
|
||||||
a.req.SetRequestURI(url)
|
a.req.SetRequestURI(url)
|
||||||
|
|
||||||
|
c.mutex.RLock()
|
||||||
a.Name = c.UserAgent
|
a.Name = c.UserAgent
|
||||||
a.NoDefaultUserAgentHeader = c.NoDefaultUserAgentHeader
|
a.NoDefaultUserAgentHeader = c.NoDefaultUserAgentHeader
|
||||||
a.jsonDecoder = c.JSONDecoder
|
a.jsonDecoder = c.JSONDecoder
|
||||||
a.jsonEncoder = c.JSONEncoder
|
a.jsonEncoder = c.JSONEncoder
|
||||||
|
if a.jsonDecoder == nil {
|
||||||
|
a.jsonDecoder = json.Unmarshal
|
||||||
|
}
|
||||||
|
c.mutex.RUnlock()
|
||||||
|
|
||||||
if err := a.Parse(); err != nil {
|
if err := a.Parse(); err != nil {
|
||||||
a.errs = append(a.errs, err)
|
a.errs = append(a.errs, err)
|
||||||
@ -810,10 +815,6 @@ func (a *Agent) String() (int, string, []error) {
|
|||||||
// Struct returns the status code, bytes body and errors of url.
|
// Struct returns the status code, bytes body and errors of url.
|
||||||
// And bytes body will be unmarshalled to given v.
|
// And bytes body will be unmarshalled to given v.
|
||||||
func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) {
|
func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) {
|
||||||
if a.jsonDecoder == nil {
|
|
||||||
a.jsonDecoder = json.Unmarshal
|
|
||||||
}
|
|
||||||
|
|
||||||
if code, body, errs = a.Bytes(); len(errs) > 0 {
|
if code, body, errs = a.Bytes(); len(errs) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -886,6 +887,8 @@ func AcquireClient() *Client {
|
|||||||
func ReleaseClient(c *Client) {
|
func ReleaseClient(c *Client) {
|
||||||
c.UserAgent = ""
|
c.UserAgent = ""
|
||||||
c.NoDefaultUserAgentHeader = false
|
c.NoDefaultUserAgentHeader = false
|
||||||
|
c.JSONEncoder = nil
|
||||||
|
c.JSONDecoder = nil
|
||||||
|
|
||||||
clientPool.Put(c)
|
clientPool.Put(c)
|
||||||
}
|
}
|
||||||
|
12
middleware/cache/cache.go
vendored
12
middleware/cache/cache.go
vendored
@ -168,23 +168,23 @@ func New(config ...Config) fiber.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// default cache expiration
|
// default cache expiration
|
||||||
expiration := uint64(cfg.Expiration.Seconds())
|
expiration := cfg.Expiration
|
||||||
// Calculate expiration by response header or other setting
|
// Calculate expiration by response header or other setting
|
||||||
if cfg.ExpirationGenerator != nil {
|
if cfg.ExpirationGenerator != nil {
|
||||||
expiration = uint64(cfg.ExpirationGenerator(c, &cfg).Seconds())
|
expiration = cfg.ExpirationGenerator(c, &cfg)
|
||||||
}
|
}
|
||||||
e.exp = ts + expiration
|
e.exp = ts + uint64(expiration.Seconds())
|
||||||
|
|
||||||
// For external Storage we store raw body separated
|
// For external Storage we store raw body separated
|
||||||
if cfg.Storage != nil {
|
if cfg.Storage != nil {
|
||||||
manager.setRaw(key+"_body", e.body, cfg.Expiration)
|
manager.setRaw(key+"_body", e.body, expiration)
|
||||||
// avoid body msgp encoding
|
// avoid body msgp encoding
|
||||||
e.body = nil
|
e.body = nil
|
||||||
manager.set(key, e, cfg.Expiration)
|
manager.set(key, e, expiration)
|
||||||
manager.release(e)
|
manager.release(e)
|
||||||
} else {
|
} else {
|
||||||
// Store entry in memory
|
// Store entry in memory
|
||||||
manager.set(key, e, cfg.Expiration)
|
manager.set(key, e, expiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Set(cfg.CacheHeader, cacheMiss)
|
c.Set(cfg.CacheHeader, cacheMiss)
|
||||||
|
63
middleware/cache/cache_test.go
vendored
63
middleware/cache/cache_test.go
vendored
@ -19,6 +19,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_Cache_CacheControl(t *testing.T) {
|
func Test_Cache_CacheControl(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
@ -77,6 +79,8 @@ func Test_Cache_Expired(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache(t *testing.T) {
|
func Test_Cache(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
app.Use(New())
|
app.Use(New())
|
||||||
|
|
||||||
@ -102,6 +106,8 @@ func Test_Cache(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_WithSeveralRequests(t *testing.T) {
|
func Test_Cache_WithSeveralRequests(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
@ -135,6 +141,8 @@ func Test_Cache_WithSeveralRequests(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_Invalid_Expiration(t *testing.T) {
|
func Test_Cache_Invalid_Expiration(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
cache := New(Config{Expiration: 0 * time.Second})
|
cache := New(Config{Expiration: 0 * time.Second})
|
||||||
app.Use(cache)
|
app.Use(cache)
|
||||||
@ -161,6 +169,8 @@ func Test_Cache_Invalid_Expiration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_Invalid_Method(t *testing.T) {
|
func Test_Cache_Invalid_Method(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New())
|
app.Use(New())
|
||||||
@ -199,6 +209,8 @@ func Test_Cache_Invalid_Method(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_NothingToCache(t *testing.T) {
|
func Test_Cache_NothingToCache(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{Expiration: -(time.Second * 1)}))
|
app.Use(New(Config{Expiration: -(time.Second * 1)}))
|
||||||
@ -225,6 +237,8 @@ func Test_Cache_NothingToCache(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_CustomNext(t *testing.T) {
|
func Test_Cache_CustomNext(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
@ -263,6 +277,8 @@ func Test_Cache_CustomNext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_CustomKey(t *testing.T) {
|
func Test_CustomKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
var called bool
|
var called bool
|
||||||
app.Use(New(Config{KeyGenerator: func(c *fiber.Ctx) string {
|
app.Use(New(Config{KeyGenerator: func(c *fiber.Ctx) string {
|
||||||
@ -281,6 +297,8 @@ func Test_CustomKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_CustomExpiration(t *testing.T) {
|
func Test_CustomExpiration(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
var called bool
|
var called bool
|
||||||
var newCacheTime int
|
var newCacheTime int
|
||||||
@ -291,18 +309,45 @@ func Test_CustomExpiration(t *testing.T) {
|
|||||||
}}))
|
}}))
|
||||||
|
|
||||||
app.Get("/", func(c *fiber.Ctx) error {
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
c.Response().Header.Add("Cache-Time", "6000")
|
c.Response().Header.Add("Cache-Time", "1")
|
||||||
return c.SendString("hi")
|
now := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||||
|
return c.SendString(now)
|
||||||
})
|
})
|
||||||
|
|
||||||
req := httptest.NewRequest("GET", "/", nil)
|
resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||||
_, err := app.Test(req)
|
|
||||||
utils.AssertEqual(t, nil, err)
|
utils.AssertEqual(t, nil, err)
|
||||||
utils.AssertEqual(t, true, called)
|
utils.AssertEqual(t, true, called)
|
||||||
utils.AssertEqual(t, 6000, newCacheTime)
|
utils.AssertEqual(t, 1, newCacheTime)
|
||||||
|
|
||||||
|
// Sleep until the cache is expired
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
cachedResp, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
cachedBody, err := ioutil.ReadAll(cachedResp.Body)
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
|
||||||
|
if bytes.Equal(body, cachedBody) {
|
||||||
|
t.Errorf("Cache should have expired: %s, %s", body, cachedBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next response should be cached
|
||||||
|
cachedRespNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil))
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
cachedBodyNextRound, err := ioutil.ReadAll(cachedRespNextRound.Body)
|
||||||
|
utils.AssertEqual(t, nil, err)
|
||||||
|
|
||||||
|
if !bytes.Equal(cachedBodyNextRound, cachedBody) {
|
||||||
|
t.Errorf("Cache should not have expired: %s, %s", cachedBodyNextRound, cachedBody)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_AdditionalE2EResponseHeaders(t *testing.T) {
|
func Test_AdditionalE2EResponseHeaders(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
StoreResponseHeaders: true,
|
StoreResponseHeaders: true,
|
||||||
@ -325,6 +370,8 @@ func Test_AdditionalE2EResponseHeaders(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_CacheHeader(t *testing.T) {
|
func Test_CacheHeader(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
@ -364,6 +411,8 @@ func Test_CacheHeader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_WithHead(t *testing.T) {
|
func Test_Cache_WithHead(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
app.Use(New())
|
app.Use(New())
|
||||||
|
|
||||||
@ -389,6 +438,8 @@ func Test_Cache_WithHead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_Cache_WithHeadThenGet(t *testing.T) {
|
func Test_Cache_WithHeadThenGet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
app.Use(New())
|
app.Use(New())
|
||||||
app.Get("/", func(c *fiber.Ctx) error {
|
app.Get("/", func(c *fiber.Ctx) error {
|
||||||
@ -425,6 +476,8 @@ func Test_Cache_WithHeadThenGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_CustomCacheHeader(t *testing.T) {
|
func Test_CustomCacheHeader(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
app := fiber.New()
|
app := fiber.New()
|
||||||
|
|
||||||
app.Use(New(Config{
|
app.Use(New(Config{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user