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

gerlowskija pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new f9c50fa101e Replace HTTP-method string-handling with enum
f9c50fa101e is described below

commit f9c50fa101e3ff92d8529fb19a6cb44bfed199d7
Author: Jason Gerlowski <[email protected]>
AuthorDate: Tue Jan 6 06:19:38 2026 -0500

    Replace HTTP-method string-handling with enum
    
    A number of places in Solr referenced the HTTP method used to make the
    request, often inspecting the value as a string.  This is needlessly
    brittle (e.g. case sensitivity issues).
    
    This commit fixes this problem by parsing the method value into an enum,
    which can then be used by later inspection.
---
 .../client/solrj/embedded/EmbeddedSolrServer.java  |  4 +-
 .../org/apache/solr/handler/SchemaHandler.java     | 46 +++++++++++++---------
 .../org/apache/solr/handler/SolrConfigHandler.java | 40 +++++++++++--------
 .../solr/handler/admin/SecurityConfHandler.java    |  7 ++--
 .../org/apache/solr/request/SolrQueryRequest.java  |  3 +-
 .../apache/solr/servlet/SolrRequestParsers.java    |  7 +++-
 .../handler/admin/SecurityConfHandlerTest.java     | 17 ++++----
 .../apache/solr/servlet/SolrRequestParserTest.java | 22 +++++++++++
 .../handler/MirroringConfigSetsHandlerTest.java    |  2 +-
 .../org/apache/solr/client/solrj/SolrRequest.java  | 18 ++++++++-
 10 files changed, 113 insertions(+), 53 deletions(-)

diff --git 
a/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java
 
