HTTP polling provider for Cocoar.Configuration enabling remote configuration sources with configurable polling intervals, automatic retries, and failure sentinel values for resilient distributed configuration management.
$ dotnet add package Cocoar.Configuration.HttpPolling
Lightweight, strongly-typed configuration aggregation for .NET (current target framework: net9.0).
At a glance
- Layer multiple sources (files, environment variables, HTTP, adapters) with deterministic last‑write‑wins merge
- Providers return JSON objects; some emit change signals for live updates
- On any change a full ordered recompute occurs; result swap is atomic
- Direct DI access to your config types (no required
IConfiguration/IOptionswrappers)
Currently only net9.0 is built. Multi-targeting can be added later.
| Package | Description | TFM (current) |
|---|---|---|
Cocoar.Configuration |
| Core (File + Environment providers, merge orchestration) |
| net9.0 |
Cocoar.Configuration.AspNetCore | WebApplicationBuilder / DI convenience | net9.0 |
Cocoar.Configuration.HttpPolling | HTTP polling provider | net9.0 |
Cocoar.Configuration.MicrosoftAdapter | Adapter for Microsoft IConfigurationSource | net9.0 |
<ItemGroup>
<PackageReference Include="Cocoar.Configuration" />
<!-- Optional extensions -->
<PackageReference Include="Cocoar.Configuration.AspNetCore" />
<PackageReference Include="Cocoar.Configuration.HttpPolling" />
<PackageReference Include="Cocoar.Configuration.MicrosoftAdapter" />
</ItemGroup>Quick CLI install commands:
dotnet add package Cocoar.Configuration
dotnet add package Cocoar.Configuration.AspNetCore
dotnet add package Cocoar.Configuration.HttpPolling
dotnet add package Cocoar.Configuration.MicrosoftAdapter
Minimal example (file + environment layering, strongly-typed access):
using Cocoar.Configuration;
using Cocoar.Configuration.AspNetCore;
using Cocoar.Configuration.Providers.FileSourceProvider.Fluent;
using Cocoar.Configuration.Providers.EnvironmentVariableProvider.Fluent;
public class AppSettings
{
public string ConnectionString { get; set; } = ""; // base value in JSON, can be overridden
public bool EnableFeatureX { get; set; } // overridden by env var APP_EnableFeatureX
public int CacheSeconds { get; set; } = 30; // overridden by env var APP_CacheSeconds
}
var builder = WebApplication.CreateBuilder(args);
builder.AddCocoarConfiguration(
// Base layer (optional): read a section from a JSON file if it exists
Rule.From.File(_ => FileSourceRuleOptions.FromFilePath("appsettings.json", "App"))
.For<AppSettings>().Optional(),
// Environment variables (prefix APP_) override matching properties
Rule.From.Environment(_ => new EnvironmentVariableRuleOptions("APP_"))
.For<AppSettings>()
);
var app = builder.Build();
// Resolve the manager and get a typed snapshot (throws if missing)
var settings = app.Services
.GetRequiredService<ConfigManager>()
.GetRequiredConfig<AppSettings>();
Console.WriteLine($"FeatureX: {settings.EnableFeatureX}, Cache: {settings.CacheSeconds}s");Example overlay (JSON + environment):
appsettings.json (optional):
{
"App": {
"ConnectionString": "Server=localhost;Database=MyApp;",
"EnableFeatureX": false,
"CacheSeconds": 30
}
}
Environment variables:
APP_EnableFeatureX=true
APP_CacheSeconds=60
More examples (multi-project solution under src/Examples/):
IConfigurationSourceOpen the solution: src/Examples/Examples.sln or run an example directly, e.g.:
dotnet run --project src/Examples/BasicUsage
Section:Key) → last-write-wins per key → unflatten → bind to target typeSee src/Examples/StaticProviderExample/Program.cs for seeding defaults and composing dependent configuration.
Control how configuration types are registered in DI container. Default is Singleton, but you can specify Scoped/Transient and use keyed services for multiple configurations of the same type.
→ Example: src/Examples/ServiceLifetimes/Program.cs
Use Rule.From.Provider<TProvider, TOptions, TQuery>() for full control over any provider type, including third-party providers.
→ Example: src/Examples/GenericProviderAPI/Program.cs
Plug any Microsoft IConfigurationSource (JSON, XML, Key Vault, User Secrets, etc.) into Cocoar's rule-based system.
→ Example: src/Examples/MicrosoftAdapterExample/Program.cs
Fetch configuration from HTTP endpoints with automatic polling. Only triggers recomputes when response actually changes.
→ Example: src/Examples/HttpPollingExample/Program.cs
| Provider | Package | Change Signal | Notes |
|---|---|---|---|
| File (JSON) | Core | ✅ Filesystem watcher (debounced) | Paths/sections; good base layer |
| Environment | Core | ❌ Snapshot only | Prefix filter; __ & : nesting |
| HTTP Polling | Extension | ✅ On real payload change | Optional headers; polling interval |
| Microsoft Adapter | Extension | Depends on source | Wrap any IConfigurationSource |
All providers support: Optional/required rules, dynamic factories, provider instance pooling.
Provider Documentation:
ConfigManager
| Method | Behavior |
|---|---|
GetConfig<T>() | Snapshot or null; never throws |
GetRequiredConfig<T>() | Snapshot or exception if missing |
TryGetConfig<T>(out T?) | true + value if available; else false/null |
GetConfigAsJson(Type) | Merged JSON (or null) |
Rule Creation (Fluent)
| Entry | Description |
|---|---|
Rule.From.File(...) | JSON files (watcher + debounce) |
Rule.From.Environment(...) | Environment snapshot (optional prefix) |
Rule.From.Http(...) | (Extension) polling with change detection |
Rule.From.Provider<TProv,TOpt,TQuery>(...) | Generic provider entry point |
Rule.From.Static(...) | Static object (seeding / defaults) |
Semantics: Use Optional for non-critical layers; Required enforces presence.
Cocoar.Configuration is designed to be extensible. Third-party packages can add their own fluent entry points (like Rule.From.MyProvider()) without modifying the core library.
For provider developers: See the Provider Development Guide for complete documentation on:
ConfigSourceProvider<TInstanceOptions,TQueryOptions> base classFor users: Simply install third-party provider packages and use their fluent APIs alongside the built-in providers.
Multi-project examples live under src/Examples/ (see src/Examples/README.md). Each folder is a runnable console (or minimal) project with its own Program.cs:
BasicUsage – File + environment layeringAspNetCoreExample – Web app integrationFileLayering – Multi-file deterministic layeringServiceLifetimes – DI lifetimes & keyed registrationsDynamicDependencies – Reading prior config during recomputeGenericProviderAPI – Generic provider compositionMicrosoftAdapterExample – Adapting IConfigurationSourceHttpPollingExample – Remote polling with change detectionStaticProviderExample – Static seeding & dependent compositionRun any example:
dotnet run --project src/Examples/GenericProviderAPI
Quality Assurance: The examples solution (
src/Examples/Examples.sln) is built in CI to ensure examples stay aligned with the API. Functional behaviors are additionally covered by unit/integration tests.
Section:Enabled), merged in order, then unflattened and deserialized into your target type.Guidance for recompute-time reads:
Rule.From.Static).Section:Key flattening during merge; final objects are unflattened before binding to your types.Packages are published under the NuGet organization cocoar.
Issues and PRs welcome. Please keep provider abstractions stable and deterministic (e.g., option keys for instance pooling) and follow the merge semantics described in ARCHITECTURE.md.
📝 Documentation Quality: Projects under src/Examples/ are built in CI to ensure they remain in sync with the current API. Keep examples minimal and idiomatic when contributing.
For deeper details, examples, and roadmap, check src/Cocoar.Configuration/README.md and ARCHITECTURE.md.
Recompute model: On any change emission, the manager recomputes all rules from scratch in order (last-write-wins merge). This is simple and correct but not minimal; a future optimization could recompute only affected rules downstream from the changed provider.
Provider lifecycle: Managed by an internal RuleManager per rule which reuses providers when instance options don't change and rebuilds subscriptions when query options change. This enables dynamic factories without recreating instances unnecessarily.
Array merge semantics: Arrays are replaced completely (not merged element-wise). Objects are flattened to colon keys and merged key-by-key. Additional array strategies (append/merge/custom) are under consideration.
Null/empty handling: Edge cases (nulls and empty objects) follow JSON deserialization defaults after key merge. Precise behavior will be documented as the API stabilizes.
Change emissions: Environment provider does not emit changes by default (snapshot only). File provider emits on filesystem changes. If you need change-driven recompute for environment variables, combine with other providers that do emit.
Circular dependencies: Rules can read any type's current snapshot during recompute via GetConfig<T>() calls in options/query factories. Avoid circular dependencies across types; detection/guardrails may be added.
DI lifetimes: Configuration types are registered as singletons by default. Support for scoped/transient lifetimes exists but may evolve.
IDisposable support for long-lived connectionsSee ARCHITECTURE.md & provider READMEs for details.
(This README reflects the current code state – future multi-targeting or optimizations will be documented when implemented.)
| Aspect | Cocoar.Configuration | Plain IConfiguration |
|---|---|---|
| Strong typing | Direct injection of concrete / interface config types | Manual binding or IOptions<T> wrappers |
| Multi-source layering | Explicit, ordered rule list (deterministic) | Order influenced by registration / provider ordering |
| Change handling | Atomic full recompute with dynamic rule factories | Incremental value lookups; custom reload logic per provider |
| Dynamic dependencies | Rules can read in-progress snapshots | Typically manual: pull values after build or inside factories |
| Extensibility model | Generic provider base + fluent rule DSL | Add or implement IConfigurationSource / IConfigurationProvider |
| Diagnostics | Access merged JSON per type | Must traverse configuration tree / know keys |
| Keyed DI & lifetimes | Built-in for each config type | Additional wiring required |
Use Cocoar when you want deterministic layering, dynamic dependency evaluation, atomic snapshots and strongly typed access without repetitive binding code. Stay with plain IConfiguration if you only need a simple hierarchical key/value store and existing providers already cover your scenario.