Our proxy fault rules (simplified) look like this:
<FaultRules> <FaultRule name="api_key_faults"> <Step> <Name>fault_invalid_key</Name> <Condition>(fault.name = "InvalidApiKey")</Condition> </Step> <Step> <Name>fault_missing_key</Name> <Condition>(fault.name = "FailedToResolveAPIKey")</Condition> </Step> </FaultRule> <FaultRule name="no_routes_matched_rule"> <Step> <Name>fault_no_route</Name> </Step> <Condition>(fault.name = "NoRoutesMatched" || fault.name = "RouteFailed" || fault.name = "NotFound")</Condition> </FaultRule> <FaultRule name="quota_rule"> <Step> <Name>fault_developer_quota_exceeded</Name> <Condition>(ratelimit.check_developer_quota.exceed.count > 0)</Condition> </Step> <Step> <Name>fault_global_quota_exceeded</Name> <Condition>(ratelimit.check_global_quota.exceed.count > 0)</Condition> </Step> <Condition>(fault.name = "QuotaViolation")</Condition> </FaultRule> <FaultRule name="spike_arrest"> <Step> <Name>fault_spike_arrest</Name> <Condition>spikearrest.spike_protection.failed = "true"</Condition> </Step> <Step> <Name>fault_revalidate_spike_arrest</Name> <Condition>(spikearrest.revalidate_spike_protection.failed = "true")</Condition> </Step> <Condition>(fault.name = "SpikeArrestViolation")</Condition> </FaultRule> </FaultRules> <DefaultFaultRule name="default_fault_rule"> <Step> <Name>fault_generic_error</Name> <Condition>(wf.fault_assigned != "true")</Condition> </Step> <Step> <Name>calculate_response_headers</Name> </Step> <AlwaysEnforce>true</AlwaysEnforce> </DefaultFaultRule>
Note that every fault_xxxx policy sets (wf.fault_assigned = "true") so if any error has been assigned the fault_generic_error policy will not execute. This works.
As described here, conditions can go inside or outside the STEP tag.. I'm seeing a couple of issues:
If I move the CONDITION tag from outside to inside the STEP tag on "no_routes_matched_rule", then *all* error fault conditions fail and the fault_generic_error executes. Since there is only one step, the execution behaviour would seem that it should not change, not to mention fail.
The same failure happens if I remove from "spike_arrest" the outer condition (fault.name = "SpikeArrestViolation") that is actually not needed.
Additionally, in the existing form, "spike_arrest" never executes at least "fault_revalidate_spike_arrest", even though the condition appears to be correct.
Note that the "quota_rule" above seems to execute as desired, so its basic structure of nested conditionals seems to work.
I suspect that this is a bug, otherwise, what is wrong here?
Thanks, George
Solved! Go to Solution.
@Floyd Jones- I think I recently observed the Fault Rules are evaluated Bottom to Top in the ProxyEndpoint, but Top to Bottom in the TargetEndpoint. Might be worth double checking!
I have confirmed that this is weirdly true.
I added the following to both proxy and target endpoint FaultRules and observed the test flow in the UI and in trace downloads.
<FaultRule name="raise-fault-1"> <Condition>(fault.name = "RaiseFault")</Condition> <Step> <FaultRules/> <Name>Assign-Message-Raise-Fault-1</Name> </Step> </FaultRule> <FaultRule name="raise-fault-2"> <Condition>(fault.name = "RaiseFault") and ("fault.name" = "RaiseFault")</Condition> <Step> <FaultRules/> <Name>Assign-Message-Raise-Fault-2</Name> </Step> </FaultRule> <FaultRule name="raise-fault-3"> <Condition>(fault.name = "RaiseFault") and ("fault.name" = "RaiseFault") and ("fault.name" = "RaiseFault")</Condition> <Step> <FaultRules/> <Name>Assign-Message-Raise-Fault-3</Name> </Step> </FaultRule>
In the proxy trace download I see:
<Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:24:763</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">((fault.name equals "RaiseFault") and (("fault.name" equals "RaiseFault") and ("fault.name" equals "RaiseFault")))</Property> <Property name="Tree">FAULT_raise-fault-3</Property> </Properties> </DebugInfo> <VariableAccess> <Get value="InvalidApiKey" name="fault.name"/> </VariableAccess> </Point> <Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:24:763</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">((fault.name equals "RaiseFault") and ("fault.name" equals "RaiseFault"))</Property> <Property name="Tree">FAULT_raise-fault-2</Property> </Properties> </DebugInfo> <VariableAccess> <Get value="InvalidApiKey" name="fault.name"/> </VariableAccess> </Point> <Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:24:763</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">(fault.name equals "RaiseFault")</Property> <Property name="Tree">FAULT_raise-fault-1</Property> </Properties> </DebugInfo> <VariableAccess> <Get value="InvalidApiKey" name="fault.name"/> </VariableAccess> </Point>
In the target trace download I see:
<Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:41:101</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">(fault.name equals "RaiseFault")</Property> <Property name="Tree">FAULT_raise-fault-1</Property> </Properties> </DebugInfo> <VariableAccess> <Get value="ErrorResponseCode" name="fault.name"/> </VariableAccess> </Point> <Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:41:101</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">((fault.name equals "RaiseFault") and ("fault.name" equals "RaiseFault"))</Property> <Property name="Tree">FAULT_raise-fault-2</Property> </Properties> </DebugInfo> <VariableAccess> <Get value="ErrorResponseCode" name="fault.name"/> </VariableAccess> </Point> <Point id="Condition"> <DebugInfo> <Timestamp>02-08-16 14:11:41:101</Timestamp> <Properties> <Property name="ExpressionResult">false</Property> <Property name="Expression">((fault.name equals "RaiseFault") and (("fault.name" equals "RaiseFault") and ("fault.name" equals "RaiseFault")))</Property> <Property name="Tree">FAULT_raise-fault-3</Property> </Properties> </DebugInfo>