doRequestWithConnRefusedRetry executes client.Do, re-sending ONLY on a pre-response connection-refused (bounded, ctx-aware backoff, request body rewound via GetBody). Any other error, or a real response, returns immediately — so a genuinely crashed/unreachable app still fails fast after the bounded
(ctx context.Context, logger *zap.Logger, client *http.Client, req *http.Request)
| 675 | // immediately — so a genuinely crashed/unreachable app still fails fast after |
| 676 | // the bounded retries, and a mid-response reset is never retried. |
| 677 | func doRequestWithConnRefusedRetry(ctx context.Context, logger *zap.Logger, client *http.Client, req *http.Request) (*http.Response, error) { |
| 678 | for attempt := 0; ; attempt++ { |
| 679 | resp, err := client.Do(req) |
| 680 | if err == nil { |
| 681 | return resp, nil |
| 682 | } |
| 683 | if attempt >= maxConnRefusedRetries || !isPreResponseConnRefused(err) { |
| 684 | return nil, err |
| 685 | } |
| 686 | // Only retry if we can faithfully re-send the body; otherwise stop so we |
| 687 | // never send a truncated/empty body (which would fabricate a result). |
| 688 | if req.Body != nil && req.GetBody == nil { |
| 689 | return nil, err |
| 690 | } |
| 691 | if req.GetBody != nil { |
| 692 | body, gbErr := req.GetBody() |
| 693 | if gbErr != nil { |
| 694 | return nil, err |
| 695 | } |
| 696 | req.Body = body |
| 697 | } |
| 698 | logger.Warn("test request refused by app (app not yet accepting connections); re-sending — the request was never processed, so this is not a retry of an assertion", |
| 699 | zap.Int("attempt", attempt+1), zap.Int("maxRetries", maxConnRefusedRetries), zap.Error(err)) |
| 700 | select { |
| 701 | case <-ctx.Done(): |
| 702 | return nil, err |
| 703 | case <-time.After(connRefusedRetryBackoff * time.Duration(attempt+1)): |
| 704 | } |
| 705 | } |
| 706 | } |
| 707 | |
| 708 | func SimulateHTTP(ctx context.Context, tc *models.TestCase, testSet string, logger *zap.Logger, cfg SimulationConfig) (*models.HTTPResp, error) { |
| 709 | templatedResponse := tc.HTTPResp // keep a copy of the original templatized response |