Skip to content

Commit e68d292

Browse files
authored
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.
1 parent 2ac2aa2 commit e68d292

File tree

8 files changed

+298
-3
lines changed

8 files changed

+298
-3
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
2+
title: Provide NoOpRequestWriter and NoOpRequestHandler that can be used to disable implicitly configured equivalents.
3+
type: added # added, changed, fixed, deprecated, removed, dependency_update, security, other
4+
authors:
5+
- name: Eric Pugh
6+
links:
7+
- name: SOLR-18095
8+
url: https://issues.apache.org/jira/browse/SOLR-18095

solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java renamed to solr/core/src/java/org/apache/solr/handler/NoOpRequestHandler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@
2323
import org.apache.solr.response.SolrQueryResponse;
2424
import org.apache.solr.security.AuthorizationContext;
2525

26-
/** Does nothing other than showing a 404 message */
27-
public class NotFoundRequestHandler extends RequestHandlerBase {
26+
/** Does nothing other than showing a 403 message */
27+
public class NoOpRequestHandler extends RequestHandlerBase {
2828
@Override
2929
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
3030
throw new SolrException(
31-
SolrException.ErrorCode.NOT_FOUND, "" + req.getContext().get(PATH) + " is not found");
31+
SolrException.ErrorCode.FORBIDDEN, req.getContext().get(PATH) + " has been disabled");
3232
}
3333

