@@ -113,6 +113,106 @@ class StreamableHTTPTransportTest < ActiveSupport::TestCase
113
113
assert response [ 2 ] . is_a? ( Proc ) # The body should be a Proc for streaming
114
114
end
115
115
116
+ test "handles POST request when IOError raised" do
117
+ # Create and initialize a session
118
+ init_request = create_rack_request (
119
+ "POST" ,
120
+ "/" ,
121
+ { "CONTENT_TYPE" => "application/json" } ,
122
+ { jsonrpc : "2.0" , method : "initialize" , id : "123" } . to_json ,
123
+ )
124
+ init_response = @transport . handle_request ( init_request )
125
+ session_id = init_response [ 1 ] [ "Mcp-Session-Id" ]
126
+
127
+ # Connect with SSE
128
+ io = StringIO . new
129
+ get_request = create_rack_request (
130
+ "GET" ,
131
+ "/" ,
132
+ { "HTTP_MCP_SESSION_ID" => session_id } ,
133
+ )
134
+ response = @transport . handle_request ( get_request )
135
+ response [ 2 ] . call ( io ) if response [ 2 ] . is_a? ( Proc )
136
+
137
+ # Give the stream time to set up
138
+ sleep ( 0.1 )
139
+
140
+ # Close the stream
141
+ io . close
142
+
143
+ request = create_rack_request (
144
+ "POST" ,
145
+ "/" ,
146
+ {
147
+ "CONTENT_TYPE" => "application/json" ,
148
+ "HTTP_MCP_SESSION_ID" => session_id ,
149
+ } ,
150
+ { jsonrpc : "2.0" , method : "ping" , id : "456" } . to_json ,
151
+ )
152
+
153
+ # This should handle IOError and return the original response
154
+ response = @transport . handle_request ( request )
155
+ assert_equal 200 , response [ 0 ]
156
+ assert_equal ( { "Content-Type" => "application/json" } , response [ 1 ] )
157
+
158
+ # Verify session was cleaned up
159
+ assert_not @transport . instance_variable_get ( :@sessions ) . key? ( session_id )
160
+ end
161
+
162
+ test "handles POST request when Errno::EPIPE raised" do
163
+ # Create and initialize a session
164
+ init_request = create_rack_request (
165
+ "POST" ,
166
+ "/" ,
167
+ { "CONTENT_TYPE" => "application/json" } ,
168
+ { jsonrpc : "2.0" , method : "initialize" , id : "123" } . to_json ,
169
+ )
170
+ init_response = @transport . handle_request ( init_request )
171
+ session_id = init_response [ 1 ] [ "Mcp-Session-Id" ]
172
+
173
+ # Create a pipe to simulate EPIPE condition
174
+ reader , writer = IO . pipe
175
+
176
+ # Connect with SSE using the writer end of the pipe
177
+ get_request = create_rack_request (
178
+ "GET" ,
179
+ "/" ,
180
+ { "HTTP_MCP_SESSION_ID" => session_id } ,
181
+ )
182
+ response = @transport . handle_request ( get_request )
183
+ response [ 2 ] . call ( writer ) if response [ 2 ] . is_a? ( Proc )
184
+
185
+ # Give the stream time to set up
186
+ sleep ( 0.1 )
187
+
188
+ # Close the reader end to break the pipe - this will cause EPIPE on write
189
+ reader . close
190
+
191
+ request = create_rack_request (
192
+ "POST" ,
193
+ "/" ,
194
+ {
195
+ "CONTENT_TYPE" => "application/json" ,
196
+ "HTTP_MCP_SESSION_ID" => session_id ,
197
+ } ,
198
+ { jsonrpc : "2.0" , method : "ping" , id : "789" } . to_json ,
199
+ )
200
+
201
+ # This should handle Errno::EPIPE and return the original response
202
+ response = @transport . handle_request ( request )
203
+ assert_equal 200 , response [ 0 ]
204
+ assert_equal ( { "Content-Type" => "application/json" } , response [ 1 ] )
205
+
206
+ # Verify session was cleaned up
207
+ assert_not @transport . instance_variable_get ( :@sessions ) . key? ( session_id )
208
+
209
+ begin
210
+ writer . close
211
+ rescue
212
+ nil
213
+ end
214
+ end
215
+
116
216
test "handles GET request with missing session ID" do
117
217
request = create_rack_request (
118
218
"GET" ,
0 commit comments