< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Middleware.FaviconMiddlewareExtensions
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Middleware/FaviconMiddlewareExtensions.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
100%
Covered lines: 44
Uncovered lines: 0
Coverable lines: 44
Total lines: 93
Line coverage: 100%
Branch coverage
95%
Covered branches: 19
Total branches: 20
Branch coverage: 95%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
UseFavicon(...)95%2020100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Middleware/FaviconMiddlewareExtensions.cs

#LineLine coverage
 1// File: Middleware/FaviconMiddlewareExtensions.cs
 2using Microsoft.AspNetCore.StaticFiles;
 3using Serilog;
 4
 5namespace Kestrun.Middleware;
 6
 7/// <summary>
 8/// Provides extension methods for serving a favicon in ASP.NET Core applications.
 9/// </summary>
 10public static class FaviconMiddlewareExtensions
 11{
 12    /// <summary>
 13    /// Adds middleware to serve a favicon for the application.
 14    /// </summary>
 15    /// <param name="app">The application builder.</param>
 16    /// <param name="iconPath">Optional path to a custom favicon file. If not provided, uses the embedded favicon.</para
 17    /// <returns>The application builder.</returns>
 18    public static IApplicationBuilder UseFavicon(this IApplicationBuilder app, string? iconPath = null)
 19    {
 420        if (Log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
 21        {
 422            Log.Debug("Using favicon middleware, iconPath={IconPath}", iconPath);
 23        }
 24
 425        ArgumentNullException.ThrowIfNull(app);
 26
 27        // MIME-type detection
 428        var contentTypeProvider = new FileExtensionContentTypeProvider();
 29        string? contentType;
 30        byte[] iconBytes;
 31
 32        // Check if user provided a custom icon path
 433        if (!string.IsNullOrWhiteSpace(iconPath) && File.Exists(iconPath))
 34        {
 235            if (Log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
 36            {
 237                Log.Debug("Using user-provided favicon at {IconPath}", iconPath);
 38            }
 39            // Serve user-provided file
 240            iconBytes = File.ReadAllBytes(iconPath);
 41
 242            if (!contentTypeProvider.TryGetContentType(iconPath, out contentType) || contentType is null)
 43            {
 144                contentType = "application/octet-stream"; // fallback
 45            }
 46        }
 47        else
 48        {
 249            if (Log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
 50            {
 251                Log.Debug("Using embedded favicon, no custom path provided");
 52            }
 53            // Fallback to embedded .ico
 254            var asm = typeof(FaviconMiddlewareExtensions).Assembly;
 55            const string embedded = "Kestrun.Assets.favicon.ico";
 256            using var stream = asm.GetManifestResourceStream(embedded)
 257                ?? throw new InvalidOperationException($"Embedded favicon not found: {embedded}");
 258            using var ms = new MemoryStream();
 259            stream.CopyTo(ms);
 260            iconBytes = ms.ToArray();
 261            contentType = "image/x-icon";
 62        }
 63
 464        var headers = new HeaderDictionary
 465        {
 466            ["Content-Type"] = contentType,
 467            ["Cache-Control"] = "public,max-age=31536000"
 468        };
 469        if (Log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
 70        {
 471            Log.Debug("Favicon content type: {ContentType}, size={Size} bytes", contentType, iconBytes.Length);
 72        }
 73
 474        return app.Map("/favicon.ico", branch =>
 475        {
 476            branch.Run(async ctx =>
 477            {
 478                if (Log.IsEnabled(Serilog.Events.LogEventLevel.Debug))
 479                {
 480                    Log.Debug("Serving favicon.ico, size={Size} bytes", iconBytes.Length);
 481                }
 482
 483                ctx.Response.StatusCode = 200;
 2484                foreach (var kv in headers)
 485                {
 886                    ctx.Response.Headers[kv.Key] = kv.Value;
 487                }
 488
 489                await ctx.Response.Body.WriteAsync(iconBytes);
 890            });
 891        });
 92    }
 93}