Class HttpChaosProfile


public class HttpChaosProfile extends ObjectWithJsonToString
Declarative HTTP fault/chaos injection for mocked and forwarded responses: probabilistic connection-drop injection, error status injection (e.g. 500, 503, 429 with an optional Retry-After header), latency injection, and response-body corruption (truncateBodyAtFraction keeps only a leading fraction of the body bytes; malformedBody appends a broken-JSON fragment) for testing client-side body-parsing resilience, and a slow ("dribbled") response (slowResponseChunkSize + slowResponseChunkDelay send the body in small chunks with a delay between each) for testing read timeouts on a trickling response.

Body corruption and slow-response are deterministic (no probability draw): they apply to the real (non-error) response whenever the count and time windows are eligible, and are skipped for streaming bodies. Connection-drop and error injection take priority — when an error is injected the synthetic error body is returned uncorrupted and at full speed.

It also carries an optional stateful request quota (a fixed-window rate limit): when quotaName, quotaLimit and quotaWindowMillis are set, requests beyond quotaLimit within the window are rejected with quotaErrorStatus (default 429) and the retryAfter header. Unlike the probabilistic error this is deterministic and counts real requests across the process (see HttpQuotaRegistry); expectations sharing a quotaName share one counter. The quota gate takes priority over the probabilistic error and the body/slow faults (after connection-drop).

It can also model gradual degradation: when degradationRampMillis is set, the probabilistic fault rates (errorProbability and dropConnectionProbability) ramp linearly from 0.0 at the expectation's first match up to their configured values once the ramp duration has elapsed, so a dependency appears to deteriorate over time (useful for alerting / SLO-burn tests). The ramp is measured with the controllable clock (deterministic under freeze/advance) and does not affect the deterministic faults (latency, body corruption, slow response, quota).

Attach to an Expectation via expectation.withChaos(httpChaosProfile()...) to inject faults into the following action types:

  • Mocked responses: RESPONSE, RESPONSE_TEMPLATE, RESPONSE_CLASS_CALLBACK
  • Forward actions: FORWARD, FORWARD_TEMPLATE, FORWARD_CLASS_CALLBACK, FORWARD_REPLACE, FORWARD_VALIDATE
Not yet covered: RESPONSE_OBJECT_CALLBACK and FORWARD_OBJECT_CALLBACK (both use their own callback-driven write path) and the anonymous/unmatched proxy-pass path.

Determinism: with errorProbability of 1.0 (always) or 0.0/null (never) the error decision is fully deterministic. A fractional probability draws once per response; set seed to make that single draw reproducible (note: a fixed seed yields the same decision every time).

Count-based stateful fault window: succeedFirst and failRequestCount define a window over the expectation's 1-based match count where chaos is eligible:

  • Matches 1..succeedFirst are NOT eligible (chaos is suppressed).
  • Matches (succeedFirst+1)..(succeedFirst+failRequestCount) ARE eligible.
  • Matches beyond succeedFirst+failRequestCount recover (no chaos).
When both fields are null every match is eligible, preserving backward compatibility. The window check is deterministic and composes with the probabilistic error draw: a match must be within the window AND pass the probability check to receive an injected fault.

Time-based outage window: outageAfterMillis and outageDurationMillis define a self-healing window, measured relative to the expectation's first match, during which chaos is active. The window opens outageAfterMillis ms after the first match and (when a duration is set) closes after outageDurationMillis ms, after which the service behaves normally again. The window is measured with the controllable clock (TimeService), so freezing/advancing the clock (e.g. via PUT /mockserver/clock) makes it deterministic in tests. It composes with the count window and the probability draws: a fault fires only when the request is inside the time window AND the count window AND the draw passes. When both fields are null there is no time gate.

