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

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

commit 5ac7f533253eede5cc818221e768e5b8171d82d2
Author: Eric Pugh <[email protected]>
AuthorDate: Tue Oct 20 07:08:56 2026 -0400

    Fix v2 API 404 when a /config request is proxied to another node (#5178)
    
    Co-authored-by: Claude Sonnet 5 <[email protected]>
    (cherry picked from commit f22faf07af110c783aaf079ef68b4455b59dba66)
---
 .../org/apache/solr/client/api/util/Constants.java | 12 +++++++++-
 .../handler/admin/api/GetConfigSolrCloudTest.java  | 28 +++++++++++++++++++++-
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java 
b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
index 3e3c7443c18..13f8009fbb4 100644
--- a/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
+++ b/solr/api/src/java/org/apache/solr/client/api/util/Constants.java
@@ -24,8 +24,18 @@ public class Constants {
 
   public static final String INDEX_TYPE_PATH_PARAMETER = "indexType";
   public static final String INDEX_NAME_PATH_PARAMETER = "indexName";
+
+  // "c" is accepted alongside "collections" because Solr internally forwards 
a v2 request to the
+  // node actually hosting a collection using the short "/c/{collection}/..." 
alias (see
+  // HttpSolrCall#sendRemoteProxy and V2HttpCall's "c"/"collections" 
handling); without it, any
+  // JAX-RS resource built on this prefix 404s with "Cannot find API for the 
path" whenever the
+  // request has to be proxied to another node.
   public static final String INDEX_PATH_PREFIX =
-      "/{" + INDEX_TYPE_PATH_PARAMETER + ":cores|collections}/{" + 
INDEX_NAME_PATH_PARAMETER + "}";
+      "/{"
+          + INDEX_TYPE_PATH_PARAMETER
+          + ":cores|collections|c}/{"
+          + INDEX_NAME_PATH_PARAMETER
+          + "}";
 
   public static final String CORE_NAME_PATH_PARAMETER = "coreName";
 
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
 
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
index 19b29380afd..dd51d21ddea 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/api/GetConfigSolrCloudTest.java
@@ -19,9 +19,12 @@ package org.apache.solr.handler.admin.api;
 import static org.apache.solr.core.CoreContainer.ALLOW_PATHS_SYSPROP;
 
 import org.apache.solr.client.api.model.IndexType;
+import org.apache.solr.client.solrj.jetty.HttpJettySolrClient;
 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.ConfigApi;
 import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.embedded.JettySolrRunner;
 import org.apache.solr.util.ExternalPaths;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -34,7 +37,9 @@ public class GetConfigSolrCloudTest extends SolrCloudTestCase 
{
   @BeforeClass
   public static void setupCluster() throws Exception {
     System.setProperty(ALLOW_PATHS_SYSPROP, 
ExternalPaths.SERVER_HOME.toAbsolutePath().toString());
-    configureCluster(1).addConfig("conf", 
configset("cloud-minimal")).configure();
+    // 2 nodes so that a request can land on a node that does *not* host the 
collection's only
+    // replica, forcing Solr to internally proxy the request to the node that 
does.
+    configureCluster(2).addConfig("conf", 
configset("cloud-minimal")).configure();
     CollectionAdminRequest.createCollection(COLLECTION_NAME, "conf", 1, 1)
         .process(cluster.getSolrClient());
   }
@@ -77,6 +82,27 @@ public class GetConfigSolrCloudTest extends 
SolrCloudTestCase {
     assertTrue(response.znodeVersion.params >= -1);
   }
 
+  @Test
+  public void testGetOverlayFromCollectionViaNonHostingNode() throws Exception 
{
+    Replica replica = 
getCollectionState(COLLECTION_NAME).replicaStream().findFirst().orElseThrow();
+    JettySolrRunner hostingJetty = cluster.getReplicaJetty(replica);
+    JettySolrRunner otherJetty =
+        cluster.getJettySolrRunners().stream()
+            .filter(jetty -> !jetty.equals(hostingJetty))
+            .findFirst()
+            .orElseThrow();
+
+    try (var client = new 
HttpJettySolrClient.Builder(otherJetty.getBaseUrl().toString()).build()) {
+      var request = new ConfigApi.GetOverlay(IndexType.COLLECTION, 
COLLECTION_NAME);
+      var response = request.process(client);
+
+      assertNotNull(response);
+      assertNull(response.error);
+      assertNotNull(response.overlay);
+      assertTrue(response.overlay.containsKey("znodeVersion"));
+    }
+  }
+
   @Test
   public void testGetConfigComponentFromCollection() throws Exception {
     for (String component : GetConfig.MIGRATED_CONFIG_COMPONENTS) {

Reply via email to