Skip to content

fix(vmcp): exempt SSE connections from WriteTimeout to prevent drops#4167

Open
yrobla wants to merge 1 commit intomainfrom
issue-3691
Open

fix(vmcp): exempt SSE connections from WriteTimeout to prevent drops#4167
yrobla wants to merge 1 commit intomainfrom
issue-3691

Conversation

@yrobla
Copy link
Contributor

@yrobla yrobla commented Mar 16, 2026

Summary

Go's http.Server.WriteTimeout sets an absolute deadline on the entire response duration. For long-lived SSE connections used by the MCP Streamable HTTP transport, this caused streams to be killed after 30s regardless of write activity (see golang/go#16100).

Approach: Keep http.Server.WriteTimeout (it protects all routes from slow/hung non-SSE clients), and add transportmiddleware.WriteTimeout in pkg/transport/middleware that uses http.ResponseController.SetWriteDeadline(time.Time{}) to clear the write deadline for qualifying SSE connections only. A connection qualifies when all three hold: method is GET, Accept header contains text/event-stream, and path matches the MCP endpoint exactly. Non-qualifying requests are left completely untouched — http.Server.WriteTimeout remains in effect for them.

Tests added:

  • Unit tests verifying SetWriteDeadline is called (qualifying SSE) / not called (POST, DELETE, GET without SSE headers, GET on wrong path)
  • TCP-level integration test confirming an SSE stream survives past WriteTimeout (asserts both elapsed time ≥ stream duration and event count > what's possible before timeout fires)
  • vMCP server integration tests verifying the middleware is correctly wired end-to-end

Fixes #3691

Type of change

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe):

Test plan

  • Unit tests (task test)
  • E2E tests (task test-e2e)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Changes

File Change
pkg/transport/middleware/write_timeout.go New: WriteTimeout middleware — clears write deadline for qualifying SSE connections only
pkg/transport/middleware/write_timeout_test.go New: unit + TCP-level integration tests
pkg/vmcp/server/server.go Apply transportmiddleware.WriteTimeout to MCP handler; keep http.Server.WriteTimeout for all other routes
pkg/vmcp/server/write_timeout_integration_test.go New: vMCP server integration tests for SSE write timeout behaviour
pkg/vmcp/server/session_management_realbackend_integration_test.go Refactor: extract newRealTestHandler helper to allow httptest.Server configuration in tests

Does this introduce a user-facing change?

No — this is an internal fix. SSE streams that were silently dropped after 30s will now remain open.

Special notes for reviewers

