fusillade 3.0.1
Prefix Reserveddotnet add package fusillade --version 3.0.1
NuGet\Install-Package fusillade -Version 3.0.1
<PackageReference Include="fusillade" Version="3.0.1" />
<PackageVersion Include="fusillade" Version="3.0.1" />
<PackageReference Include="fusillade" />
paket add fusillade --version 3.0.1
#r "nuget: fusillade, 3.0.1"
#:package fusillade@3.0.1
#addin nuget:?package=fusillade&version=3.0.1
#tool nuget:?package=fusillade&version=3.0.1
<br /> <a href="https://github.com/reactiveui/fusillade"> <img width="120" heigth="120" src="https://raw.githubusercontent.com/reactiveui/styleguide/master/logo_fusillade/main.png"> </a>
Fusillade: An opinionated HTTP library for .NET apps
Fusillade helps you write efficient, resilient networked apps by composing HttpMessageHandlers for HttpClient. It focuses on:
- Request de-duplication for relevant HTTP methods
- Concurrency limiting via a priority-aware operation queue
- Request prioritization for predictable UX
- Speculative background fetching with byte-budget limits
- Optional caching of responses and an offline replay handler
Design inspirations include Android's Volley and Picasso.
Supported targets: library is built for .NET Standard 2.0 and is used from modern .NET (e.g., .NET 8/9), Xamarin/Mono, and .NET for iOS/Android/Mac Catalyst apps.
Install
- Package Manager: Install-Package fusillade
- .NET CLI: dotnet add package fusillade
Optional (examples below): Akavache for caching.
- .NET CLI: dotnet add package Akavache.SystemTextJson
Quick start
Create HttpClient instances by picking the right handler from NetCache:
using Fusillade;
using System.Net.Http;
// Highest priority: the user is waiting now
var client = new HttpClient(NetCache.UserInitiated);
var json = await client.GetStringAsync("https://httpbin.org/get");
Available built-ins:
- NetCache.UserInitiated: foreground work the user is waiting for
- NetCache.Background: background work that should not block UI work
- NetCache.Speculative: background prefetching with a byte budget
- NetCache.Offline: fetch from cache only (no network)
By default, requests are processed four at a time via an operation queue.
Core ideas
1) Request de-duplication
Fusillade de-duplicates concurrent requests for the same resource when the method is GET, HEAD, or OPTIONS. If multiple callers request the same URL concurrently, only one on-the-wire request is made; the others join the same in-flight response.
This happens transparently in RateLimitedHttpMessageHandler.
2) Concurrency limiting and prioritization
All work is scheduled through an OperationQueue (default parallelism is 4). Each handler has an effective priority:
- Priority.UserInitiated (100)
- Priority.Background (20)
- Priority.Speculative (10)
- Priority.Explicit (custom base with offset)
Higher numbers run before lower ones. You can set a custom base (Explicit) and an offset to fit your scenario.
using Fusillade;
using Punchclock;
using System.Net.Http;
// Custom queue with 2 concurrent slots
var queue = new OperationQueue(2);
var handler = new RateLimitedHttpMessageHandler(
new HttpClientHandler(),
basePriority: Priority.Explicit,
priority: 500, // higher runs earlier
opQueue: queue);
var client = new HttpClient(handler);
3) Speculative background fetching with byte budgets
Use NetCache.Speculative for prefetching scenarios. Limit the total number of bytes fetched; once the limit is reached, further speculative requests are canceled.
// Reset byte budget to 5 MB (e.g., on app resume)
NetCache.Speculative.ResetLimit(5 * 1024 * 1024);
var prefetch = new HttpClient(NetCache.Speculative);
_ = prefetch.GetStringAsync("https://example.com/expensive-data");
To stop speculative fetching immediately:
NetCache.Speculative.ResetLimit(-1); // any further requests will be canceled
Caching and offline
Fusillade can optionally cache responses (body bytes + headers) and replay them when offline.
There are two ways to wire caching:
Provide a cacheResultFunc to RateLimitedHttpMessageHandler, which gets called with the response and a unique request key when a response is received.
Set NetCache.RequestCache with an implementation of IRequestCache. Fusillade will invoke Save and Fetch automatically.
IRequestCache
public interface IRequestCache
{
Task Save(HttpRequestMessage request, HttpResponseMessage response, string key, CancellationToken ct);
Task<byte[]> Fetch(HttpRequestMessage request, string key, CancellationToken ct);
}
- Save is called once the handler has fully buffered the body (as ByteArrayContent) and cloned headers.
- Fetch should return the previously saved body bytes for the key (or null if not found).
Keys are generated by RateLimitedHttpMessageHandler.UniqueKeyForRequest(request). Treat the key as an implementation detail; persist what you receive and return it during Fetch.
Simple Akavache-based cache
using Akavache;
using Akavache.SystemTextJson;
using Fusillade;
using System.Net.Http;
// Initialize a simple in-memory Akavache cache
var database = CacheDatabase.CreateBuilder().WithSerializerSystemTextJson().Build();
var blobCache = new InMemoryBlobCache(database.Serializer);
// Option A: Provide a cacheResultFunc directly
var cachingHandler = new RateLimitedHttpMessageHandler(
new HttpClientHandler(),
Priority.UserInitiated,
cacheResultFunc: async (rq, resp, key, ct) =>
{
var data = await resp.Content.ReadAsByteArrayAsync(ct);
await blobCache.Insert(key, data);
});
var client = new HttpClient(cachingHandler);
var fresh = await client.GetStringAsync("https://httpbin.org/get");
// Option B: Implement IRequestCache and set NetCache.RequestCache
NetCache.RequestCache = new MyRequestCache(blobCache);
// Example IRequestCache wrapper over Akavache
class MyRequestCache : IRequestCache
{
private readonly IBlobCache _cache;
public MyRequestCache(IBlobCache cache) => _cache = cache;
public async Task Save(HttpRequestMessage request, HttpResponseMessage response, string key, CancellationToken ct)
{
var bytes = await response.Content.ReadAsByteArrayAsync(ct);
await _cache.Insert(key, bytes);
}
public Task<byte[]> Fetch(HttpRequestMessage request, string key, CancellationToken ct)
=> _cache.Get(key);
}
Offline replay
Use OfflineHttpMessageHandler to serve cached data only (no network). This handler asks IRequestCache (or your custom retrieveBodyFunc) for the cached body and returns:
- 200 OK with the cached body, or
- 503 Service Unavailable if not found
// Use NetCache.Offline after setting NetCache.RequestCache
var offline = new HttpClient(NetCache.Offline);
var data = await offline.GetStringAsync("https://httpbin.org/get");
// Or construct explicitly
var offlineExplicit = new HttpClient(new OfflineHttpMessageHandler(
async (rq, key, ct) => await blobCache.Get(key)));
Dependency injection and Splat integration
If you use Splat, you can initialize NetCache to use your container’s services via the provided extension:
using Splat.Builder;
var app = AppBuilder.CreateSplatBuilder().Build();
app.CreateFusilladeNetCache();
You can also register a platform-specific HttpMessageHandler (e.g., NSUrlSessionHandler on iOS, AndroidMessageHandler on Android) in your container beforehand; NetCache will pick it up as the inner HTTP handler.
Advanced configuration
- Custom OperationQueue: override NetCache.OperationQueue with your own queue to control concurrency for the entire app.
using Punchclock;
NetCache.OperationQueue = new OperationQueue(maxConcurrency: 6);
- Custom priorities: compose RateLimitedHttpMessageHandler with Priority.Explicit and an offset to place certain pipelines ahead or behind the defaults.
var urgent = new RateLimitedHttpMessageHandler(new HttpClientHandler(), Priority.Explicit, priority: 1_000);
var slow = new RateLimitedHttpMessageHandler(new HttpClientHandler(), Priority.Explicit, priority: -50);
- Deduplication scope: deduplication is per-HttpMessageHandler instance via an in-memory in-flight map. Multiple handlers mean multiple scopes.
Usage recipes
Image gallery / avatars
- Use RateLimitedHttpMessageHandler for GETs
- De-dup prevents duplicate downloads for the same URL
- Use Background for preloading next images; switch to UserInitiated for visible images
Boot-time warmup
- On app start/resume, set NetCache.Speculative.ResetLimit to a sensible budget
- Queue speculative GETs for likely-next screens to reduce perceived latency
Offline-first data views
- Populate cache during online sessions using cacheResultFunc or IRequestCache
- When network is unavailable, point HttpClient to NetCache.Offline
FAQ
How many requests run at once?
- Default is 4 (OperationQueue with concurrency 4). Override via NetCache.OperationQueue or pass a custom queue to a handler.
Which methods are de-duplicated?
- GET, HEAD, and OPTIONS.
How are cache keys generated?
- Via RateLimitedHttpMessageHandler.UniqueKeyForRequest(request). Treat this as an implementation detail; persist and reuse as given.
Can I cancel a request?
- Use CancellationToken in HttpClient APIs; dedup ensures the underlying request cancels only when all dependents cancel.
Contribute
Fusillade is developed under an OSI-approved open source license, making it freely usable and distributable, even for commercial use. We ❤ our contributors and welcome new contributors of all experience levels.
- Answer questions on StackOverflow: https://stackoverflow.com/questions/tagged/fusillade
- Share knowledge and mentor the next generation of developers
- Donations: https://reactiveui.net/donate and Corporate Sponsorships: https://reactiveui.net/sponsorship
- Ask your employer to support open-source: https://github.com/github/balanced-employee-ip-agreement
- Improve documentation and examples
- Contribute features and bugfixes via PRs
What’s with the name?
“Fusillade” is a synonym for Volley 🙂
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- punchclock (>= 4.0.2)
- Splat (>= 16.1.1)
-
net8.0
- punchclock (>= 4.0.2)
- Splat (>= 16.1.1)
-
net9.0
- punchclock (>= 4.0.2)
- Splat (>= 16.1.1)
NuGet packages (10)
Showing the top 5 NuGet packages that depend on fusillade:
Package | Downloads |
---|---|
ModSink.Common
This library contains most of the logic for ModSink. |
|
BD.Common.Mvvm
次元超越 Mvvm 库 |
|
XamBasePacket
Includes all needed classes models and libs to start developing Xamarin application. |
|
Xablu.WebApiClient
The Xablu WebApiClient is a C# HTTP library which aims to simplify consuming of Web API services in .NET projects. |
|
Apizr.Integrations.Fusillade
Refit based web api client management, but resilient (retry, connectivity, cache, auth, log, priority...) |
GitHub repositories (4)
Showing the top 4 popular GitHub repositories that depend on fusillade:
Repository | Stars |
---|---|
BeyondDimension/SteamTools
🛠「Watt Toolkit」是一个开源跨平台的多功能 Steam 工具箱。
|
|
reactiveui/ReactiveUI.Samples
This repository contains ReactiveUI samples.
|
|
HTBox/crisischeckin
Crisischeckin Humanitarian Toolbox repository
|
|
Respawnsive/Apizr
Refit based web api client management, but resilient (retry, connectivity, cache, auth, log, priority, etc...)
|
Version | Downloads | Last Updated |
---|---|---|
3.0.1 | 162 | 9/3/2025 |
2.6.30 | 13,649 | 5/2/2024 |
2.6.1 | 9,708 | 9/21/2023 |
2.4.67 | 22,116 | 2/1/2023 |
2.4.62 | 3,038 | 11/24/2022 |
2.4.47 | 4,385 | 6/22/2022 |
2.4.1 | 13,779 | 12/13/2021 |
2.3.1 | 33,069 | 1/22/2021 |
2.2.9 | 3,111 | 11/27/2020 |
2.2.1 | 2,592 | 10/23/2020 |
2.1.9 | 13,162 | 7/29/2020 |
2.1.1 | 27,892 | 5/10/2020 |
2.0.5 | 100,619 | 4/2/2019 |
2.0.2 | 3,897 | 2/19/2019 |
2.0.1 | 5,690 | 2/5/2019 |
1.0.0 | 52,559 | 9/5/2017 |
0.7.0 | 44,077 | 11/10/2016 |
0.6.0 | 97,430 | 5/1/2014 |
0.5.0 | 2,002 | 4/25/2014 |