Skip to content

Commit 10ba4dd

Browse files
authored
fix(proxyd): separate context for tx validation middleware (#567)
* fix(proxyd): separate context for tx validation middleware * update comment * Remove stale test * Readd test to check that context is NOT canceled
1 parent f46da0d commit 10ba4dd

File tree

2 files changed

+30
-19
lines changed

2 files changed

+30
-19
lines changed

proxyd/tx_validation_middleware.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ func NewTxValidationClient(timeoutSeconds int) *TxValidationClient {
8080
// Validate performs the HTTP request to the validation middleware service.
8181
// Returns a map of tx hashes to unauthorized status.
8282
func (c *TxValidationClient) Validate(ctx context.Context, endpoint string, payload []byte) (map[string]bool, error) {
83-
ctx, cancel := context.WithTimeout(ctx, c.timeout)
83+
// Detach from the request context so that client disconnects don't cancel
84+
// the middleware request, allowing transactions to bypass validation when failing open.
85+
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), c.timeout)
8486
defer cancel()
8587

8688
req, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(payload))

proxyd/tx_validation_middleware_test.go

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,33 @@ func TestTxValidationClient_ErrorResponse(t *testing.T) {
293293
require.Equal(t, ErrInternal, err)
294294
}
295295

296+
func TestTxValidationClient_CanceledParentContextStillValidates(t *testing.T) {
297+
requestReachedServer := make(chan struct{}, 1)
298+
299+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
300+
requestReachedServer <- struct{}{}
301+
time.Sleep(50 * time.Millisecond)
302+
w.Header().Set("Content-Type", "application/json")
303+
w.WriteHeader(http.StatusOK)
304+
_, _ = w.Write([]byte(`{"unauthorized": {}}`))
305+
}))
306+
defer server.Close()
307+
308+
ctx, cancel := context.WithCancel(context.Background())
309+
cancel() // Cancel parent request context before validation call
310+
311+
client := NewTxValidationClient(5)
312+
unauthorized, err := client.Validate(ctx, server.URL, []byte(`{}`))
313+
require.NoError(t, err)
314+
require.Empty(t, unauthorized)
315+
316+
select {
317+
case <-requestReachedServer:
318+
case <-time.After(500 * time.Millisecond):
319+
t.Fatal("validation request did not reach server")
320+
}
321+
}
322+
296323
func TestDecodeSignedTx(t *testing.T) {
297324
tx := createSignedTestTransaction(t)
298325
txBytes, err := tx.MarshalBinary()
@@ -322,24 +349,6 @@ func TestDecodeSignedTx_Missing0xPrefix(t *testing.T) {
322349
require.Error(t, err)
323350
}
324351

325-
func TestTxValidationClient_CanceledContext(t *testing.T) {
326-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
327-
time.Sleep(100 * time.Millisecond)
328-
w.Header().Set("Content-Type", "application/json")
329-
w.WriteHeader(http.StatusOK)
330-
_, _ = w.Write([]byte(`{"unauthorized": {}}`))
331-
}))
332-
defer server.Close()
333-
334-
ctx, cancel := context.WithCancel(context.Background())
335-
cancel() // Cancel immediately
336-
337-
client := NewTxValidationClient(5)
338-
_, err := client.Validate(ctx, server.URL, []byte(`{}`))
339-
require.Error(t, err)
340-
require.True(t, errors.Is(err, context.Canceled))
341-
}
342-
343352
func TestValidateTransactions_CanceledContext(t *testing.T) {
344353
tx := createSignedTestTransaction(t)
345354

0 commit comments

Comments
 (0)