|
7 | 7 |
|
8 | 8 | import anyio |
9 | 9 | import pytest |
10 | | -from starlette.types import Message, Receive, Scope, Send |
| 10 | +from starlette.types import Message, Scope |
11 | 11 |
|
12 | 12 | from mcp.server import streamable_http_manager |
13 | 13 | from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser |
|
16 | 16 | from mcp.server.streamable_http import MCP_SESSION_ID_HEADER, StreamableHTTPServerTransport |
17 | 17 | from mcp.server.streamable_http_manager import ( |
18 | 18 | DEFAULT_MAX_REQUEST_BODY_SIZE, |
19 | | - RequestBodyLimitMiddleware, |
20 | 19 | StreamableHTTPSessionManager, |
21 | 20 | ) |
22 | 21 | from mcp.types import INVALID_REQUEST |
@@ -143,92 +142,6 @@ async def send(message: Message) -> None: |
143 | 142 | assert response_start["status"] == 413 |
144 | 143 |
|
145 | 144 |
|
146 | | -@pytest.mark.anyio |
147 | | -async def test_request_body_chunks_are_replayed_as_one_message() -> None: |
148 | | - """SDK-defined: raw ASGI proves chunk overhead is discarded before the body reaches the transport.""" |
149 | | - request_messages: Iterator[Message] = iter( |
150 | | - [ |
151 | | - {"type": "http.request", "body": b"12", "more_body": True}, |
152 | | - {"type": "http.request", "body": b"34", "more_body": True}, |
153 | | - {"type": "http.request", "body": b"56", "more_body": False}, |
154 | | - {"type": "http.disconnect"}, |
155 | | - ] |
156 | | - ) |
157 | | - received_messages: list[Message] = [] |
158 | | - |
159 | | - async def receive() -> Message: |
160 | | - return next(request_messages) |
161 | | - |
162 | | - async def app(scope: Scope, receive: Receive, send: Send) -> None: |
163 | | - received_messages.append(await receive()) |
164 | | - received_messages.append(await receive()) |
165 | | - |
166 | | - scope: Scope = {"type": "http", "method": "POST", "path": "/mcp", "headers": []} |
167 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
168 | | - |
169 | | - await middleware(scope, receive, AsyncMock()) |
170 | | - |
171 | | - assert received_messages == [ |
172 | | - {"type": "http.request", "body": b"123456", "more_body": False}, |
173 | | - {"type": "http.disconnect"}, |
174 | | - ] |
175 | | - |
176 | | - |
177 | | -@pytest.mark.anyio |
178 | | -async def test_disconnect_before_request_body_is_replayed() -> None: |
179 | | - """SDK-defined: raw ASGI proves a disconnect before the first body message reaches the transport.""" |
180 | | - disconnect: Message = {"type": "http.disconnect"} |
181 | | - received_messages: list[Message] = [] |
182 | | - |
183 | | - async def receive() -> Message: |
184 | | - return disconnect |
185 | | - |
186 | | - async def app(scope: Scope, receive: Receive, send: Send) -> None: |
187 | | - received_messages.append(await receive()) |
188 | | - |
189 | | - scope: Scope = {"type": "http", "method": "POST", "path": "/mcp", "headers": []} |
190 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
191 | | - |
192 | | - await middleware(scope, receive, AsyncMock()) |
193 | | - |
194 | | - assert received_messages == [disconnect] |
195 | | - |
196 | | - |
197 | | -@pytest.mark.anyio |
198 | | -@pytest.mark.parametrize("method", ["GET", "PUT", "OPTIONS", "HEAD", "DELETE"]) |
199 | | -async def test_request_body_limit_applies_to_every_method(method: str) -> None: |
200 | | - """SDK-defined: the limit is a property of the request body, not of the method that carries it.""" |
201 | | - app = AsyncMock() |
202 | | - sent_messages: list[Message] = [] |
203 | | - receive = AsyncMock(return_value={"type": "http.request", "body": b"123456789", "more_body": False}) |
204 | | - |
205 | | - async def send(message: Message) -> None: |
206 | | - sent_messages.append(message) |
207 | | - |
208 | | - scope: Scope = {"type": "http", "method": method, "path": "/mcp", "headers": []} |
209 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
210 | | - |
211 | | - await middleware(scope, receive, send) |
212 | | - |
213 | | - assert [message["status"] for message in sent_messages if message["type"] == "http.response.start"] == [413] |
214 | | - app.assert_not_awaited() |
215 | | - |
216 | | - |
217 | | -@pytest.mark.anyio |
218 | | -async def test_request_body_limit_leaves_non_http_scopes_alone() -> None: |
219 | | - """SDK-defined: only HTTP requests carry a body to limit; other ASGI scopes go straight to the app.""" |
220 | | - app = AsyncMock() |
221 | | - receive = AsyncMock() |
222 | | - send = AsyncMock() |
223 | | - scope: Scope = {"type": "lifespan"} |
224 | | - middleware = RequestBodyLimitMiddleware(app, max_body_size=8) |
225 | | - |
226 | | - await middleware(scope, receive, send) |
227 | | - |
228 | | - app.assert_awaited_once_with(scope, receive, send) |
229 | | - receive.assert_not_awaited() |
230 | | - |
231 | | - |
232 | 145 | def test_request_body_limit_defaults_to_four_mib() -> None: |
233 | 146 | """SDK-defined: Streamable HTTP request bodies are limited to 4 MiB by default.""" |
234 | 147 | manager = StreamableHTTPSessionManager(app=Server("test-default-size-limit")) |
|
0 commit comments