@@ -2455,10 +2455,10 @@ def _leaf_exception(exc: BaseException) -> BaseException:
24552455 return exc
24562456
24572457
2458- async def _redirected_post_error (url : str , location : str ) -> httpx . HTTPStatusError :
2458+ async def _assert_redirected_post_fails (url : str , location : str , expected_message : str ) -> None :
24592459 """Send one request through streamable_http_client, with a client configured to follow redirects,
2460- to a server answering `url` with a 307 to `location`; return the error that ends the connection,
2461- having checked that nothing but `url` was requested."""
2460+ to a server answering `url` with a 307 to `location`, and check that the connection ends with
2461+ HTTPStatusError carrying `expected_message` and that nothing but `url` was requested."""
24622462 urls : list [str ] = []
24632463
24642464 def handler (request : httpx .Request ) -> httpx .Response :
@@ -2480,57 +2480,65 @@ def handler(request: httpx.Request) -> httpx.Response:
24802480 error = _leaf_exception (exc_info .value )
24812481 assert isinstance (error , httpx .HTTPStatusError )
24822482 assert error .response .status_code == 307
2483+ assert str (error ) == expected_message
24832484 assert urls == [url ]
2484- return error
24852485
24862486
24872487@pytest .mark .anyio
24882488async def test_redirect_to_another_origin_is_not_followed_and_fails_the_request () -> None :
24892489 """SDK-defined: a redirect pointing outside the endpoint's origin is not followed, whatever the
24902490 caller's client is configured to do: nothing is sent to the other origin, and the request fails
24912491 the way any non-2xx response does, with HTTPStatusError naming the location."""
2492- error = await _redirected_post_error ("http://mcp.example/mcp" , "http://other.example/mcp/" )
2493-
2494- assert str (error ) == snapshot (
2495- "Redirect to http://other.example/mcp/ not followed; use that URL as the endpoint if it is the intended server"
2492+ await _assert_redirected_post_fails (
2493+ "http://mcp.example/mcp" ,
2494+ "http://other.example/x" ,
2495+ snapshot (
2496+ "Redirect to http://other.example/x not followed; use that URL as the endpoint if it is the intended server"
2497+ ),
24962498 )
24972499
24982500
24992501@pytest .mark .anyio
25002502async def test_https_endpoint_redirected_to_plain_http_is_explained_and_the_https_form_suggested () -> None :
25012503 """SDK-authored text: a redirect of an HTTPS endpoint to plain HTTP on the same host (the usual
25022504 sign of a TLS-terminating proxy the server does not trust) never suggests the http:// URL."""
2503- error = await _redirected_post_error ("https://mcp.example/mcp" , "http://mcp.example/mcp/" )
2504-
2505- assert str (error ) == snapshot ("""\
2505+ await _assert_redirected_post_fails (
2506+ "https://mcp.example/mcp" ,
2507+ "http://mcp.example/mcp/" ,
2508+ snapshot ("""\
25062509 Redirect to http://mcp.example/mcp/ not followed: it would downgrade this HTTPS endpoint to plain HTTP.
25072510The server is likely behind a TLS-terminating proxy whose forwarded headers it does not trust,
25082511often combined with a trailing-slash difference. Try https://mcp.example/mcp/ instead, or fix the proxy settings.\
2509- """ )
2512+ """ ),
2513+ )
25102514
25112515
25122516@pytest .mark .anyio
25132517async def test_unfollowed_redirect_location_is_named_without_its_query_string () -> None :
25142518 """SDK-authored text: the location is reported without query or userinfo, which may carry state
25152519 that does not belong in an error message or a log line."""
2516- error = await _redirected_post_error ("http://mcp.example/mcp" , "https://sso.example/login?state=s3cr3t&nonce=n" )
2517-
2518- assert str (error ) == snapshot (
2519- "Redirect to https://sso.example/login not followed; use that URL as the endpoint if it is the intended server"
2520+ await _assert_redirected_post_fails (
2521+ "http://mcp.example/mcp" ,
2522+ "https://idp.example/l?state=s3cr3t&nonce=n" ,
2523+ snapshot (
2524+ "Redirect to https://idp.example/l not followed; use that URL as the endpoint if it is the intended server"
2525+ ),
25202526 )
25212527
25222528
25232529@pytest .mark .anyio
25242530async def test_https_endpoint_redirected_to_plain_http_elsewhere_never_suggests_the_http_url () -> None :
25252531 """SDK-authored text: the downgrade explanation applies whatever host the http:// location names,
25262532 so the message never offers a plain-HTTP URL as the endpoint to configure."""
2527- error = await _redirected_post_error ("https://mcp.example/mcp" , "http://backend.lan:8000/mcp/" )
2528-
2529- assert str (error ) == snapshot ("""\
2533+ await _assert_redirected_post_fails (
2534+ "https://mcp.example/mcp" ,
2535+ "http://backend.lan:8000/mcp/" ,
2536+ snapshot ("""\
25302537 Redirect to http://backend.lan:8000/mcp/ not followed: it would downgrade this HTTPS endpoint to plain HTTP.
25312538The server is likely behind a TLS-terminating proxy whose forwarded headers it does not trust,
25322539often combined with a trailing-slash difference. Try https://backend.lan:8000/mcp/ instead, or fix the proxy settings.\
2533- """ )
2540+ """ ),
2541+ )
25342542
25352543
25362544@pytest .mark .anyio
0 commit comments