From 62e64e5154376d08794cda7f23d66723f2f525f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A0Ilya=20Atamas?= Date: Tue, 23 Apr 2019 17:59:18 +0300 Subject: [PATCH] perf: move dynamic options reciever to server, enable Cache-Control HTTP header --- cli/list.go | 10 +++--- cli/main.go | 3 +- cli/purge.go | 10 +++--- cli/root.go | 25 ++++++++++----- example/main.go | 8 ++--- proxy/cache.go | 21 ++---------- proxy/proxy.go | 2 -- proxy/server.go | 85 +++++++++++++++++++++++++++---------------------- readme.md | 21 ++++++------ 9 files changed, 91 insertions(+), 94 deletions(-) diff --git a/cli/list.go b/cli/list.go index d147a3c..7b23a88 100644 --- a/cli/list.go +++ b/cli/list.go @@ -12,13 +12,11 @@ var listCmd = &cobra.Command{ Use: "list", Short: "List all cached paths", Run: func(cmd *cobra.Command, args []string) { - proxy := getProxy(func() (npmproxy.Options, error) { - return npmproxy.Options{ - DatabasePrefix: persistentOptions.RedisPrefix, - }, nil - }) + proxy := getProxy() - metadatas, err := proxy.ListCachedPaths() + metadatas, err := proxy.ListCachedPaths(npmproxy.Options{ + DatabasePrefix: persistentOptions.RedisPrefix, + }) if err != nil { panic(err) } diff --git a/cli/main.go b/cli/main.go index 132cab6..36fc3f5 100644 --- a/cli/main.go +++ b/cli/main.go @@ -25,7 +25,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&persistentOptions.RedisPrefix, "redis-prefix", getEnvString("REDIS_PREFIX", "ncp-"), "Redis prefix") } -func getProxy(getOptions func() (npmproxy.Options, error)) *npmproxy.Proxy { +func getProxy() *npmproxy.Proxy { return &npmproxy.Proxy{ Database: npmproxy.DatabaseRedis{ Client: redis.NewClient(&redis.Options{ @@ -37,7 +37,6 @@ func getProxy(getOptions func() (npmproxy.Options, error)) *npmproxy.Proxy { HttpClient: &http.Client{ Transport: http.DefaultTransport, }, - GetOptions: getOptions, } } diff --git a/cli/purge.go b/cli/purge.go index f24bfd8..1edb616 100644 --- a/cli/purge.go +++ b/cli/purge.go @@ -10,13 +10,11 @@ var purgeCmd = &cobra.Command{ Use: "purge", Short: "Purge all cached paths", Run: func(cmd *cobra.Command, args []string) { - proxy := getProxy(func() (npmproxy.Options, error) { - return npmproxy.Options{ - DatabasePrefix: persistentOptions.RedisPrefix, - }, nil - }) + proxy := getProxy() - err := proxy.PurgeCachedPaths() + err := proxy.PurgeCachedPaths(npmproxy.Options{ + DatabasePrefix: persistentOptions.RedisPrefix, + }) if err != nil { panic(err) } diff --git a/cli/root.go b/cli/root.go index 956fef6..d7696d4 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1,6 +1,7 @@ package cli import ( + "log" "time" npmproxy "github.com/emeralt/npm-cache-proxy/proxy" @@ -31,16 +32,24 @@ func init() { } func run(cmd *cobra.Command, args []string) { - proxy := getProxy(func() (npmproxy.Options, error) { - return npmproxy.Options{ - DatabasePrefix: persistentOptions.RedisPrefix, - DatabaseExpiration: time.Duration(rootOptions.CacheTTL) * time.Second, - UpstreamAddress: rootOptions.UpstreamAddress, - }, nil - }) + proxy := getProxy() - proxy.Server(npmproxy.ServerOptions{ + log.Print("Listening on " + rootOptions.ListenAddress) + + err := proxy.Server(npmproxy.ServerOptions{ ListenAddress: rootOptions.ListenAddress, Silent: rootOptions.Silent, + + GetOptions: func() (npmproxy.Options, error) { + return npmproxy.Options{ + DatabasePrefix: persistentOptions.RedisPrefix, + DatabaseExpiration: time.Duration(rootOptions.CacheTTL) * time.Second, + UpstreamAddress: rootOptions.UpstreamAddress, + }, nil + }, }).ListenAndServe() + + if err != nil { + log.Fatal(err) + } } diff --git a/example/main.go b/example/main.go index be04d87..154cd08 100644 --- a/example/main.go +++ b/example/main.go @@ -18,6 +18,10 @@ func main() { }), }, HttpClient: &http.Client{}, + } + + proxy.Server(npmproxy.ServerOptions{ + ListenAddress: "localhost:8080", GetOptions: func() (npmproxy.Options, error) { return npmproxy.Options{ DatabasePrefix: "ncp-", @@ -25,9 +29,5 @@ func main() { UpstreamAddress: "https://registry.npmjs.org", }, nil }, - } - - proxy.Server(npmproxy.ServerOptions{ - ListenAddress: "localhost:8080", }).ListenAndServe() } diff --git a/proxy/cache.go b/proxy/cache.go index ceaaca2..19235c2 100644 --- a/proxy/cache.go +++ b/proxy/cache.go @@ -10,12 +10,7 @@ import ( ) // GetCachedPath returns cached upstream response for a given url path. -func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, error) { - options, err := proxy.GetOptions() - if err != nil { - return nil, err - } - +func (proxy Proxy) GetCachedPath(options Options, path string, request *http.Request) ([]byte, error) { key := options.DatabasePrefix + path // get package from database @@ -72,12 +67,7 @@ func (proxy Proxy) GetCachedPath(path string, request *http.Request) ([]byte, er } // ListCachedPaths returns list of all cached url paths. -func (proxy Proxy) ListCachedPaths() ([]string, error) { - options, err := proxy.GetOptions() - if err != nil { - return nil, err - } - +func (proxy Proxy) ListCachedPaths(options Options) ([]string, error) { metadata, err := proxy.Database.Keys(options.DatabasePrefix) if err != nil { return nil, err @@ -92,12 +82,7 @@ func (proxy Proxy) ListCachedPaths() ([]string, error) { } // PurgeCachedPaths deletes all cached url paths. -func (proxy Proxy) PurgeCachedPaths() error { - options, err := proxy.GetOptions() - if err != nil { - return err - } - +func (proxy Proxy) PurgeCachedPaths(options Options) error { metadata, err := proxy.Database.Keys(options.DatabasePrefix) if err != nil { return err diff --git a/proxy/proxy.go b/proxy/proxy.go index 23bd671..23f4527 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -14,8 +14,6 @@ import ( type Proxy struct { Database Database HttpClient *http.Client - - GetOptions func() (Options, error) } // Options provides dynamic options for Proxy. diff --git a/proxy/server.go b/proxy/server.go index cfb08cf..24525e9 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -14,10 +14,14 @@ import ( type ServerOptions struct { ListenAddress string Silent bool + + GetOptions func() (Options, error) } // Server creates http proxy server func (proxy Proxy) Server(options ServerOptions) *http.Server { + gin.SetMode("release") + router := gin.New() if options.Silent { @@ -28,9 +32,9 @@ func (proxy Proxy) Server(options ServerOptions) *http.Server { router.Use(ginzap.RecoveryWithZap(logger, true)) } - router.GET("/:scope/:name", proxy.getPackageHandler) - router.GET("/:scope", proxy.getPackageHandler) - router.NoRoute(proxy.noRouteHandler) + router.GET("/:scope/:name", proxy.getPackageHandler(options)) + router.GET("/:scope", proxy.getPackageHandler(options)) + router.NoRoute(proxy.noRouteHandler(options)) return &http.Server{ Handler: router, @@ -38,45 +42,50 @@ func (proxy Proxy) Server(options ServerOptions) *http.Server { } } -func (proxy Proxy) getPackageHandler(c *gin.Context) { - pkg, err := proxy.GetCachedPath(c.Request.URL.Path, c.Request) - - if err != nil { - c.AbortWithError(500, err) - } else { - // c.Header("Content-Encoding", "gzip") - c.Data(200, "application/json", pkg) - } -} - -func (proxy Proxy) getTarballHabdler(c *gin.Context) { - pkg, err := proxy.GetCachedPath(c.Request.URL.Path, c.Request) - - if err != nil { - c.AbortWithError(500, err) - } else { - c.Data(200, "application/json", pkg) - } -} - -func (proxy Proxy) noRouteHandler(c *gin.Context) { - if strings.Contains(c.Request.URL.Path, ".tgz") { - proxy.getTarballHabdler(c) - } else if c.Request.URL.Path == "/" { - err := proxy.Database.Health() +func (proxy Proxy) getPackageHandler(options ServerOptions) gin.HandlerFunc { + return func(c *gin.Context) { + options, err := options.GetOptions() if err != nil { - c.AbortWithStatusJSON(503, err) + c.AbortWithError(500, err) } else { - c.AbortWithStatusJSON(200, gin.H{"ok": true}) - } - } else { - options, err := proxy.GetOptions() + pkg, err := proxy.GetCachedPath(options, c.Request.URL.Path, c.Request) - if err != nil { - c.AbortWithStatusJSON(500, err) - } else { - c.Redirect(http.StatusTemporaryRedirect, options.UpstreamAddress+c.Request.URL.Path) + if err != nil { + c.AbortWithError(500, err) + } else { + c.Header("Cache-Control", "public, max-age="+string(int(options.DatabaseExpiration.Seconds()))) + c.Data(200, "application/json", pkg) + } + } + } +} + +func (proxy Proxy) noRouteHandler(options ServerOptions) gin.HandlerFunc { + tarballHandler := proxy.getPackageHandler(options) + + return func(c *gin.Context) { + if strings.Contains(c.Request.URL.Path, ".tgz") { + // get tarball + tarballHandler(c) + } else if c.Request.URL.Path == "/" { + // get health + err := proxy.Database.Health() + + if err != nil { + c.AbortWithStatusJSON(503, err) + } else { + c.AbortWithStatusJSON(200, gin.H{"ok": true}) + } + } else { + // redirect + options, err := options.GetOptions() + + if err != nil { + c.AbortWithStatusJSON(500, err) + } else { + c.Redirect(http.StatusTemporaryRedirect, options.UpstreamAddress+c.Request.URL.Path) + } } } } diff --git a/readme.md b/readme.md index b66131b..845b44a 100644 --- a/readme.md +++ b/readme.md @@ -107,23 +107,29 @@ import ( "time" npmproxy "github.com/emeralt/npm-cache-proxy/proxy" - redis "github.com/go-redis/redis" + "github.com/go-redis/redis" ) func main() { + // create proxy proxy := npmproxy.Proxy{ - // you can provide you own Database - // or use an existing one + // use redis as database Database: npmproxy.DatabaseRedis{ + // see github.com/go-redis/redis Client: redis.NewClient(&redis.Options{ Addr: "localhost:6379", }), }, - // allows to reuse tcp connections + // reuse connections HttpClient: &http.Client{}, + } - // allows to get options dynamically + // create and start server + proxy.Server(npmproxy.ServerOptions{ + ListenAddress: "localhost:8080", + + // allow fetching options dynamically on each request GetOptions: func() (npmproxy.Options, error) { return npmproxy.Options{ DatabasePrefix: "ncp-", @@ -131,11 +137,6 @@ func main() { UpstreamAddress: "https://registry.npmjs.org", }, nil }, - } - - // listen on http://localhost:8080 - proxy.Server(npmproxy.ServerOptions{ - ListenAddress: "localhost:8080", }).ListenAndServe() } ```