From 10cc7be965b54e6f4fbeb68cae3ac9581fac90ae Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 19:36:35 +0330 Subject: [PATCH 01/12] chore: remove unused bash script --- watch.bash | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 watch.bash diff --git a/watch.bash b/watch.bash deleted file mode 100644 index 1baffe5..0000000 --- a/watch.bash +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/env bash -echo "running air watch..." -air From 435abc45a416ec6cee0065c6c00c6f12852f41f2 Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 20:00:12 +0330 Subject: [PATCH 02/12] feat(cache): implement clear method using flush --- internal/cache/cache.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index b0f59d7..292135a 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -10,26 +10,27 @@ import ( "go.uber.org/zap" ) -type Cache interface { +type Caching interface { Get(ctx context.Context, key string) string Put(ctx context.Context, key string, value any) error + Clear(ctx context.Context) error } -type cache struct { +type Cache struct { rdb *redis.Client sugar *zap.SugaredLogger expiration time.Duration } -func NewCacheRepository(rdb *redis.Client, logger *zap.Logger) *cache { - return &cache{ +func NewCacheRepository(rdb *redis.Client, logger *zap.Logger) *Cache { + return &Cache{ rdb: rdb, sugar: logger.Sugar(), expiration: 60 * time.Second, } } -func (c *cache) Get(ctx context.Context, key string) (string, bool) { +func (c *Cache) Get(ctx context.Context, key string) (string, bool) { if key == "" { c.sugar.Warn("attempted to get cache with empty key") return "", false @@ -54,7 +55,7 @@ func (c *cache) Get(ctx context.Context, key string) (string, bool) { return res, true } -func (c *cache) Put(ctx context.Context, key string, value any) error { +func (c *Cache) Put(ctx context.Context, key string, value any) error { if key == "" { return err.ErrEmptyCacheKey } @@ -71,3 +72,7 @@ func (c *cache) Put(ctx context.Context, key string, value any) error { return nil } + +func (c *Cache) Clear(ctx context.Context) error { + return c.rdb.FlushAll(ctx).Err() +} From 063a4bdace76398ba0d990211dcfb2c92fdd83a6 Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 20:00:33 +0330 Subject: [PATCH 03/12] feat(config): structure origin and proxy server configuration --- pkg/config/config.go | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 64eff7a..65c0903 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,8 +3,22 @@ package config import "github.com/pyr33x/envy" type Config struct { - Redis Redis - Zap Zap + Server Server + Redis Redis + Zap Zap +} + +type Server struct { + Origin Origin + Proxy Proxy +} + +type Origin struct { + URL string +} + +type Proxy struct { + Port string } type Zap struct { @@ -25,6 +39,17 @@ type Base struct { func New() *Config { return &Config{ + Server: Server{ + Origin: Origin{ + URL: envy.GetString("ORIGIN_URL", "http://dummyjson.com"), + }, + Proxy: Proxy{ + Port: envy.GetString("PROXY_PORT", "1337"), + }, + }, + Zap: Zap{ + Environment: envy.GetString("ZAP_ENV", "dev"), + }, Redis: Redis{ Base: Base{ Host: envy.GetString("REDIS_HOST", "127.0.0.1"), @@ -34,8 +59,5 @@ func New() *Config { }, Database: envy.GetInt("REDIS_DATABASE", 0), }, - Zap: Zap{ - Environment: envy.GetString("ZAP_ENV", "dev"), - }, } } From 02837d330adafd75b99e2efa388c018ae34bb743 Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 20:43:25 +0330 Subject: [PATCH 04/12] fix(cache): marshal inbound/outbound values when interacting with cache --- internal/cache/cache.go | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 292135a..0a5ac84 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -2,7 +2,9 @@ package cache import ( "context" + "encoding/json" "fmt" + "net/http" "time" "github.com/pyr33x/proxy/pkg/err" @@ -22,6 +24,12 @@ type Cache struct { expiration time.Duration } +type CacheValue struct { + Status int + Header http.Header + Body []byte +} + func NewCacheRepository(rdb *redis.Client, logger *zap.Logger) *Cache { return &Cache{ rdb: rdb, @@ -30,37 +38,52 @@ func NewCacheRepository(rdb *redis.Client, logger *zap.Logger) *Cache { } } -func (c *Cache) Get(ctx context.Context, key string) (string, bool) { +func (c *Cache) Get(ctx context.Context, key string) (*CacheValue, bool) { if key == "" { c.sugar.Warn("attempted to get cache with empty key") - return "", false + return nil, false } - res, err := c.rdb.Get(ctx, key).Result() + raw, err := c.rdb.Get(ctx, key).Bytes() if err != nil { if err == redis.Nil { c.sugar.Warn("cache miss", "key", key, ) - return "", false + return nil, false } c.sugar.Error("failed to read from cache", "key", key, "error", err, ) - return "", false + return nil, false } - return res, true + var val *CacheValue + if err := json.Unmarshal(raw, &val); err != nil { + return nil, false + } + + return val, true } -func (c *Cache) Put(ctx context.Context, key string, value any) error { +func (c *Cache) Put(ctx context.Context, key string, value CacheValue) error { if key == "" { return err.ErrEmptyCacheKey } - err := c.rdb.Set(ctx, key, value, c.expiration).Err() + b, err := json.Marshal(value) + if err != nil { + c.sugar.Info("failed to marshal value", + "key", key, + "value", value, + "error", err, + ) + return err + } + + err = c.rdb.Set(ctx, key, b, c.expiration).Err() if err != nil { c.sugar.Error("failed to write to cache", "key", key, From 6bfca783ee4348bcf028baf87999ee21c0770bfe Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 20:43:44 +0330 Subject: [PATCH 05/12] feat(proxy): define proxy strcture and constructor --- internal/proxy/proxy.go | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 internal/proxy/proxy.go diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go new file mode 100644 index 0000000..63ffcfe --- /dev/null +++ b/internal/proxy/proxy.go @@ -0,0 +1,47 @@ +package proxy + +import ( + "net/http" + "time" + + "github.com/pyr33x/proxy/internal/cache" + "github.com/pyr33x/proxy/pkg/config" + "github.com/redis/go-redis/v9" + "go.uber.org/zap" +) + +type Server struct { + Proxy ProxyServer + Origin OriginServer + Cache *cache.Cache + logger *zap.Logger +} + +type ProxyServer struct { + Port string +} + +type OriginServer struct { + URL string +} + +func NewProxyServer(cfg *config.Server, rdb *redis.Client, logger *zap.Logger) *http.Server { + srv := &Server{ + Proxy: ProxyServer{ + Port: cfg.Proxy.Port, + }, + Origin: OriginServer{ + URL: cfg.Origin.URL, + }, + Cache: cache.NewCacheRepository(rdb, logger), + logger: logger, + } + + return &http.Server{ + Addr: ":" + srv.Proxy.Port, + Handler: srv.Serve(), + IdleTimeout: time.Minute, + ReadTimeout: 10 * time.Second, + WriteTimeout: 30 * time.Second, + } +} From c467658432a4914bb1814295a154884a8ac4d0b2 Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:23:13 +0330 Subject: [PATCH 06/12] chore(cache): change sugar logger to default zap logger --- internal/cache/cache.go | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 0a5ac84..45a6e85 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -20,7 +20,7 @@ type Caching interface { type Cache struct { rdb *redis.Client - sugar *zap.SugaredLogger + logger *zap.Logger expiration time.Duration } @@ -33,39 +33,32 @@ type CacheValue struct { func NewCacheRepository(rdb *redis.Client, logger *zap.Logger) *Cache { return &Cache{ rdb: rdb, - sugar: logger.Sugar(), + logger: logger, expiration: 60 * time.Second, } } func (c *Cache) Get(ctx context.Context, key string) (*CacheValue, bool) { if key == "" { - c.sugar.Warn("attempted to get cache with empty key") + c.logger.Warn("attempted to get cache with empty key") return nil, false } raw, err := c.rdb.Get(ctx, key).Bytes() if err != nil { - if err == redis.Nil { - c.sugar.Warn("cache miss", - "key", key, - ) - return nil, false - } - - c.sugar.Error("failed to read from cache", - "key", key, - "error", err, + c.logger.Info("cache miss", + zap.String("key", key), + zap.String("state", "MISS"), ) return nil, false } - var val *CacheValue + var val CacheValue if err := json.Unmarshal(raw, &val); err != nil { return nil, false } - return val, true + return &val, true } func (c *Cache) Put(ctx context.Context, key string, value CacheValue) error { @@ -75,20 +68,20 @@ func (c *Cache) Put(ctx context.Context, key string, value CacheValue) error { b, err := json.Marshal(value) if err != nil { - c.sugar.Info("failed to marshal value", - "key", key, - "value", value, - "error", err, + c.logger.Info("failed to marshal value", + zap.String("key", key), + zap.Any("value", value), + zap.Error(err), ) return err } err = c.rdb.Set(ctx, key, b, c.expiration).Err() if err != nil { - c.sugar.Error("failed to write to cache", - "key", key, - "expiration", c.expiration, - "error", err, + c.logger.Error("failed to write to cache", + zap.String("key", key), + zap.Duration("expiration", c.expiration), + zap.Error(err), ) return fmt.Errorf("cache put failed: %w", err) } From ce8f90e476f5ff0fb6689ebfcd329c792ae62367 Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:23:32 +0330 Subject: [PATCH 07/12] feat(proxy): create proxy route --- internal/proxy/server.go | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 internal/proxy/server.go diff --git a/internal/proxy/server.go b/internal/proxy/server.go new file mode 100644 index 0000000..e9b2e05 --- /dev/null +++ b/internal/proxy/server.go @@ -0,0 +1,78 @@ +package proxy + +import ( + "io" + "maps" + "net/http" + + "github.com/pyr33x/proxy/internal/cache" + "go.uber.org/zap" +) + +func (srv *Server) Serve() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/", srv.ServeProxy) + return mux +} + +func (srv *Server) ServeProxy(w http.ResponseWriter, r *http.Request) { + cacheKey := r.Method + ":" + r.URL.String() + + c, ok := srv.Cache.Get(r.Context(), cacheKey) + if ok { + srv.WriteHeaders(w, "HIT", c) + srv.logger.Info("cache hit", + zap.String("key", cacheKey), + zap.String("state", "HIT"), + ) + return + } + + originURL := srv.Origin.URL + r.URL.String() + resp, err := http.Get(originURL) + if err != nil { + srv.logger.Error("error forwarding request", + zap.Error(err), + zap.String("origin", originURL), + ) + http.Error(w, "error forwarding request", http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + srv.logger.Error("error reading response body", + zap.Error(err), + zap.String("origin", originURL), + ) + http.Error(w, "error reading response body", http.StatusInternalServerError) + return + } + + cached := cache.CacheValue{ + Status: resp.StatusCode, + Header: resp.Header.Clone(), + Body: body, + } + + if err := srv.Cache.Put(r.Context(), cacheKey, cached); err != nil { + srv.logger.Error("failed to write to cache", + zap.Error(err), + zap.String("key", cacheKey), + ) + } + srv.WriteHeaders(w, "MISS", &cached) +} + +func (srv *Server) WriteHeaders(w http.ResponseWriter, state string, cached *cache.CacheValue) { + maps.Copy(w.Header(), cached.Header) + w.Header().Set("X-Cache", state) + w.WriteHeader(cached.Status) + if _, err := w.Write(cached.Body); err != nil { + srv.logger.Error("failed to write cached body", + zap.Error(err), + ) + return + } +} From 7410c17c8eaeabe7a9645c2a07dbc2e44ceb707e Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:24:01 +0330 Subject: [PATCH 08/12] feat(proxy): initialize cache repository in proxy layer --- internal/proxy/proxy.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 63ffcfe..976f5c1 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -1,12 +1,13 @@ package proxy import ( + "context" "net/http" "time" + "github.com/pyr33x/proxy/internal/adapter/redis" "github.com/pyr33x/proxy/internal/cache" "github.com/pyr33x/proxy/pkg/config" - "github.com/redis/go-redis/v9" "go.uber.org/zap" ) @@ -25,13 +26,15 @@ type OriginServer struct { URL string } -func NewProxyServer(cfg *config.Server, rdb *redis.Client, logger *zap.Logger) *http.Server { +func NewProxyServer(ctx context.Context, cfg *config.Config, logger *zap.Logger) *http.Server { + rdb := redis.New(ctx, &cfg.Redis, logger).GetClient() + srv := &Server{ Proxy: ProxyServer{ - Port: cfg.Proxy.Port, + Port: cfg.Server.Proxy.Port, }, Origin: OriginServer{ - URL: cfg.Origin.URL, + URL: cfg.Server.Origin.URL, }, Cache: cache.NewCacheRepository(rdb, logger), logger: logger, From 10f11c265520f495e243ff2afd513dd215426d1d Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:24:09 +0330 Subject: [PATCH 09/12] feat(main): initialize the entire proxy --- main.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 38dd16d..9d1b53e 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,62 @@ package main -func main() {} +import ( + "context" + "net/http" + "os/signal" + "syscall" + "time" + + zl "github.com/pyr33x/proxy/internal/adapter/zap" + "github.com/pyr33x/proxy/internal/proxy" + "github.com/pyr33x/proxy/pkg/config" + "go.uber.org/zap" +) + +func gracefulShutdown(cancel context.CancelFunc, logger *zap.Logger, proxy *http.Server, done chan struct{}) { + stopCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + <-stopCtx.Done() + logger.Info("shutdown triggered...") + + shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := proxy.Shutdown(shutdownCtx); err != nil { + logger.Error("failed to shutdown proxy server", + zap.Error(err), + ) + } + + logger.Info("exiting proxy...") + done <- struct{}{} +} + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + + done := make(chan struct{}) + + cfg := config.New() + logger := zl.New(cfg.Zap.Environment).GetLogger() + + proxy := proxy.NewProxyServer(ctx, cfg, logger) + + go gracefulShutdown(cancel, logger, proxy, done) + + logger.Info("attached proxy", + zap.String("bind", proxy.Addr), + ) + + if err := proxy.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logger.Error("failed to listen and serve to the proxy server", + zap.Error(err), + ) + close(done) + return + } + + <-done + logger.Info("shutdown complete") +} From ea6432d749d314a5b2fc3bcff4a3528e42814f5d Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:36:14 +0330 Subject: [PATCH 10/12] feat(proxy): clear endpoint to flush cache --- internal/proxy/server.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/proxy/server.go b/internal/proxy/server.go index e9b2e05..de4daa8 100644 --- a/internal/proxy/server.go +++ b/internal/proxy/server.go @@ -12,11 +12,13 @@ import ( func (srv *Server) Serve() http.Handler { mux := http.NewServeMux() mux.HandleFunc("/", srv.ServeProxy) + mux.HandleFunc("/clear", srv.Clear) return mux } func (srv *Server) ServeProxy(w http.ResponseWriter, r *http.Request) { cacheKey := r.Method + ":" + r.URL.String() + originURL := srv.Origin.URL + r.URL.String() c, ok := srv.Cache.Get(r.Context(), cacheKey) if ok { @@ -25,10 +27,15 @@ func (srv *Server) ServeProxy(w http.ResponseWriter, r *http.Request) { zap.String("key", cacheKey), zap.String("state", "HIT"), ) + srv.logger.Info("forwarding", + zap.String("origin", originURL), + ) return } - originURL := srv.Origin.URL + r.URL.String() + srv.logger.Info("forwarding", + zap.String("origin", originURL), + ) resp, err := http.Get(originURL) if err != nil { srv.logger.Error("error forwarding request", @@ -62,9 +69,19 @@ func (srv *Server) ServeProxy(w http.ResponseWriter, r *http.Request) { zap.String("key", cacheKey), ) } + srv.WriteHeaders(w, "MISS", &cached) } +func (srv *Server) Clear(w http.ResponseWriter, r *http.Request) { + if err := srv.Cache.Clear(r.Context()); err != nil { + srv.logger.Error("failed to clear cache") + return + } + w.WriteHeader(http.StatusOK) + w.Write([]byte("cleaned proxy cache")) +} + func (srv *Server) WriteHeaders(w http.ResponseWriter, state string, cached *cache.CacheValue) { maps.Copy(w.Header(), cached.Header) w.Header().Set("X-Cache", state) From c134e4c615ab3b114be00fe08698a9031b08f56d Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:36:27 +0330 Subject: [PATCH 11/12] feat(main): load dotenv files --- go.mod | 10 +++++++--- go.sum | 22 ++++++++++++++++++++++ main.go | 2 ++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 50b93b5..7b3884e 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,15 @@ module github.com/pyr33x/proxy go 1.25.4 +require ( + github.com/joho/godotenv v1.5.1 + github.com/pyr33x/envy v0.4.2 + github.com/redis/go-redis/v9 v9.17.0 + go.uber.org/zap v1.27.1 +) + require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/pyr33x/envy v0.4.2 // indirect - github.com/redis/go-redis/v9 v9.17.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.1 // indirect ) diff --git a/go.sum b/go.sum index 512e3e2..8d45f51 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,34 @@ +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pyr33x/envy v0.4.2 h1:wwi3Fa5r3XvE/CzWVC6ENPpg2cBCI/4obP9wBOfAkXU= github.com/pyr33x/envy v0.4.2/go.mod h1:ZbCBVojzFK0McudtLwBZtQWRtm+4B9kj7nrCr/fQd/A= github.com/redis/go-redis/v9 v9.17.0 h1:K6E+ZlYN95KSMmZeEQPbU/c++wfmEvfFB17yEAq/VhM= github.com/redis/go-redis/v9 v9.17.0/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 9d1b53e..9204594 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,8 @@ import ( "github.com/pyr33x/proxy/internal/proxy" "github.com/pyr33x/proxy/pkg/config" "go.uber.org/zap" + + _ "github.com/joho/godotenv/autoload" ) func gracefulShutdown(cancel context.CancelFunc, logger *zap.Logger, proxy *http.Server, done chan struct{}) { From 8a4538a9a19e71efa9c25aee776ed3f1d3bdafeb Mon Sep 17 00:00:00 2001 From: pyr33x Date: Wed, 26 Nov 2025 21:43:07 +0330 Subject: [PATCH 12/12] fix: issues triggered by ci --- internal/proxy/server.go | 10 ++++++++-- main.go | 10 ++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/internal/proxy/server.go b/internal/proxy/server.go index de4daa8..d0918e8 100644 --- a/internal/proxy/server.go +++ b/internal/proxy/server.go @@ -45,7 +45,7 @@ func (srv *Server) ServeProxy(w http.ResponseWriter, r *http.Request) { http.Error(w, "error forwarding request", http.StatusInternalServerError) return } - defer resp.Body.Close() + defer resp.Body.Close() //nolint:errcheck body, err := io.ReadAll(resp.Body) if err != nil { @@ -78,8 +78,14 @@ func (srv *Server) Clear(w http.ResponseWriter, r *http.Request) { srv.logger.Error("failed to clear cache") return } + w.WriteHeader(http.StatusOK) - w.Write([]byte("cleaned proxy cache")) + if _, err := w.Write([]byte("cleaned proxy cache")); err != nil { + srv.logger.Error("failed to write proxy cache clean response", + zap.Error(err), + ) + return + } } func (srv *Server) WriteHeaders(w http.ResponseWriter, state string, cached *cache.CacheValue) { diff --git a/main.go b/main.go index 9204594..8805242 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ import ( _ "github.com/joho/godotenv/autoload" ) -func gracefulShutdown(cancel context.CancelFunc, logger *zap.Logger, proxy *http.Server, done chan struct{}) { +func gracefulShutdown(logger *zap.Logger, proxy *http.Server, done chan struct{}) { stopCtx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() @@ -29,6 +29,7 @@ func gracefulShutdown(cancel context.CancelFunc, logger *zap.Logger, proxy *http logger.Error("failed to shutdown proxy server", zap.Error(err), ) + return } logger.Info("exiting proxy...") @@ -36,16 +37,13 @@ func gracefulShutdown(cancel context.CancelFunc, logger *zap.Logger, proxy *http } func main() { - ctx, cancel := context.WithCancel(context.Background()) - done := make(chan struct{}) cfg := config.New() logger := zl.New(cfg.Zap.Environment).GetLogger() + proxy := proxy.NewProxyServer(context.Background(), cfg, logger) - proxy := proxy.NewProxyServer(ctx, cfg, logger) - - go gracefulShutdown(cancel, logger, proxy, done) + go gracefulShutdown(logger, proxy, done) logger.Info("attached proxy", zap.String("bind", proxy.Addr),