Repository navigation
fix(mcp): abandon open SSE without poisoning pooled HTTP sockets - #1080
Conversation
Stream#feed used throw to leave an open event stream after the answer arrived. throw is not an Exception, so Net::HTTP::Persistent returned the socket to its pool with the chunked terminator still unread. The next request then failed with wrong status line: "0". Raise Stream::Abandoned instead so adapters that rescue Exception can finish the connection, and rescue it in Stream#read. Add a regression that reuses net_http_persistent across two open-stream calls.
|
The library change looks right. I verified the mechanism independently: with One fix needed for CI: the new regression spec breaks the Faraday v1 job (https://github.com/crmne/ruby_llm/actions/runs/38645047543/job/116129707529). With Suggested guard: it 'reuses a pooled adapter after abandoning an open event stream' do
begin
require 'faraday/net_http_persistent'
require 'net/http/persistent'
rescue LoadError
skip 'net_http_persistent is not available'
end
...The Faraday 2 jobs keep running the example; only the v1 job, whose bundle cannot load the adapter's real dependency, skips it. The fix itself is Faraday-version independent, since the |
|
Please guard the regression example for environments where the persistent adapter's runtime dependency is unavailable. Requiring both Generated by Copilot Triage using |
The Faraday 1 adapter lists net-http-persistent only as its own development dependency. Declare it here so the pooled MCP streaming regression can run in every CI matrix job. Regenerate the Rails appraisal Gemfiles with the adapter and its HTTP client dependency. All 54 MCP HTTP specs pass under Faraday 1 and 2, and all 6,320 non-generator specs pass under Faraday 1. Follow-up to crmne#1080.
Summary
Fixes #1079.
RubyLLM::MCP::HTTP::Stream#feedstopped reading an open chunked SSE body withthrowas soon as the JSON-RPC answer arrived.throwis not anException, soNet::HTTP::Persistentreturned that socket to its pool with the chunked terminator (0\r\n\r\n) still unread. The next request on the same thread then failed with:Fix
Stream::Abandonedwhen the answer arrives instead ofthrow.Stream#readand treat it as a normal early stop.rescue Exception(includingnet_http_persistent) then finish the connection so it is not reused dirty.:net_httpbehavior is unchanged; existing open-stream specs still pass.Verification
New example: two successive
tools/callrequests against a loopback server that keeps the SSE stream open after the answer, withconfig.faraday_adapter = :net_http_persistent. Before the fix the second call failed withwrong status line: "0"; after, both succeed.Also added
faraday-net_http_persistentas a development dependency so that regression can run in CI.Summary by CodeRabbit