You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by si...@apache.org on 2026/10/11 01:42:32 UTC

(ozone) branch master updated: HDDS-16379. Recon returns 500 NPE instead of proxying error when metrics endpoint responds with a non-enum status code (#11197)

This is an automated email from the ASF dual-hosted git repository.

smengcl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 6818707698c HDDS-16379. Recon returns 500 NPE instead of proxying error when metrics endpoint responds with a non-enum status code (#11197)
6818707698c is described below

commit 6818707698c0d151ef51f64f2286d13b8b3ab329
Author: Henry Chen <he...@apache.org>
AuthorDate: Sun Oct 11 09:42:26 2026 +0800

    HDDS-16379. Recon returns 500 NPE instead of proxying error when metrics endpoint responds with a non-enum status code (#11197)
---
 .../ozone/recon/api/MetricsProxyEndpoint.java      |  7 +++-
 .../recon/spi/impl/JmxServiceProviderImpl.java     |  3 +-
 .../spi/impl/PrometheusServiceProviderImpl.java    |  3 +-
 .../hadoop/ozone/recon/api/TestEndpoints.java      | 48 ++++++++++++++++++++++
 4 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/MetricsProxyEndpoint.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/MetricsProxyEndpoint.java
index 82fd0f801f9..efc08f6ee0e 100644
--- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/MetricsProxyEndpoint.java
+++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/MetricsProxyEndpoint.java
@@ -71,13 +71,16 @@ public void getMetricsResponse(
       HttpURLConnection connection = metricsServiceProvider.getMetricsResponse(
           api, uriInfo.getRequestUri().getQuery());
       InputStream inputStream;
-      if (Response.Status.fromStatusCode(connection.getResponseCode())
-          .getFamily() == Response.Status.Family.SUCCESSFUL) {
+      if (Response.Status.Family.familyOf(connection.getResponseCode()) == Response.Status.Family.SUCCESSFUL) {
         inputStream = connection.getInputStream();
       } else {
         // Throw a bad gateway error if HttpResponseCode is not 2xx
         httpServletResponse.setStatus(HttpServletResponse.SC_BAD_GATEWAY);
         inputStream = connection.getErrorStream();
+        if (inputStream == null) {
+          // The metrics endpoint sent no error body
+          return;
+        }
       }
       try (
           OutputStream outputStream =
diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/JmxServiceProviderImpl.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/JmxServiceProviderImpl.java
index d3de292945b..8497b3a0ad2 100644
--- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/JmxServiceProviderImpl.java
+++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/JmxServiceProviderImpl.java
@@ -110,8 +110,7 @@ private List<Map<String, Object>> getMetrics(String api, String queryString)
     return SecurityUtil.doAsLoginUser(() -> {
       HttpURLConnection urlConnection =
           getMetricsResponse(api, queryString);
-      if (Response.Status.fromStatusCode(urlConnection.getResponseCode())
-          .getFamily() == Response.Status.Family.SUCCESSFUL) {
+      if (Response.Status.Family.familyOf(urlConnection.getResponseCode()) == Response.Status.Family.SUCCESSFUL) {
         try (InputStream inputStream = urlConnection.getInputStream()) {
           Map<String, Object> jsonMap = JsonUtils.getDefaultMapper().readValue(inputStream, Map.class);
           Object beansObj = jsonMap.get("beans");
diff --git a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/PrometheusServiceProviderImpl.java b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/PrometheusServiceProviderImpl.java
index 0a784c50c90..ee200e02e44 100644
--- a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/PrometheusServiceProviderImpl.java
+++ b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/PrometheusServiceProviderImpl.java
@@ -126,8 +126,7 @@ private List<Metric> getMetrics(String api, String queryString)
     HttpURLConnection urlConnection =
         getMetricsResponse(api, queryString);
     List<Metric> metrics = null;
-    if (Response.Status.fromStatusCode(urlConnection.getResponseCode())
-        .getFamily() == Response.Status.Family.SUCCESSFUL) {
+    if (Response.Status.Family.familyOf(urlConnection.getResponseCode()) == Response.Status.Family.SUCCESSFUL) {
       InputStream inputStream = urlConnection.getInputStream();
       ObjectMapper mapper = new ObjectMapper();
       Map<String, Object> jsonMap = mapper.readValue(inputStream, Map.class);
diff --git a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java
index a8a8cab6403..afb3f4b56e1 100644
--- a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java
+++ b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestEndpoints.java
@@ -17,6 +17,7 @@
 
 package org.apache.hadoop.ozone.recon.api;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.apache.hadoop.hdds.protocol.MockDatanodeDetails.randomDatanodeDetails;
 import static org.apache.hadoop.ozone.container.upgrade.UpgradeUtils.defaultVersionProto;
 import static org.apache.hadoop.ozone.recon.OMMetadataManagerTestUtils.getRandomPipeline;
@@ -43,6 +44,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -803,6 +805,52 @@ public void testGetMetricsResponse() throws Exception {
     verify(outputStreamMock).write(fileBytes, 0, fileBytes.length);
   }
 
+  @Test
+  public void testGetMetricsResponseWithNonEnumStatusCode() throws Exception {
+    HttpServletResponse responseMock = mock(HttpServletResponse.class);
+    ServletOutputStream outputStreamMock = mock(ServletOutputStream.class);
+    when(responseMock.getOutputStream()).thenReturn(outputStreamMock);
+    UriInfo uriInfoMock = mock(UriInfo.class);
+    URI uriMock = mock(URI.class);
+    when(uriMock.getQuery()).thenReturn("");
+    when(uriInfoMock.getRequestUri()).thenReturn(uriMock);
+
+    // 422 has no Response.Status enum constant; the error body must still be proxied back.
+    byte[] errorBody = "{\"status\":\"error\",\"errorType\":\"bad_data\"}".getBytes(UTF_8);
+    HttpURLConnection urlConnectionMock = mock(HttpURLConnection.class);
+    when(urlConnectionMock.getResponseCode()).thenReturn(422);
+    when(urlConnectionMock.getErrorStream())
+        .thenReturn(new ByteArrayInputStream(errorBody));
+    when(reconUtilsMock.makeHttpCall(any(URLConnectionFactory.class),
+        anyString(), anyBoolean())).thenReturn(urlConnectionMock);
+
+    metricsProxyEndpoint.getMetricsResponse(PROMETHEUS_INSTANT_QUERY_API,
+        uriInfoMock, responseMock);
+
+    verify(responseMock).setStatus(HttpServletResponse.SC_BAD_GATEWAY);
+    verify(outputStreamMock).write(errorBody, 0, errorBody.length);
+  }
+
+  @Test
+  public void testGetMetricsResponseWithNullErrorStream() throws Exception {
+    HttpServletResponse responseMock = mock(HttpServletResponse.class);
+    UriInfo uriInfoMock = mock(UriInfo.class);
+    URI uriMock = mock(URI.class);
+    when(uriMock.getQuery()).thenReturn("");
+    when(uriInfoMock.getRequestUri()).thenReturn(uriMock);
+
+    HttpURLConnection urlConnectionMock = mock(HttpURLConnection.class);
+    when(urlConnectionMock.getResponseCode()).thenReturn(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+    when(urlConnectionMock.getErrorStream()).thenReturn(null);
+    when(reconUtilsMock.makeHttpCall(any(URLConnectionFactory.class),
+        anyString(), anyBoolean())).thenReturn(urlConnectionMock);
+
+    assertDoesNotThrow(() -> metricsProxyEndpoint.getMetricsResponse(PROMETHEUS_INSTANT_QUERY_API,
+        uriInfoMock, responseMock));
+
+    verify(responseMock).setStatus(HttpServletResponse.SC_BAD_GATEWAY);
+  }
+
   @Test
   public void testGetClusterState() throws Exception {
     Response response = clusterStateEndpoint.getClusterState();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org