Follows the model field/withX/getter convention so it round-trips without a bespoke (de)serializer.

  • Constructor Details

    • HttpChaosProfile

      public HttpChaosProfile()
  • Method Details

    • httpChaosProfile

      public static HttpChaosProfile httpChaosProfile()
    • withErrorStatus

      public HttpChaosProfile withErrorStatus(Integer errorStatus)
    • getErrorStatus

      public Integer getErrorStatus()
    • withRetryAfter

      public HttpChaosProfile withRetryAfter(String retryAfter)
    • getRetryAfter

      public String getRetryAfter()
    • withErrorProbability

      public HttpChaosProfile withErrorProbability(Double errorProbability)
    • getErrorProbability

      public Double getErrorProbability()
    • withDropConnectionProbability

      public HttpChaosProfile withDropConnectionProbability(Double dropConnectionProbability)
    • getDropConnectionProbability

      public Double getDropConnectionProbability()
    • withLatency

      public HttpChaosProfile withLatency(Delay latency)
    • getLatency

      public Delay getLatency()
    • withSeed

      public HttpChaosProfile withSeed(Long seed)
    • getSeed

      public Long getSeed()
    • withSucceedFirst

      public HttpChaosProfile withSucceedFirst(Integer succeedFirst)
    • getSucceedFirst

      public Integer getSucceedFirst()
    • withFailRequestCount

      public HttpChaosProfile withFailRequestCount(Integer failRequestCount)
    • getFailRequestCount

      public Integer getFailRequestCount()
    • withOutageAfterMillis

      public HttpChaosProfile withOutageAfterMillis(Long outageAfterMillis)
    • getOutageAfterMillis

      public Long getOutageAfterMillis()
    • withOutageDurationMillis

      public HttpChaosProfile withOutageDurationMillis(Long outageDurationMillis)
    • getOutageDurationMillis

      public Long getOutageDurationMillis()
    • withTruncateBodyAtFraction

      public HttpChaosProfile withTruncateBodyAtFraction(Double truncateBodyAtFraction)
    • getTruncateBodyAtFraction

      public Double getTruncateBodyAtFraction()
    • withMalformedBody

      public HttpChaosProfile withMalformedBody(Boolean malformedBody)
    • getMalformedBody

      public Boolean getMalformedBody()
    • withSlowResponseChunkSize

      public HttpChaosProfile withSlowResponseChunkSize(Integer slowResponseChunkSize)
    • getSlowResponseChunkSize

      public Integer getSlowResponseChunkSize()
    • withSlowResponseChunkDelay

      public HttpChaosProfile withSlowResponseChunkDelay(Delay slowResponseChunkDelay)
    • getSlowResponseChunkDelay

      public Delay getSlowResponseChunkDelay()
    • withQuotaName

      public HttpChaosProfile withQuotaName(String quotaName)
    • getQuotaName

      public String getQuotaName()
    • withQuotaLimit

      public HttpChaosProfile withQuotaLimit(Integer quotaLimit)
    • getQuotaLimit

      public Integer getQuotaLimit()
    • withQuotaWindowMillis

      public HttpChaosProfile withQuotaWindowMillis(Long quotaWindowMillis)
    • getQuotaWindowMillis

      public Long getQuotaWindowMillis()
    • withQuotaErrorStatus

      public HttpChaosProfile withQuotaErrorStatus(Integer quotaErrorStatus)
    • getQuotaErrorStatus

      public Integer getQuotaErrorStatus()
    • withDegradationRampMillis

      public HttpChaosProfile withDegradationRampMillis(Long degradationRampMillis)
    • getDegradationRampMillis

      public Long getDegradationRampMillis()
    • withGraphqlErrors

      public HttpChaosProfile withGraphqlErrors(Boolean graphqlErrors)
    • getGraphqlErrors

      public Boolean getGraphqlErrors()
    • withGraphqlErrorMessage

      public HttpChaosProfile withGraphqlErrorMessage(String graphqlErrorMessage)
    • getGraphqlErrorMessage

      public String getGraphqlErrorMessage()
    • withGraphqlErrorCode

      public HttpChaosProfile withGraphqlErrorCode(String graphqlErrorCode)
    • getGraphqlErrorCode

      public String getGraphqlErrorCode()
    • withGraphqlNullifyData

      public HttpChaosProfile withGraphqlNullifyData(Boolean graphqlNullifyData)
    • getGraphqlNullifyData

      public Boolean getGraphqlNullifyData()
    • degradationFactor

      public double degradationFactor(long firstMatchEpochMillis, long nowEpochMillis)
      Returns the gradual-degradation ramp factor in [0.0, 1.0] for the given timing. When degradationRampMillis is null there is no ramp and this returns 1.0 (faults at full configured rate). Otherwise the factor climbs linearly from 0.0 at the first match to 1.0 once degradationRampMillis has elapsed (and stays at 1.0 after). When the first-match instant is unknown (<= 0) the factor is 1.0 (degenerate — no ramp data, so do not suppress faults). The instants come from the controllable clock so the ramp is deterministic under clock freeze/advance.
      Parameters:
      firstMatchEpochMillis - epoch-ms of the expectation's first match (0 when not yet recorded)
      nowEpochMillis - the current epoch-ms (from the controllable clock)
    • copy

      public HttpChaosProfile copy()
      Returns a copy of this profile with all fields duplicated. Used to derive a transient, gradually-degraded variant without mutating the shared profile attached to the expectation.
    • timeWindowEligible

      public boolean timeWindowEligible(long firstMatchEpochMillis, long nowEpochMillis)
      Returns true when the request falls within the time-based outage window defined by outageAfterMillis and outageDurationMillis, measured relative to the expectation's first match. When both fields are null there is no time gate and this returns true (backward compatible). The window opens outageAfterMillis ms after the first match and, when outageDurationMillis is set, closes (self-heals) after that duration. The instants are supplied by the controllable clock (TimeService) so the window is deterministic under clock freeze/advance.
      Parameters:
      firstMatchEpochMillis - epoch-ms of the expectation's first match (0 when not yet recorded)
      nowEpochMillis - the current epoch-ms (from the controllable clock)
      Returns:
      true if this request is inside the outage window (or there is no window)
    • countWindowEligible

      public boolean countWindowEligible(int matchCount)
      Returns true when the given 1-based match count falls within the chaos-eligible window defined by succeedFirst and failRequestCount. When both fields are null this returns true for any matchCount (backward compatible), including matchCount == 0 which the handler passes when chaos is null (the no-chaos overloads).
      Parameters:
      matchCount - 1-based match count from the expectation (0 when unknown)
      Returns:
      true if this match is eligible for chaos injection
    • equals

      public boolean equals(Object o)
      Overrides:
      equals in class ObjectWithReflectiveEqualsHashCodeToString
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class ObjectWithReflectiveEqualsHashCodeToString