diff --git a/src/Fluxzy.Core/Clients/Dns/DnsOverHttpsResolver.cs b/src/Fluxzy.Core/Clients/Dns/DnsOverHttpsResolver.cs index 4f8ac60a..207150e9 100644 --- a/src/Fluxzy.Core/Clients/Dns/DnsOverHttpsResolver.cs +++ b/src/Fluxzy.Core/Clients/Dns/DnsOverHttpsResolver.cs @@ -62,7 +62,7 @@ public DnsOverHttpsResolver(string nameOrUrl, ProxyConfiguration? proxyConfigura { var key = new DnsCacheKey(type, hostName); - using var _ = await Synchronizer.Instance.LockAsync(key).ConfigureAwait(false); + using var _ = await Synchronizer.Shared.LockAsync(key).ConfigureAwait(false); if (_cache.TryGetValue(key, out var cached)) { diff --git a/src/Fluxzy.Core/Clients/DnsUtility.cs b/src/Fluxzy.Core/Clients/DnsUtility.cs index b73ad8d4..4f91aad8 100644 --- a/src/Fluxzy.Core/Clients/DnsUtility.cs +++ b/src/Fluxzy.Core/Clients/DnsUtility.cs @@ -24,18 +24,17 @@ public DnsResolutionResult(IPEndPoint endPoint, DateTime dnsSolveStart, DateTime public DateTime DnsSolveEnd { get; } } - + internal static class DnsUtility { - public static async ValueTask<(DnsResolutionResult, MockedConnectionPool ?)> + public static async ValueTask ComputeDnsUpdateExchange(Exchange exchange, ITimingProvider timingProvider, IDnsSolver dnsSolver, ProxyRuntimeSetting? runtimeSetting) { var dnsSolveStart = timingProvider.Instant(); var connectHostName = exchange.Context.ProxyConfiguration?.Host ?? exchange.Authority.HostName; - - + var ipAddress = exchange.Context.RemoteHostIp ?? await dnsSolver.SolveDns(connectHostName).ConfigureAwait(false); @@ -49,18 +48,7 @@ internal static class DnsUtility var remoteEndPoint = new IPEndPoint(ipAddress, remotePort); - if (runtimeSetting != null) { - - await runtimeSetting.EnforceRules(exchange.Context, - FilterScope.DnsSolveDone, - exchange.Connection, exchange).ConfigureAwait(false); - - if (exchange.Context.PreMadeResponse != null) - return (new(remoteEndPoint, dnsSolveStart, dnsSolveEnd), new MockedConnectionPool( - exchange.Authority, exchange.Context.PreMadeResponse)); - } - - return (new(remoteEndPoint, dnsSolveStart, dnsSolveEnd), null); + return new(remoteEndPoint, dnsSolveStart, dnsSolveEnd); } public static async ValueTask diff --git a/src/Fluxzy.Core/Clients/IHttpConnectionPool.cs b/src/Fluxzy.Core/Clients/IHttpConnectionPool.cs index 59e4a5d2..5833147a 100644 --- a/src/Fluxzy.Core/Clients/IHttpConnectionPool.cs +++ b/src/Fluxzy.Core/Clients/IHttpConnectionPool.cs @@ -1,28 +1,28 @@ -// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak - -using System; -using System.Threading; -using System.Threading.Tasks; +// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak + +using System; +using System.Threading; +using System.Threading.Tasks; using Fluxzy.Core; -using Fluxzy.Misc.ResizableBuffers; - -namespace Fluxzy.Clients -{ - /// - /// Represents a connection pool to the same authority, using the same . - /// - public interface IHttpConnectionPool : IAsyncDisposable - { - Authority Authority { get; } - - bool Complete { get; } - - void Init(); - - ValueTask CheckAlive(); - - ValueTask Send( - Exchange exchange, ILocalLink localLink, RsBuffer buffer, - CancellationToken cancellationToken = default); - } -} +using Fluxzy.Misc.ResizableBuffers; + +namespace Fluxzy.Clients +{ + /// + /// Represents a connection pool to the same authority, using the same . + /// + public interface IHttpConnectionPool : IAsyncDisposable + { + Authority Authority { get; } + + bool Complete { get; } + + void Init(); + + ValueTask CheckAlive(); + + ValueTask Send( + Exchange exchange, ILocalLink localLink, RsBuffer buffer, + CancellationToken cancellationToken = default); + } +} diff --git a/src/Fluxzy.Core/Clients/IRemoteConnectionBuilder.cs b/src/Fluxzy.Core/Clients/IRemoteConnectionBuilder.cs index 35f326be..5fb69aca 100644 --- a/src/Fluxzy.Core/Clients/IRemoteConnectionBuilder.cs +++ b/src/Fluxzy.Core/Clients/IRemoteConnectionBuilder.cs @@ -3,15 +3,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Net.Security; using System.Threading; using System.Threading.Tasks; -using Fluxzy.Clients.Headers; using Fluxzy.Clients.Ssl; using Fluxzy.Core; using Fluxzy.Misc.Streams; -using Fluxzy.Rules; namespace Fluxzy.Clients { @@ -69,7 +66,6 @@ public async ValueTask OpenConnectionToRemote( setting.ArchiveWriter != null! ? setting.ArchiveWriter.GetDumpfilePath(exchange.Connection.Id)! : string.Empty); - var localEndpoint = await tcpConnection.ConnectAsync( resolutionResult.EndPoint.Address, @@ -145,7 +141,6 @@ await _sslConnectionBuilder.AuthenticateAsClient( : RemoteConnectionResultType.Http11; exchange.Connection.ReadStream = exchange.Connection.WriteStream = resultStream; - return new RemoteConnectionResult(protoType, exchange.Connection); } diff --git a/src/Fluxzy.Core/Clients/PoolBuilder.cs b/src/Fluxzy.Core/Clients/PoolBuilder.cs index 726753d8..8ba1a23a 100644 --- a/src/Fluxzy.Core/Clients/PoolBuilder.cs +++ b/src/Fluxzy.Core/Clients/PoolBuilder.cs @@ -13,6 +13,8 @@ using Fluxzy.Clients.Mock; using Fluxzy.Core; using Fluxzy.Misc; +using Fluxzy.Rules; +using Fluxzy.Utils; using Fluxzy.Writers; namespace Fluxzy.Clients @@ -39,8 +41,6 @@ static PoolBuilder() private readonly IDictionary _connectionPools = new Dictionary(); - - private readonly ConcurrentDictionary _lock = new(); private readonly CancellationTokenSource _poolCheckHaltSource = new(); private readonly RemoteConnectionBuilder _remoteConnectionBuilder; @@ -48,6 +48,8 @@ static PoolBuilder() private readonly ConcurrentDictionary _dnsSolversCache = new(); + private Synchronizer _synchronizer = new(true); + public PoolBuilder( RemoteConnectionBuilder remoteConnectionBuilder, ITimingProvider timingProvider, @@ -102,31 +104,29 @@ public async ValueTask ProxyRuntimeSetting proxyRuntimeSetting, CancellationToken cancellationToken = default) { - // At this point, we'll trying the suitable pool for exchange - - if (exchange.Context.PreMadeResponse != null) { - return new MockedConnectionPool(exchange.Authority, - exchange.Context.PreMadeResponse); - } - - var dnsSolver = string.IsNullOrWhiteSpace(exchange.Context.DnsOverHttpsNameOrUrl) ? - _dnsSolver : _dnsSolversCache.GetOrAdd(exchange.Context.DnsOverHttpsNameOrUrl, - n => new DnsOverHttpsResolver(n, exchange.Context.DnsOverHttpsCapture ? - proxyRuntimeSetting.GetInternalProxyAuthentication() : null)); + var dnsSolver = ResolveDnsProvider(exchange, proxyRuntimeSetting); - // We should solve DNS here var computeDnsPromise = DnsUtility.ComputeDnsUpdateExchange(exchange, _timingProvider, dnsSolver, proxyRuntimeSetting); - IHttpConnectionPool? result = null; + var dnsResolutionResult = await computeDnsPromise.ConfigureAwait(false); - var semaphorePerAuthority = _lock.GetOrAdd(exchange.Authority, auth => new SemaphoreSlim(1)); - var released = false; + await proxyRuntimeSetting.EnforceRules(exchange.Context, + FilterScope.DnsSolveDone, + exchange.Connection, exchange).ConfigureAwait(false); + + if (exchange.Context.PreMadeResponse != null) { + var mockedConnectionPool = + new MockedConnectionPool(exchange.Authority, exchange.Context.PreMadeResponse); + mockedConnectionPool.Init(); + return mockedConnectionPool; + } + + IHttpConnectionPool? result = null; try { - if (!semaphorePerAuthority.Wait(0)) - await semaphorePerAuthority.WaitAsync(cancellationToken).ConfigureAwait(false); + using var _ = await _synchronizer.LockAsync(exchange.Authority); var forceNewConnection = exchange.Context.ForceNewConnection; @@ -136,20 +136,21 @@ public async ValueTask // Looking for existing HttpPool if (!forceNewConnection) { - lock (_connectionPools) { - while (_connectionPools.TryGetValue(exchange.Authority, out var pool)) { + lock (_connectionPools) + { + if (_connectionPools.TryGetValue(exchange.Authority, out var pool)) + { if (pool.Complete) { _connectionPools.Remove(pool.Authority); - - continue; } + else { + if (exchange.Metrics.RetrievingPool == default) + exchange.Metrics.RetrievingPool = ITimingProvider.Default.Instant(); - if (exchange.Metrics.RetrievingPool == default) - exchange.Metrics.RetrievingPool = ITimingProvider.Default.Instant(); + exchange.Metrics.ReusingConnection = true; - exchange.Metrics.ReusingConnection = true; - - return pool; + return pool; + } } } } @@ -157,19 +158,12 @@ public async ValueTask if (exchange.Metrics.RetrievingPool == default) exchange.Metrics.RetrievingPool = ITimingProvider.Default.Instant(); - var dnsResolutionResult = await computeDnsPromise.ConfigureAwait(false); - - if (dnsResolutionResult.Item2 != null) - { - dnsResolutionResult.Item2.Init(); - return dnsResolutionResult.Item2; - } // pool if (exchange.Context.BlindMode) { var tunneledConnectionPool = new TunnelOnlyConnectionPool( exchange.Authority, _timingProvider, - _remoteConnectionBuilder, proxyRuntimeSetting, dnsResolutionResult.Item1); + _remoteConnectionBuilder, proxyRuntimeSetting, dnsResolutionResult); return result = tunneledConnectionPool; } @@ -177,7 +171,7 @@ public async ValueTask if (exchange.Request.Header.IsWebSocketRequest) { var tunneledConnectionPool = new WebsocketConnectionPool( exchange.Authority, _timingProvider, - _remoteConnectionBuilder, proxyRuntimeSetting, dnsResolutionResult.Item1); + _remoteConnectionBuilder, proxyRuntimeSetting, dnsResolutionResult); return result = tunneledConnectionPool; } @@ -187,7 +181,7 @@ public async ValueTask var http11ConnectionPool = new Http11ConnectionPool(exchange.Authority, _remoteConnectionBuilder, _timingProvider, proxyRuntimeSetting, - _archiveWriter!, dnsResolutionResult.Item1); + _archiveWriter!, dnsResolutionResult); exchange.HttpVersion = "HTTP/1.1"; @@ -209,7 +203,7 @@ public async ValueTask { openingResult = (await _remoteConnectionBuilder.OpenConnectionToRemote( - exchange, dnsResolutionResult.Item1, + exchange, dnsResolutionResult, exchange.Context.SslApplicationProtocols ?? AllProtocols, proxyRuntimeSetting, exchange.Context.ProxyConfiguration, cancellationToken).ConfigureAwait(false))!; @@ -228,12 +222,11 @@ public async ValueTask throw; } - // exchange.Connection = openingResult.Connection; if (openingResult.Type == RemoteConnectionResultType.Http11) { var http11ConnectionPool = new Http11ConnectionPool(exchange.Authority, _remoteConnectionBuilder, _timingProvider, proxyRuntimeSetting, _archiveWriter, - dnsResolutionResult.Item1); + dnsResolutionResult); exchange.HttpVersion = exchange.Connection!.HttpVersion = "HTTP/1.1"; @@ -264,25 +257,27 @@ public async ValueTask throw new NotSupportedException($"Unhandled protocol type {openingResult.Type}"); } finally { - try { - if (result != null) { - released = true; - semaphorePerAuthority.Release(); - + if (result != null) { + try + { result.Init(); } - } - catch { - if (result != null) + catch + { OnConnectionFaulted(result); - } - finally { - if (!released) - semaphorePerAuthority.Release(); + } } } } + private IDnsSolver ResolveDnsProvider(Exchange exchange, ProxyRuntimeSetting proxyRuntimeSetting) + { + return string.IsNullOrWhiteSpace(exchange.Context.DnsOverHttpsNameOrUrl) ? + _dnsSolver : _dnsSolversCache.GetOrAdd(exchange.Context.DnsOverHttpsNameOrUrl, + n => new DnsOverHttpsResolver(n, exchange.Context.DnsOverHttpsCapture ? + proxyRuntimeSetting.GetInternalProxyAuthentication() : null)); + } + private void OnConnectionFaulted(IHttpConnectionPool h2ConnectionPool) { lock (_connectionPools) { diff --git a/src/Fluxzy.Core/Core/Authority.cs b/src/Fluxzy.Core/Core/Authority.cs index 9533b41d..0e89c847 100644 --- a/src/Fluxzy.Core/Core/Authority.cs +++ b/src/Fluxzy.Core/Core/Authority.cs @@ -1,60 +1,60 @@ -// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak - -using System; - -namespace Fluxzy.Core -{ - /// - /// Hold information about a hostname and a port number - /// - public readonly struct Authority : IEquatable, IAuthority - { - public Authority(string hostName, int port, bool secure) - { - HostName = hostName; - Port = port; - Secure = secure; - } - - /// - /// Hostname - /// - public string HostName { get; } - - /// - /// Port number - /// - public int Port { get; } - - /// - /// - public bool Secure { get; } - - public bool Equals(Authority other) - { - return - string.Equals(HostName, other.HostName, StringComparison.OrdinalIgnoreCase) - && Port == other.Port && Secure == other.Secure; - } - - public override bool Equals(object? obj) - { - return obj is Authority other && Equals(other); - } - - public override int GetHashCode() - { - if (HostName == null) - return 0; - - Span destBuffer = stackalloc char[HostName.Length]; - - return HashCode.Combine(HostName.AsSpan().ToLowerInvariant(destBuffer), Port, Secure); - } - - public override string ToString() - { - return $"{HostName}:{Port}"; - } - } -} +// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak + +using System; + +namespace Fluxzy.Core +{ + /// + /// Hold information about a hostname and a port number + /// + public readonly struct Authority : IEquatable, IAuthority + { + public Authority(string hostName, int port, bool secure) + { + HostName = hostName; + Port = port; + Secure = secure; + } + + /// + /// Hostname + /// + public string HostName { get; } + + /// + /// Port number + /// + public int Port { get; } + + /// + /// + public bool Secure { get; } + + public bool Equals(Authority other) + { + return + string.Equals(HostName, other.HostName, StringComparison.OrdinalIgnoreCase) + && Port == other.Port && Secure == other.Secure; + } + + public override bool Equals(object? obj) + { + return obj is Authority other && Equals(other); + } + + public override int GetHashCode() + { + if (HostName == null) + return 0; + + Span destBuffer = stackalloc char[HostName.Length]; + + return HashCode.Combine(HostName.AsSpan().ToLowerInvariant(destBuffer), Port, Secure); + } + + public override string ToString() + { + return $"{HostName}:{Port}"; + } + } +} diff --git a/src/Fluxzy.Core/Core/ExchangeMetrics.cs b/src/Fluxzy.Core/Core/ExchangeMetrics.cs index 2f01937b..c4462ca7 100644 --- a/src/Fluxzy.Core/Core/ExchangeMetrics.cs +++ b/src/Fluxzy.Core/Core/ExchangeMetrics.cs @@ -1,117 +1,117 @@ -// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak - -using System; +// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak -namespace Fluxzy.Core -{ +using System; + +namespace Fluxzy.Core +{ /// /// An object holding metrics information about an exchange - /// - public class ExchangeMetrics - { + /// + public class ExchangeMetrics + { /// /// true if the exchange reused an existing connection - /// - public bool ReusingConnection { get; set; } - + /// + public bool ReusingConnection { get; set; } + /// /// Instant received from client - /// - public DateTime ReceivedFromProxy { get; set; } - + /// + public DateTime ReceivedFromProxy { get; set; } + /// /// Instant obtaining an HTTP connection pool - /// - public DateTime RetrievingPool { get; set; } - + /// + public DateTime RetrievingPool { get; set; } + /// /// Instant request header was about to be sent - /// - public DateTime RequestHeaderSending { get; set; } - + /// + public DateTime RequestHeaderSending { get; set; } + /// /// Instant request header was sent - /// - public DateTime RequestHeaderSent { get; set; } - + /// + public DateTime RequestHeaderSent { get; set; } + /// /// Instant request body was sent - /// - public DateTime RequestBodySent { get; set; } - + /// + public DateTime RequestBodySent { get; set; } + /// /// Instant first byte of response header was received - /// - public DateTime ResponseHeaderStart { get; set; } - + /// + public DateTime ResponseHeaderStart { get; set; } + /// /// Instant last byte of response header was received - /// - public DateTime ResponseHeaderEnd { get; set; } - + /// + public DateTime ResponseHeaderEnd { get; set; } + /// /// Instant first byte of response body was received - /// - public DateTime ResponseBodyStart { get; set; } - + /// + public DateTime ResponseBodyStart { get; set; } + /// /// Instant last byte of response body was received - /// - public DateTime ResponseBodyEnd { get; set; } - + /// + public DateTime ResponseBodyEnd { get; set; } + /// /// Instant the remote closed the connection. DateTime.MinValue if never happens during the capture - /// - public DateTime RemoteClosed { get; set; } - - public DateTime CreateCertStart { get; set; } - - public DateTime CreateCertEnd { get; set; } - + /// + public DateTime RemoteClosed { get; set; } + + public DateTime CreateCertStart { get; set; } + + public DateTime CreateCertEnd { get; set; } + /// /// Instant an error was declared - /// - public DateTime ErrorInstant { get; set; } - + /// + public DateTime ErrorInstant { get; set; } + /// /// Total byte sent for this exchange - /// - public long TotalSent { get; set; } - + /// + public long TotalSent { get; set; } + /// /// Total byte received for this exchange - /// - public long TotalReceived { get; set; } - + /// + public long TotalReceived { get; set; } + /// /// Full request header length - /// - public int RequestHeaderLength { get; set; } - + /// + public int RequestHeaderLength { get; set; } + /// /// Full response header length - /// - public int ResponseHeaderLength { get; set; } - + /// + public int ResponseHeaderLength { get; set; } + /// /// Port used by proxy client - /// - public int DownStreamClientPort { get; set; } - + /// + public int DownStreamClientPort { get; set; } + /// /// Address used by proxy client - /// - public string DownStreamClientAddress { get; set; } = string.Empty; - + /// + public string DownStreamClientAddress { get; set; } = string.Empty; + /// /// The endpoint port that receive the client request - /// - public int DownStreamLocalPort { get; set; } - + /// + public int DownStreamLocalPort { get; set; } + /// /// The endpoint address that receive the client request - /// + /// public string DownStreamLocalAddress { get; set; } = string.Empty; - } -} + } +} diff --git a/src/Fluxzy.Core/Core/ProxyOrchestrator.cs b/src/Fluxzy.Core/Core/ProxyOrchestrator.cs index c61d6f71..93e5a55f 100644 --- a/src/Fluxzy.Core/Core/ProxyOrchestrator.cs +++ b/src/Fluxzy.Core/Core/ProxyOrchestrator.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Fluxzy.Clients; +using Fluxzy.Clients.Mock; using Fluxzy.Extensions; using Fluxzy.Misc.ResizableBuffers; using Fluxzy.Misc.Streams; @@ -25,6 +26,8 @@ internal class ProxyOrchestrator : IDisposable private readonly ProxyRuntimeSetting _proxyRuntimeSetting; private readonly ExchangeContextBuilder _exchangeContextBuilder; + public static int loopCount; + public ProxyOrchestrator( ProxyRuntimeSetting proxyRuntimeSetting, ExchangeSourceProvider exchangeSourceProvider, @@ -46,6 +49,12 @@ public async ValueTask Operate(TcpClient client, RsBuffer buffer, bool closeImme Exchange? exchange = null; ExchangeSourceInitResult? exchangeSourceInitResult = null; + Interlocked.Increment(ref loopCount); + + if (loopCount > 3) { + + } + try { @@ -208,11 +217,19 @@ await exchange.Context.BreakPointContext.RequestHeaderCompletion } } + + while (true) { - // get a connection pool for the current exchange - - connectionPool = await _poolBuilder.GetPool(exchange, _proxyRuntimeSetting, token).ConfigureAwait(false); + if (exchange.Context.PreMadeResponse != null) + { + connectionPool = new MockedConnectionPool(exchange.Authority, exchange.Context.PreMadeResponse); + connectionPool.Init(); + } + else { + // get a connection pool for the current exchange + connectionPool = await _poolBuilder.GetPool(exchange, _proxyRuntimeSetting, token).ConfigureAwait(false); + } if (D.EnableTracing) { @@ -257,6 +274,7 @@ await exchange.Context.BreakPointContext.RequestHeaderCompletion break; } + } catch (Exception exception) { diff --git a/src/Fluxzy.Core/Rules/Filters/RequestFilters/PathFilter.cs b/src/Fluxzy.Core/Rules/Filters/RequestFilters/PathFilter.cs index 3bedf81e..5b3d02ab 100644 --- a/src/Fluxzy.Core/Rules/Filters/RequestFilters/PathFilter.cs +++ b/src/Fluxzy.Core/Rules/Filters/RequestFilters/PathFilter.cs @@ -1,5 +1,6 @@ // Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using Fluxzy.Core; @@ -42,8 +43,16 @@ public override IEnumerable GetExamples() protected override IEnumerable GetMatchInputs( ExchangeContext? exchangeContext, IAuthority authority, IExchange? exchange) { - if (exchange != null) - yield return exchange.Path; + if (exchange != null) { + + if (authority.Secure || !Uri.TryCreate(exchange.Path, UriKind.Absolute, out var uri)) + { + yield return exchange.Path; + yield break; + } + + yield return uri.PathAndQuery; + } } } diff --git a/src/Fluxzy.Core/Utils/Synchronizer.cs b/src/Fluxzy.Core/Utils/Synchronizer.cs index 8b3a428c..ea2bb6eb 100644 --- a/src/Fluxzy.Core/Utils/Synchronizer.cs +++ b/src/Fluxzy.Core/Utils/Synchronizer.cs @@ -13,19 +13,23 @@ namespace Fluxzy.Utils /// internal class Synchronizer where T : IEquatable { + private readonly bool _preserve; private readonly ConcurrentDictionary _locks = new(); - public static Synchronizer Instance { get; } = new(); + public static Synchronizer Shared { get; } = new(); - private Synchronizer() + public Synchronizer(bool preserve = false) { - + _preserve = preserve; } - public async Task LockAsync(T key) + public async ValueTask LockAsync(T key) { var semaphore = _locks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1)); - await semaphore.WaitAsync(); + + if (!semaphore.Wait(0)) + await semaphore.WaitAsync(); + return new Releaser(this, key); } @@ -36,7 +40,7 @@ private void Release(T key) semaphore.Release(); // Clean up if no one is waiting - if (semaphore.CurrentCount == 1) + if (!_preserve && semaphore.CurrentCount == 1) { _locks.TryRemove(key, out _); } diff --git a/test/Fluxzy.Tests/Cases/SelfCallTests.cs b/test/Fluxzy.Tests/Cases/SelfCallTests.cs index a02c3123..dc425445 100644 --- a/test/Fluxzy.Tests/Cases/SelfCallTests.cs +++ b/test/Fluxzy.Tests/Cases/SelfCallTests.cs @@ -1,15 +1,25 @@ // Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak +using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; using System.Threading.Tasks; +using Fluxzy.Certificates; +using Fluxzy.Rules.Actions.HighLevelActions; +using Fluxzy.Rules.Filters; +using Fluxzy.Rules.Filters.RequestFilters; using Xunit; namespace Fluxzy.Tests.Cases { public class SelfCallTests { - [Fact] - public async Task MakeMultipleSelfCall() + [Theory] + [MemberData(nameof(AllValidHosts))] + public async Task MakeMultipleSelfCall(string host) { var setting = FluxzySetting.CreateLocalRandomPort(); @@ -19,10 +29,94 @@ public async Task MakeMultipleSelfCall() using var client = HttpClientUtility.CreateHttpClient(endPoints, setting); for (int i = 0; i < 4; i++) { - var response = await client.GetAsync($"http://127.0.0.1:{endPoints.First().Port}/welcome"); + var response = await client.GetAsync($"http://{host}:{endPoints.First().Port}/welcome"); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); } } + + [Theory] + [MemberData(nameof(AllValidHosts))] + public async Task MakeMultipleSelfCallCa(string host) + { + var setting = FluxzySetting.CreateLocalRandomPort(); + + await using var proxy = new Proxy(setting); + + var endPoints = proxy.Run(); + using var client = HttpClientUtility.CreateHttpClient(endPoints, setting); + + var certificate = setting.CaCertificate.GetX509Certificate(); + var certificateString = Encoding.UTF8.GetString(certificate.ExportToPem()); + + for (int i = 0; i < 5; i++) { + var response = await client.GetAsync($"http://{host}:{endPoints.First().Port}/ca"); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(certificateString, content); + } + } + + [Theory] + [MemberData(nameof(AllValidHosts))] + public async Task MakeMultipleSelfCallNothing(string host) + { + var setting = FluxzySetting.CreateLocalRandomPort(); + + await using var proxy = new Proxy(setting); + + var endPoints = proxy.Run(); + using var client = HttpClientUtility.CreateHttpClient(endPoints, setting); + + for (int i = 0; i < 4; i++) + { + var response = await client.GetAsync($"http://{host}:{endPoints.First().Port}/notmounted"); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStringAsync(); + } + } + + [Theory] + [MemberData(nameof(AllValidHosts))] + public async Task MakeCustomHookFilter(string host) + { + var setting = FluxzySetting.CreateLocalRandomPort(); + + setting.ConfigureRule() + .When(new FilterCollection(new IsSelfFilter(), new PathFilter("/hello"))) + .ReplyText("Hello"); + + await using var proxy = new Proxy(setting); + + var endPoints = proxy.Run(); + using var client = HttpClientUtility.CreateHttpClient(endPoints, setting); + + for (int i = 0; i < 4; i++) + { + var response = await client.GetAsync($"http://{host}:{endPoints.First().Port}/hello"); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal("Hello", content); + } + } + + public static IEnumerable AllValidHosts() + { + var allIps = new IPAddress[] { IPAddress.IPv6Loopback, IPAddress.Loopback } + .Select(s => s.AddressFamily == AddressFamily.InterNetworkV6 ? + $"[{s}]" + : s.ToString()) + .ToList(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + allIps.Add(Dns.GetHostName()); + + allIps.Add("local.fluxzy.io"); + + return allIps.Select(s => new object[] { s }); + } } }