|
27 | 27 | import io.modelcontextprotocol.util.Assert; |
28 | 28 | import io.modelcontextprotocol.util.KeepAliveScheduler; |
29 | 29 | import jakarta.servlet.AsyncContext; |
| 30 | +import jakarta.servlet.AsyncEvent; |
| 31 | +import jakarta.servlet.AsyncListener; |
30 | 32 | import jakarta.servlet.ServletException; |
31 | 33 | import jakarta.servlet.annotation.WebServlet; |
32 | 34 | import jakarta.servlet.http.HttpServlet; |
@@ -232,6 +234,40 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) |
232 | 234 | AsyncContext asyncContext = request.startAsync(); |
233 | 235 | asyncContext.setTimeout(0); |
234 | 236 |
|
| 237 | + asyncContext.addListener(new AsyncListener() { |
| 238 | + @Override |
| 239 | + public void onComplete(AsyncEvent event) { |
| 240 | + logger.debug("AsyncContext completed for session {}", sessionId); |
| 241 | + sessions.remove(sessionId); |
| 242 | + } |
| 243 | + |
| 244 | + @Override |
| 245 | + public void onTimeout(AsyncEvent event) { |
| 246 | + logger.warn("Session {} timeout, cleaning up", sessionId); |
| 247 | + sessions.remove(sessionId); |
| 248 | + try { |
| 249 | + event.getAsyncContext().complete(); |
| 250 | + } catch (Exception e) { |
| 251 | + logger.error("Error completing async context on timeout", e); |
| 252 | + } |
| 253 | + } |
| 254 | + |
| 255 | + @Override |
| 256 | + public void onError(AsyncEvent event) { |
| 257 | + logger.error("AsyncContext error for session {}: {}", sessionId, event.getThrowable().getMessage()); |
| 258 | + sessions.remove(sessionId); |
| 259 | + try { |
| 260 | + event.getAsyncContext().complete(); |
| 261 | + } catch (Exception e) { |
| 262 | + logger.error("Error completing async context on error", e); |
| 263 | + } |
| 264 | + } |
| 265 | + |
| 266 | + @Override |
| 267 | + public void onStartAsync(AsyncEvent event) { |
| 268 | + } |
| 269 | + }); |
| 270 | + |
235 | 271 | PrintWriter writer = response.getWriter(); |
236 | 272 |
|
237 | 273 | // Create a new session transport |
|
0 commit comments