The middleware only touches qualifying SSE requests (GET + Accept: text/event-stream + matching endpoint path). Non-qualifying requests are intentionally left untouched — resetting the deadline to time.Now().Add(defaultTimeout) would extend it (since time.Now() is later than connection-accept time), weakening the protection for health, metrics, and JSON-RPC POST routes. If SetWriteDeadline fails on a qualifying SSE connection (e.g., a wrapping ResponseWriter that doesn't implement the interface), a Warn log is emitted so the gap is visible in observability tooling.

@github-actions github-actions bot added the size/S Small PR: 100-299 lines changed label Mar 16, 2026
@github-actions github-actions bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 16, 2026
@yrobla yrobla requested a review from Copilot March 16, 2026 12:16
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 04169cdc72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes long-lived SSE (Streamable HTTP) connections in the vMCP server being dropped after ~30s by removing the global http.Server.WriteTimeout and replacing it with a per-request write deadline applied only to non-SSE requests.

Changes:

  • Removed http.Server.WriteTimeout from the vMCP server to avoid killing long-lived SSE (GET) streams.
  • Added writeTimeoutMiddleware using http.ResponseController.SetWriteDeadline to enforce per-request write deadlines on non-GET requests.
  • Added unit + integration tests to validate deadline-setting behavior and confirm SSE streams survive past the timeout.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
pkg/vmcp/server/server.go Removes global WriteTimeout; adds per-request write-deadline middleware and wires it into the MCP handler chain.
pkg/vmcp/server/write_timeout_middleware_test.go Adds tests verifying deadline behavior by method and an end-to-end SSE survivability test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Mar 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.13%. Comparing base (0de510d) to head (4eaf86e).
⚠️ Report is 18 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #4167    +/-   ##
========================================
  Coverage   69.13%   69.13%            
========================================
  Files         462      467     +5     
  Lines       46554    46808   +254     
========================================
+ Hits        32185    32362   +177     
+ Misses      11897    11894     -3     
- Partials     2472     2552    +80     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 16, 2026
@yrobla yrobla requested a review from Copilot March 16, 2026 12:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses long-lived SSE connections being dropped due to Go’s http.Server.WriteTimeout applying an absolute response deadline, impacting the vMCP Streamable HTTP transport.

Changes:

  • Adds a writeTimeoutMiddleware intended to exempt GET (SSE) requests from write timeouts by clearing the write deadline via http.ResponseController.SetWriteDeadline(time.Time{}).
  • Wires the middleware into the MCP handler chain.
  • Adds unit + TCP-level integration tests validating SSE streams survive past a configured WriteTimeout.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
pkg/vmcp/server/server.go Introduces and wires writeTimeoutMiddleware to clear write deadlines for SSE GET requests.
pkg/vmcp/server/write_timeout_middleware_test.go Adds unit tests for deadline behavior by method and an integration-style test ensuring SSE survives beyond WriteTimeout.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@yrobla yrobla changed the title fix(vmcp): remove WriteTimeout to prevent SSE connection drops fix(vmcp): exempt SSE connections from WriteTimeout to prevent drops Mar 16, 2026
@github-actions github-actions bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 16, 2026
@yrobla yrobla requested review from ChrisJBurns and blkt as code owners March 17, 2026 11:14
@yrobla yrobla requested a review from Copilot March 17, 2026 11:14
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/S Small PR: 100-299 lines changed labels Mar 17, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses long-lived MCP Streamable HTTP SSE connections being dropped due to Go’s http.Server.WriteTimeout, by introducing a per-request write-deadline middleware that clears the deadline for qualifying SSE GET requests while keeping server-level timeouts enabled for other traffic.

Changes:

  • Add a transport-level WriteTimeout middleware that clears the write deadline for qualifying SSE requests via http.ResponseController.SetWriteDeadline(time.Time{}).
  • Wire the middleware into the vMCP server handler chain for MCP routes while retaining http.Server.WriteTimeout.
  • Add unit tests plus a TCP-level integration test to validate SSE streams aren’t terminated by server WriteTimeout.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
pkg/vmcp/server/server.go Applies the new write-deadline middleware to the MCP handler chain and updates timeout-related comments.
pkg/transport/middleware/write_timeout.go Introduces the per-request write-deadline management middleware and SSE request detection.
pkg/transport/middleware/write_timeout_test.go Adds unit and integration tests covering deadline setting/clearing behavior and SSE stream survivability.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@yrobla yrobla requested a review from Copilot March 17, 2026 11:28
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Mar 17, 2026
@github-actions github-actions bot removed the size/M Medium PR: 300-599 lines changed label Mar 17, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes vMCP Streamable HTTP SSE connections being dropped by Go’s http.Server.WriteTimeout by clearing the per-connection write deadline for qualifying SSE GET requests, while keeping the server-level timeout protection for non-SSE traffic.

Changes:

  • Add transportmiddleware.WriteTimeout to clear the write deadline for SSE GET requests to the MCP endpoint path.
  • Wire the middleware into the vMCP handler chain while retaining http.Server.WriteTimeout.
  • Add unit + integration coverage for the middleware and refactor real-backend test helpers to allow configuring httptest.Server.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/vmcp/server/server.go Wires write-timeout middleware into MCP handler chain and updates timeout documentation.
pkg/transport/middleware/write_timeout.go Introduces middleware that clears write deadlines for qualifying SSE requests.
pkg/transport/middleware/write_timeout_test.go Adds unit tests (deadline clearing) and an end-to-end TCP test for SSE survivability past WriteTimeout.
pkg/vmcp/server/write_timeout_integration_test.go Adds integration tests against the full vMCP handler/server configuration.
pkg/vmcp/server/session_management_realbackend_integration_test.go Refactors helper to return a handler so tests can control httptest.Server config (e.g., WriteTimeout).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions github-actions bot added the size/M Medium PR: 300-599 lines changed label Mar 17, 2026
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Mar 17, 2026
@yrobla yrobla requested a review from Copilot March 17, 2026 11:41
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Mar 17, 2026
@yrobla yrobla requested a review from jerm-dro March 17, 2026 11:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes vMCP Streamable HTTP SSE connections being dropped by Go’s http.Server.WriteTimeout by introducing a targeted middleware that clears the write deadline only for qualifying SSE GET requests to the MCP endpoint, while keeping WriteTimeout protections for all other routes.

Changes:

  • Added transportmiddleware.WriteTimeout to clear per-request write deadlines for qualifying SSE connections.
  • Wired the new middleware into the vMCP server’s MCP handler chain while retaining http.Server.WriteTimeout globally.
  • Added unit and integration tests to verify deadline clearing and end-to-end SSE survivability past server write timeouts.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/transport/middleware/write_timeout.go New middleware that clears write deadlines for qualifying SSE GET requests to the MCP endpoint.
pkg/transport/middleware/write_timeout_test.go Unit tests for qualification logic + TCP-level integration test verifying SSE survives past server WriteTimeout.
pkg/vmcp/server/server.go Applies the new middleware to the MCP handler chain; documents rationale for keeping server-level WriteTimeout.
pkg/vmcp/server/write_timeout_integration_test.go New vMCP integration tests covering SSE survival past WriteTimeout and non-SSE GET rejection behavior.
pkg/vmcp/server/session_management_realbackend_integration_test.go Refactors test setup to expose a reusable handler builder for configurable httptest.Server settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Go's http.Server.WriteTimeout sets an absolute deadline on the entire
response duration. For long-lived SSE connections used by the MCP
Streamable HTTP transport, this caused streams to be killed after 30s
regardless of write activity (see golang/go#16100).

Remove WriteTimeout from the http.Server config and replace it with a
writeTimeoutMiddleware that uses http.ResponseController.SetWriteDeadline
to apply a per-request 30s write deadline only to non-GET (non-SSE)
requests. GET requests remain exempt so SSE streams can stay open
indefinitely.

Tests added:
- Unit tests verifying SetWriteDeadline is called/not called by method
- Integration test over a real TCP connection confirming an SSE stream
  survives past the timeout without being cut

Fixes #3691
@github-actions github-actions bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Mar 17, 2026
// SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

package server_test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: delete this test. It is redundant with the new write_timeout_test.go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Medium PR: 300-599 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove WriteTimeout from vMCP server to prevent SSE connection drops

4 participants