|
5 | 5 | "encoding/json" |
6 | 6 | "io" |
7 | 7 | "net/http" |
| 8 | + "strings" |
8 | 9 | "testing" |
9 | 10 | "time" |
10 | 11 |
|
@@ -56,20 +57,56 @@ func FetchVaultPublicKey(t *testing.T, gatewayURL string) (publicKey string) { |
56 | 57 | } |
57 | 58 |
|
58 | 59 | func sendVaultRequestToGateway(t *testing.T, gatewayURL string, requestBody []byte) (statusCode int, body []byte) { |
| 60 | + const maxRetries = 7 |
| 61 | + const retryInterval = 2 * time.Second |
| 62 | + |
59 | 63 | framework.L.Info().Msgf("Request Body: %s", string(requestBody)) |
60 | | - req, err := http.NewRequestWithContext(t.Context(), "POST", gatewayURL, bytes.NewBuffer(requestBody)) |
61 | | - require.NoError(t, err, "failed to create request") |
62 | 64 |
|
63 | | - req.Header.Set("Content-Type", "application/json") |
64 | | - req.Header.Set("Accept", "application/json") |
| 65 | + for attempt := range maxRetries + 1 { |
| 66 | + req, err := http.NewRequestWithContext(t.Context(), "POST", gatewayURL, bytes.NewBuffer(requestBody)) |
| 67 | + require.NoError(t, err, "failed to create request") |
| 68 | + |
| 69 | + req.Header.Set("Content-Type", "application/json") |
| 70 | + req.Header.Set("Accept", "application/json") |
| 71 | + |
| 72 | + client := &http.Client{} |
| 73 | + resp, err := client.Do(req) |
| 74 | + require.NoError(t, err, "failed to execute request") |
65 | 75 |
|
66 | | - client := &http.Client{} |
67 | | - resp, err := client.Do(req) |
68 | | - require.NoError(t, err, "failed to execute request") |
69 | | - defer resp.Body.Close() |
| 76 | + body, err = io.ReadAll(resp.Body) |
| 77 | + resp.Body.Close() |
| 78 | + require.NoError(t, err, "failed to read http response body") |
| 79 | + statusCode = resp.StatusCode |
70 | 80 |
|
71 | | - body, err = io.ReadAll(resp.Body) |
72 | | - require.NoError(t, err, "failed to read http response body") |
73 | | - framework.L.Info().Msgf("HTTP Response Body: %s", string(body)) |
74 | | - return resp.StatusCode, body |
| 81 | + framework.L.Info().Msgf("HTTP Response Body: %s", string(body)) |
| 82 | + |
| 83 | + if !isGatewayNotAllowlistedError(body) { |
| 84 | + return statusCode, body |
| 85 | + } |
| 86 | + |
| 87 | + if attempt < maxRetries { |
| 88 | + framework.L.Warn().Msgf("Request not yet allowlisted, retrying in %s (attempt %d/%d)...", retryInterval, attempt+1, maxRetries) |
| 89 | + time.Sleep(retryInterval) |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + return statusCode, body |
| 94 | +} |
| 95 | + |
| 96 | +// isGatewayNotAllowlistedError checks whether the response is a gateway-level |
| 97 | +// "request not allowlisted" rejection (method is empty, error code -32600). |
| 98 | +// Node-level rejections (method is set, code -32603) have a different format |
| 99 | +// and must not be retried because the gateway has already consumed the request. |
| 100 | +func isGatewayNotAllowlistedError(body []byte) bool { |
| 101 | + var resp struct { |
| 102 | + Method string `json:"method"` |
| 103 | + Error *struct { |
| 104 | + Message string `json:"message"` |
| 105 | + } `json:"error"` |
| 106 | + } |
| 107 | + if json.Unmarshal(body, &resp) != nil { |
| 108 | + return false |
| 109 | + } |
| 110 | + return resp.Method == "" && resp.Error != nil && |
| 111 | + strings.Contains(resp.Error.Message, "request not allowlisted") |
75 | 112 | } |
0 commit comments