3434
@Override
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.response;
18+
19+
import java.io.IOException;
20+
import java.io.Writer;
21+
import org.apache.solr.request.SolrQueryRequest;
22+
23+
public class NoOpResponseWriter implements TextQueryResponseWriter {
24+
static String MESSAGE = "noop response writer";
25+
26+
@Override
27+
public void write(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) throws IOException {
28+
writer.write(MESSAGE);
29+
}
30+
31+
@Override
32+
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
33+
return QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8;
34+
}
35+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" ?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one or more
5+
contributor license agreements. See the NOTICE file distributed with
6+
this work for additional information regarding copyright ownership.
7+
The ASF licenses this file to You under the Apache License, Version 2.0
8+
(the "License"); you may not use this file except in compliance with
9+
the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-->
19+
20+
<!-- Test config that demonstrates overriding implicit handlers and response writers with NoOp versions -->
21+
22+
<config>
23+
24+
<dataDir>${solr.data.dir:}</dataDir>
25+
26+
<schemaFactory class="ClassicIndexSchemaFactory"/>
27+
28+
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
29+
30+
<updateHandler class="solr.DirectUpdateHandler2">
31+
<updateLog enable="${solr.index.updatelog.enabled:true}">
32+
<str name="dir">${solr.ulog.dir:}</str>
33+
</updateLog>
34+
</updateHandler>
35+
36+
<!--
37+
Override the implicit /schema handler (from ImplicitPlugins.json)
38+
with NoOpRequestHandler to demonstrate that implicit handlers can be disabled
39+
-->
40+
<requestHandler name="/schema" class="solr.NoOpRequestHandler" />
41+
42+
<!-- Standard search handler for testing other functionality still works -->
43+
<requestHandler name="/select" class="solr.SearchHandler">
44+
<lst name="defaults">
45+
<str name="echoParams">explicit</str>
46+
<str name="indent">true</str>
47+
<str name="df">text</str>
48+
</lst>
49+
</requestHandler>
50+
51+
<!--
52+
Override the implicit CSV response writer (from ImplicitPlugins.json)
53+
with NoOpResponseWriter to demonstrate that implicit response writers can be disabled
54+
-->
55+
<queryResponseWriter name="csv" class="solr.NoOpResponseWriter" />
56+
57+
</config>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.handler;
18+
19+
import org.apache.solr.SolrTestCaseJ4;
20+
import org.apache.solr.common.SolrException;
21+
import org.apache.solr.request.SolrQueryRequest;
22+
import org.apache.solr.response.SolrQueryResponse;
23+
import org.junit.BeforeClass;
24+
import org.junit.Test;
25+
26+
/**
27+
* Test that demonstrates NoOpRequestHandler can be used to disable implicit handlers like
28+
* SchemaHandler that are loaded via ImplicitPlugins.json.
29+
*/
30+
public class NoOpRequestHandlerTest extends SolrTestCaseJ4 {
31+
32+
@BeforeClass
33+
public static void beforeClass() throws Exception {
34+
initCore("solrconfig-noop.xml", "schema.xml");
35+
}
36+
37+
@Test
38+
public void testSchemaHandlerDisabled() {
39+
// Test that /schema endpoint is disabled and returns 403 FORBIDDEN
40+
SolrException exception =
41+
expectThrows(
42+
SolrException.class,
43+
() -> {
44+
try (SolrQueryRequest req = req("qt", "/schema")) {
45+
SolrQueryResponse rsp = new SolrQueryResponse();
46+
h.getCore().execute(h.getCore().getRequestHandler("/schema"), req, rsp);
47+
if (rsp.getException() != null) {
48+
throw rsp.getException();
49+
}
50+
}
51+
});
52+
53+
assertEquals(
54+
"Should return FORBIDDEN status code",
55+
SolrException.ErrorCode.FORBIDDEN.code,
56+
exception.code());
57+
assertTrue(
58+
"Error message should indicate endpoint has been disabled",
59+
exception.getMessage().contains("has been disabled"));
60+
}
61+
62+
@Test
63+
public void testSchemaHandlerSubPathDisabled() {
64+
// Test that /schema/fields endpoint is also disabled
65+
SolrException exception =
66+
expectThrows(
67+
SolrException.class,
68+
() -> {
69+
try (SolrQueryRequest req = req("qt", "/schema/fields")) {
70+
SolrQueryResponse rsp = new SolrQueryResponse();
71+
h.getCore().execute(h.getCore().getRequestHandler("/schema"), req, rsp);
72+
if (rsp.getException() != null) {
73+
throw rsp.getException();
74+
}
75+
}
76+
});
77+
78+
assertEquals(
79+
"Should return FORBIDDEN status code",
80+
SolrException.ErrorCode.FORBIDDEN.code,
81+
exception.code());
82+
}
83+
84+
@Test
85+
public void testNoOpHandlerRegistered() {
86+
// Verify that the NoOpRequestHandler is actually registered at /schema
87+
assertNotNull("Schema handler should be registered", h.getCore().getRequestHandler("/schema"));
88+
assertTrue(
89+
"Handler at /schema should be NoOpRequestHandler",
90+
h.getCore().getRequestHandler("/schema") instanceof NoOpRequestHandler);
91+
}
92+
93+
@Test
94+
public void testOtherHandlersStillWork() {
95+
assertQ("Standard query handler should still work", req("q", "*:*"), "//result[@numFound='0']");
96+
}
97+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.solr.response;
18+
19+
import java.io.ByteArrayOutputStream;
20+
import java.io.IOException;
21+
import java.io.StringWriter;
22+
import java.io.Writer;
23+
import java.nio.charset.StandardCharsets;
24+
import org.apache.solr.SolrTestCaseJ4;
25+
import org.junit.BeforeClass;
26+
import org.junit.Test;
27+
28+
/**
29+
* Test that demonstrates NoOpResponseWriter can be used to disable implicit response writers that
30+
* are loaded via ImplicitPlugins.json.
31+
*/
32+
public class NoOpResponseWriterTest extends SolrTestCaseJ4 {
33+
34+
@BeforeClass
35+
public static void beforeClass() throws Exception {
36+
initCore("solrconfig-noop.xml", "schema.xml");
37+
}
38+
39+
@Test
40+
public void testWrite() throws IOException {
41+
NoOpResponseWriter writer = new NoOpResponseWriter();
42+
43+
Writer stringWriter = new StringWriter();
44+
45+
writer.write(stringWriter, null, null);
46+
47+
assertEquals(NoOpResponseWriter.MESSAGE, stringWriter.toString());
48+
}
49+
50+
@Test
51+
public void testGetContentType() {
52+
NoOpResponseWriter writer = new NoOpResponseWriter();
53+
54+
String contentType = writer.getContentType(null, null);
55+
assertEquals(QueryResponseWriter.CONTENT_TYPE_TEXT_UTF8, contentType);
56+
}
57+
58+
@Test
59+
public void testCsvResponseWriterDisabled() throws Exception {
60+
QueryResponseWriter csvWriter = h.getCore().getQueryResponseWriter("csv");
61+
62+
assertNotNull("CSV response writer should be registered", csvWriter);
63+
assertTrue(
64+
"CSV response writer should be NoOpResponseWriter, not the implicit CSVResponseWriter",
65+
csvWriter instanceof NoOpResponseWriter);
66+
67+
// Verify it returns the NoOp message when used
68+
ByteArrayOutputStream out = new ByteArrayOutputStream();
69+
csvWriter.write(out, null, null);
70+
String output = out.toString(StandardCharsets.UTF_8);
71+
assertEquals("CSV writer should return NoOp message", NoOpResponseWriter.MESSAGE, output);
72+
}
73+
}

solr/solr-ref-guide/modules/configuration-guide/pages/implicit-requesthandlers.adoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,15 @@ The response will look similar to:
363363
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.
364364
However, other parameters, including SearchHandler components, may not be modified.
365365
The invariants and appends specified in the implicit configuration cannot be overridden.
366+
367+
== How to Disable an Implicit Handler
368+
369+
You may want to disable the loading of an implicit handler.
370+
This is supported by remapping the name of the handler to the `NoOpRequestHandler` in `solrconfig.xml`, which will return a 403 FORBIDDEN status code.
371+
372+
For example, to disable the `/update/csv` handler you would re-define it in `solrconfig.xml` as:
373+
374+
[source,xml]
375+
----
376+
<requestHandler name="/update/csv" class="solr.NoOpRequestHandler"></requestHandler>
377+
----

solr/solr-ref-guide/modules/query-guide/pages/response-writers.adoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The list below describe shows the most common settings for the `wt` parameter, w
3232
* <<Smile Response Writer,smile>>
3333
* <<Standard XML Response Writer,xml>>
3434
* <<XSLT Response Writer,xslt>>
35+
* <<NoOp Response Writer,noop>>
3536
3637
== JSON Response Writer
3738

@@ -386,3 +387,15 @@ else:
386387
== Smile Response Writer
387388

388389
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)]
390+
391+
== NoOp Response Writer
392+
393+
You may want to disable a specific response writer.
394+
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`.
395+
396+
For example, to disable the `csv` handler you would re-define it in `solrconfig.xml` as:
397+
398+
[source,xml]
399+
----
400+
<queryResponseWriter name="csv" class="solr.NoOpResponseWriter" />
401+
----

0 commit comments

Comments
 (0)