Skip to content

Commit

Permalink
Add fluent methods when configuring FluxzySetting alteration rule (#…
Browse files Browse the repository at this point in the history
…112)

* Add extension methods structure to add rules in a fluent way

* Lower modifiers for internal implementation

* Make SetupRule immediate method

* Add unit tests

---------

Co-authored-by: fluxzy-ci <[email protected]>
  • Loading branch information
haga-rak and fluxzy-ci authored Dec 8, 2023
1 parent f9d3732 commit 20a4273
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 35 deletions.
23 changes: 23 additions & 0 deletions samples/Samples.No002.Filtering/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
using System.Net;
using Fluxzy;
using Fluxzy.Rules;
using Fluxzy.Rules.Actions;
using Fluxzy.Rules.Extensions;
using Fluxzy.Rules.Filters;
using Fluxzy.Rules.Filters.RequestFilters;

namespace Samples.No002.Filtering
{
internal class Program
{

static async Task Do()
{
var fluxzyStartupSetting = FluxzySetting
.CreateDefault(IPAddress.Loopback, 44344)
.AddAlterationRules(
new Rule(
new AddResponseHeaderAction("X-Proxy", "Passed through fluxzy"),
AnyFilter.Default
));

await using (var proxy = new Proxy(fluxzyStartupSetting))
{
var _ = proxy.Run();

Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}

/// <summary>
/// Capture only specific traffic.
/// </summary>
Expand Down
48 changes: 18 additions & 30 deletions samples/Samples.No004.BasicAlterations/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using Fluxzy;
using Fluxzy.Certificates;
using Fluxzy.Clients.Mock;
using Fluxzy.Rules;
using Fluxzy.Rules.Actions;
using Fluxzy.Rules.Actions.HighLevelActions;
using Fluxzy.Rules.Extensions;
using Fluxzy.Rules.Filters;
using Fluxzy.Rules.Filters.RequestFilters;

Expand All @@ -22,51 +22,39 @@ static async Task Main(string[] args)
var tempDirectory = "basic_alteration";

// Create a default run settings
var fluxzyStartupSetting = FluxzySetting
var fluxzySetting = FluxzySetting
.CreateDefault(IPAddress.Loopback, 44344)
.SetOutDirectory(tempDirectory);

// The full list of available actions and rules are available at
// https://www.fluxzy.io/rule/search

fluxzyStartupSetting.AddAlterationRules(


fluxzySetting
// Set up rule configuration in a fluent way
.ConfigureRule()

// Append "fluxzy-on" header to any request
new Rule(
new AddRequestHeaderAction("fluxzy-on", "true"),
new AnyFilter()
),
.WhenAny().Do(new AddRequestHeaderAction("fluxzy-on", "true"))

// Remove any cache directive from any request
new Rule(
new RemoveCacheAction(),
new AnyFilter()
),
.WhenAny().Do(new RemoveCacheAction())

// Avoid decrypting particular host
new Rule(
new SkipSslTunnelingAction(),
new HostFilter("secure.domain.com", StringSelectorOperation.Exact)
),
.When(new HostFilter("secure.domain.com", StringSelectorOperation.Exact))
.Do(new SkipSslTunnelingAction())

// Mock an entire response according to an URL
new Rule(
new MockedResponseAction(
new MockedResponseContent(
200,
BodyContent.CreateFromString("This is a plain text content"))
),
new AbsoluteUriFilter(@"^https\:\/\/api\.example\.com", StringSelectorOperation.Regex)
),
.When(new AbsoluteUriFilter(@"^https\:\/\/api\.example\.com", StringSelectorOperation.Regex))
.Do(new MockedResponseAction(MockedResponseContent.CreateFromPlainText("This is a plain text content")))

// Using a client certificate
new Rule(
new SetClientCertificateAction(Certificate.LoadFromUserStoreBySerialNumber("xxxxxx")),
new HostFilter("domain.with.mandatory.client.cert.com", StringSelectorOperation.Exact)
)
);
.When(new HostFilter("domain.with.mandatory.client.cert.com", StringSelectorOperation.Exact))
.Do(new SetClientCertificateAction(Certificate.LoadFromUserStoreBySerialNumber("xxxxxx")));


// Create a proxy instance
await using (var proxy = new Proxy(fluxzyStartupSetting))
await using (var proxy = new Proxy(fluxzySetting))
{
var endpoints = proxy.Run();

Expand Down
2 changes: 0 additions & 2 deletions src/Fluxzy.Core/Clients/Mock/MockedResponseContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ public override Stream ReadBody(Authority authority)
}
}



public class MockedResponseHeader
{
public MockedResponseHeader(string name, string value)
Expand Down
11 changes: 11 additions & 0 deletions src/Fluxzy.Core/FluxzySetting.Fluent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Fluxzy.Certificates;
using Fluxzy.Rules;
using Fluxzy.Rules.Actions;
using Fluxzy.Rules.Extensions;
using Fluxzy.Rules.Filters;
using Fluxzy.Rules.Filters.RequestFilters;
using Action = Fluxzy.Rules.Action;
Expand Down Expand Up @@ -374,6 +375,16 @@ public FluxzySetting SetCertificateCacheDirectory(string path)
{
CertificateCacheDirectory = path;
return this;
}

/// <summary>
/// Set up a new rule adding chain
/// </summary>
/// <returns></returns>
public IConfigureFilterBuilder ConfigureRule()
{
var addFilter = new ConfigureFilterBuilderBuilder(this);
return addFilter;
}
}
}
30 changes: 30 additions & 0 deletions src/Fluxzy.Core/Rules/Extensions/ConfigureActionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Linq;
using Fluxzy.Rules.Filters;

namespace Fluxzy.Rules.Extensions
{
internal class ConfigureActionBuilder : IConfigureActionBuilder
{
public FluxzySetting Setting { get; }

private readonly Filter _filter;

public ConfigureActionBuilder(FluxzySetting setting, Filter filter)
{
Setting = setting;
_filter = filter;
}

public IConfigureFilterBuilder Do(Action action, params Action [] actions)
{
if (action == null)
throw new ArgumentNullException(nameof(action));

Setting.AddAlterationRules(new Rule(action, _filter));
Setting.AddAlterationRules(actions.Select(a => new Rule(a, _filter)));

return new ConfigureFilterBuilderBuilder(Setting);
}
}
}
37 changes: 37 additions & 0 deletions src/Fluxzy.Core/Rules/Extensions/ConfigureFilterBuilderBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Linq;
using Fluxzy.Rules.Filters;

namespace Fluxzy.Rules.Extensions
{
internal class ConfigureFilterBuilderBuilder : IConfigureFilterBuilder
{
public ConfigureFilterBuilderBuilder(FluxzySetting fluxzySetting)
{
FluxzySetting = fluxzySetting;
}

public FluxzySetting FluxzySetting { get; }

public IConfigureActionBuilder When(Filter filter)
{
if (filter == null)
throw new ArgumentNullException(nameof(filter));

return new ConfigureActionBuilder(FluxzySetting, filter);
}

public IConfigureActionBuilder WhenAny(params Filter[] filters)
{
return new ConfigureActionBuilder(FluxzySetting,
filters.Any() ? new FilterCollection(filters) { Operation = SelectorCollectionOperation.Or }: AnyFilter.Default);
}

public IConfigureActionBuilder WhenAll(params Filter[] filters)
{
return new ConfigureActionBuilder(FluxzySetting, filters.Any() ?
new FilterCollection(filters) { Operation = SelectorCollectionOperation.And }
: NoFilter.Default);
}
}
}
21 changes: 21 additions & 0 deletions src/Fluxzy.Core/Rules/Extensions/IConfigureActionBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Fluxzy.Rules.Extensions
{
/// <summary>
/// Helper to build alteration rules in a fluent way
/// </summary>
public interface IConfigureActionBuilder
{
/// <summary>
/// The current setting
/// </summary>
FluxzySetting Setting { get; }

/// <summary>
/// Add one or more actions to the rule
/// </summary>
/// <param name="action"></param>
/// <param name="actions"></param>
/// <returns></returns>
IConfigureFilterBuilder Do(Action action, params Action [] actions);
}
}
37 changes: 37 additions & 0 deletions src/Fluxzy.Core/Rules/Extensions/IConfigureFilterBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Fluxzy.Rules.Filters;

namespace Fluxzy.Rules.Extensions
{
/// <summary>
/// Helper to build alteration rules in a fluent way
/// </summary>
public interface IConfigureFilterBuilder
{
/// <summary>
/// The current fluxzy setting
/// </summary>
FluxzySetting FluxzySetting { get; }

/// <summary>
/// Create a rule that will be applied when the filter passes
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>

IConfigureActionBuilder When(Filter filter);

/// <summary>
/// Create a rule that will be applied when any of the filters passes. If no filters are provided, the rule will be applied always.
/// </summary>
/// <param name="filters"></param>
/// <returns></returns>
IConfigureActionBuilder WhenAny(params Filter[] filters);

/// <summary>
/// Create a rule that will be applied when all of the filters passes
/// </summary>
/// <param name="filters"></param>
/// <returns></returns>
IConfigureActionBuilder WhenAll(params Filter[] filters);
}
}
5 changes: 5 additions & 0 deletions src/Fluxzy.Core/Rules/Filters/NoFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ protected override bool InternalApply(
return false;
}

public static NoFilter Default { get; } = new()
{
Locked = true
};

public override IEnumerable<FilterExample> GetExamples()
{
var defaultSample = GetDefaultSample();
Expand Down
2 changes: 1 addition & 1 deletion test/Fluxzy.Tests/Misc/MultiPartBoundaryReaderTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak
// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak

using System;
using System.Collections.Generic;
Expand Down
2 changes: 0 additions & 2 deletions test/Fluxzy.Tests/NativeOps/Macos/CliMacosTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak

using System.Linq;
using System.Threading;
using Fluxzy.Utils.NativeOps.SystemProxySetup.macOs;
using Xunit;

Expand Down
90 changes: 90 additions & 0 deletions test/Fluxzy.Tests/UnitTests/RuleExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Linq;
using Fluxzy.Rules.Actions;
using Fluxzy.Rules.Filters;
using Fluxzy.Rules.Filters.RequestFilters;
using Fluxzy.Rules.Filters.ResponseFilters;
using Xunit;

namespace Fluxzy.Tests.UnitTests
{
public class RuleExtensions
{
[Fact]
public void Test_When_Any()
{
var setting = FluxzySetting.CreateDefault();

var action = new AddRequestHeaderAction("x", "y");

setting.ClearAlterationRules();

setting.ConfigureRule().WhenAny().Do(action);

Assert.Single(setting.AlterationRules);
Assert.Equal(action, setting.AlterationRules.First().Action);
Assert.Equal(AnyFilter.Default, setting.AlterationRules.First().Filter);
}

[Fact]
public void Test_When_Multiple_Actions()
{
var setting = FluxzySetting.CreateDefault();

var filter = new HostFilter("myhost.com", StringSelectorOperation.Contains);
var actionA = new AddRequestHeaderAction("x", "y");
var actionB = new ForceHttp11Action();

setting.ClearAlterationRules();

setting.ConfigureRule().When(filter).Do(actionA, actionB);

Assert.Equal(2, setting.AlterationRules.Count);
Assert.Equal(actionA, setting.AlterationRules.First().Action);
Assert.Equal(actionB, setting.AlterationRules.Last().Action);
}

[Fact]
public void Test_When_Any_Multiple_Actions()
{
var setting = FluxzySetting.CreateDefault();

var filterA = new HostFilter("myhost.com", StringSelectorOperation.Contains);
var filterB = new StatusCodeSuccessFilter();

var actionA = new AddRequestHeaderAction("x", "y");
var actionB = new ForceHttp11Action();

setting.ClearAlterationRules();

setting.ConfigureRule().WhenAny(filterA, filterB).Do(actionA, actionB);

Assert.Equal(2, setting.AlterationRules.Count);
Assert.Equal(actionA, setting.AlterationRules.First().Action);
Assert.Equal(actionB, setting.AlterationRules.Last().Action);
Assert.Equal(typeof(FilterCollection), setting.AlterationRules.Last().Filter.GetType());
Assert.Equal(SelectorCollectionOperation.Or, ((FilterCollection) setting.AlterationRules.Last().Filter).Operation);
}

[Fact]
public void Test_When_All()
{
var setting = FluxzySetting.CreateDefault();

var filterA = new HostFilter("myhost.com", StringSelectorOperation.Contains);
var filterB = new StatusCodeSuccessFilter();

var actionA = new AddRequestHeaderAction("x", "y");
var actionB = new ForceHttp11Action();

setting.ClearAlterationRules();

setting.ConfigureRule().WhenAll(filterA, filterB).Do(actionA, actionB);

Assert.Equal(2, setting.AlterationRules.Count);
Assert.Equal(actionA, setting.AlterationRules.First().Action);
Assert.Equal(actionB, setting.AlterationRules.Last().Action);
Assert.Equal(typeof(FilterCollection), setting.AlterationRules.Last().Filter.GetType());
Assert.Equal(SelectorCollectionOperation.And, ((FilterCollection)setting.AlterationRules.Last().Filter).Operation);
}
}
}

0 comments on commit 20a4273

Please sign in to comment.