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:
Matcherinterface. All toxic condition matchers have to implement this interface.HttpRequestHeaderMatcher, which implementsMatcher. This concrete type is also registered in a newMatcherRegistryunder the keyhttpRequestHeaderMatcher.boolindicating whether this check passed or not.ToxicCollectionstruct to storetoxicConditions, a 2D array of*ToxicConditionthat has the dimensions asToxicCollection.chain, always. The first element in either direction in thistoxicConditionsarray is anilpointer. The corresponding CRUD methods againstToxicCollection.chain(chain*Toxicmethods) have been updated to update this field accordingly.ToxicCollection.StartLinkmethod to support toxic condition matching, only forupstreamdirections. This is done by first using theTeeReaderto write the data into a newWriterwhen it is being read by the main thread inlink.Start, and then firing a separate goroutine that passes the underlying data structure of this newWritertomatchAllToxicConditionsto attempt to match against toxic conditions. If a toxic condition has successfully matched, the correspondingToxicWrapperthat wraps this condition will have itsEnabledfield turned on.matchAllToxicConditionsrequires a lock onToxicCollectionas it is expected to be run in its own goroutine.c.chainUpdateToxic(newToxicWrapper). The current code works from manual testing from a Rust client.ToxicWrapperto store two new fields -ToxicConditionandEnabled.Enabledis used inToxicStub.Runsuch that, ifEnabledisfalse, the toxic is disabled and theNoopToxicis used to pipe data through the stub instead.ToxicConditionand implemented custom unmarshalling of it that is similar toToxicWrapper.Toxicembedded 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
{ "name": "latency_downstream", "type": "latency", "stream": "downstream", "toxicity": 1, "attributes": { "jitter": 0, "latency": 2000 }, "condition": { "matcherType": "httpRequestHeaderMatcher", "matcherParameters": { "headerKey": "x-api-key", "headerValueRegex": "123" } } }this latency toxic would be created but disabled, and only enabled when there has been an HTTP request with
x-api-keyheader and value matching the regex123sent 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:
Matcherinterface that just take in a byte sliceLimitations
These are the current limitations of this feature:
upstreamtoxics.bufio.Readeras input, http.ReadResponse requires taking in ahttp.Requestas 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.chainUpdateToxicneeds 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.
matchAllToxicConditionsgoroutine, the only concreteMatcheravailable -HttpRequestHeaderMatcherruns quick.Next Steps
Getting Alignment / Buy-In:
Test Coverage
Update interfacing tools
Optimize performance
Preventing Crashes
matchAllToxicConditionsc.chainUpdateToxic(newToxicWrapper)if in thematchedcase?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-keyHTTP request header key and value123.This test case passes.