From 7ab8287b2683164ab7fa70b0f298025bf154d5b4 Mon Sep 17 00:00:00 2001 From: Elliot Campbell Date: Thu, 22 Aug 2024 15:18:13 -0400 Subject: [PATCH 1/6] add a v1 and v2 handler that uses different devcycle clients which fetch from v1 and v2 cdn endpoints respectively --- go.mod | 4 ++-- go.sum | 6 ++---- http_endpoints.go | 3 +-- options.go | 7 +++++++ proxy.go | 20 ++++++++++++++++---- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 68a423b..2d33deb 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/devcyclehq/sdk-proxy go 1.20 require ( - github.com/devcyclehq/go-server-sdk/v2 v2.17.0 + github.com/devcyclehq/go-server-sdk/v2 v2.18.0 github.com/gin-gonic/gin v1.10.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/kr/pretty v0.3.1 + github.com/launchdarkly/eventsource v1.7.1 github.com/stretchr/testify v1.9.0 ) @@ -27,7 +28,6 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/kr/text v0.2.0 // indirect - github.com/launchdarkly/eventsource v1.7.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 244a1ee..0ed4556 100644 --- a/go.sum +++ b/go.sum @@ -11,10 +11,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/devcyclehq/go-server-sdk/v2 v2.16.1 h1:NZ0vHZhhrPOb2hbU/r5sIIDSG4tPc+TiO/ovJaP0Gk8= -github.com/devcyclehq/go-server-sdk/v2 v2.16.1/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= -github.com/devcyclehq/go-server-sdk/v2 v2.17.0 h1:eBvoJesVPYvy7htJBjhIWRGbQq5fsmlcv4nvsrHqPDU= -github.com/devcyclehq/go-server-sdk/v2 v2.17.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= +github.com/devcyclehq/go-server-sdk/v2 v2.18.0 h1:9STdu/bTnrjM1cFh3OJddO4nxAaAUqlOAO180x6SWpk= +github.com/devcyclehq/go-server-sdk/v2 v2.18.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= diff --git a/http_endpoints.go b/http_endpoints.go index d3ce2f5..d2fd434 100644 --- a/http_endpoints.go +++ b/http_endpoints.go @@ -111,10 +111,9 @@ func BatchEvents() gin.HandlerFunc { } } -func GetConfig() gin.HandlerFunc { +func GetConfig(client *devcycle.Client) gin.HandlerFunc { return func(c *gin.Context) { instance := c.Value("instance").(*ProxyInstance) - client := c.Value("devcycle").(*devcycle.Client) if c.Param("sdkKey") == "" || !strings.HasSuffix(c.Param("sdkKey"), ".json") { c.AbortWithStatus(http.StatusForbidden) diff --git a/options.go b/options.go index f94c333..e1f686a 100644 --- a/options.go +++ b/options.go @@ -73,6 +73,7 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options { EnableBetaRealtimeUpdates: i.SSEEnabled, AdvancedOptions: devcycle.AdvancedOptions{ OverridePlatformData: &i.PlatformData, + OverrideConfigWithV1: false, }, ClientEventHandler: i.sseEvents, } @@ -80,6 +81,12 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options { return &options } +func (i *ProxyInstance) BuildDevCycleV1Options() *devcycle.Options { + options := i.BuildDevCycleOptions() + options.AdvancedOptions.OverrideConfigWithV1 = true + return options +} + func (i *ProxyInstance) EventRebroadcaster() { for event := range i.sseEvents { if event.EventType == api.ClientEventType_RealtimeUpdates { diff --git a/proxy.go b/proxy.go index c14c36e..706032b 100644 --- a/proxy.go +++ b/proxy.go @@ -34,10 +34,19 @@ func NewBucketingProxyInstance(instance *ProxyInstance) (*ProxyInstance, error) } options := instance.BuildDevCycleOptions() + optionsV1 := instance.BuildDevCycleV1Options() client, err := devcycle.NewClient(instance.SDKKey, options) + if err != nil { + return nil, fmt.Errorf("error creating DevCycle client: %v", err) + } + + clientV1, err := devcycle.NewClient(instance.SDKKey, optionsV1) + if err != nil { + return nil, fmt.Errorf("error creating DevCycle V1 client: %v", err) + } instance.dvcClient = client - r := newRouter(client, instance) + r := newRouter(client, clientV1, instance) if instance.HTTPEnabled { if instance.HTTPPort == 0 { @@ -94,7 +103,7 @@ func sdkProxyMiddleware(instance *ProxyInstance) gin.HandlerFunc { } } -func newRouter(client *devcycle.Client, instance *ProxyInstance) *gin.Engine { +func newRouter(client *devcycle.Client, clientV1 *devcycle.Client, instance *ProxyInstance) *gin.Engine { r := gin.New() r.Use(gin.Logger()) @@ -116,9 +125,12 @@ func newRouter(client *devcycle.Client, instance *ProxyInstance) *gin.Engine { } configCDNv1 := r.Group("/config/v1") { - configCDNv1.GET("/server/:sdkKey", GetConfig()) + configCDNv1.GET("/server/:sdkKey", GetConfig(clientV1)) + } + configCDNv2 := r.Group("/config/v2") + { + configCDNv2.GET("/server/:sdkKey", GetConfig(client)) } - r.GET("/event-stream", SSE()) return r From 1d8132312d016e98eb29fdba27d0cbedc9a9fbf3 Mon Sep 17 00:00:00 2001 From: Jamie Sinn Date: Thu, 22 Aug 2024 17:47:28 -0400 Subject: [PATCH 2/6] store an override config that's per instance level --- go.mod | 2 +- go.sum | 2 ++ http_endpoints.go | 68 ++++++++++++++++++++++++++--------------------- options.go | 36 ++++++++++++++++++++++--- proxy.go | 12 +++------ 5 files changed, 75 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 2d33deb..9a7c785 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/devcyclehq/sdk-proxy go 1.20 require ( - github.com/devcyclehq/go-server-sdk/v2 v2.18.0 + github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c github.com/gin-gonic/gin v1.10.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/kr/pretty v0.3.1 diff --git a/go.sum b/go.sum index 0ed4556..730cb9f 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ 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/devcyclehq/go-server-sdk/v2 v2.18.0 h1:9STdu/bTnrjM1cFh3OJddO4nxAaAUqlOAO180x6SWpk= github.com/devcyclehq/go-server-sdk/v2 v2.18.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c h1:HABqfIWonwH731k8kd+yYLExjsuVH5S+xL9Ed3Kywhw= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= diff --git a/http_endpoints.go b/http_endpoints.go index d2fd434..465f2e6 100644 --- a/http_endpoints.go +++ b/http_endpoints.go @@ -111,7 +111,7 @@ func BatchEvents() gin.HandlerFunc { } } -func GetConfig(client *devcycle.Client) gin.HandlerFunc { +func GetConfig(client *devcycle.Client, version ...string) gin.HandlerFunc { return func(c *gin.Context) { instance := c.Value("instance").(*ProxyInstance) @@ -119,44 +119,50 @@ func GetConfig(client *devcycle.Client) gin.HandlerFunc { c.AbortWithStatus(http.StatusForbidden) return } - var ret []byte - rawConfig, etag, err := client.GetRawConfig() - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{}) - return - } - if instance.SSEEnabled { - config := map[string]interface{}{} - err = json.Unmarshal(rawConfig, &config) + var ret, rawConfig []byte + var etag, lm string + var err error + if client != nil { + rawConfig, etag, lm, err = client.GetRawConfig() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{}) return } - hostname := fmt.Sprintf("http://%s:%d", instance.SSEHostname, instance.HTTPPort) - // This is the only indicator that a unix socket request was made - if c.Request.RemoteAddr == "" { - hostname = fmt.Sprintf("unix:%s", instance.UnixSocketPath) - } - fmt.Println(c.Request) - if val, ok := config["sse"]; ok { - path := val.(map[string]interface{})["path"].(string) - - config["sse"] = devcycle_api.SSEHost{ - Hostname: hostname, - Path: path, + if instance.SSEEnabled { + config := map[string]interface{}{} + err = json.Unmarshal(rawConfig, &config) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + hostname := fmt.Sprintf("http://%s:%d", instance.SSEHostname, instance.HTTPPort) + // This is the only indicator that a unix socket request was made + if c.Request.RemoteAddr == "" { + hostname = fmt.Sprintf("unix:%s", instance.UnixSocketPath) + } + fmt.Println(c.Request) + if val, ok := config["sse"]; ok { + path := val.(map[string]interface{})["path"].(string) + + config["sse"] = devcycle_api.SSEHost{ + Hostname: hostname, + Path: path, + } } - } - - ret, err = json.Marshal(config) - } else { - ret = rawConfig - } - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{}) - return + ret, err = json.Marshal(config) + } else { + ret = rawConfig + } + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{}) + return + } + } else if client == nil && len(version) > 0 { + ret, etag, lm = instance.BypassSDKConfig(version[0]) } c.Header("ETag", etag) + c.Header("Last-Modified", lm) c.Data(http.StatusOK, "application/json", ret) } } diff --git a/options.go b/options.go index e1f686a..2c3bb7c 100644 --- a/options.go +++ b/options.go @@ -5,7 +5,9 @@ import ( "fmt" "github.com/devcyclehq/go-server-sdk/v2/api" "github.com/launchdarkly/eventsource" + "io" "log" + "net/http" "os" "runtime" "time" @@ -38,6 +40,7 @@ type ProxyInstance struct { dvcClient *devcycle.Client sseServer *eventsource.Server sseEvents chan api.ClientEvent + bypassConfig []byte } type SDKConfig struct { @@ -81,10 +84,35 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options { return &options } -func (i *ProxyInstance) BuildDevCycleV1Options() *devcycle.Options { - options := i.BuildDevCycleOptions() - options.AdvancedOptions.OverrideConfigWithV1 = true - return options +func (i *ProxyInstance) BypassSDKConfig(version string) (config []byte, etag, lastModified string) { + + request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/%s", version, i.SDKKey), nil) + if err != nil { + return i.bypassConfig, "", "" + } + + resp, err := http.DefaultClient.Do(request) + + if err != nil { + return i.bypassConfig, "", "" + } + + if resp.StatusCode == http.StatusNotModified { + return i.bypassConfig, resp.Header.Get("ETag"), resp.Header.Get("Last-Modified") + } + + if resp.StatusCode != http.StatusOK { + return i.bypassConfig, "", "" + } + + body, err := io.ReadAll(resp.Body) + defer resp.Body.Close() + if err != nil { + return i.bypassConfig, "", "" + } + + i.bypassConfig = body + return i.bypassConfig, resp.Header.Get("ETag"), resp.Header.Get("Last-Modified") } func (i *ProxyInstance) EventRebroadcaster() { diff --git a/proxy.go b/proxy.go index 706032b..36b3abf 100644 --- a/proxy.go +++ b/proxy.go @@ -34,19 +34,13 @@ func NewBucketingProxyInstance(instance *ProxyInstance) (*ProxyInstance, error) } options := instance.BuildDevCycleOptions() - optionsV1 := instance.BuildDevCycleV1Options() client, err := devcycle.NewClient(instance.SDKKey, options) if err != nil { return nil, fmt.Errorf("error creating DevCycle client: %v", err) } - - clientV1, err := devcycle.NewClient(instance.SDKKey, optionsV1) - if err != nil { - return nil, fmt.Errorf("error creating DevCycle V1 client: %v", err) - } instance.dvcClient = client - r := newRouter(client, clientV1, instance) + r := newRouter(client, instance) if instance.HTTPEnabled { if instance.HTTPPort == 0 { @@ -103,7 +97,7 @@ func sdkProxyMiddleware(instance *ProxyInstance) gin.HandlerFunc { } } -func newRouter(client *devcycle.Client, clientV1 *devcycle.Client, instance *ProxyInstance) *gin.Engine { +func newRouter(client *devcycle.Client, instance *ProxyInstance) *gin.Engine { r := gin.New() r.Use(gin.Logger()) @@ -125,7 +119,7 @@ func newRouter(client *devcycle.Client, clientV1 *devcycle.Client, instance *Pro } configCDNv1 := r.Group("/config/v1") { - configCDNv1.GET("/server/:sdkKey", GetConfig(clientV1)) + configCDNv1.GET("/server/:sdkKey", GetConfig(nil)) } configCDNv2 := r.Group("/config/v2") { From c9ada6a69c6209150bc88ae5bb37168e463d630e Mon Sep 17 00:00:00 2001 From: Jamie Sinn Date: Thu, 22 Aug 2024 17:48:59 -0400 Subject: [PATCH 3/6] pass in v1 arg --- proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy.go b/proxy.go index 36b3abf..5036c76 100644 --- a/proxy.go +++ b/proxy.go @@ -119,7 +119,7 @@ func newRouter(client *devcycle.Client, instance *ProxyInstance) *gin.Engine { } configCDNv1 := r.Group("/config/v1") { - configCDNv1.GET("/server/:sdkKey", GetConfig(nil)) + configCDNv1.GET("/server/:sdkKey", GetConfig(nil, "v1")) } configCDNv2 := r.Group("/config/v2") { From 4776b356f6fe58c5c243893e09101fcdd7f6b22a Mon Sep 17 00:00:00 2001 From: Jamie Sinn Date: Thu, 22 Aug 2024 17:49:52 -0400 Subject: [PATCH 4/6] path fix --- options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.go b/options.go index 2c3bb7c..6414a9b 100644 --- a/options.go +++ b/options.go @@ -86,7 +86,7 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options { func (i *ProxyInstance) BypassSDKConfig(version string) (config []byte, etag, lastModified string) { - request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/%s", version, i.SDKKey), nil) + request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/%s.json", version, i.SDKKey), nil) if err != nil { return i.bypassConfig, "", "" } From c9555629c23105020c5520df6e6d7d9a6f826ed0 Mon Sep 17 00:00:00 2001 From: Jamie Sinn Date: Thu, 22 Aug 2024 17:50:24 -0400 Subject: [PATCH 5/6] path fix v2 --- options.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.go b/options.go index 6414a9b..21a9de8 100644 --- a/options.go +++ b/options.go @@ -86,7 +86,7 @@ func (i *ProxyInstance) BuildDevCycleOptions() *devcycle.Options { func (i *ProxyInstance) BypassSDKConfig(version string) (config []byte, etag, lastModified string) { - request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/%s.json", version, i.SDKKey), nil) + request, err := http.NewRequest("GET", fmt.Sprintf("https://config-cdn.devcycle.com/config/%s/server/%s.json", version, i.SDKKey), nil) if err != nil { return i.bypassConfig, "", "" } From 3a71d54b234d6659a7b06a5310f6e225b41e0afb Mon Sep 17 00:00:00 2001 From: Jamie Sinn Date: Fri, 23 Aug 2024 09:47:20 -0400 Subject: [PATCH 6/6] use released version of sdk --- go.mod | 2 +- go.sum | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9a7c785..0ecb093 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/devcyclehq/sdk-proxy go 1.20 require ( - github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c + github.com/devcyclehq/go-server-sdk/v2 v2.18.1 github.com/gin-gonic/gin v1.10.0 github.com/kelseyhightower/envconfig v1.4.0 github.com/kr/pretty v0.3.1 diff --git a/go.sum b/go.sum index 730cb9f..fb1e0ac 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,10 @@ github.com/devcyclehq/go-server-sdk/v2 v2.18.0 h1:9STdu/bTnrjM1cFh3OJddO4nxAaAUq github.com/devcyclehq/go-server-sdk/v2 v2.18.0/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c h1:HABqfIWonwH731k8kd+yYLExjsuVH5S+xL9Ed3Kywhw= github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240822213335-a3201264237c/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240823134625-106c09774905 h1:tOTq2TbgeYtdFuaOl71Sups5p8setOA5VHan6DRGOnU= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1-0.20240823134625-106c09774905/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1 h1:4Rwxr0Sx4S56zGgSqm0C06Xj88038iYVbWZinpPx8C4= +github.com/devcyclehq/go-server-sdk/v2 v2.18.1/go.mod h1:DzKrJ4s2apfphFwB/Aq8YDf7brB+NDr6IxX0TNi2c24= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=