< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Client.KrHttpClientFactory
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Client/KrHttpClientFactory.cs
Tag: Kestrun/Kestrun@9d3a582b2d63930269564a7591aa77ef297cadeb
Line coverage
0%
Covered lines: 0
Uncovered lines: 71
Coverable lines: 71
Total lines: 159
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 24
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

File(s)

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

#LineLine coverage
 1// src/CSharp/Kestrun.Net/KrHttp.cs
 2using System.IO.Pipes;
 3using System.Net;
 4using System.Net.Security;
 5using System.Net.Sockets;
 6
 7namespace Kestrun.Client;
 8
 9/// <summary>
 10/// Factory methods to create HttpClient instances for different transport types.
 11/// </summary>
 12public static class KrHttpClientFactory
 13{
 14    // ---- Internal helper ----------------------------------------------------
 15    private static SocketsHttpHandler CreateHandler(KrHttpClientOptions opts)
 16    {
 017        var effectiveTimeout = (opts.Timeout <= TimeSpan.Zero) ? TimeSpan.FromSeconds(100) : opts.Timeout;
 18
 019        var handler = new SocketsHttpHandler
 020        {
 021            AutomaticDecompression = opts.Decompression,
 022            ConnectTimeout = effectiveTimeout,
 023
 024            // Redirects
 025            AllowAutoRedirect = opts.AllowAutoRedirect,
 026            MaxAutomaticRedirections = Math.Max(1, opts.MaxAutomaticRedirections),
 027
 028            // Cookies/session
 029            UseCookies = opts.Cookies is not null,
 030            CookieContainer = opts.Cookies ?? new CookieContainer(),
 031
 032            // Proxy
 033            UseProxy = opts.UseProxy && opts.Proxy is not null,
 034            Proxy = opts.Proxy,
 035
 036            // Server auth
 037            Credentials = opts.UseDefaultCredentials
 038                ? CredentialCache.DefaultCredentials
 039                : opts.Credentials
 040        };
 41
 42        // Proxy auth wiring if provided
 043        if (opts.Proxy is not null)
 44        {
 045            if (opts.ProxyUseDefaultCredentials)
 46            {
 047                opts.Proxy.Credentials = CredentialCache.DefaultCredentials;
 48            }
 049            else if (opts.Proxy.Credentials is null && opts.Credentials is not null)
 50            {
 51                // If caller didn't set proxy creds explicitly but provided server creds,
 52                // reuse them for the proxy (common IWR behavior).
 053                opts.Proxy.Credentials = opts.Credentials;
 54            }
 55        }
 56
 057        if (opts.IgnoreCertErrors)
 58        {
 059            handler.SslOptions = new SslClientAuthenticationOptions
 060            {
 061                RemoteCertificateValidationCallback = static (sender, certificate, chain, errors) => true
 062            };
 63        }
 64
 065        return handler;
 66    }
 67
 68    private static HttpClient MakeClient(HttpMessageHandler handler, Uri baseAddress, TimeSpan timeout)
 069        => new(handler)
 070        {
 071            Timeout = (timeout <= TimeSpan.Zero) ? TimeSpan.FromSeconds(100) : timeout,
 072            BaseAddress = baseAddress
 073        };
 74
 75    // ---- Named Pipe ---------------------------------------------------------
 76    /// <summary>Create an HttpClient that talks HTTP over a Windows Named Pipe.</summary>
 77    public static HttpClient CreateNamedPipeClient(string pipeName, TimeSpan timeout)
 078        => CreateNamedPipeClient(pipeName, timeout, ignoreCertErrors: false);
 79
 80    /// <summary>Create an HttpClient that talks HTTP over a Windows Named Pipe (legacy overload).</summary>
 81    public static HttpClient CreateNamedPipeClient(string pipeName, TimeSpan timeout, bool ignoreCertErrors)
 82    {
 083        var opts = new KrHttpClientOptions { Timeout = timeout, IgnoreCertErrors = ignoreCertErrors };
 084        return CreateNamedPipeClient(pipeName, opts);
 85    }
 86
 87    /// <summary>Create an HttpClient that talks HTTP over a Windows Named Pipe (full options).</summary>
 88    public static HttpClient CreateNamedPipeClient(string pipeName, KrHttpClientOptions opts)
 89    {
 090        if (string.IsNullOrWhiteSpace(pipeName))
 91        {
 092            throw new ArgumentNullException(nameof(pipeName));
 93        }
 94
 095        var h = CreateHandler(opts);
 96
 97        // capture pipeName in the lambda (works on .NET 6/7/8)
 098        h.ConnectCallback = (ctx, ct) =>
 099        {
 0100            var stream = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
 0101            stream.Connect();
 0102            return new ValueTask<Stream>(stream);
 0103        };
 104
 0105        return MakeClient(h, new Uri("http://localhost"), opts.Timeout);
 106    }
 107
 108    // ---- Unix Domain Socket -------------------------------------------------
 109    /// <summary>Create an HttpClient that talks HTTP over a Unix Domain Socket.</summary>
 110    public static HttpClient CreateUnixSocketClient(string socketPath, TimeSpan timeout)
 0111        => CreateUnixSocketClient(socketPath, timeout, ignoreCertErrors: false);
 112
 113    /// <summary>Create an HttpClient that talks HTTP over a Unix Domain Socket (legacy overload).</summary>
 114    public static HttpClient CreateUnixSocketClient(string socketPath, TimeSpan timeout, bool ignoreCertErrors)
 115    {
 0116        var opts = new KrHttpClientOptions { Timeout = timeout, IgnoreCertErrors = ignoreCertErrors };
 0117        return CreateUnixSocketClient(socketPath, opts);
 118    }
 119
 120    /// <summary>Create an HttpClient that talks HTTP over a Unix Domain Socket (full options).</summary>
 121    public static HttpClient CreateUnixSocketClient(string socketPath, KrHttpClientOptions opts)
 122    {
 0123        if (string.IsNullOrWhiteSpace(socketPath))
 124        {
 0125            throw new ArgumentNullException(nameof(socketPath));
 126        }
 127
 0128        var h = CreateHandler(opts);
 129
 0130        h.ConnectCallback = (ctx, ct) =>
 0131        {
 0132            var sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
 0133            sock.Connect(new UnixDomainSocketEndPoint(socketPath));
 0134            return new ValueTask<Stream>(new NetworkStream(sock, ownsSocket: true));
 0135        };
 136
 0137        return MakeClient(h, new Uri("http://localhost"), opts.Timeout);
 138    }
 139
 140    // ---- TCP (HTTP/HTTPS) ---------------------------------------------------
 141    /// <summary>Classic TCP HttpClient (normal HTTP/S).</summary>
 142    public static HttpClient CreateTcpClient(Uri baseUri, TimeSpan timeout)
 0143        => CreateTcpClient(baseUri, timeout, ignoreCertErrors: false);
 144
 145    /// <summary>Classic TCP HttpClient (normal HTTP/S, legacy overload).</summary>
 146    public static HttpClient CreateTcpClient(Uri baseUri, TimeSpan timeout, bool ignoreCertErrors)
 147    {
 0148        var opts = new KrHttpClientOptions { Timeout = timeout, IgnoreCertErrors = ignoreCertErrors };
 0149        return CreateTcpClient(baseUri, opts);
 150    }
 151
 152    /// <summary>Classic TCP HttpClient (normal HTTP/S, full options).</summary>
 153    public static HttpClient CreateTcpClient(Uri baseUri, KrHttpClientOptions opts)
 154    {
 0155        ArgumentNullException.ThrowIfNull(baseUri);
 0156        var h = CreateHandler(opts);
 0157        return MakeClient(h, baseUri, opts.Timeout);
 158    }
 159}