Advanced HttpClient extension methods with real-time progress reporting, latency tracking, and detailed transfer statistics. Built for .NET 8, 9 & 10, this library provides essential functionality for file downloads, uploads, and HTTP operations requiring comprehensive monitoring and performance insights.
$ dotnet add package Blazing.Extensions.HttpHigh-performance HttpClient extension methods with real-time asynchronous Get/Post progress reporting, latency tracking, and detailed transfer statistics. Built for .NET 8, 9 & 10, this library provides essential functionality for file downloads, uploads, and HTTP operations requiring comprehensive monitoring and performance insights.

Get started with advanced HTTP operations featuring progress reporting and latency tracking in minutes. This library extends HttpClient with powerful monitoring capabilities perfect for file transfers, API calls, and performance-critical applications.
Add the Blazing.Extensions.Http package to your project.
dotnet add package Blazing.Extensions.Http
<PackageReference Include="Blazing.Extensions.Http" Version="2.0.0" />
Configure HttpClient using IHttpClientFactory for optimal performance and proper resource management. The library works seamlessly with any HttpClient configuration pattern.
Console applications can use a simple IHttpClientFactory setup for HttpClient management.
using Microsoft.Extensions.DependencyInjection;
using Blazing.Extensions.Http;
using Blazing.Extensions.Http.Models;
// Initialize HttpClientFactory
IHttpClientFactory InitializeHttpClientFactory()
{
ServiceCollection builder = new();
builder.AddHttpClient();
ServiceProvider serviceProvider = builder.BuildServiceProvider();
return serviceProvider.GetRequiredService<IHttpClientFactory>();
}
var httpClientFactory = InitializeHttpClientFactory();
// Create progress reporter with detailed transfer information
var progress = new Progress<TransferState>(state =>
{
double progressPercent = state.CalcProgressPercentage();
var (speed, unit) = state.Chunk.ByteUnit;
Console.WriteLine($"Progress: {progressPercent:P0} - {state.Total.Transferred:N0}/{state.TotalBytes:N0} bytes | Speed: {speed:N2} {unit}/s");
});
// Download with progress tracking and latency measurement
var latencyTracker = new LatencyTracker();
using var httpClient = httpClientFactory.CreateClient();
await using var fileStream = File.Create("downloaded-file.exe");
await httpClient.GetAsync("https://example.com/largefile.zip", fileStream, progress, interval: 250, bufferSize: 512, latencyTracker);
Desktop applications can integrate progress reporting with UI controls for real-time user feedback during file operations.
// WinForms/WPF Service Registration
using Blazing.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
// Register HttpClient with factory
services.AddHttpClient("DownloadClient", client =>
{
client.Timeout = TimeSpan.FromMinutes(30);
});
// Auto-discover and register all services with AutoRegister attribute
services.Register(typeof(Program).Assembly);
var serviceProvider = services.BuildServiceProvider();
// Download Service Implementation
[AutoRegister(ServiceLifetime.Singleton)]
public class DownloadService
{
private readonly IHttpClientFactory _httpClientFactory;
public DownloadService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task DownloadFileAsync(
string url,
string destinationPath,
IProgress<TransferState> progress,
LatencyTracker latencyTracker,
CancellationToken cancellationToken = default)
{
using var client = _httpClientFactory.CreateClient("DownloadClient");
await using var fileStream = File.Create(destinationPath);
await client.GetAsync(
url,
fileStream,
progress,
interval: 100,
bufferSize: 65536, // 64KB buffer
latencyTracker,
cancellationToken);
}
}
// UI Integration Example
private async void StartDownload_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<TransferState>(UpdateProgress);
var latencyTracker = new LatencyTracker();
await _downloadService.DownloadFileAsync(
"https://example.com/file.zip",
"download.zip",
progress,
latencyTracker);
}
private void UpdateProgress(TransferState state)
{
// Update UI on the main thread
Dispatcher.Invoke(() =>
{
ProgressBar.Value = state.CalcProgressPercentage() * 100;
StatusLabel.Content = $"{state.Total.Transferred:N0}/{state.TotalBytes:N0} bytes";
var (speed, unit) = state.Chunk.ByteUnit;
SpeedLabel.Content = $"{speed:N2} {unit}/s";
if (state.Latency != null && state.Latency.PacketCount > 0)
{
LatencyLabel.Content = $"Latency: {state.Latency.PacketAvgMs:N2} ms";
}
});
}
The library provides intuitive extension methods for HttpClient that enable comprehensive monitoring of HTTP operations with minimal code changes.
Download files with real-time progress reporting, transfer statistics, and latency measurements.
using Blazing.Extensions.Http;
using Blazing.Extensions.Http.Models;
// Create detailed progress reporter for comprehensive monitoring
var progress = new Progress<TransferState>(state =>
{
double percent = state.CalcProgressPercentage();
var (speed, unit) = state.Chunk.ByteUnit;
var (bitSpeed, bitUnit) = state.Chunk.BitUnit;
var remaining = state.CalcEstimatedRemainingTime();
bool isDownloading = Math.Abs(1 - percent) > 0.001;
Console.WriteLine($"Progress: {percent:P1} | Speed: {speed:N2} {unit}/s ({bitSpeed:N2} {bitUnit}) | ETA: {remaining.TotalSeconds:N0}s");
if (!isDownloading)
{
Console.WriteLine("Download completed!");
}
});
// Optional latency tracking for network performance analysis
var latencyTracker = new LatencyTracker();
// Download with comprehensive monitoring
using var client = httpClientFactory.CreateClient();
await using var destination = File.Create("large-file.exe");
await client.GetAsync(
url: "https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
destStream: destination,
progress: progress,
interval: 250, // Report progress every 250ms
bufferSize: 512, // 512-byte buffer
latencyTracker: latencyTracker
);
// Access final statistics
if (latencyTracker.PacketCount > 0)
{
Console.WriteLine($"Average latency: {latencyTracker.PacketAvgMs:N2} ms");
Console.WriteLine($"Time to first byte: {latencyTracker.TimeToFirstByte:N0} ns");
Console.WriteLine($"Latency range: {latencyTracker.PacketMinMs:N3} - {latencyTracker.PacketMaxMs:N3} ms");
}
Upload files with comprehensive progress tracking and latency measurement.
// Upload file with detailed progress monitoring
var uploadProgress = new Progress<TransferState>(state =>
{
double percent = state.CalcProgressPercentage();
var (currentSpeed, speedUnit) = state.Chunk.ByteUnit;
var (avgSpeed, avgUnit) = state.Average.ByteUnit;
Console.WriteLine($"Upload: {percent:P1} | Current: {currentSpeed:N2} {speedUnit}/s | Average: {avgSpeed:N2} {avgUnit}/s");
});
var latencyTracker = new LatencyTracker();
using var client = httpClientFactory.CreateClient();
await client.PostAsync(
url: "https://api.example.com/upload",
filePath: @"C:\Documents\presentation.pptx",
progress: uploadProgress,
interval: 250,
bufferSize: 512, // 512-byte buffer
latencyTracker: latencyTracker
);
Add custom headers to download and upload requests for authentication, API keys, or specialized requirements.
// Download with custom headers
var headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
["X-API-Key"] = "your-api-key-here",
["User-Agent"] = "MyApp/1.0.0"
};
using var client = httpClientFactory.CreateClient();
await using var fileStream = File.Create("secured-file.pdf");
await client.GetAsync(
url: "https://secure-api.example.com/files/document.pdf",
destStream: fileStream,
progress: progress,
interval: 250,
bufferSize: 512,
latencyTracker: latencyTracker,
headers: headers
);
// Upload with custom headers
await client.PostAsync(
url: "https://api.example.com/files",
filePath: @"C:\Documents\file.pdf",
progress: uploadProgress,
interval: 250,
bufferSize: 512,
latencyTracker: latencyTracker,
headers: headers
);
Configure advanced scenarios with custom buffer sizes, reporting intervals, and performance optimizations.
// High-performance configuration for large files
DateTime lastUpdate = DateTime.Now;
var highPerfProgress = new Progress<TransferState>(state =>
{
// Update UI every 2 seconds for large transfers
if (DateTime.Now - lastUpdate > TimeSpan.FromSeconds(2))
{
UpdateProgressBar(state);
lastUpdate = DateTime.Now;
}
});
await client.GetAsync(
url: largeFileUrl,
destStream: fileStream,
progress: highPerfProgress,
interval: 2000, // Report every 2 seconds
bufferSize: 65536, // 64KB buffer for high-speed connections
latencyTracker: new LatencyTracker()
);
// Memory-efficient configuration for constrained environments
await client.GetAsync(
url: fileUrl,
destStream: fileStream,
progress: memoryEfficientProgress,
interval: 1000, // Less frequent updates
bufferSize: 512, // Smaller 512-byte buffer
latencyTracker: null // Skip latency tracking to save memory
);
// Parallel downloads for multiple files (Console Example pattern)
var downloadTasks = new List<Task>();
string[] saveFiles = ["file1.exe", "file2.exe", "file3.exe", "file4.exe"];
for (int i = 0; i < saveFiles.Length; i++)
{
int fileIndex = i; // Capture for closure
downloadTasks.Add(Task.Run(async () =>
{
var progress = new Progress<TransferState>(state =>
{
// Each download gets its own progress reporting
Console.WriteLine($"File {fileIndex + 1}: {state.CalcProgressPercentage():P1}");
});
using var client = httpClientFactory.CreateClient();
await using var fileStream = File.Create(saveFiles[fileIndex]);
await client.GetAsync(downloadUrl, fileStream, progress, 250, 512, new LatencyTracker());
}));
}
await Task.WhenAll(downloadTasks); // Wait for all downloads to finish
This library delivers enterprise-grade HTTP operations with comprehensive monitoring capabilities, built specifically for performance-critical applications requiring detailed transfer insights.
Advanced progress reporting system provides detailed transfer statistics and real-time updates for both downloads and uploads.
Key Features:
// Comprehensive progress reporting
var progress = new Progress<TransferState>(state =>
{
// Progress percentage
double percent = state.CalcProgressPercentage();
// Current transfer rates
var (chunkSpeed, chunkUnit) = state.Chunk.ByteUnit;
var (chunkBitSpeed, chunkBitUnit) = state.Chunk.BitUnit;
// Average and maximum rates
var (avgSpeed, avgUnit) = state.Average.ByteUnit;
var (maxSpeed, maxUnit) = state.Maximum.ByteUnit;
// Time calculations
TimeSpan elapsed = state.Total.Elapsed;
TimeSpan remaining = state.CalcEstimatedRemainingTime();
// Remaining bytes calculation
var (remainingBytes, remainingUnit) = state.CalcRemainingSize();
Console.WriteLine($"Progress: {percent:P2}");
Console.WriteLine($"Current: {chunkSpeed:N2} {chunkUnit}/s ({chunkBitSpeed:N2} {chunkBitUnit})");
Console.WriteLine($"Average: {avgSpeed:N2} {avgUnit}/s");
Console.WriteLine($"Maximum: {maxSpeed:N2} {maxUnit}/s");
Console.WriteLine($"Elapsed: {elapsed.TotalSeconds:N1}s | Remaining: {remaining.TotalSeconds:N1}s");
Console.WriteLine($"Left: {remainingBytes:N2} {remainingUnit}");
});
Comprehensive latency measurement system tracks network performance metrics including Time To First Byte (TTFB) and per-packet latency statistics.
Key Features:
// Enable comprehensive latency tracking
var latencyTracker = new LatencyTracker();
await client.GetAsync(url, stream, progress, interval: 100, bufferSize: 512, latencyTracker);
// Access latency statistics after transfer
if (latencyTracker.TimeToFirstByte.HasValue)
{
Console.WriteLine($"Time to First Byte: {latencyTracker.TimeToFirstByte.Value:N0} ns");
}
Console.WriteLine($"Average Packet Latency: {latencyTracker.PacketAvgMs:N3} ms");
Console.WriteLine($"Latency Range: {latencyTracker.PacketMinMs:N3} - {latencyTracker.PacketMaxMs:N3} ms");
Console.WriteLine($"Packets Measured: {latencyTracker.PacketCount}");
// Use in progress reporting for real-time latency display
var progress = new Progress<TransferState>(state =>
{
if (state.Latency != null && state.Latency.PacketCount > 0 && state.Latency.PacketMinMs >= 0)
{
Console.WriteLine($"Current Latency: {state.Latency.PacketAvgMs:N2} ms");
if (state.Latency.TimeToFirstByte.HasValue)
{
Console.WriteLine($"TTFB: {state.Latency.TimeToFirstByte.Value / 1_000_000:N0} ms");
}
}
});
Detailed transfer statistics provide comprehensive insights into HTTP operation performance with multiple measurement perspectives.
Statistical Measurements:
// Access comprehensive transfer statistics
var progress = new Progress<TransferState>(state =>
{
// Current chunk statistics (most recent transfer)
Console.WriteLine($"Current Chunk: {state.Chunk.Transferred} bytes in {state.Chunk.Elapsed.TotalMilliseconds:N2} ms");
// Total transfer statistics
Console.WriteLine($"Total: {state.Total.Transferred:N0}/{state.TotalBytes:N0} bytes in {state.Total.Elapsed.TotalSeconds:N1}s");
// Average performance over entire transfer
Console.WriteLine($"Average Rate: {state.Average.ByteUnit.Speed:N2} {state.Average.ByteUnit.Size}/s");
// Peak performance measurement
Console.WriteLine($"Peak Rate: {state.Maximum.ByteUnit.Speed:N2} {state.Maximum.ByteUnit.Size}/s");
// Performance analysis
double efficiency = state.Average.RawSpeed / state.Maximum.RawSpeed * 100;
Console.WriteLine($"Transfer Efficiency: {efficiency:N1}%");
});
Built-in performance monitoring capabilities provide insights into network conditions, transfer efficiency, and optimization opportunities.
Monitoring Features:
If you like or are using this project to learn or start your solution, please give it a star. Thanks! Also, if you find this library useful, and you're feeling really generous, then please consider buying me a coffee ☕.
The library provides comprehensive HTTP extension methods with detailed progress reporting and latency tracking capabilities for any .NET application requiring advanced HTTP monitoring.
The core extension methods provide powerful HTTP operations with built-in progress reporting and performance monitoring capabilities.
| Method | Description | Use Case |
|---|---|---|
GetAsync(url, stream, progress) | Downloads content to stream with progress reporting | Basic file downloads with progress |
GetAsync(url, stream, progress, interval, bufferSize, latencyTracker) | Advanced download with customizable settings | High-performance downloads with monitoring |
GetAsync(url, stream, progress, interval, bufferSize, latencyTracker, headers, cancellationToken) | Download with custom headers and cancellation | Authenticated downloads with full control |
PostAsync(url, filePath, progress, interval, bufferSize, latencyTracker, cancellationToken) | Uploads file using multipart/form-data with progress | File uploads with progress monitoring |
PostAsync(url, content, progress, interval, bufferSize, latencyTracker, headers, cancellationToken) | Advanced upload with custom content and headers | Complex upload scenarios with full control |
Comprehensive data models provide detailed insights into transfer operations and performance characteristics.
Central state management for transfer operations with comprehensive statistics and progress tracking.
public sealed class TransferState
{
// Timing information
public DateTimeOffset StartTime { get; }
public DateTimeOffset LastCheckTime { get; }
public DateTimeOffset StopTime { get; }
// Transfer data
public double TotalBytes { get; set; }
public Transfer Total { get; set; } // Cumulative statistics
public Transfer Chunk { get; set; } // Current chunk statistics
public AverageTransfer Average { get; set; } // Running averages
public Transfer Maximum { get; set; } // Peak performance
// Performance tracking
public LatencyTracker? Latency { get; set; }
// Calculation methods
public double CalcProgressPercentage();
public (double Bytes, ByteUnit Unit) CalcRemainingSize();
public TimeSpan CalcEstimatedRemainingTime();
}
Detailed transfer rate calculations with multiple unit representations and statistical analysis.
// Base transfer rate functionality
public abstract class TransferRateBase
{
public double RawSpeed { get; protected set; }
public (double Speed, ByteUnit Size) ByteUnit { get; protected set; }
public (double Speed, BitUnit Size) BitUnit { get; protected set; }
}
// Individual transfer measurement
public sealed class Transfer : TransferRateBase
{
public long Transferred { get; set; }
public TimeSpan Elapsed { get; set; }
public void CalcRates();
}
// Running average calculator
public sealed class AverageTransfer : TransferRateBase
{
public int Count { get; set; }
public void Update(TransferRateBase rate);
}
High-precision latency measurement and statistical analysis for network performance monitoring.
Comprehensive latency tracking with nanosecond precision and statistical analysis capabilities.
public sealed class LatencyTracker
{
// Real-time measurements
public double CurrentPacketMs { get; }
// Statistical analysis
public double PacketAvg { get; } // Average in nanoseconds
public double PacketAvgMs { get; } // Average in milliseconds
public double PacketMinMs { get; } // Minimum in milliseconds
public double PacketMaxMs { get; } // Maximum in milliseconds
public int PacketCount { get; } // Number of measurements
// Network performance
public double? TimeToFirstByte { get; } // TTFB in nanoseconds
// Measurement method
public void UpdatePacketLatency(double nanoseconds);
}
Complete API reference for all extension methods, models, and configuration options available in the library.
Extension methods that enhance HttpClient with progress reporting and latency tracking capabilities.
// Basic download with progress
Task GetAsync(
this HttpClient client,
string url,
Stream destStream,
IProgress<TransferState> progress,
int interval = 100,
int bufferSize = 512,
LatencyTracker? latencyTracker = null)
// Advanced download with headers
Task GetAsync(
this HttpClient client,
string url,
Stream destStream,
IProgress<TransferState> progress,
int interval,
int bufferSize,
LatencyTracker? latencyTracker,
IDictionary<string, string>? headers)
Parameters:
client: HttpClient instance to use for the requesturl: URL to download fromdestStream: Destination stream for downloaded contentprogress: Progress reporter for transfer state updatesinterval: Progress reporting interval in milliseconds (default: 100)bufferSize: Buffer size for stream operations (default: 512)latencyTracker: Optional latency tracker for performance monitoringheaders: Optional custom headers for the request// File upload with multipart/form-data
Task PostAsync(
this HttpClient client,
string url,
string filePath,
IProgress<TransferState> progress,
int interval = 100,
int bufferSize = 512,
LatencyTracker? latencyTracker = null,
CancellationToken cancellationToken = default)
// Advanced upload with custom content
Task PostAsync(
this HttpClient client,
string url,
HttpContent content,
IProgress<TransferState> progress,
int interval = 100,
int bufferSize = 512,
LatencyTracker? latencyTracker = null,
IDictionary<string, string>? headers = null,
CancellationToken cancellationToken = default)
Comprehensive models for tracking and reporting transfer progress and performance statistics.
// Byte units for file size and transfer rate display
public enum ByteUnit
{
B, // Bytes
KiB, // Kibibytes (1024 bytes)
MiB, // Mebibytes (1024^2 bytes)
GiB, // Gibibytes (1024^3 bytes)
TiB // Tebibytes (1024^4 bytes)
}
// Bit units for network speed display
public enum BitUnit
{
bit, // Bits
Kibit, // Kibibits
Mibit, // Mebibits
Gibit, // Gibibits
Tibit // Tebibits
}
Practical examples demonstrating common use cases and advanced scenarios for HTTP operations with comprehensive monitoring.
Complete example showing integration with WPF UI for large file downloads with comprehensive progress reporting.
public partial class DownloadManager : Window
{
private readonly IHttpClientFactory _httpClientFactory;
private CancellationTokenSource? _cancellationTokenSource;
public DownloadManager(IHttpClientFactory httpClientFactory)
{
InitializeComponent();
_httpClientFactory = httpClientFactory;
}
private async void StartDownload_Click(object sender, RoutedEventArgs e)
{
_cancellationTokenSource = new CancellationTokenSource();
var progress = new Progress<TransferState>(UpdateProgress);
var latencyTracker = new LatencyTracker();
try
{
using var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromHours(2); // Long timeout for large files
await using var fileStream = File.Create(DestinationPath.Text);
await client.GetAsync(
DownloadUrl.Text,
fileStream,
progress,
interval: 100,
bufferSize: 65536, // 64KB buffer
latencyTracker,
_cancellationTokenSource.Token);
MessageBox.Show("Download completed successfully!");
}
catch (OperationCanceledException)
{
MessageBox.Show("Download was cancelled.");
}
catch (Exception ex)
{
MessageBox.Show($"Download failed: {ex.Message}");
}
}
private void UpdateProgress(TransferState state)
{
Dispatcher.Invoke(() =>
{
double percent = state.CalcProgressPercentage();
ProgressBar.Value = percent * 100;
PercentageLabel.Content = $"{percent:P1}";
TransferredLabel.Content = $"{state.Total.Transferred:N0} / {state.TotalBytes:N0} bytes";
var (speed, unit) = state.Chunk.ByteUnit;
SpeedLabel.Content = $"{speed:N2} {unit}/s";
var remaining = state.CalcEstimatedRemainingTime();
if (remaining != TimeSpan.MinValue)
{
EtaLabel.Content = $"ETA: {remaining:hh\\:mm\\:ss}";
}
if (state.Latency != null && state.Latency.PacketCount > 0 && state.Latency.PacketMinMs >= 0)
{
LatencyLabel.Content = $"Latency: {state.Latency.PacketAvgMs:N2} ms";
}
});
}
}
Example demonstrating parallel downloads with individual progress tracking for each file (based on ConsoleExample pattern).
public class BatchDownloadManager
{
private readonly IHttpClientFactory _httpClientFactory;
public BatchDownloadManager(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task DownloadFilesAsync(IEnumerable<DownloadItem> items)
{
var downloadTasks = items.Select(async (item, index) =>
{
var progress = new Progress<TransferState>(state =>
{
Console.SetCursorPosition(0, index * 3);
var (speed, unit) = state.Chunk.ByteUnit;
var (bitSpeed, bitUnit) = state.Chunk.BitUnit;
bool isDownloading = Math.Abs(1 - state.CalcProgressPercentage()) > 0.001;
Console.WriteLine($"File {index + 1}: {state.CalcProgressPercentage():P1} - {bitSpeed:N2} {bitUnit} | {speed:N2} {unit}/s {(isDownloading ? "" : "done.")}");
});
var latencyTracker = new LatencyTracker();
using var client = _httpClientFactory.CreateClient();
await using var fileStream = File.Create(item.DestinationPath);
await client.GetAsync(
item.Url,
fileStream,
progress,
interval: 250,
bufferSize: 512,
latencyTracker);
Console.SetCursorPosition(0, index * 3 + 1);
Console.WriteLine($"Completed: {item.DestinationPath} - Avg Latency: {latencyTracker.PacketAvgMs:N2} ms");
});
await Task.WhenAll(downloadTasks);
}
}
public record DownloadItem(string Url, string DestinationPath);
Example showing integration with REST APIs that require file uploads with authentication and progress tracking.
public class ApiFileUploader
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly string _apiKey;
public ApiFileUploader(IHttpClientFactory httpClientFactory, string apiKey)
{
_httpClientFactory = httpClientFactory;
_apiKey = apiKey;
}
public async Task<string> UploadDocumentAsync(string filePath, IProgress<TransferState> progress)
{
var headers = new Dictionary<string, string>
{
["Authorization"] = $"Bearer {_apiKey}",
["X-Upload-Type"] = "document",
["X-Client-Version"] = "1.0.0"
};
var latencyTracker = new LatencyTracker();
using var client = _httpClientFactory.CreateClient();
var uploadResponse = await client.PostAsync(
"https://api.example.com/documents/upload",
filePath,
progress,
interval: 100,
bufferSize: 512,
latencyTracker,
headers: headers);
// Parse response for document ID
var responseContent = await uploadResponse.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<UploadResult>(responseContent);
Console.WriteLine($"Upload completed - Latency: {latencyTracker.PacketAvgMs:N2} ms");
if (latencyTracker.TimeToFirstByte.HasValue)
{
Console.WriteLine($"TTFB: {latencyTracker.TimeToFirstByte.Value / 1_000_000:N0} ms");
}
return result.DocumentId;
}
}
public record UploadResult(string DocumentId);
The included sample applications provide practical, real-world examples of HTTP operations with comprehensive monitoring across different application types. All samples feature identical visual designs showcasing detailed progress reporting, latency tracking, and overall performance statistics.
A comprehensive console application demonstrating all library features including downloads, uploads, progress reporting, and latency tracking with parallel processing.
Location: samples/ConsoleExample/
Run the example:
dotnet run --project samples/consoleExample --framework net8.0ss
Key Features:
Core Implementation:
// Program.cs - Main entry point with HttpClientFactory setup
using Microsoft.Extensions.DependencyInjection;
// Initialize the IHttpClientFactory for creating HttpClient instances
IHttpClientFactory InitializeHttpClientFactory()
{
ServiceCollection builder = new();
builder.AddHttpClient();
ServiceProvider serviceProvider = builder.BuildServiceProvider();
return serviceProvider.GetRequiredService<IHttpClientFactory>();
}
IHttpClientFactory httpClientFactory = InitializeHttpClientFactory();
// Example download URL and file paths
string url = "https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe";
string[] saveFiles = ["ac95c389-31ae-416f-a8cd-fdfb5969d528.cbz", "ac95c389-31ae-416f-a8cd-fdfb5969d529.cbz", "ac95c389-31ae-416f-a8cd-fdfb5969d527.cbz", "ac95c389-31ae-416f-a8cd-fdfb5969d526.cbz"];
// Run parallel downloads
await FileTransferHelper.RunDownloadAsync(httpClientFactory, new Uri(url), saveFiles, reportInterval: 250);
FileTransferHelper Implementation:
public static class FileTransferHelper
{
/// <summary>
/// Runs multiple downloads in parallel, each with its own progress bar.
/// </summary>
public static async Task RunDownloadAsync(IHttpClientFactory httpClientFactory, Uri urlPath, string[] saveFiles, int interval)
{
Console.WriteLine($"Downloading: {urlPath}");
Console.WriteLine();
List<Task> downloadTasks = [];
for (int i = 0; i < saveFiles.Length; i++)
{
// Each file gets its own progress bar at a different console row
downloadTasks.Add(FileDownloadAsync(httpClientFactory, urlPath, saveFiles[i], 0, 8 + i, interval, true));
}
await Task.WhenAll(downloadTasks); // Wait for all downloads to finish
}
/// <summary>
/// Handles a single file download with progress and latency tracking.
/// </summary>
public static async Task FileDownloadAsync(IHttpClientFactory httpClientFactory, Uri downloadUrl, string file, int left, int top, int interval = 100, bool isCompactMode = false)
{
await Task.Yield();
using HttpClient client = httpClientFactory.CreateClient();
await using FileStream fileStream = File.Create(file);
Progress progress = new(left, top);
LatencyTracker latency = new();
await client.GetAsync(downloadUrl, fileStream,
progress: new Progress<TransferState>(async state =>
{
if (isCompactMode)
await progress.CompactReport(state);
else
await progress.Report(state);
}),
interval, 512, latency);
}
}
Progress Reporting:
public sealed class Progress
{
public async Task CompactReport(TransferState state)
{
double progress = state.CalcProgressPercentage();
(double Speed, ByteUnit Size) chunkByteRate = state.Chunk.ByteUnit;
(double Speed, BitUnit Size) chunkBitRate = state.Chunk.BitUnit;
bool isDownloading = Math.Abs(1 - progress) > 0.001;
StringBuilder sb = new();
sb.Append($"Receiving: {(progress < 0D ? "" : $"{progress:P0}")} ({state.Total.Transferred:N0}/{state.TotalBytes:N0}), {chunkBitRate.Speed:N2}{chunkBitRate.Size} | {chunkByteRate.Speed:N2}{chunkByteRate.Size}/s {(isDownloading ? "" : "done.")}");
// Show latency if available
if (state.Latency is not null && state.Latency.PacketCount > 0 && state.Latency.PacketMinMs >= 0)
{
sb.Append($"| Lat: {state.Latency.PacketAvgMs:N3} ms ({state.Latency.PacketMinMs:N5} ms - {state.Latency.PacketMaxMs:N3} ms | TTFB: {state.Latency.TimeToFirstByte:N0} ms");
}
DisplayReport(sb);
}
}
A complete Windows Forms application showcasing parallel downloads with comprehensive visual progress tracking and performance statistics using dependency injection.
Location: samples/WinFormsExample/
Run the example:
dotnet run --project samples/WinFormsExample --framework net8.0-windows
Key Features:
Blazing.Extensions.DependencyInjection for automatic service registrationService Registration (Program.cs):
using Blazing.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
internal static class Program
{
public static IServiceProvider ServiceProvider { get; private set; } = null!;
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
// Configure services
var services = new ServiceCollection();
// Register HttpClient with factory
services.AddHttpClient("DownloadClient", client =>
{
client.Timeout = TimeSpan.FromMinutes(30);
});
// Auto-discover and register all services with AutoRegister attribute
services.Register(typeof(Program).Assembly);
// Build service provider
ServiceProvider = services.BuildServiceProvider();
// Resolve and run main form
var mainForm = ServiceProvider.GetRequiredService<MainForm>();
Application.Run(mainForm);
}
}
DownloadService Implementation:
[AutoRegister(ServiceLifetime.Singleton)]
public class DownloadService
{
private readonly IHttpClientFactory _httpClientFactory;
public DownloadService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task DownloadFileAsync(
string url,
string destinationPath,
IProgress<TransferState> progress,
LatencyTracker latencyTracker,
CancellationToken cancellationToken = default)
{
using var client = _httpClientFactory.CreateClient("DownloadClient");
await using var fileStream = File.Create(destinationPath);
await client.GetAsync(
url,
fileStream,
progress,
interval: 100,
bufferSize: 65536, // 64KB buffer
latencyTracker,
cancellationToken);
}
}
MainForm Implementation:
[AutoRegister(ServiceLifetime.Transient)]
public class MainForm : Form
{
private readonly DownloadService _downloadService;
private readonly List<(DownloadProgressControl Control, CancellationTokenSource Cts)> _downloadControls = new();
// Download URLs for testing
private readonly string[] _downloadUrls =
[
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe"
];
public MainForm(DownloadService downloadService)
{
_downloadService = downloadService;
InitializeComponent();
}
}
Visual Features:
A complete WPF application using MVVM pattern with CommunityToolkit.Mvvm and Blazing.Extensions.DependencyInjection for a clean, maintainable architecture featuring parallel downloads with comprehensive visual tracking.
Location: samples/WpfExample/
Run the example:
dotnet run --project samples/WpfExample --framework net8.0-windows
Key Features:
CommunityToolkit.MvvmBlazing.Extensions.DependencyInjection for automatic service registrationObservableObject and RelayCommandApp.xaml.cs - Dependency Injection Setup:
using System.Windows;
using Blazing.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using WpfExample.Views;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Configure services using Blazing.Extensions.DependencyInjection
this.ConfigureServices(services =>
{
// Register HttpClient with factory
services.AddHttpClient("DownloadClient", client =>
{
client.Timeout = TimeSpan.FromMinutes(30);
});
// Auto-discover and register all services, ViewModels, and Views with AutoRegister attribute
services.Register(typeof(App).Assembly);
});
// Resolve and show main window
var serviceProvider = this.GetServices();
var mainWindow = serviceProvider!.GetRequiredService<MainWindow>();
mainWindow.Show();
}
}
DownloadService Implementation:
[AutoRegister(ServiceLifetime.Singleton)]
public class DownloadService
{
private readonly IHttpClientFactory _httpClientFactory;
public DownloadService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task DownloadFileAsync(
string url,
string destinationPath,
IProgress<TransferState> progress,
LatencyTracker latencyTracker,
CancellationToken cancellationToken = default)
{
using var client = _httpClientFactory.CreateClient("DownloadClient");
await using var fileStream = File.Create(destinationPath);
await client.GetAsync(
url,
fileStream,
progress,
interval: 100,
bufferSize: 65536, // 64KB buffer
latencyTracker,
cancellationToken);
}
}
MainViewModel - MVVM with CommunityToolkit:
[AutoRegister(ServiceLifetime.Transient)]
public partial class MainViewModel : ObservableObject
{
private readonly DownloadService _downloadService;
private CancellationTokenSource? _globalCancellationTokenSource;
[ObservableProperty]
private bool _isDownloading;
[ObservableProperty]
private ObservableCollection<DownloadItemViewModel> _downloads = new();
// Statistics properties with ObservableProperty
[ObservableProperty] private string _totalDownloadsText = "0";
[ObservableProperty] private string _activeDownloadsText = "0";
[ObservableProperty] private string _completedDownloadsText = "0";
[ObservableProperty] private string _failedDownloadsText = "0";
[ObservableProperty] private string _totalBytesText = "0 B";
[ObservableProperty] private string _overallSpeedText = "0.00 B/s";
[ObservableProperty] private string _averageLatencyText = "0.00 ms";
[ObservableProperty] private string _totalElapsedText = "0.0s";
// Download URLs for testing
private readonly string[] _downloadUrls = new[]
{
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe",
"https://download.visualstudio.microsoft.com/download/pr/89a2923a-18df-4dce-b069-51e687b04a53/9db4348b561703e622de7f03b1f11e93/dotnet-sdk-7.0.203-win-x64.exe"
};
public MainViewModel(DownloadService downloadService)
{
_downloadService = downloadService;
}
[RelayCommand]
private async Task StartDownloadsAsync()
{
IsDownloading = true;
Downloads.Clear();
ResetStatistics();
// Initialize statistics
_totalDownloads = _downloadUrls.Length;
_activeDownloads = _downloadUrls.Length;
_startTime = DateTime.Now;
UpdateStatisticsDisplay();
// Create download items and start downloads
var downloadTasks = new List<Task>();
for (int i = 0; i < _downloadUrls.Length; i++)
{
var downloadItem = new DownloadItemViewModel
{
Url = _downloadUrls[i],
FileName = $"dotnet-sdk-{i + 1}.exe"
};
Downloads.Add(downloadItem);
downloadTasks.Add(StartSingleDownloadAsync(downloadItem, i));
}
await Task.WhenAll(downloadTasks);
IsDownloading = false;
}
[RelayCommand]
private void StopDownloads()
{
_globalCancellationTokenSource?.Cancel();
IsDownloading = false;
}
}
Visual Design:
Dependency Injection Integration:
Both WinForms and WPF examples demonstrate proper DI patterns:
ConfigureServices extensionCommon Features Across All Samples:
Follow these recommended patterns for optimal performance, reliability, and maintainability when using the library.
Implement comprehensive error handling for network operations, file I/O, and progress reporting scenarios.
public async Task<bool> SafeDownloadAsync(string url, string filePath)
{
var progress = new Progress<TransferState>(state =>
{
// Safe progress reporting with exception handling
try
{
UpdateUI(state);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating progress UI");
}
});
try
{
using var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromMinutes(30);
await using var fileStream = File.Create(filePath);
await client.GetAsync(url, fileStream, progress,
interval: 1000,
bufferSize: 32768);
return true;
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "HTTP error during download: {StatusCode}", ex.Data["StatusCode"]);
return false;
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
_logger.LogError("Download timed out after {Timeout}", client.Timeout);
return false;
}
catch (IOException ex)
{
_logger.LogError(ex, "File I/O error during download");
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unexpected error during download");
return false;
}
finally
{
// Clean up partial files on failure
if (File.Exists(filePath) && new FileInfo(filePath).Length == 0)
{
File.Delete(filePath);
}
}
}
Optimize memory usage for large file operations and long-running applications.
// Optimal configuration for different scenarios
public static class TransferConfig
{
// Large files over fast connections
public static readonly TransferSettings HighPerformance = new()
{
BufferSize = 1048576, // 1MB buffer
ReportInterval = 2000, // Report every 2 seconds
EnableLatencyTracking = true
};
// Constrained memory environments
public static readonly TransferSettings MemoryEfficient = new()
{
BufferSize = 4096, // 4KB buffer
ReportInterval = 5000, // Less frequent reporting
EnableLatencyTracking = false
};
// Mobile/battery-conscious settings
public static readonly TransferSettings BatteryOptimized = new()
{
BufferSize = 8192, // 8KB buffer
ReportInterval = 10000, // Minimal UI updates
EnableLatencyTracking = false
};
}
// Proper resource disposal
public async Task TransferWithProperCleanupAsync()
{
LatencyTracker? latencyTracker = null;
HttpClient? client = null;
Stream? fileStream = null;
try
{
latencyTracker = new LatencyTracker();
client = _httpClientFactory.CreateClient();
fileStream = File.Create("download.tmp");
await client.GetAsync(url, fileStream, progress,
latencyTracker: latencyTracker);
}
finally
{
// Explicit cleanup in reverse order
fileStream?.Dispose();
client?.Dispose();
latencyTracker = null;
}
}
Ensure thread-safe operations when using progress reporting in multi-threaded scenarios.
public class ThreadSafeProgressReporter
{
private readonly object _lockObject = new();
private readonly IProgress<TransferState> _progress;
private DateTime _lastUpdate = DateTime.MinValue;
private readonly TimeSpan _minimumInterval = TimeSpan.FromMilliseconds(100);
public ThreadSafeProgressReporter(IProgress<TransferState> progress)
{
_progress = progress;
}
public void ReportProgress(TransferState state)
{
lock (_lockObject)
{
var now = DateTime.Now;
if (now - _lastUpdate >= _minimumInterval)
{
_progress.Report(state);
_lastUpdate = now;
}
}
}
}
// Usage with multiple concurrent transfers
public async Task ConcurrentDownloadsAsync()
{
var progressReporter = new ThreadSafeProgressReporter(
new Progress<TransferState>(UpdateGlobalProgress));
var tasks = urls.Select(async url =>
{
var localProgress = new Progress<TransferState>(state =>
{
progressReporter.ReportProgress(state);
});
using var client = _httpClientFactory.CreateClient();
await using var stream = File.Create($"download-{Guid.NewGuid()}.tmp");
await client.GetAsync(url, stream, localProgress);
});
await Task.WhenAll(tasks);
}
The solution is organized with clear separation between core functionality and sample applications, demonstrating usage across Console, WinForms, and WPF application types.
Blazing.Extensions.Http/ # Solution root
├── src/
│ └── Blazing.Extensions.Http/ # Main HTTP extensions library
│ ├── Models/ # Transfer and latency models
│ │ ├── TransferState.cs # Main transfer state tracking
│ │ ├── LatencyTracker.cs # Network latency measurement
│ │ ├── TransferRate.cs # Transfer rate calculations
│ │ ├── AverageTransferRate.cs # Running average calculations
│ │ ├── TransferRateBase.cs # Base transfer rate functionality
│ │ ├── ByteUnit.cs # Byte unit enumeration
│ │ └── BitUnit.cs # Bit unit enumeration
│ └── HttpClientExtension.cs # Core extension methods
├── samples/
│ ├── ConsoleExample/ # Console application demo
│ │ ├── Program.cs # Main application entry
│ │ ├── FileTransferHelper.cs # Transfer operation helpers
│ │ ├── Progress.cs # Progress reporting implementation
│ │ └── InternalLock.cs # Thread synchronization helper
│ ├── WinFormsExample/ # Windows Forms application demo
│ │ ├── Program.cs # Application entry with DI setup
│ │ ├── MainForm.cs # Main form with download UI
│ │ ├── DownloadService.cs # HTTP download service
│ │ ├── DownloadProgressControl.cs # Individual download progress control
│ │ └── StatisticsPanel.cs # Overall statistics display
│ └── WpfExample/ # WPF application demo (MVVM)
│ ├── App.xaml # Application definition
│ ├── App.xaml.cs # Application startup with DI
│ ├── Services/
│ │ └── DownloadService.cs # HTTP download service
│ ├── ViewModels/
│ │ ├── MainViewModel.cs # Main window view model
│ │ └── DownloadItemViewModel.cs # Per-download view model
│ ├── Views/
│ │ ├── MainWindow.xaml # Main window XAML
│ │ └── MainWindow.xaml.cs # Main window code-behind
│ └── Converters/
│ └── InverseBoolConverter.cs # Boolean inversion converter
├── LICENSE # MIT License
└── README.md # This file
The solution supports .NET 8.0, .NET 9.0, & .NET 10.0 and provides straightforward build and execution commands for all projects and samples.
# Build the entire solution
dotnet build
# Build only the library
dotnet build src/Blazing.Extensions.Http
# Create NuGet package
dotnet pack src/Blazing.Extensions.Http -c Release
# Run sample applications
dotnet run --project samples/ConsoleExample --framework net8.0 # Console example
dotnet run --project samples/WinFormsExample --framework net8.0-windows # WinForms example
dotnet run --project samples/WpfExample --framework net8.0-windows # WPF example
We welcome contributions! Please see our Contributing Guide for details.
dotnet restoredotnet build# Console example with interactive menu
dotnet run --project samples/ConsoleExample --framework net8.0
# WinForms example with visual download manager
dotnet run --project samples/WinFormsExample --framework net8.0-windows
# WPF example with MVVM architecture
dotnet run --project samples/WpfExample --framework net8.0-windows
This project is licensed under the MIT License - see the LICENSE file for details.