From 56ea02c495fc70fe34539267c1c6c487f763b2b5 Mon Sep 17 00:00:00 2001 From: Richard Vowles Date: Mon, 22 Apr 2024 17:38:26 +1200 Subject: [PATCH 1/2] Support failure for bad SDK key The default operation of the EventSource SDK is to continually poll, and the library does not provide for absolute failure without an error handler telling it to stop. If the server sends 4xx errors, it should stop, absolutely, and not retry. --- FeatureHubSDK/EdgeFeatureHubConfig.cs | 18 ++++++++++++++++++ FeatureHubSDK/EventServiceListener.cs | 22 ++++++++++++++++++++-- FeatureHubTest/ConfigTest.cs | 14 ++++++++++---- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/FeatureHubSDK/EdgeFeatureHubConfig.cs b/FeatureHubSDK/EdgeFeatureHubConfig.cs index 1d70dcd..b571707 100644 --- a/FeatureHubSDK/EdgeFeatureHubConfig.cs +++ b/FeatureHubSDK/EdgeFeatureHubConfig.cs @@ -37,6 +37,19 @@ public interface IFeatureHubConfig void AddAnalyticCollector(IAnalyticsCollector collector); } + + public class FeatureHubKeyInvalidException : Exception + { + public FeatureHubKeyInvalidException(string message) + : base(message) + { + } + + public FeatureHubKeyInvalidException(string message, Exception innerException) + : base(message, innerException) + { + } + } public class EdgeFeatureHubConfig : IFeatureHubConfig { @@ -47,6 +60,11 @@ public EdgeFeatureHubConfig(string edgeUrl, string sdkKey) { _serverEvaluation = sdkKey != null && !sdkKey.Contains("*"); // two part keys are server evaluated + if (!sdkKey.Contains("/")) + { + throw new FeatureHubKeyInvalidException($"The SDK key `{sdkKey}` is invalid"); + } + if (edgeUrl.EndsWith("/")) { edgeUrl = edgeUrl.Substring(0, edgeUrl.Length - 1); diff --git a/FeatureHubSDK/EventServiceListener.cs b/FeatureHubSDK/EventServiceListener.cs index 58b3fa3..9fc16ea 100644 --- a/FeatureHubSDK/EventServiceListener.cs +++ b/FeatureHubSDK/EventServiceListener.cs @@ -106,14 +106,19 @@ public async Task ContextChange(string newHeader) return headers; } + + private string DefaultEnvConfig(string envVar, string defaultValue) + { + return Environment.GetEnvironmentVariable(envVar) ?? defaultValue; + } public void Init() { if (_closed) return; var config = new Configuration(uri: new UriBuilder(_featureHost.Url).Uri, - backoffResetThreshold: TimeSpan.MaxValue, - delayRetryDuration: TimeSpan.Zero, + backoffResetThreshold: TimeSpan.FromSeconds(int.Parse(DefaultEnvConfig("FEATUREHUB_BACKOFF_RETRY_LIMIT", "100"))), + delayRetryDuration: TimeSpan.FromSeconds(int.Parse(DefaultEnvConfig("FEATUREHUB_DELAY_RETRY_MS", "10000"))), requestHeaders: _featureHost.ServerEvaluation ? BuildContextHeader() : null); if (FeatureLogging.InfoLogger != null) @@ -122,6 +127,19 @@ public void Init() } _eventSource = new EventSource(config); + _eventSource.Error += (sender, ex) => + { + if (ex.Exception is EventSourceServiceUnsuccessfulResponseException result) + { + if (result.StatusCode != 503) + { + _repository.Notify(SSEResultState.Failure, null); + FeatureLogging.ErrorLogger(this, "Server issued a failure, stopping."); + _closed = true; + _eventSource.Close(); + } + } + }; // if (FeatureLogging.DebugLogger != null) // { diff --git a/FeatureHubTest/ConfigTest.cs b/FeatureHubTest/ConfigTest.cs index 3e94c5f..6586f2c 100644 --- a/FeatureHubTest/ConfigTest.cs +++ b/FeatureHubTest/ConfigTest.cs @@ -9,15 +9,21 @@ class ConfigTest [Test] public void EnsureConfigCorrectlyDeterminesUrl() { - var cfg = new EdgeFeatureHubConfig("http://localhost:80/", "123*123"); + var cfg = new EdgeFeatureHubConfig("http://localhost:80/", "id/123*123"); Assert.IsTrue(!cfg.ServerEvaluation); - Assert.AreEqual("http://localhost:80/features/123*123", cfg.Url); + Assert.AreEqual("http://localhost:80/features/id/123*123", cfg.Url); - cfg = new EdgeFeatureHubConfig("http://localhost:80", "123"); + cfg = new EdgeFeatureHubConfig("http://localhost:80", "id/123"); Assert.IsTrue(cfg.ServerEvaluation); - Assert.AreEqual("http://localhost:80/features/123", cfg.Url); + Assert.AreEqual("http://localhost:80/features/id/123", cfg.Url); } + [Test] + public void InvalidKeyStructureFails() + { + Assert.Throws(() => + new EdgeFeatureHubConfig("http://localhost:80/", "123*123")); + } } } From 58daf3c9cdef26ffa55965cc4ee6bdfc904ef0d1 Mon Sep 17 00:00:00 2001 From: Richard Vowles Date: Mon, 22 Apr 2024 17:50:19 +1200 Subject: [PATCH 2/2] Add changelog and version bump --- FeatureHubSDK/FeatureHubSDK.csproj | 6 +++--- README.md | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/FeatureHubSDK/FeatureHubSDK.csproj b/FeatureHubSDK/FeatureHubSDK.csproj index 5cd6bb8..11b6273 100644 --- a/FeatureHubSDK/FeatureHubSDK.csproj +++ b/FeatureHubSDK/FeatureHubSDK.csproj @@ -16,9 +16,9 @@ https://github.com/featurehub-io/featurehub-dotnet-sdk git FeatureFlags FeatureToggles SDK Flags Toggles RemoteConfig - 2.2.0 - updates for caching support, update min dependencies. - 2.2.0 - 2.2.0 + 2.3.0 - check valid keys and stop calling server on failure. + 2.3.0 + 2.3.0 diff --git a/README.md b/README.md index f43f5e0..11e2bec 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ Details about what general features are available in FeatureHub SDKs are [availa ## Changelog +- 2.3.0 - Support for preventing badly formatted API keys from being passed in. Support for API keys and cause 4xx errors +to stop polling. Support for overriding the number of backoff attempts made (`FEATUREHUB_BACKOFF_RETRY_LIMIT` - defaults to 100), +and delay retry timeout (was zero, now 10s, controlled by `FEATUREHUB_DELAY_RETRY_MS`). - 2.2.0 - FeatureHub 1.5.9 support - supporting Fastly integration, server side polling period control, stale environments. We have upgraded to the 6.0.1 OpenAPI compiler, but gone no further because it generates code that does not work. - 2.1.5 - FeatureHub 1.5.6 is not returning the name of the feature and this is causing the 2.1.4 to version to break.