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

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


The following commit(s) were added to refs/heads/main by this push:
     new e68d292f050 SOLR-18095: Add ability to map writer types and handlers 
to a No Operation version. (#4091)
e68d292f050 is described below

commit e68d292f0506449707cc4a079c8f7d5da221d651
Author: Eric Pugh <[email protected]>
AuthorDate: Wed Feb 18 05:23:29 2026 -0500

    SOLR-18095: Add ability to map writer types and handlers to a No Operation 
version. (#4091)
    
    This approach for specifically response writers existed in previous 
versions of Solr as a hidden feature/side effect of something else and was 
removed.  It's now restored for the specific purpose of disabling 
ImplicitPlugins created components.
---
 changelog/unreleased/SOLR-18095.yml                |  8 ++
 ...RequestHandler.java => NoOpRequestHandler.java} |  6 +-
 .../NoOpResponseWriter.java}                       | 26 ++----
 .../solr/collection1/conf/solrconfig-noop.xml      | 57 +++++++++++++
 .../solr/handler/NoOpRequestHandlerTest.java       | 97 ++++++++++++++++++++++
 .../solr/response/NoOpResponseWriterTest.java      | 73 ++++++++++++++++
 .../pages/implicit-requesthandlers.adoc            | 12 +++
 .../query-guide/pages/response-writers.adoc        | 13 +++
 8 files changed, 272 insertions(+), 20 deletions(-)

diff --git a/changelog/unreleased/SOLR-18095.yml 
b/changelog/unreleased/SOLR-18095.yml
new file mode 100644
index 00000000000..8884ef9ab2d
--- /dev/null
+++ b/changelog/unreleased/SOLR-18095.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Provide NoOpRequestWriter and NoOpRequestHandler that can be used to 
disable implicitly configured equivalents.
+type: added # added, changed, fixed, deprecated, removed, dependency_update, 
security, other
+authors:
+  - name: Eric Pugh
+links:
+  - name: SOLR-18095
+    url: https://issues.apache.org/jira/browse/SOLR-18095
diff --git 
a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java 
b/solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java
similarity index 86%
copy from solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
copy to solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java
index 50e2fa1f27c..6ce2e82d877 100644
--- a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java
@@ -23,12 +23,12 @@ import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.security.AuthorizationContext;
 
