Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Expose HttpConfig so retry behaviour is user-configurable - #147

Open
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig
Open

Expose HttpConfig so retry behaviour is user-configurable#147
MichaelGHSeg wants to merge 4 commits into
mainfrom
csharp-public-httpconfig

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Why

The retry state machine added in #144 can only be configured from CDN settings. RateLimitConfig, BackoffConfig and HttpConfig are all internal, and Configuration has no entry point — so a C# consumer currently cannot set retry behaviour at all.

Kotlin and Swift both expose this, as a single config object:

SDKEntry point
KotlinConfiguration.httpConfig: HttpConfig? = null (public data class HttpConfig)
Swiftpublic func httpConfig(_ config: HttpConfig?) -> Configuration
C# (today)— none —

This brings C# in line with the two SDKs #144 was explicitly written to match.

What

  • Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public. RetryConfig stays internal — it's plumbing built from HttpConfig, never supplied by callers.
  • Add Configuration.HttpConfig, as a trailing optional constructor argument so existing positional callers are unaffected. Defaults to null, preserving today's CDN-only behaviour exactly.
  • EventPipelineProvider / SyncEventPipelineProvider pass it through as the pipeline's starting retry config. CDN settings still override it later via UpdateHttpConfig.
  • Make the pipeline constructors that accept an HttpConfig public, so a custom IEventPipelineProvider can pass one on rather than only read it.

Usage

varconfig=newConfiguration(writeKey:"...",httpConfig:newHttpConfig(backoffConfig:newBackoffConfig(enabled:true,maxRetryCount:10)));

Testing

216 tests pass, including 6 new cases in Tests/Retry/ConfigurationHttpConfigTest.cs asserting that a config set on Configuration reaches both pipelines' retry state machines (and that omitting it still yields legacy mode).

Notes

This supersedes the Configuration portion of #143, which added individual MaxRetries / MaxTotalBackoffDuration / MaxRateLimitDuration knobs. That shape matches analytics-python but not Kotlin/Swift; #143 and #144 were independent branches off 85f025e and never shared history, so the divergence was never reconciled.

The retry state machine added in #144 could only ever be configured from CDN
settings: RateLimitConfig, BackoffConfig and HttpConfig were all internal and
Configuration had no entry point, so a C# consumer could not set retry
behaviour at all.
Kotlin and Swift both expose this. Kotlin has `Configuration.httpConfig:
HttpConfig?` with a public `data class HttpConfig`; Swift has
`public func httpConfig(_ config: HttpConfig?) -> Configuration`. This brings
C# in line with the SDKs #144 was written to match.
- Make RetryBehavior, RateLimitConfig, BackoffConfig and HttpConfig public.
RetryConfig stays internal — it is plumbing built from HttpConfig, never
supplied by callers.
- Add Configuration.HttpConfig, as a trailing optional constructor argument so
existing positional callers are unaffected. Defaults to null, preserving
today's CDN-only behaviour.
- Have EventPipelineProvider and SyncEventPipelineProvider pass it through as
the pipeline's starting retry config. CDN settings still override it later
via UpdateHttpConfig.
- Make the pipeline constructors that take an HttpConfig public, so a custom
IEventPipelineProvider can pass one on rather than only read it.
216 tests pass, including 6 new ones covering that a config set on
Configuration reaches both pipelines' retry state machines.
Two problems that only matter once these types are public:
- BackoffConfig stored a reference to the shared static DefaultStatusCodeOverrides
whenever no map was supplied. With StatusCodeOverrides exposed as a public
property, a caller doing the natural thing — cfg.StatusCodeOverrides[500] =
Drop — corrupted the defaults for every BackoffConfig constructed afterwards
in the process, including ones parsed from CDN settings, with no way to reset.
The constructor now copies the map.
- A user-supplied HttpConfig reached the retry state machine unclamped, while
the CDN path is validated by HttpConfigParser. Configuration.HttpConfig was
therefore the only unvalidated route in, so out-of-range values such as
maxRetryInterval: 0 or a negative jitterPercent took effect verbatim. Both
pipelines now call Validated() on user-supplied config, matching the CDN path.
218 tests pass, including two new cases covering the copy and the clamping.
The property doc said retry settings come from CDN settings alone when this is
null, which reads as 'non-null means yours is used'. It is not: SegmentDestination
calls UpdateHttpConfig on every settings refresh carrying an httpConfig key, which
replaces the whole config. A CDN payload also counts as enabling a subsystem unless
it explicitly says enabled: false, so a payload tuning something unrelated can turn
retries back on. Only a payload with no httpConfig key leaves this value in effect.
This matches analytics-kotlin (SegmentDestination.kt:133) and analytics-swift
(SegmentDestination.swift:83-91), which assign CDN config over the user's the same
way and share the enabled-defaults-true rule, so the behaviour is left alone and
only the documentation is corrected.
Adding a trailing optional parameter to Configuration's constructor is source
compatible but not binary compatible: the compiler bakes optional defaults into
the call site, so the assembly loses the old 13-parameter .ctor and anything
compiled against it fails with MissingMethodException. That is fine for NuGet
consumers, who recompile, but this SDK also ships Unity and Xamarin samples
where DLLs are dropped in.
#144 never touched Configuration.cs, so the break would have been new here.
Making HttpConfig a settable property is purely additive, leaves the existing
constructor signature untouched, and is closer to analytics-kotlin, which uses
a mutable 'var httpConfig' rather than a constructor argument.
new Configuration("writeKey") { HttpConfig = new HttpConfig(...) }
218 tests pass.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@MichaelGHSeg