< Summary - Kestrun — Combined Coverage

Information
Class: DelegateBuilder
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/DelegateBuilder.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
97%
Covered lines: 112
Uncovered lines: 3
Coverable lines: 115
Total lines: 233
Line coverage: 97.3%
Branch coverage
97%
Covered branches: 39
Total branches: 40
Branch coverage: 97.5%
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
PrepareExecutionAsync()92.85%141494.44%
ApplyResponseAsync()100%66100%
BuildBaselineReferences()100%1010100%
Add()100%22100%
NamespaceExistsInAssembly(...)100%9880%
.cctor()100%11100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Languages/DelegateBuilder.cs

#LineLine coverage
 1using System.Security.Claims;
 2using System.Text;
 3using Kestrun.Hosting;
 4using Kestrun.Languages;
 5using Kestrun.Logging;
 6using Kestrun.Models;
 7using Kestrun.SharedState;
 8using Microsoft.CodeAnalysis;
 9using Serilog.Events;
 10using System.Reflection;
 11
 12internal static class DelegateBuilder
 13{
 14    /// <summary>
 15    /// Prepares the Kestrun context, response, and script globals for execution.
 16    /// Encapsulates request parsing, shared state snapshot, arg injection, and logging.
 17    /// </summary>
 18    /// <param name="ctx">The current HTTP context.</param>
 19    /// <param name="log">Logger for diagnostics.</param>
 20    /// <param name="args">Optional variables to inject into the globals.</param>
 21    /// <returns>Tuple containing the prepared CsGlobals, KestrunResponse, and KestrunContext.</returns>
 22    internal static async Task<(CsGlobals Globals, KestrunResponse Response, KestrunContext Context)> PrepareExecutionAs
 23        HttpContext ctx,
 24        Serilog.ILogger log,
 25        Dictionary<string, object?>? args)
 26    {
 527        var krRequest = await KestrunRequest.NewRequest(ctx).ConfigureAwait(false);
 528        var krResponse = new KestrunResponse(krRequest);
 529        var Context = new KestrunContext(krRequest, krResponse, ctx);
 530        if (log.IsEnabled(LogEventLevel.Debug))
 31        {
 332            log.DebugSanitized("Kestrun context created for {Path}", ctx.Request.Path);
 33        }
 34
 35        // Create a shared state dictionary that will be used to store global variables
 36        // This will be shared across all requests and can be used to store state
 37        // that needs to persist across multiple requests
 538        if (log.IsEnabled(LogEventLevel.Debug))
 39        {
 340            log.Debug("Creating shared state store for Kestrun context");
 41        }
 42
 543        var glob = new Dictionary<string, object?>(SharedStateStore.Snapshot());
 544        if (log.IsEnabled(LogEventLevel.Debug))
 45        {
 346            log.Debug("Shared state store created with {Count} items", glob.Count);
 47        }
 48
 49        // Inject the provided arguments into the globals so the script can access them
 550        if (args != null && args.Count > 0)
 51        {
 152            if (log.IsEnabled(LogEventLevel.Debug))
 53            {
 054                log.Debug("Setting variables from arguments: {Count}", args.Count);
 55            }
 56
 457            foreach (var kv in args)
 58            {
 159                glob[kv.Key] = kv.Value; // add args to globals
 60            }
 61        }
 62
 63        // Create a new CsGlobals instance with the current context and shared state
 564        var globals = new CsGlobals(glob, Context);
 565        return (globals, krResponse, Context);
 566    }
 67
 68
 69    /// <summary>
 70    /// Decides the VB return type string that matches TResult.
 71    /// </summary>
 72    /// <param name="ctx">The current HTTP context.</param>
 73    /// <param name="response">The Kestrun response to apply.</param>
 74    /// <param name="log">The logger to use for logging.</param>
 75    /// <returns>A task representing the asynchronous operation.</returns>
 76    internal static async Task ApplyResponseAsync(HttpContext ctx, KestrunResponse response, Serilog.ILogger log)
 77    {
 578        if (log.IsEnabled(LogEventLevel.Debug))
 79        {
 380            log.DebugSanitized("Applying response to Kestrun context for {Path}", ctx.Request.Path);
 81        }
 82
 583        if (!string.IsNullOrEmpty(response.RedirectUrl))
 84        {
 285            ctx.Response.Redirect(response.RedirectUrl);
 286            return;
 87        }
 88
 389        await response.ApplyTo(ctx.Response).ConfigureAwait(false);
 90
 391        if (log.IsEnabled(LogEventLevel.Debug))
 92        {
 293            log.DebugSanitized("Response applied to Kestrun context for {Path}", ctx.Request.Path);
 94        }
 595    }
 96
 97    /// <summary>
 98    /// Common baseline references shared across script compilations.
 99    /// </summary>
 100    /// <returns> MetadataReference[] </returns>
 101    internal static MetadataReference[] BuildBaselineReferences()
 102    {
 103        // Collect unique assembly locations
 33104        var locations = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 105
 106        void Add(Type t)
 107        {
 495108            var loc = t.Assembly.Location;
 495109            if (!string.IsNullOrWhiteSpace(loc))
 110            {
 495111                _ = locations.Add(loc); // capture return to satisfy analyzer
 112            }
 495113        }
 114
 115        // Seed core & always-required assemblies
 33116        Add(typeof(object));                                                              // System.Private.CoreLib
 33117        Add(typeof(Enumerable));                                                          // System.Linq
 33118        Add(typeof(HttpContext));                                                         // Microsoft.AspNetCore.Http
 33119        Add(typeof(Console));                                                             // System.Console
 33120        Add(typeof(StringBuilder));                                                       // System.Text
 33121        Add(typeof(Serilog.Log));                                                         // Serilog
 33122        Add(typeof(ClaimsPrincipal));                                                     // System.Security.Claims
 33123        Add(typeof(ClaimsIdentity));                                                      // System.Security.Claims
 33124        Add(typeof(System.Security.Cryptography.X509Certificates.X509Certificate2));      // X509 Certificates
 125
 126        // Snapshot loaded assemblies once
 33127        var loadedAssemblies = AppDomain.CurrentDomain
 33128            .GetAssemblies()
 8668129            .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location))
 33130            .ToArray();
 131
 132        // Attempt to bind each namespace in PlatformImports to a loaded assembly
 2904133        foreach (var ns in PlatformImports)
 134        {
 672090135            foreach (var asm in loadedAssemblies)
 136            {
 334626137                if (locations.Contains(asm.Location))
 138                {
 139                    continue; // already referenced
 140                }
 196695141                if (NamespaceExistsInAssembly(asm, ns))
 142                {
 5356143                    _ = locations.Add(asm.Location); // capture return to satisfy analyzer
 144                }
 145            }
 146        }
 147
 148        // Explicitly ensure critical assemblies needed for dynamic auth / razor / encodings even if not yet scanned
 33149        Add(typeof(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults));
 33150        Add(typeof(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults));
 33151        Add(typeof(Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults));
 33152        Add(typeof(Microsoft.AspNetCore.Authentication.Negotiate.NegotiateDefaults));
 33153        Add(typeof(Microsoft.AspNetCore.Mvc.Razor.RazorPageBase));
 33154        Add(typeof(System.Text.Encodings.Web.HtmlEncoder));
 155
 33156        return [.. locations
 33157            .Distinct(StringComparer.OrdinalIgnoreCase)
 5620158            .Select(loc => MetadataReference.CreateFromFile(loc))];
 159    }
 160
 161    private static bool NamespaceExistsInAssembly(Assembly asm, string @namespace)
 162    {
 163        try
 164        {
 165            // Using DefinedTypes to avoid loading reflection-only contexts; guard against type load issues.
 45051186166            foreach (var t in asm.DefinedTypes)
 167            {
 22331576168                var ns = t.Namespace;
 22331576169                if (ns == null)
 170                {
 171                    continue;
 172                }
 21423343173                if (ns.Equals(@namespace, StringComparison.Ordinal) || ns.StartsWith(@namespace + ".", StringComparison.
 174                {
 5356175                    return true;
 176                }
 177            }
 191339178        }
 0179        catch (ReflectionTypeLoadException)
 180        {
 181            // Ignore partially loadable assemblies; namespace absence treated as false.
 0182        }
 191339183        return false;
 5356184    }
 185
 186    // Ordered & de-duplicated platform / framework imports used for dynamic script compilation.
 1187    internal static string[] PlatformImports = [
 1188        "Kestrun.Languages",
 1189        "Microsoft.AspNetCore.Authentication",
 1190        "Microsoft.AspNetCore.Authentication.Cookies",
 1191        "Microsoft.AspNetCore.Authentication.JwtBearer",
 1192        "Microsoft.AspNetCore.Authentication.Negotiate",
 1193        "Microsoft.AspNetCore.Authentication.OpenIdConnect",
 1194        "Microsoft.AspNetCore.Authorization",
 1195        "Microsoft.AspNetCore.Http",
 1196        "Microsoft.AspNetCore.Mvc",
 1197        "Microsoft.AspNetCore.Mvc.RazorPages",
 1198        // "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation".
 1199        "Microsoft.AspNetCore.Server.Kestrel.Core",
 1200        "Microsoft.AspNetCore.SignalR",
 1201        "Microsoft.CodeAnalysis",
 1202        "Microsoft.CodeAnalysis.CSharp",
 1203        "Microsoft.CodeAnalysis.CSharp.Scripting",
 1204        "Microsoft.CodeAnalysis.Scripting",
 1205        "Microsoft.Extensions.FileProviders",
 1206        "Microsoft.Extensions.Logging",
 1207        "Microsoft.Extensions.Options",
 1208        "Microsoft.Extensions.Primitives",
 1209        "Microsoft.IdentityModel.Tokens",
 1210        "Microsoft.PowerShell",
 1211        "Microsoft.VisualBasic",
 1212        "Serilog",
 1213        "Serilog.Events",
 1214        "System",
 1215        "System.Collections",
 1216        "System.Collections.Generic",
 1217        "System.Collections.Immutable",
 1218        "System.IO",
 1219        "System.Linq",
 1220        "System.Management.Automation",
 1221        "System.Management.Automation.Runspaces",
 1222        "System.Net",
 1223        "System.Net.Http.Headers",
 1224        "System.Reflection",
 1225        "System.Runtime.InteropServices",
 1226        "System.Security.Claims",
 1227        "System.Security.Cryptography.X509Certificates",
 1228        "System.Text",
 1229        "System.Text.Encodings.Web",
 1230        "System.Text.RegularExpressions",
 1231        "System.Threading.Tasks"
 1232    ];
 233}