b/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java
index 45aaea07d1d..82edafe2b7b 100644
--- 
a/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java
+++ 
b/solr/core/src/java/org/apache/solr/client/solrj/embedded/EmbeddedSolrServer.java
@@ -171,7 +171,7 @@ public class EmbeddedSolrServer extends SolrClient {
         SolrQueryRequest req =
             _parser.buildRequestFrom(
                 null, getParams(request), getContentStreams(request), 
request.getUserPrincipal());
-        req.getContext().put("httpMethod", request.getMethod().name());
+        req.getContext().put("httpMethod", request.getMethod());
         req.getContext().put(PATH, path);
         SolrQueryResponse resp = new SolrQueryResponse();
         handler.handleRequest(req, resp);
@@ -224,7 +224,7 @@ public class EmbeddedSolrServer extends SolrClient {
               .buildRequestFrom(
                   core, params, getContentStreams(request), 
request.getUserPrincipal());
       req.getContext().put(PATH, path);
-      req.getContext().put("httpMethod", request.getMethod().name());
+      req.getContext().put("httpMethod", request.getMethod());
       SolrQueryResponse rsp = new SolrQueryResponse();
       SolrRequestInfo.setRequestInfo(new SolrRequestInfo(req, rsp));
 
diff --git a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java 
b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
index c81fa8eb572..86a5c8b2323 100644
--- a/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SchemaHandler.java
@@ -36,6 +36,7 @@ import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
 import org.apache.solr.api.ApiBag;
 import org.apache.solr.api.JerseyResource;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.SolrParams;
@@ -77,26 +78,33 @@ public class SchemaHandler extends RequestHandlerBase
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
     RequestHandlerUtils.setWt(req, JSON);
-    String httpMethod = (String) req.getContext().get("httpMethod");
-    if ("POST".equals(httpMethod)) {
-      if (isImmutableConfigSet) {
-        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, 
"ConfigSet is immutable");
-      }
-      if (req.getContentStreams() == null) {
-        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "no 
stream");
-      }
-
-      try {
-        List<Map<String, Object>> errs = new 
SchemaManager(req).performOperations();
-        if (!errs.isEmpty())
-          throw new ApiBag.ExceptionWithErrObject(
-              SolrException.ErrorCode.BAD_REQUEST, "error processing 
commands", errs);
-      } catch (IOException e) {
+    final SolrRequest.METHOD httpMethod = (SolrRequest.METHOD) 
req.getContext().get("httpMethod");
+    switch (httpMethod) {
+      case POST:
+        if (isImmutableConfigSet) {
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, 
"ConfigSet is immutable");
+        }
+        if (req.getContentStreams() == null) {
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "no 
stream");
+        }
+        try {
+          List<Map<String, Object>> errs = new 
SchemaManager(req).performOperations();
+          if (!errs.isEmpty())
+            throw new ApiBag.ExceptionWithErrObject(
+                SolrException.ErrorCode.BAD_REQUEST, "error processing 
commands", errs);
+        } catch (IOException e) {
+          throw new SolrException(
+              SolrException.ErrorCode.BAD_REQUEST,
+              "Error reading input String " + e.getMessage(),
+              e);
+        }
+        break;
+      case GET:
+        handleGET(req, rsp);
+        break;
+      default:
         throw new SolrException(
-            SolrException.ErrorCode.BAD_REQUEST, "Error reading input String " 
+ e.getMessage(), e);
-      }
-    } else {
-      handleGET(req, rsp);
+            SolrException.ErrorCode.BAD_REQUEST, "Unexpected HTTP method: " + 
httpMethod);
     }
   }
 
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java 
b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index 2e82dd375f9..a9cb278b2f0 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -131,24 +131,30 @@ public class SolrConfigHandler extends RequestHandlerBase
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
 
     RequestHandlerUtils.setWt(req, CommonParams.JSON);
-    String httpMethod = (String) req.getContext().get("httpMethod");
-    Command command = new Command(req, rsp, httpMethod);
-    if ("POST".equals(httpMethod)) {
-      if (configEditing_disabled || isImmutableConfigSet) {
-        final String reason =
-            configEditing_disabled
-                ? "due to " + CONFIGSET_EDITING_DISABLED_ARG
-                : "because ConfigSet is immutable";
+    final var httpMethod = (SolrRequest.METHOD) 
req.getContext().get("httpMethod");
+    Command command = new Command(req, rsp, httpMethod.name());
+    switch (httpMethod) {
+      case POST:
+        if (configEditing_disabled || isImmutableConfigSet) {
+          final String reason =
+              configEditing_disabled
+                  ? "due to " + CONFIGSET_EDITING_DISABLED_ARG
+                  : "because ConfigSet is immutable";
+          throw new SolrException(
+              SolrException.ErrorCode.FORBIDDEN, " solrconfig editing is not 
enabled " + reason);
+        }
+        try {
+          command.handlePOST();
+        } finally {
+          RequestHandlerUtils.addExperimentalFormatWarning(rsp);
+        }
+        break;
+      case GET:
+        command.handleGET();
+        break;
+      default:
         throw new SolrException(
-            SolrException.ErrorCode.FORBIDDEN, " solrconfig editing is not 
enabled " + reason);
-      }
-      try {
-        command.handlePOST();
-      } finally {
-        RequestHandlerUtils.addExperimentalFormatWarning(rsp);
-      }
-    } else {
-      command.handleGET();
+            SolrException.ErrorCode.BAD_REQUEST, "Unexpected HTTP method: " + 
httpMethod);
     }
   }
 
diff --git 
a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java 
b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
index 95e1d10f351..f5b528f3d70 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SecurityConfHandler.java
@@ -33,6 +33,7 @@ import org.apache.solr.api.AnnotatedApi;
 import org.apache.solr.api.Api;
 import org.apache.solr.api.ApiBag;
 import org.apache.solr.api.ApiBag.ReqHandlerToApi;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SpecProvider;
 import org.apache.solr.common.params.CommonParams;
@@ -81,12 +82,12 @@ public abstract class SecurityConfHandler extends 
RequestHandlerBase
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
     RequestHandlerUtils.setWt(req, CommonParams.JSON);
-    String httpMethod = (String) req.getContext().get("httpMethod");
+    final var httpMethod = (SolrRequest.METHOD) 
req.getContext().get("httpMethod");
     String path = (String) req.getContext().get("path");
     String key = path.substring(path.lastIndexOf('/') + 1);
-    if ("GET".equals(httpMethod)) {
+    if (SolrRequest.METHOD.GET.equals(httpMethod)) {
       getConf(rsp, key);
-    } else if ("POST".equals(httpMethod)) {
+    } else if (SolrRequest.METHOD.POST.equals(httpMethod)) {
       Object plugin = getPlugin(key);
       doEdit(req, rsp, path, key, plugin);
     }
diff --git a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java 
b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
index bf571619e3e..5c139751f7b 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
@@ -24,6 +24,7 @@ import java.security.Principal;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.cloud.CloudDescriptor;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.SolrParams;
@@ -165,7 +166,7 @@ public interface SolrQueryRequest extends AutoCloseable {
   }
 
   default String getHttpMethod() {
-    return (String) getContext().get("httpMethod");
+    return ((SolrRequest.METHOD) getContext().get("httpMethod")).name();
   }
 
   default HttpSolrCall getHttpSolrCall() {
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java 
b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
index 83dcb65c9b2..dfc7e056585 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java
@@ -45,6 +45,7 @@ import javax.servlet.http.Part;
 import org.apache.commons.io.input.CloseShieldInputStream;
 import org.apache.lucene.util.IOUtils;
 import org.apache.solr.api.V2HttpCall;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.common.params.CommonParams;
@@ -173,7 +174,11 @@ public class SolrRequestParsers {
     // Handlers and login will want to know the path. If it contains a ':'
     // the handler could use it for RESTful URLs
     sreq.getContext().put(PATH, RequestHandlers.normalize(path));
-    sreq.getContext().put("httpMethod", req.getMethod());
+
+    final var methodStr = req.getMethod();
+    final var parsedMethod =
+        SolrRequest.METHOD.fromString(methodStr); // Throws error if method 
not recognized
+    sreq.getContext().put("httpMethod", parsedMethod);
 
     if (addHttpRequestToContext) {
       sreq.getContext().put("httpRequest", req);
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
index c9335b2a7b6..5a971eb7dd6 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/SecurityConfHandlerTest.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.CommandOperation;
 import org.apache.solr.common.util.ContentStreamBase;
@@ -44,7 +45,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
             + "'set-user':{ 'tom':'TomIsUberCool'}\n"
             + "}";
     LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, new 
ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authentication");
     ContentStreamBase.ByteArrayStream o =
         new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
@@ -77,7 +78,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 {
             + "}";
 
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authorization");
     o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
     req.setContentStreams(Collections.singletonList(o));
@@ -101,7 +102,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
             + "                  'role': ['admin','dev']\n"
             + "                  }}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authorization");
     o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
     req.setContentStreams(Collections.singletonList(o));
@@ -122,7 +123,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
             + "                  'role': ['guest','admin']\n"
             + "                  }}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authorization");
     o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
     req.setContentStreams(Collections.singletonList(o));
@@ -139,7 +140,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
 
     command = "{\n" + "delete-permission: 1,\n" + " set-user-role : { tom 
:null}\n" + "}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authorization");
     o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
     req.setContentStreams(Collections.singletonList(o));
@@ -163,7 +164,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
             + "                  'role': 'admin'\n"
             + "                  }}";
     req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-    req.getContext().put("httpMethod", "POST");
+    req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
     req.getContext().put("path", "/admin/authorization");
     o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
     req.setContentStreams(Collections.singletonList(o));
@@ -237,7 +238,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
     public String getStandardJson() throws Exception {
       String command = "{\n" + "'set-user': {'solr':'SolrRocks'}\n" + "}";
       LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, new 
ModifiableSolrParams());
-      req.getContext().put("httpMethod", "POST");
+      req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
       req.getContext().put("path", "/admin/authentication");
       ContentStreamBase.ByteArrayStream o =
           new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
@@ -249,7 +250,7 @@ public class SecurityConfHandlerTest extends SolrTestCaseJ4 
{
               + "'set-permission':{'name': 'security-edit', 'role': 'admin'}"
               + "}";
       req = new LocalSolrQueryRequest(null, new ModifiableSolrParams());
-      req.getContext().put("httpMethod", "POST");
+      req.getContext().put("httpMethod", SolrRequest.METHOD.POST);
       req.getContext().put("path", "/admin/authorization");
       o = new 
ContentStreamBase.ByteArrayStream(command.getBytes(StandardCharsets.UTF_8), "");
       req.setContentStreams(Collections.singletonList(o));
diff --git 
a/solr/core/src/test/org/apache/solr/servlet/SolrRequestParserTest.java 
b/solr/core/src/test/org/apache/solr/servlet/SolrRequestParserTest.java
index e86e6c12c4b..2c7c947e2ca 100644
--- a/solr/core/src/test/org/apache/solr/servlet/SolrRequestParserTest.java
+++ b/solr/core/src/test/org/apache/solr/servlet/SolrRequestParserTest.java
@@ -263,6 +263,28 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
     }
   }
 
+  @Test
+  public void testReportsErrorForUnexpectedHttpMethod() throws Exception {
+    final String getParams = "q=hello";
+    HttpServletRequest request = getMock("/solr/select", 
"application/x-www-form-urlencoded", 0);
+    when(request.getMethod()).thenReturn("UNEXPECTED");
+    when(request.getQueryString()).thenReturn(getParams);
+
+    MultipartRequestParser multipart = new MultipartRequestParser(2048);
+    RawRequestParser raw = new RawRequestParser();
+    FormDataRequestParser formdata = new FormDataRequestParser(2048);
+    StandardRequestParser standard = new StandardRequestParser(multipart, raw, 
formdata);
+
+    final SolrException thrown =
+        expectThrows(
+            SolrException.class,
+            () -> {
+              parser.parse(h.getCore(), "/select", request);
+            });
+    assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, thrown.code());
+    assertEquals("Request contained unexpected HTTP method: UNEXPECTED", 
thrown.getMessage());
+  }
+
   static class ByteServletInputStream extends ServletInputStream {
     final BufferedInputStream in;
     final int len;
diff --git 
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/handler/MirroringConfigSetsHandlerTest.java
 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/handler/MirroringConfigSetsHandlerTest.java
index 21a8d378208..ad93644e7f3 100644
--- 
a/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/handler/MirroringConfigSetsHandlerTest.java
+++ 
b/solr/modules/cross-dc/src/test/org/apache/solr/crossdc/handler/MirroringConfigSetsHandlerTest.java
@@ -91,7 +91,7 @@ public class MirroringConfigSetsHandlerTest extends 
SolrTestCaseJ4 {
           List.of(new ContentStreamBase.ByteArrayStream(content, 
configSetName, "application/zip"));
       req.setContentStreams(streams);
     }
-    req.getContext().put("httpMethod", method);
+    req.getContext().put("httpMethod", SolrRequest.METHOD.fromString(method));
     return req;
   }
 
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java 
b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
index 49c52f18d36..03b23d5dcdd 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/SolrRequest.java
@@ -23,11 +23,13 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import org.apache.solr.client.solrj.impl.HttpSolrClientBase;
 import org.apache.solr.client.solrj.request.RequestWriter;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.ContentStream;
 
@@ -48,9 +50,23 @@ public abstract class SolrRequest<T extends SolrResponse> 
implements Serializabl
 
   public enum METHOD {
     GET,
+    HEAD,
     POST,
     PUT,
-    DELETE
+    DELETE;
+
+    /**
+     * Returns the METHOD enum value matching the provided string, or 'null' 
if no match is found.
+     */
+    public static METHOD fromString(String methodStr) {
+      try {
+        return METHOD.valueOf(methodStr.toUpperCase(Locale.ROOT));
+      } catch (IllegalArgumentException e) {
+        throw new SolrException(
+            SolrException.ErrorCode.BAD_REQUEST,
+            "Request contained unexpected HTTP method: " + methodStr);
+      }
+    }
   };
 
   public enum ApiVersion {

Reply via email to