< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Client.KrHttpDownloads
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Client/KrHttpDownloads.cs
Tag: Kestrun/Kestrun@5f1d2b981c9d7292c11fd448428c6ab6c811c5de
Line coverage
92%
Covered lines: 26
Uncovered lines: 2
Coverable lines: 28
Total lines: 66
Line coverage: 92.8%
Branch coverage
85%
Covered branches: 12
Total branches: 14
Branch coverage: 85.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 03/26/2026 - 03:54:59 Line coverage: 92.8% (26/28) Branch coverage: 85.7% (12/14) Total lines: 66 Tag: Kestrun/Kestrun@844b5179fb0492dc6b1182bae3ff65fa7365521d 03/26/2026 - 03:54:59 Line coverage: 92.8% (26/28) Branch coverage: 85.7% (12/14) Total lines: 66 Tag: Kestrun/Kestrun@844b5179fb0492dc6b1182bae3ff65fa7365521d

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
DownloadToFileAsync()85.71%141492.86%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Client/KrHttpDownloads.cs

#LineLine coverage
 1// src/CSharp/Kestrun.Net/KrHttp.Downloads.cs
 2using System.Net.Http.Headers;
 3
 4namespace Kestrun.Client;
 5
 6/// <summary>
 7/// Helper methods for common HTTP download scenarios.
 8/// </summary>
 9public static class KrHttpDownloads
 10{
 11    /// <summary>
 12    /// Streams an HTTP response body to a file, supporting very large payloads and optional resume.
 13    /// Returns the final file length in bytes.
 14    /// </summary>
 15    public static async Task<long> DownloadToFileAsync(
 16        HttpClient client,
 17        HttpRequestMessage request,
 18        string filePath,
 19        bool resume = false,
 20        int bufferBytes = 1 << 20, // 1 MiB
 21        CancellationToken cancellationToken = default)
 22    {
 223        ArgumentNullException.ThrowIfNull(client);
 224        ArgumentNullException.ThrowIfNull(request);
 225        if (string.IsNullOrWhiteSpace(filePath))
 26        {
 027            throw new ArgumentNullException(nameof(filePath));
 28        }
 29
 230        if (bufferBytes < 81920)
 31        {
 032            bufferBytes = 81920;
 33        }
 34
 235        var mode = FileMode.Create;
 236        if (resume && File.Exists(filePath))
 37        {
 38            // Resume support: if file exists, set Range header and append to file.
 139            var existing = new FileInfo(filePath).Length;
 140            if (existing > 0)
 41            {
 142                request.Headers.Range = new RangeHeaderValue(existing, null);
 143                mode = FileMode.Append;
 44            }
 45        }
 46
 247        using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken
 248                                        .ConfigureAwait(false);
 249        _ = response.EnsureSuccessStatusCode();
 50
 251        await using var source = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
 252        _ = Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(filePath))!);
 253        await using var target = new FileStream(
 254            filePath,
 255            mode,
 256            FileAccess.Write,
 257            FileShare.None,
 258            bufferBytes,
 259            FileOptions.Asynchronous | FileOptions.SequentialScan);
 60
 261        await source.CopyToAsync(target, bufferBytes, cancellationToken).ConfigureAwait(false);
 62
 263        await target.FlushAsync(cancellationToken).ConfigureAwait(false);
 264        return new FileInfo(filePath).Length;
 265    }
 66}

Methods/Properties

DownloadToFileAsync()