Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What
This PR is currently in draft stage to gauge the interest in this feature / implementation. If we like it, I will proceed to add tests before marking it as reviewable.
Closes #518.
This PR implements a new feature, Conditional Toxics. While conditions are registered at the same time as toxics, when they are provided the toxic is disabled until the condition has been matched, which turns it on.
Specifically, this PR:
Matcher
interface. All toxic condition matchers have to implement this interface.HttpRequestHeaderMatcher
, which implementsMatcher
. This concrete type is also registered in a newMatcherRegistry
under the keyhttpRequestHeaderMatcher
.bool
indicating whether this check passed or not.ToxicCollection
struct to storetoxicConditions
, a 2D array of*ToxicCondition
that has the dimensions asToxicCollection.chain
, always. The first element in either direction in thistoxicConditions
array is anil
pointer. The corresponding CRUD methods againstToxicCollection.chain
(chain*Toxic
methods) have been updated to update this field accordingly.ToxicCollection.StartLink
method to support toxic condition matching, only forupstream
directions. This is done by first using theTeeReader
to write the data into a newWriter
when it is being read by the main thread inlink.Start
, and then firing a separate goroutine that passes the underlying data structure of this newWriter
tomatchAllToxicConditions
to attempt to match against toxic conditions. If a toxic condition has successfully matched, the correspondingToxicWrapper
that wraps this condition will have itsEnabled
field turned on.matchAllToxicConditions
requires a lock onToxicCollection
as it is expected to be run in its own goroutine.c.chainUpdateToxic(newToxicWrapper)
. The current code works from manual testing from a Rust client.ToxicWrapper
to store two new fields -ToxicCondition
andEnabled
.Enabled
is used inToxicStub.Run
such that, ifEnabled
isfalse
, the toxic is disabled and theNoopToxic
is used to pipe data through the stub instead.ToxicCondition
and implemented custom unmarshalling of it that is similar toToxicWrapper.Toxic
embedded field.Motivation
The motivation for this is explained in the #518 issue.
Expected Behavior
The expected behavior is that, by passing in the following new request payload
this latency toxic would be created but disabled, and only enabled when there has been an HTTP request with
x-api-key
header and value matching the regex123
sent from the client to upstream. Once such a request has been sent, all subsequent requests from client to upstream will be subject to this toxic.Design Goals
These are the design goals for this feature:
Matcher
interface that just take in a byte sliceLimitations
These are the current limitations of this feature:
upstream
toxics.bufio.Reader
as input, http.ReadResponse requires taking in ahttp.Request
as input as well. At this point, I think supporting this would introduce some new assumptions into the system and also require state storage that would complicate the system overall, and hence I am unsure how to (or whether) spec this out yet.Risks
Low / Medium.
c.chainUpdateToxic
needs to be called when a condition has been matched. There may be fields that needs updating / functions that need to be ran (eg. in the stubs?) but are not currently because this function isn't being called right now.Performance
This PR should not incur any noticeable performance regressions, at least not for small chains of toxics.
matchAllToxicConditions
goroutine, the only concreteMatcher
available -HttpRequestHeaderMatcher
runs quick.Next Steps
Getting Alignment / Buy-In:
Test Coverage
Update interfacing tools
Optimize performance
Preventing Crashes
matchAllToxicConditions
c.chainUpdateToxic(newToxicWrapper)
if in thematched
case?Testing
The Rust client has been updated in a fork to support this feature: https://github.com/hwrdtm/toxiproxy_rust/tree/hwrdtm/conditional-toxics
There has been a new test case added to this Rust client that sends 2 HTTP requests serially, and checks for their latencies:
x-api-key
HTTP request header key and value123
.This test case passes.