diff --git a/webserver/webserver/pom.xml b/webserver/webserver/pom.xml
index b0a79378691..2ed36cbf88c 100644
--- a/webserver/webserver/pom.xml
+++ b/webserver/webserver/pom.xml
@@ -247,5 +247,15 @@
junit-jupiter-params
test
+
+ io.helidon.media
+ helidon-media-jsonp
+ test
+
+
+ io.helidon.metrics
+ helidon-metrics
+ test
+
diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/HashRequestHeaders.java b/webserver/webserver/src/main/java/io/helidon/webserver/HashRequestHeaders.java
index 27b0e14c11d..d2d0004fc27 100644
--- a/webserver/webserver/src/main/java/io/helidon/webserver/HashRequestHeaders.java
+++ b/webserver/webserver/src/main/java/io/helidon/webserver/HashRequestHeaders.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -118,7 +118,6 @@ public List acceptedTypes() {
if (acceptValues.size() == 1 && HUC_ACCEPT_DEFAULT.equals(acceptValues.get(0))) {
result = HUC_ACCEPT_DEFAULT_TYPES;
-
} else {
result = LazyList.create(acceptValues.stream()
.flatMap(h -> Utils.tokenize(',', "\"", false, h).stream())
@@ -145,29 +144,33 @@ public Optional bestAccepted(MediaType... mediaTypes) {
if (mediaTypes == null || mediaTypes.length == 0) {
return Optional.empty();
}
- List accepts = acceptedTypes();
- if (accepts == null || accepts.isEmpty()) {
- return Optional.ofNullable(mediaTypes[0]);
- }
+ try {
+ List accepts = acceptedTypes();
+ if (accepts == null || accepts.isEmpty()) {
+ return Optional.ofNullable(mediaTypes[0]);
+ }
- double best = 0;
- MediaType result = null;
- for (MediaType mt : mediaTypes) {
- if (mt != null) {
- for (MediaType acc : accepts) {
- double q = acc.qualityFactor();
- if (q > best && acc.test(mt)) {
- if (q == 1) {
- return Optional.of(mt);
- } else {
- best = q;
- result = mt;
+ double best = 0;
+ MediaType result = null;
+ for (MediaType mt : mediaTypes) {
+ if (mt != null) {
+ for (MediaType acc : accepts) {
+ double q = acc.qualityFactor();
+ if (q > best && acc.test(mt)) {
+ if (q == 1) {
+ return Optional.of(mt);
+ } else {
+ best = q;
+ result = mt;
+ }
}
}
}
}
+ return Optional.ofNullable(result);
+ } catch (IllegalArgumentException e) {
+ throw new BadRequestException("Unable to parse Accept header", e);
}
- return Optional.ofNullable(result);
}
@Override
diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/Response.java b/webserver/webserver/src/main/java/io/helidon/webserver/Response.java
index d480871712d..bb26eba6a56 100644
--- a/webserver/webserver/src/main/java/io/helidon/webserver/Response.java
+++ b/webserver/webserver/src/main/java/io/helidon/webserver/Response.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -181,6 +181,10 @@ public Single send(T content) {
sendPublisher.subscribe(bareResponse);
}, content == null);
return whenSent();
+ } catch (IllegalStateException e) {
+ eventListener.finish();
+ throw e.getCause() instanceof IllegalArgumentException
+ ? new BadRequestException("Unable to process request", e) : e;
} catch (RuntimeException | Error e) {
eventListener.finish();
throw e;
diff --git a/webserver/webserver/src/test/java/io/helidon/webserver/BadRequestTest.java b/webserver/webserver/src/test/java/io/helidon/webserver/BadRequestTest.java
new file mode 100644
index 00000000000..c51d8ff04a8
--- /dev/null
+++ b/webserver/webserver/src/test/java/io/helidon/webserver/BadRequestTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.webserver;
+
+import java.util.List;
+
+import io.helidon.common.http.Http;
+import io.helidon.webserver.utils.SocketHttpClient;
+import io.helidon.media.jsonp.JsonpSupport;
+import io.helidon.metrics.MetricsSupport;
+
+import jakarta.json.JsonObject;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+
+/**
+ * Tests bad types in Accept header
+ */
+public class BadRequestTest {
+
+ private static WebServer server;
+
+ @BeforeAll
+ static void createAndStartServer() {
+ server = WebServer.builder()
+ .addMediaSupport(JsonpSupport.create())
+ .addRouting(Routing.builder()
+ .register(MetricsSupport.create())
+ .post("/echo", Handler.create(JsonObject.class,
+ (req, res, entity) -> res.status(Http.Status.OK_200).send(entity))))
+ .build();
+ server.start().await();
+ }
+
+ @AfterAll
+ static void stopServer() {
+ server.shutdown().await();
+ }
+
+ @Test
+ void testBadAcceptType() throws Exception {
+ try (SocketHttpClient c = new SocketHttpClient(server)) {
+ c.request(Http.Method.POST, "/echo", "{ }", List.of("Accept: application.json"));
+ String result = c.receive();
+ assertThat(result, containsString("400 Bad Request"));
+ }
+ }
+
+ @Test
+ void testBadAcceptTypeMetrics() throws Exception {
+ try (SocketHttpClient c = new SocketHttpClient(server)) {
+ c.request(Http.Method.GET, "/metrics", "", List.of("Accept: application.json"));
+ String result = c.receive();
+ assertThat(result, containsString("400 Bad Request"));
+ }
+ }
+}