Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions 103-http/proxy/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,19 +32,29 @@ 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,
ttl: ttl,
}
}

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
}
Expand All @@ -46,29 +63,51 @@ 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)

if err := proxy.Do(c, url); err != nil {
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()
}
89 changes: 68 additions & 21 deletions 103-http/proxy/limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -38,38 +40,83 @@ 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
}

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