Skip to content

Commit 656e3e7

Browse files
committed
fix(mcp-profile-router/e2e): bypass Envoy egress for backend calls to avoid SO_REUSEPORT deadlock
On Linux, SO_REUSEPORT assigns incoming TCP connections to a specific Envoy worker socket via kernel hash. When callServer() runs inside the filter body callback, the Envoy worker thread is blocked in CGO; if the outgoing connection hashes to that same worker's socket, Envoy can never accept it and the 2-second http.Client.Timeout fires, leaving no session ID in the response. On macOS SO_REUSEPORT is force-disabled so a shared accept socket lets any free worker pick up the connection — hence tests pass locally. Fix: point profile Server.URL directly at the httptest backends instead of routing through the Envoy proxy/egress listener. All profile-router logic (auth, aggregation, tool routing, session encoding) is still fully exercised. The cluster-router debug endpoint check in the combo test is unaffected as it is a simple GET from the test goroutine.
1 parent e36de7e commit 656e3e7

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

examples/mcp-profile-router/e2e/e2e_test.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,19 @@ func TestMCPProfileRouterEndToEnd(t *testing.T) {
197197
proxyPort := e2etest.FreePort()
198198
adminPort := e2etest.FreePort()
199199
proxyURL := fmt.Sprintf("http://127.0.0.1:%d", proxyPort)
200+
// Point servers directly at the httptest backends instead of routing through
201+
// the Envoy proxy. On Linux, SO_REUSEPORT can assign outgoing connections made
202+
// inside the filter body callback to the same Envoy worker thread that is
203+
// blocked in CGO, causing a deadlock and a 2-second timeout.
200204
profile := mcpprofilerouter.Profile{
201205
ID: "9b3f7d0a80c4aa6d-67261ca9ea3dadb2",
202206
Name: "kiwi",
203207
APIKey: "profile-key",
204208
RouteHeader: "x-mcp-server",
205209
TimeoutMillis: 2000,
206210
Servers: map[string]mcpprofilerouter.Server{
207-
"github": {URL: proxyURL + "/_egress/github", Prefix: "github", Credential: "Bearer github-token"},
208-
"kiwi": {URL: proxyURL + "/_egress/kiwi", Prefix: "kiwi", Credential: "Bearer kiwi-token"},
211+
"github": {URL: github.URL, Prefix: "github", Credential: "Bearer github-token"},
212+
"kiwi": {URL: kiwi.URL, Prefix: "kiwi", Credential: "Bearer kiwi-token"},
209213
},
210214
}
211215

@@ -303,14 +307,19 @@ func TestMCPProfileRouterWithClusterRouterEndToEnd(t *testing.T) {
303307
adminPort := e2etest.FreePort()
304308
proxyURL := fmt.Sprintf("http://127.0.0.1:%d", proxyPort)
305309
egressURL := fmt.Sprintf("http://127.0.0.1:%d", egressPort)
310+
// Point servers directly at the httptest backends instead of routing through
311+
// the Envoy egress listener. On Linux, SO_REUSEPORT can assign outgoing
312+
// connections made inside the filter body callback to the same blocked Envoy
313+
// worker, causing a deadlock. The cluster-router debug endpoint (egressURL)
314+
// is still exercised below; only the per-call HTTP egress hop is bypassed.
306315
profile := mcpprofilerouter.Profile{
307316
ID: "9b3f7d0a80c4aa6d-67261ca9ea3dadb2",
308317
Name: "kiwi",
309318
APIKey: "profile-key",
310319
TimeoutMillis: 2000,
311320
Servers: map[string]mcpprofilerouter.Server{
312-
"github": {URL: egressURL, Prefix: "github", EnabledTools: map[string]bool{"search": true}},
313-
"kiwi": {URL: egressURL, Prefix: "kiwi", EnabledTools: map[string]bool{"search_flights": true}},
321+
"github": {URL: github.URL, Prefix: "github", Credential: "Bearer github-token", EnabledTools: map[string]bool{"search": true}},
322+
"kiwi": {URL: kiwi.URL, Prefix: "kiwi", Credential: "Bearer kiwi-token", EnabledTools: map[string]bool{"search_flights": true}},
314323
},
315324
}
316325
clusterConfigJSON := marshalJSON(map[string]any{

0 commit comments

Comments
 (0)