-/** Does nothing other than showing a 404 message */
-public class NotFoundRequestHandler extends RequestHandlerBase {
+/** Does nothing other than showing a 403 message */
+public class NoOpRequestHandler extends RequestHandlerBase {
   @Override
   public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
     throw new SolrException(
-        SolrException.ErrorCode.NOT_FOUND, "" + req.getContext().get(PATH) + " 
is not found");
+        SolrException.ErrorCode.FORBIDDEN, req.getContext().get(PATH) + " has 
been disabled");
   }
 
   @Override
diff --git 
a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java 
b/solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java
similarity index 54%
rename from 
solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
rename to solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java
index 50e2fa1f27c..125f3338f20 100644
--- a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/response/NoOpResponseWriter.java
@@ -14,30 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.solr.handler;
+package org.apache.solr.response;
 
-import static org.apache.solr.common.params.CommonParams.PATH;
-
-import org.apache.solr.common.SolrException;
+import java.io.IOException;
+import java.io.Writer;
 import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.security.AuthorizationContext;
 
-/** Does nothing other than showing a 404 message */
-public class NotFoundRequestHandler extends RequestHandlerBase {
-  @Override
-  public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) 
throws Exception {
-    throw new SolrException(
-        SolrException.ErrorCode.NOT_FOUND, "" + req.getContext().get(PATH) + " 
is not found");
-  }
+public class NoOpResponseWriter implements TextQueryResponseWriter {
+  static String MESSAGE = "noop response writer";
 
   @Override
-  public String getDescription() {
-    return "No Operation";
+  public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse 
rsp) throws IOException {
+    writer.write(MESSAGE);
   }
 
   @Override
-  public Name getPermissionName(AuthorizationContext request) {
-    return Name.ALL;
+  public String getContentType(SolrQueryRequest request, SolrQueryResponse 
response) {
+    return QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8;
   }
 }
diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml 
b/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml
new file mode 100644
index 00000000000..2e9a5e463d0
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-noop.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" ?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<!-- Test config that demonstrates overriding implicit handlers and response 
writers with NoOp versions -->
+
+<config>
+
+  <dataDir>${solr.data.dir:}</dataDir>
+
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+
+  <updateHandler class="solr.DirectUpdateHandler2">
+    <updateLog enable="${solr.index.updatelog.enabled:true}">
+      <str name="dir">${solr.ulog.dir:}</str>
+    </updateLog>
+  </updateHandler>
+
+  <!--
+    Override the implicit /schema handler (from ImplicitPlugins.json)
+    with NoOpRequestHandler to demonstrate that implicit handlers can be 
disabled
+  -->
+  <requestHandler name="/schema" class="solr.NoOpRequestHandler" />
+
+  <!-- Standard search handler for testing other functionality still works -->
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <str name="indent">true</str>
+      <str name="df">text</str>
+    </lst>
+  </requestHandler>
+
+  <!--
+    Override the implicit CSV response writer (from ImplicitPlugins.json)
+    with NoOpResponseWriter to demonstrate that implicit response writers can 
be disabled
+  -->
+  <queryResponseWriter name="csv" class="solr.NoOpResponseWriter" />
+
+</config>
diff --git 
a/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java 
b/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java
new file mode 100644
index 00000000000..4193a7829bd
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/handler/NoOpRequestHandlerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.handler;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test that demonstrates NoOpRequestHandler can be used to disable implicit 
handlers like
+ * SchemaHandler that are loaded via ImplicitPlugins.json.
+ */
+public class NoOpRequestHandlerTest extends SolrTestCaseJ4 {
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-noop.xml", "schema.xml");
+  }
+
+  @Test
+  public void testSchemaHandlerDisabled() {
+    // Test that /schema endpoint is disabled and returns 403 FORBIDDEN
+    SolrException exception =
+        expectThrows(
+            SolrException.class,
+            () -> {
+              try (SolrQueryRequest req = req("qt", "/schema")) {
+                SolrQueryResponse rsp = new SolrQueryResponse();
+                h.getCore().execute(h.getCore().getRequestHandler("/schema"), 
req, rsp);
+                if (rsp.getException() != null) {
+                  throw rsp.getException();
+                }
+              }
+            });
+
+    assertEquals(
+        "Should return FORBIDDEN status code",
+        SolrException.ErrorCode.FORBIDDEN.code,
+        exception.code());
+    assertTrue(
+        "Error message should indicate endpoint has been disabled",
+        exception.getMessage().contains("has been disabled"));
+  }
+
+  @Test
+  public void testSchemaHandlerSubPathDisabled() {
+    // Test that /schema/fields endpoint is also disabled
+    SolrException exception =
+        expectThrows(
+            SolrException.class,
+            () -> {
+              try (SolrQueryRequest req = req("qt", "/schema/fields")) {
+                SolrQueryResponse rsp = new SolrQueryResponse();
+                h.getCore().execute(h.getCore().getRequestHandler("/schema"), 
req, rsp);
+                if (rsp.getException() != null) {
+                  throw rsp.getException();
+                }
+              }
+            });
+
+    assertEquals(
+        "Should return FORBIDDEN status code",
+        SolrException.ErrorCode.FORBIDDEN.code,
+        exception.code());
+  }
+
+  @Test
+  public void testNoOpHandlerRegistered() {
+    // Verify that the NoOpRequestHandler is actually registered at /schema
+    assertNotNull("Schema handler should be registered", 
h.getCore().getRequestHandler("/schema"));
+    assertTrue(
+        "Handler at /schema should be NoOpRequestHandler",
+        h.getCore().getRequestHandler("/schema") instanceof 
NoOpRequestHandler);
+  }
+
+  @Test
+  public void testOtherHandlersStillWork() {
+    assertQ("Standard query handler should still work", req("q", "*:*"), 
"//result[@numFound='0']");
+  }
+}
diff --git 
a/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java 
b/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java
new file mode 100644
index 00000000000..fc8888eda82
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/response/NoOpResponseWriterTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.response;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import org.apache.solr.SolrTestCaseJ4;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test that demonstrates NoOpResponseWriter can be used to disable implicit 
response writers that
+ * are loaded via ImplicitPlugins.json.
+ */
+public class NoOpResponseWriterTest extends SolrTestCaseJ4 {
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-noop.xml", "schema.xml");
+  }
+
+  @Test
+  public void testWrite() throws IOException {
+    NoOpResponseWriter writer = new NoOpResponseWriter();
+
+    Writer stringWriter = new StringWriter();
+
+    writer.write(stringWriter, null, null);
+
+    assertEquals(NoOpResponseWriter.MESSAGE, stringWriter.toString());
+  }
+
+  @Test
+  public void testGetContentType() {
+    NoOpResponseWriter writer = new NoOpResponseWriter();
+
+    String contentType = writer.getContentType(null, null);
+    assertEquals(QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8, contentType);
+  }
+
+  @Test
+  public void testCsvResponseWriterDisabled() throws Exception {
+    QueryResponseWriter csvWriter = h.getCore().getQueryResponseWriter("csv");
+
+    assertNotNull("CSV response writer should be registered", csvWriter);
+    assertTrue(
+        "CSV response writer should be NoOpResponseWriter, not the implicit 
CSVResponseWriter",
+        csvWriter instanceof NoOpResponseWriter);
+
+    // Verify it returns the NoOp message when used
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    csvWriter.write(out, null, null);
+    String output = out.toString(StandardCharsets.UTF_8);
+    assertEquals("CSV writer should return NoOp message", 
NoOpResponseWriter.MESSAGE, output);
+  }
+}
diff --git 
a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
 
b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
index 05784283e4d..16b2f691281 100644
--- 
a/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
+++ 
b/solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc
@@ -363,3 +363,15 @@ The response will look similar to:
 Because implicit request handlers are not present in `solrconfig.xml`, 
configuration of their associated `default`, `invariant` and `appends` 
parameters may be edited via the xref:request-parameters-api.adoc[] using the 
paramset listed in the above table.
 However, other parameters, including SearchHandler components, may not be 
modified.
 The invariants and appends specified in the implicit configuration cannot be 
overridden.
+
+== How to Disable an Implicit Handler
+
+You may want to disable the loading of an implicit handler.
+This is supported by remapping the name of the handler to the 
`NoOpRequestHandler` in `solrconfig.xml`, which will return a 403 FORBIDDEN 
status code.
+
+For example, to disable the `/update/csv` handler you would re-define it in 
`solrconfig.xml` as:
+
+[source,xml]
+----
+<requestHandler name="/update/csv" 
class="solr.NoOpRequestHandler"></requestHandler>
+----
diff --git 
a/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc 
b/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc
index b4f29b8e681..0aff169b739 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc
@@ -32,6 +32,7 @@ The list below describe shows the most common settings for 
the `wt` parameter, w
 * <<Smile Response Writer,smile>>
 * <<Standard XML Response Writer,xml>>
 * <<XSLT Response Writer,xslt>>
+* <<NoOp Response Writer,noop>>
 
 == JSON Response Writer
 
@@ -386,3 +387,15 @@ else:
 == Smile Response Writer
 
 The Smile format is a JSON-compatible binary format, described in detail here: 
https://en.wikipedia.org/wiki/Smile_%28data_interchange_format%29[https://en.wikipedia.org/wiki/Smile_(data_interchange_format)]
+
+== NoOp Response Writer
+
+You may want to disable a specific response writer.
+This is supported by remapping the name of the response writer to the 
`NoOpResponseWriter` in `solrconfig.xml`, which will return a 200 OK status 
code with the plain text message `noop response writer`.
+
+For example, to disable the `csv` handler you would re-define it in 
`solrconfig.xml` as:
+
+[source,xml]
+----
+<queryResponseWriter name="csv" class="solr.NoOpResponseWriter" />
+----

Reply via email to