diff --git a/103-http/proxy/cache.go b/103-http/proxy/cache.go index b65f275..9ec15cb 100644 --- a/103-http/proxy/cache.go +++ b/103-http/proxy/cache.go @@ -3,17 +3,24 @@ package main import ( "fmt" "strings" + "sync" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/proxy" ) -// TODO: Burada ki mapi thread safe hale getirebilirsiniz. +// DONE: Buradaki mapi thread safe hale getirebilirsiniz. // 102-concurrency egitimindeki mutex orneklerine bakabilirsiniz. // Ref: https://pmihaylov.com/thread-safety-concerns-go/ // Ref: https://medium.com/@deckarep/the-new-kid-in-town-gos-sync-map-de24a6bf7c2c -var cache = map[string]Cache{} + +var cache = &CacheStore{v: map[string]Cache{}} + +type CacheStore struct { + sync.Mutex + v map[string]Cache +} type Cache struct { body []byte @@ -25,12 +32,6 @@ type CacheProxy struct { ttl time.Duration } -func EvictCacheHandler(c *fiber.Ctx) error { - // TODO: [DELETE] /cache/:key/* pathine istek atildiginda memorydeki cachei temizleyen handleri implement edebilirsiniz. - // TODO: implement me! - return nil -} - func NewCacheProxy(key string, ttl time.Duration) CacheProxy { return CacheProxy{ key: key, @@ -38,6 +39,22 @@ func NewCacheProxy(key string, ttl time.Duration) CacheProxy { } } +func EvictCacheHandler(c *fiber.Ctx) error { + // DONE: [DELETE] /cache/:key/* pathine istek atildiginda memorydeki cachei temizleyen handleri implement edebilirsiniz. + // DONE: implement me! + + key := strings.TrimPrefix(c.Path(), "/cache") + + if _, ok := cache.v[key]; !ok { + return fiber.ErrNotFound + } + + cache.Delete(key) + + c.Response().SetStatusCode(fiber.StatusNoContent) + return nil +} + func (p CacheProxy) Accept(key string) bool { return p.key == key } @@ -46,15 +63,16 @@ func (p CacheProxy) Proxy(c *fiber.Ctx) error { path := c.Path() key := c.Params("key") - if r, ok := cache[path]; ok && r.ttl.After(time.Now()) { + if r, ok := cache.v[path]; ok && r.ttl.After(time.Now()) { c.Response().SetBody(r.body) - c.Response().Header.Add("cache-control", fmt.Sprintf("max-age:%d", p.ttl/time.Second)) + c.Response().Header.Add(fiber.HeaderContentType, fiber.MIMEApplicationJSON) + c.Response().Header.Add(fiber.HeaderCacheControl, fmt.Sprintf("max-age:%d", p.ttl/time.Second)) return nil } // https://mocki.io/v1/d4d63bce-1809-4250-91ac-0470aa392ca5 - url := "https://mocki.io/" + strings.TrimPrefix(path, "/"+key+"/") + url := "https://mocki.io" + strings.TrimPrefix(path, "/"+key) fmt.Printf("http request redirecting to %s \n", url) @@ -62,13 +80,34 @@ func (p CacheProxy) Proxy(c *fiber.Ctx) error { return err } + respStatusCode := c.Response().StatusCode() + + if respStatusCode != fiber.StatusOK { + return fiber.NewError(respStatusCode, "Check your request") + } + ch := Cache{ ttl: time.Now().Add(p.ttl), body: c.Response().Body(), } - cache[path] = ch + //cache[path] = ch + + // thread safe version + cache.Set(path, ch) c.Response().Header.Del(fiber.HeaderServer) return nil } + +func (c *CacheStore) Set(key string, cache Cache) { + c.Lock() + c.v[key] = cache + c.Unlock() +} + +func (c *CacheStore) Delete(key string) { + c.Lock() + delete(c.v, key) + c.Unlock() +} diff --git a/103-http/proxy/limit.go b/103-http/proxy/limit.go index df5cc56..5236e5e 100644 --- a/103-http/proxy/limit.go +++ b/103-http/proxy/limit.go @@ -2,16 +2,24 @@ package main import ( "fmt" + "strings" + "sync" "time" "github.com/gofiber/fiber/v2" ) -// TODO: Burada ki mapi thread safe hale getirebilirsiniz. +// DONE: Buradaki mapi thread safe hale getirebilirsiniz. // 102-concurrency egitimindeki mutex orneklerine bakabilirsiniz. // Ref: https://pmihaylov.com/thread-safety-concerns-go/ // Ref: https://medium.com/@deckarep/the-new-kid-in-town-gos-sync-map-de24a6bf7c2c -var counter = map[string]*Limit{} + +var counter = &LimitCounter{v: map[string]*Limit{}} + +type LimitCounter struct { + sync.Mutex + v map[string]*Limit +} type Limit struct { count int @@ -24,12 +32,6 @@ type LimitProxy struct { ttl time.Duration } -func ResetLimitHandler(c *fiber.Ctx) error { - // TODO: [DELETE] /limit/:key/* pathine istek atildiginda limiti sifirlayan handleri implement edebilirsiniz. - // TODO: implement me! - return nil -} - func NewLimitProxy(key string, limit int, ttl time.Duration) LimitProxy { return LimitProxy{ key: key, @@ -38,6 +40,22 @@ func NewLimitProxy(key string, limit int, ttl time.Duration) LimitProxy { } } +func ResetLimitHandler(c *fiber.Ctx) error { + // DONE: [DELETE] /limit/:key/* pathine istek atildiginda limiti sifirlayan handleri implement edebilirsiniz. + // DONE: implement me! + + key := strings.TrimPrefix(c.Path(), "/limit") + + if _, ok := counter.v[key]; !ok { + return fiber.ErrNotFound + } + + counter.Delete(key) + + c.Response().SetStatusCode(fiber.StatusNoContent) + return nil +} + func (p LimitProxy) Accept(key string) bool { return p.key == key } @@ -45,31 +63,60 @@ func (p LimitProxy) Accept(key string) bool { func (p LimitProxy) Proxy(c *fiber.Ctx) error { path := c.Path() - if r, ok := counter[path]; ok && r.count >= p.limit { + if r, ok := counter.v[path]; ok && r.count >= p.limit { if r.ttl.After(time.Now()) { - c.Response().SetStatusCode(429) + c.Response().SetStatusCode(fiber.StatusTooManyRequests) fmt.Printf("request limit reached for %s \n", path) - return nil + return fiber.ErrTooManyRequests } else { fmt.Printf("resetting counter values \n") - counter[path] = &Limit{ - count: 0, - ttl: time.Now().Add(p.ttl), - } + //counter[path] = &Limit{ + // count: 0, + // ttl: time.Now().Add(p.ttl), + //} + + // thread safe version + counter.Set(path, p.ttl) } } else if !ok { - counter[path] = &Limit{ - count: 0, - ttl: time.Now().Add(p.ttl), - } + //counter[path] = &Limit{ + // count: 0, + // ttl: time.Now().Add(p.ttl), + //} + + // thread safe version + counter.Set(path, p.ttl) + } + + if err := c.SendString("Go Turkiye - 103 Http Package"); err != nil { + return err } - c.SendString("Go Turkiye - 103 Http Package") + //counter[path].count++ - counter[path].count++ + // thread safe version + counter.Increment(path) return nil } + +func (l *LimitCounter) Set(key string, ttl time.Duration) { + l.Lock() + l.v[key] = &Limit{count: 0, ttl: time.Now().Add(ttl)} + l.Unlock() +} + +func (l *LimitCounter) Increment(key string) { + l.Lock() + l.v[key].count++ + l.Unlock() +} + +func (l *LimitCounter) Delete(key string) { + l.Lock() + delete(l.v, key) + l.Unlock() +}