Repository navigation
fix(transport): match unix-socket media types case-insensitively - #1453
jokemanfire wants to merge 1 commit into
Conversation
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
@michaelneale is on triage rotation this week and is the reviewer for this one. Labels come from the PR labeler. Please review the change. Automated triage. Labels are a first pass and the assigned maintainer has the final say. |
There was a problem hiding this comment.
🟡 Changes recommended
Misplaced and missing client-side-sse feature gates cause default-feature builds and library tests without that feature to fail with unresolved dependency and function errors.
2 open issues
What changed in this PR
This PR addresses #1443 by aligning Unix-socket response media-type matching with the reqwest transport, accepting case variants and parameters while rejecting prefix lookalikes.
Changes:
- Share the media-type essence matcher through
common/http_header.rs. - Use it for Unix-socket POST dispatch, error handling, and GET stream validation.
- Add helper unit tests and a Unix-socket mixed-case JSON regression test.
| File | Description |
|---|---|
crates/rmcp/tests/test_unix_socket_transport.rs |
Tests parsing JSON with a mixed-case, parameterized content type. |
crates/rmcp/src/transport/common/unix_socket.rs |
Replaces content-type prefix checks with the shared matcher. |
crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs |
Imports the shared matcher instead of defining its own. |
crates/rmcp/src/transport/common/http_header.rs |
Houses the shared matcher and adds unit coverage. |
Review effort: Balanced
| /// `Content-Type` values are case-insensitive by RFC 9110 (``Text/Event-Stream`` | ||
| /// is the same type as ``text/event-stream``) and may carry parameters such as | ||
| /// ``; charset=utf-8``. A byte-prefix check gets both wrong. | ||
| pub(crate) fn media_type_is(value: &[u8], expected: &str) -> bool { |
There was a problem hiding this comment.
Correct, and that is what broke four CI gates (Lint with Clippy, Example test, SemVer Check, Public API Check all failed with cannot find module or crate http at http_header.rs:48) — my insertion split the validator's doc block, so the #[cfg(feature = "client-side-sse")] line migrated to the new function.
Fixed in 257d55b by not putting the helper there at all: common::http_header is compiled without the http dependency, so an ungated helper cannot live in it. media_type_is now lives in common::client_side_http, the module both HTTP clients already use for response handling and which is gated on exactly the features that have a client (transport-streamable-http-client-reqwest or unix + transport-streamable-http-client-unix-socket). http_header.rs is back to its pre-change content, validator gate included.
Verified: cargo clippy --package rmcp --lib --no-default-features --features {client, client-side-sse, transport-streamable-http-client-reqwest, transport-streamable-http-client-unix-socket} -- -D warnings clean for each of the four (the four gates above are green on 257d55b).
| #[test] | ||
| fn media_type_is_case_insensitive() { | ||
| for value in [ | ||
| b"application/json".as_slice(), | ||
| b"Application/JSON".as_slice(), | ||
| b"APPLICATION/JSON; charset=utf-8".as_slice(), | ||
| b" application/json ", | ||
| ] { | ||
| assert!(media_type_is(value, "application/json"), "{value:?}"); | ||
| assert!(!media_type_is(value, "text/event-stream"), "{value:?}"); | ||
| } | ||
| } | ||
|
|
||
| #[test] |
There was a problem hiding this comment.
Also fixed by the same move. The helper and its tests now sit in common::client_side_http, which is only compiled when a streamable HTTP client feature is on, so there is no feature combination where the tests reference a missing function — the ungated-mod tests problem disappears rather than needing per-test gates.
cargo test -p rmcp --lib --features "client transport-streamable-http-client-reqwest transport-streamable-http-client-unix-socket" -- client_side_http -> 23 passed, including the 7 new media_type_is_* cases.
`UnixSocketHttpClient` decided JSON vs SSE with a byte-prefix check, so a response labelled `Application/JSON; charset=utf-8` was rejected with `UnexpectedContentType` even though media types are case-insensitive (RFC 9110) and parameters do not change the essence. Move `media_type_is` out of the reqwest client into `common::client_side_http`, the module both HTTP clients already share for response handling, and use it at all four unix-socket call sites so the two clients agree. `common::http_header` is compiled without the `http` dependency, so the helper cannot live there. Fixes modelcontextprotocol#1443 Signed-off-by: jokemanfire <[email protected]>
8ed8e55 to
257d55b
Compare

Fixes #1443
Problem
UnixSocketHttpClientchose JSON vs SSE by byte-prefix comparison:Media types are case-insensitive (RFC 9110 §8.3.1) and may carry parameters, so
a response labelled
Application/JSON; charset=utf-8— legal and common behindproxies — was rejected with
UnexpectedContentType, failing the request eventhough the body was a valid JSON-RPC response.
Change
media_type_isfromcommon/reqwest/streamable_http_client.rsintocommon/http_header.rsas the one shared essence-comparison helper (italready ignores parameters and ASCII case).
GET stream validation).
copy, so the two transports cannot drift again.
Evidence
New transport test
test_unix_socket_mixed_case_json_content_type_is_parsedserves
{"jsonrpc":"2.0","id":1,"result":{}}withContent-Type: Application/JSON; charset=utf-8.Before the change (source reverted, test kept):
After:
Unit tests for the helper cover
Application/JSON,APPLICATION/JSON; charset=utf-8,leading/trailing spaces, and non-matches (
application/jsonx,application/json-patch+json).Notes
pub(crate).test_unix_socket_*iscfg(unix), so the regression test is compiled out onWindows, matching the rest of that suite.