Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/a2a/server/apps/jsonrpc/jsonrpc_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from starlette.authentication import BaseUser
from starlette.requests import Request
from starlette.responses import JSONResponse, Response
from starlette.exceptions import HTTPException

from a2a.auth.user import UnauthenticatedUser
from a2a.auth.user import User as A2AUser
Expand Down Expand Up @@ -232,6 +233,13 @@ async def _handle_requests(self, request: Request) -> Response:
request_id,
A2AError(root=InvalidRequestError(data=json.loads(e.json()))),
)
except HTTPException as e:
if e.status_code == 413: # Payload Too Large
Comment thread
holtskinner marked this conversation as resolved.
Outdated
return self._generate_error_response(
request_id,
A2AError(root=InvalidRequestError(message="Payload too large")),
)
raise e
except Exception as e:
logger.error(f'Unhandled exception: {e}')
traceback.print_exc()
Expand Down
89 changes: 89 additions & 0 deletions tests/server/apps/jsonrpc/test_serialization.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from unittest import mock

import pytest
Expand All @@ -9,7 +10,13 @@
AgentCapabilities,
AgentCard,
In,
Message,
Part,
Role,
SecurityScheme,
TextPart,
JSONParseError,
InvalidRequestError,
)
from pydantic import ValidationError

Expand Down Expand Up @@ -92,3 +99,85 @@ def test_fastapi_agent_card_with_api_key_scheme_alias(
assert 'in' in security_scheme_json
assert 'in_' not in security_scheme_json
assert security_scheme_json['in'] == 'header'


def test_handle_invalid_json(agent_card_with_api_key: AgentCard):
"""Test handling of malformed JSON."""
handler = mock.AsyncMock()
app_instance = A2AStarletteApplication(agent_card_with_api_key, handler)
client = TestClient(app_instance.build())

response = client.post('/', content='{ "jsonrpc": "2.0", "method": "test", "id": 1, "params": { "key": "value" }')
assert response.status_code == 200
data = response.json()
assert data['error']['code'] == JSONParseError().code


def test_handle_oversized_payload(agent_card_with_api_key: AgentCard):
"""Test handling of oversized JSON payloads."""
handler = mock.AsyncMock()
app_instance = A2AStarletteApplication(agent_card_with_api_key, handler)
client = TestClient(app_instance.build())

large_string = "a" * 2_000_000 # 2MB string
payload = {
"jsonrpc": "2.0",
"method": "test",
"id": 1,
"params": {"data": large_string},
}

# Starlette/FastAPI's default max request size is around 1MB.
# This test will likely fail with a 413 Payload Too Large if the default is not increased.
# If the application is expected to handle larger payloads, the server configuration needs to be adjusted.
# For this test, we expect a 413 or a graceful JSON-RPC error if the app handles it.

try:
response = client.post('/', json=payload)
# If the app handles it gracefully and returns a JSON-RPC error
if response.status_code == 200:
data = response.json()
assert data['error']['code'] == InvalidRequestError().code
else:
assert response.status_code == 413
Comment thread
holtskinner marked this conversation as resolved.
Outdated
except Exception as e:
# Depending on server setup, it might just drop the connection for very large payloads
assert isinstance(e, (ConnectionResetError, RuntimeError))


def test_handle_unicode_characters(agent_card_with_api_key: AgentCard):
"""Test handling of unicode characters in JSON payload."""
handler = mock.AsyncMock()
app_instance = A2AStarletteApplication(agent_card_with_api_key, handler)
client = TestClient(app_instance.build())

unicode_text = "こんにちは世界" # "Hello world" in Japanese
unicode_payload = {
"jsonrpc": "2.0",
"method": "message/send",
"id": "unicode_test",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": unicode_text}],
"messageId": "msg-unicode"
}
}
}

# Mock a handler for this method
handler.on_message_send.return_value = Message(
role=Role.agent,
parts=[Part(root=TextPart(text=f"Received: {unicode_text}"))],
messageId="response-unicode"
)

response = client.post('/', json=unicode_payload)

# We are not testing the handler logic here, just that the server can correctly
# deserialize the unicode payload without errors. A 200 response with any valid
# JSON-RPC response indicates success.
assert response.status_code == 200
data = response.json()
assert 'error' not in data or data['error'] is None
assert data['result']['parts'][0]['text'] == f"Received: {unicode_text}"
Loading