Skip to content

Commit 686e40a

Browse files
authored
fix: Add Content-Type header to push notification requests (#489)
Set `Content-Type: application/json` header when sending push notifications to prevent `415 Unsupported Media Type` errors. The request was previously defaulting to `application/octet-stream`. Updates tests to verify that the `Content-Type` header is always present in push notification requests, regardless of whether authentication token is included. Fixes: #486 Signed-off-by: Jeff Mesnil <[email protected]> - [X] Follow the [`CONTRIBUTING` Guide](../CONTRIBUTING.md). - [X] Make your Pull Request title in the <https://www.conventionalcommits.org/> specification. - Important Prefixes for [release-please](https://github.com/googleapis/release-please): - `fix:` which represents bug fixes, and correlates to a [SemVer](https://semver.org/) patch. - `feat:` represents a new feature, and correlates to a SemVer minor. - `feat!:`, or `fix!:`, `refactor!:`, etc., which represent a breaking change (indicated by the `!`) and will result in a SemVer major. - [X] Ensure the tests pass - [X] Appropriate READMEs were updated (if necessary) Fixes #486 🦕 Signed-off-by: Jeff Mesnil <[email protected]>
1 parent fcc02ec commit 686e40a

File tree

5 files changed

+30
-8
lines changed

5 files changed

+30
-8
lines changed

extras/push-notification-config-store-database-jpa/src/test/java/io/a2a/extras/pushnotificationconfigstore/database/jpa/JpaPushNotificationConfigStoreTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.a2a.extras.pushnotificationconfigstore.database.jpa;
22

3+
import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
4+
import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
35
import static org.junit.jupiter.api.Assertions.assertEquals;
46
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
57
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -235,6 +237,7 @@ public void testSendNotificationSuccess() throws Exception {
235237
// Mock successful HTTP response
236238
when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
237239
when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
240+
when(mockPostBuilder.addHeader(CONTENT_TYPE, APPLICATION_JSON)).thenReturn(mockPostBuilder);
238241
when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
239242
when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
240243
when(mockHttpResponse.success()).thenReturn(true);
@@ -245,6 +248,7 @@ public void testSendNotificationSuccess() throws Exception {
245248
ArgumentCaptor<String> bodyCaptor = ArgumentCaptor.forClass(String.class);
246249
verify(mockHttpClient).createPost();
247250
verify(mockPostBuilder).url(config.url());
251+
verify(mockPostBuilder).addHeader(CONTENT_TYPE, APPLICATION_JSON);
248252
verify(mockPostBuilder).body(bodyCaptor.capture());
249253
verify(mockPostBuilder).post();
250254

http-client/src/main/java/io/a2a/client/http/A2AHttpClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
public interface A2AHttpClient {
99

10+
String CONTENT_TYPE= "Content-Type";
11+
String APPLICATION_JSON= "application/json";
12+
1013
GetBuilder createGet();
1114

1215
PostBuilder createPost();

server-common/src/main/java/io/a2a/server/tasks/BasePushNotificationSender.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.a2a.server.tasks;
22

3+
import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
4+
import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
35
import static io.a2a.common.A2AHeaders.X_A2A_NOTIFICATION_TOKEN;
46
import jakarta.enterprise.context.ApplicationScoped;
57
import jakarta.inject.Inject;
@@ -90,6 +92,7 @@ private boolean dispatchNotification(Task task, PushNotificationConfig pushInfo)
9092
try {
9193
postBuilder
9294
.url(url)
95+
.addHeader(CONTENT_TYPE, APPLICATION_JSON)
9396
.body(body)
9497
.post();
9598
} catch (IOException | InterruptedException e) {

server-common/src/test/java/io/a2a/server/tasks/InMemoryPushNotificationConfigStoreTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.a2a.server.tasks;
22

3+
import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
4+
import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
35
import static org.junit.jupiter.api.Assertions.assertEquals;
46
import static org.junit.jupiter.api.Assertions.assertNotNull;
57
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -50,6 +52,7 @@ public void setUp() {
5052
private void setupBasicMockHttpResponse() throws Exception {
5153
when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
5254
when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
55+
when(mockPostBuilder.addHeader(CONTENT_TYPE, APPLICATION_JSON)).thenReturn(mockPostBuilder);
5356
when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
5457
when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
5558
when(mockHttpResponse.success()).thenReturn(true);
@@ -230,11 +233,7 @@ public void testSendNotificationSuccess() throws Exception {
230233
configStore.setInfo(taskId, config);
231234

232235
// Mock successful HTTP response
233-
when(mockHttpClient.createPost()).thenReturn(mockPostBuilder);
234-
when(mockPostBuilder.url(any(String.class))).thenReturn(mockPostBuilder);
235-
when(mockPostBuilder.body(any(String.class))).thenReturn(mockPostBuilder);
236-
when(mockPostBuilder.post()).thenReturn(mockHttpResponse);
237-
when(mockHttpResponse.success()).thenReturn(true);
236+
setupBasicMockHttpResponse();
238237

239238
notificationSender.sendNotification(task);
240239

server-common/src/test/java/io/a2a/server/tasks/PushNotificationSenderTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package io.a2a.server.tasks;
22

3+
import static io.a2a.client.http.A2AHttpClient.APPLICATION_JSON;
4+
import static io.a2a.client.http.A2AHttpClient.CONTENT_TYPE;
35
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.assertFalse;
47
import static org.junit.jupiter.api.Assertions.assertTrue;
58

69
import java.io.IOException;
@@ -158,7 +161,12 @@ private void testSendNotificationWithInvalidToken(String token, String testName)
158161
// Verify that no authentication header was sent (invalid token should not add header)
159162
assertEquals(1, testHttpClient.headers.size());
160163
Map<String, String> sentHeaders = testHttpClient.headers.get(0);
161-
assertTrue(sentHeaders.isEmpty(), "No headers should be sent when token is invalid");
164+
assertEquals(1, sentHeaders.size());
165+
assertFalse(sentHeaders.containsKey(A2AHeaders.X_A2A_NOTIFICATION_TOKEN),
166+
"X-A2A-Notification-Token header should not be sent when token is invalid");
167+
// Content-Type header should always be present
168+
assertTrue(sentHeaders.containsKey(CONTENT_TYPE));
169+
assertEquals(APPLICATION_JSON, sentHeaders.get(CONTENT_TYPE));
162170
}
163171

164172
private Task createSampleTask(String taskId, TaskState state) {
@@ -227,8 +235,13 @@ public void testSendNotificationWithTokenSuccess() throws InterruptedException {
227235
// Verify that the X-A2A-Notification-Token header is sent with the correct token
228236
assertEquals(1, testHttpClient.headers.size());
229237
Map<String, String> sentHeaders = testHttpClient.headers.get(0);
238+
assertEquals(2, sentHeaders.size());
230239
assertTrue(sentHeaders.containsKey(A2AHeaders.X_A2A_NOTIFICATION_TOKEN));
231240
assertEquals(config.token(), sentHeaders.get(A2AHeaders.X_A2A_NOTIFICATION_TOKEN));
241+
// Content-Type header should always be present
242+
assertTrue(sentHeaders.containsKey(CONTENT_TYPE));
243+
assertEquals(APPLICATION_JSON, sentHeaders.get(CONTENT_TYPE));
244+
232245
}
233246

234247
@Test
@@ -290,10 +303,10 @@ public void testSendNotificationHttpError() {
290303
String taskId = "task_send_http_err";
291304
Task taskData = createSampleTask(taskId, TaskState.COMPLETED);
292305
PushNotificationConfig config = createSamplePushConfig("http://notify.me/http_error", "cfg1", null);
293-
306+
294307
// Set up the configuration in the store
295308
configStore.setInfo(taskId, config);
296-
309+
297310
// Configure the test client to throw an exception
298311
testHttpClient.shouldThrowException = true;
299312

0 commit comments

Comments
 (0)