You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ds...@apache.org on 2026/04/02 21:00:43 UTC
(solr) branch main updated: SOLR-18168: Remove PathExclusionFilter; serve static content via ResourceServlet (#4243)
This is an automated email from the ASF dual-hosted git repository.
dsmiley 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 00e0870c4d5 SOLR-18168: Remove PathExclusionFilter; serve static content via ResourceServlet (#4243)
00e0870c4d5 is described below
commit 00e0870c4d50a6020f1a9039722b376741e569b8
Author: David Smiley <ds...@apache.org>
AuthorDate: Thu Apr 2 17:00:35 2026 -0400
SOLR-18168: Remove PathExclusionFilter; serve static content via ResourceServlet (#4243)
Instead of using a bespoke PathExclusionFilter to short-circuit Solr's filter chain
for Admin UI static resources (/css/*, /js/*, /img/*, /partials/*, etc.), web.xml now
maps those paths directly to Jetty's built-in ResourceServlet. Solr's filters are
remapped from url-pattern="/*" to servlet-name="SolrServlet", so they naturally apply
only to Solr requests and bypass static resources without bespoke code.
BATS integration tests were added to replace ones that had used JettySolrRunner previously.
Co-authored-by: copilot-swe-agent[bot] <19...@users.noreply.github.com>
Co-authored-by: dsmiley <37...@users.noreply.github.com>
---
AGENTS.md | 24 +++--
changelog/unreleased/SOLR-18041.yml | 8 +-
.../apache/solr/servlet/PathExclusionFilter.java | 80 ----------------
.../solr/servlet/LoadAdminUiServletTest.java | 106 ---------------------
solr/packaging/build.gradle | 5 +-
solr/packaging/test/test_adminconsole_urls.bats | 28 +++++-
solr/solrj/build.gradle | 1 -
.../client/solrj/embedded/JettyWebappTest.java | 102 --------------------
.../org/apache/solr/embedded/JettySolrRunner.java | 12 ---
solr/webapp/web/WEB-INF/web.xml | 86 ++++++++++-------
10 files changed, 105 insertions(+), 347 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 4395a2a4ee5..d53f75fe777 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -29,7 +29,7 @@ While README.md and CONTRIBUTING.md are mainly written for humans, this file is
## Build and Development Workflow
-- When done or preparing to commit changes to java source files, be sure to run `gradlew tidy` to format the code
+- When done or preparing to commit changes to java source files, be sure to run `gradlew tidy` to format the code. Don't bother beforehand.
- Always run `gradlew check -x test` before declaring a feature done
## Code Quality and Best Practices
@@ -38,15 +38,25 @@ While README.md and CONTRIBUTING.md are mainly written for humans, this file is
- Be careful to not add non-essential logging! If you add slf4j log calls, make sure to wrap debug/trace level calls in `logger.isXxxEnabled()` clause
- Validate user input. For file paths, always call `myCoreContainer.assertPathAllowed(myPath)` before using
-## Testing
+## Running Tests
+
+- See `dev-docs/gradle-help/tests.txt` for hints on running tests
+- To run a specific test: `gradlew :solr:core:test --tests "org.apache.solr.search.TestCaffeineCache"`
+- To run a specific BATS test: `gradlew iTest --tests test_adminconsole_urls.bats`
+- The randomization seed is important. To repeat a failing tests, pass the same seed given in the failure by adding to Gradle: `-Ptests.seed=HEXADECIMALHERE`.
+- Test output goes to `solr/<module>/build/test-results/test/outputs/OUTPUT-<fully.qualified.TestName>.txt` (stdout/stderr log) and `solr/<module>/build/test-results/test/TEST-<fully.qualified.TestName>.xml` (JUnit XML with pass/fail/error details)
+- To scan test output for a specific issue across already-run tests: `grep -rl "pattern" solr/*/build/test-results/test/outputs/`
+
+## Writing Tests
- When adding a test to an existing suite/file, keep the same style / design choices
- When adding a *new* Java test suite/file:
- - Subclass SolrTestCase, or if SolrCloud is needed then SolrCloudTestCase
- - If SolrTestCase and need to embed Solr, use either EmbeddedSolrServerTestRule (doesn't use HTTP) or SolrJettyTestRule if HTTP/Jetty is relevant to what is being tested.
- - Avoid SolrTestCaseJ4 for new tests
-
-- See `dev-docs/gradle-help/tests.txt` for hints on running tests
+ - Subclass SolrTestCase, or if SolrCloud is needed then SolrCloudTestCase
+ - If SolrTestCase and need to embed Solr, use either EmbeddedSolrServerTestRule (doesn't use HTTP) or SolrJettyTestRule if HTTP/Jetty is relevant to what is being tested.
+ - Avoid SolrTestCaseJ4 for new tests
+- For BATS shell integration tests in `solr/packaging/test/`:
+ - Always use `run <command>` followed by `assert_output --partial "..."` or `refute_output --partial "..."` instead of capturing output into local variables and using `[[ ]]` comparisons
+ - Avoid patterns like `local var=$(cmd | grep ...); [[ "$var" == *"..."* ]]` — use `run cmd` + `assert_output`/`refute_output` instead
## Documentation
diff --git a/changelog/unreleased/SOLR-18041.yml b/changelog/unreleased/SOLR-18041.yml
index d36eaad3ee5..269ce9ad666 100644
--- a/changelog/unreleased/SOLR-18041.yml
+++ b/changelog/unreleased/SOLR-18041.yml
@@ -1,8 +1,10 @@
-# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
-title: SOLR 18041 - Path exclusions for the admin UI are now defined in a separate servlet filter
-type: other # added, changed, fixed, deprecated, removed, dependency_update, security, other
+title: Path exclusions for the admin UI no longer need bespoke Solr code.
+type: other
authors:
- name: Gus Heck
+ - name: David Smiley
links:
- name: SOLR-18041
url: https://issues.apache.org/jira/browse/SOLR-18041
+ - name: SOLR-18168
+ url: https://issues.apache.org/jira/browse/SOLR-18168
diff --git a/solr/core/src/java/org/apache/solr/servlet/PathExclusionFilter.java b/solr/core/src/java/org/apache/solr/servlet/PathExclusionFilter.java
deleted file mode 100644
index 87e1e195a21..00000000000
--- a/solr/core/src/java/org/apache/solr/servlet/PathExclusionFilter.java
+++ /dev/null
@@ -1,80 +0,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.
- */
-package org.apache.solr.servlet;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.FilterConfig;
-import jakarta.servlet.RequestDispatcher;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpFilter;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Filter to identify paths that should be processed by Jetty's DefaultServlet. Typically, these
- * paths contain static resources that need to be returned verbatim, with appropriate content type,
- * which Jetty will determine via
- *
- * <p>{@code org.eclipse.jetty.http.MimeTypes#getMimeByExtension(java.lang.String)}
- */
-public class PathExclusionFilter extends HttpFilter {
-
- private List<Pattern> excludePatterns;
-
- boolean shouldBeExcluded(HttpServletRequest request) {
- String requestPath = ServletUtils.getPathAfterContext(request);
- if (excludePatterns != null) {
- return excludePatterns.stream().map(p -> p.matcher(requestPath)).anyMatch(Matcher::lookingAt);
- }
- return false;
- }
-
- @Override
- public void init(FilterConfig config) throws ServletException {
- String patternConfig = config.getInitParameter("excludePatterns");
- if (patternConfig != null) {
- String[] excludeArray = patternConfig.split(",");
- this.excludePatterns = Arrays.stream(excludeArray).map(Pattern::compile).toList();
- }
- super.init(config);
- }
-
- @Override
- protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- if (shouldBeExcluded(req)) {
- // N.B. "default" is the name for org.eclipse.jetty.ee10.servlet.DefaultServlet
- // configured in solr/server/etc/webdefault.xml if it doesn't exist something is
- // very wrong.
- RequestDispatcher defaultServlet = req.getServletContext().getNamedDispatcher("default");
- if (defaultServlet == null) {
- res.sendError(
- 500,
- "Server Misconfiguration: cannot find default servlet (normally defined as org.eclipse.jetty.ee10.servlet.DefaultServlet in webdefault.xml)");
- } else {
- defaultServlet.forward(req, res);
- }
- } else {
- chain.doFilter(req, res);
- }
- }
-}
diff --git a/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java b/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java
deleted file mode 100644
index dccd954e1be..00000000000
--- a/solr/core/src/test/org/apache/solr/servlet/LoadAdminUiServletTest.java
+++ /dev/null
@@ -1,106 +0,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.
- */
-
-package org.apache.solr.servlet;
-
-import static org.apache.solr.servlet.LoadAdminUiServlet.SYSPROP_CSP_CONNECT_SRC_URLS;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import jakarta.servlet.ServletConfig;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletOutputStream;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Set;
-import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.core.CoreContainer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class LoadAdminUiServletTest extends SolrTestCaseJ4 {
-
- @InjectMocks private LoadAdminUiServlet servlet;
- @Mock private HttpServletRequest mockRequest;
- @Mock private HttpServletResponse mockResponse;
- @Mock private CoreContainer coreContainer;
- @Mock private ServletConfig servletConfig;
- @Mock private ServletContext mockServletContext;
- @Mock private ServletOutputStream mockOutputStream;
- @Mock private CoreContainerProvider coreContainerProvider;
-
- private static final Set<String> CSP_URLS =
- Set.of(
- "http://example1.com/token",
- "https://example2.com/path/uri1",
- "http://example3.com/oauth2/uri2");
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.openMocks(this);
- when(mockRequest.getRequestURI()).thenReturn("/path/URI");
- when(mockRequest.getContextPath()).thenReturn("/path");
- when(mockRequest.getAttribute("org.apache.solr.CoreContainer")).thenReturn(coreContainer);
- when(servletConfig.getServletContext()).thenReturn(mockServletContext);
- when(mockResponse.getOutputStream()).thenReturn(mockOutputStream);
- InputStream mockInputStream =
- new ByteArrayInputStream("mock content".getBytes(StandardCharsets.UTF_8));
- when(mockServletContext.getResourceAsStream(anyString())).thenReturn(mockInputStream);
- when(mockServletContext.getAttribute(CoreContainerProvider.class.getName()))
- .thenReturn(coreContainerProvider);
- when(coreContainerProvider.getCoreContainer()).thenReturn(coreContainer);
- }
-
- @BeforeClass
- public static void ensureWorkingMockito() {
- assumeWorkingMockito();
- }
-
- @Test
- public void testDefaultCSPHeaderSet() throws IOException {
- System.setProperty(SYSPROP_CSP_CONNECT_SRC_URLS, String.join(",", CSP_URLS));
- ArgumentCaptor<String> headerNameCaptor = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor<String> headerValueCaptor = ArgumentCaptor.forClass(String.class);
- servlet.doGet(mockRequest, mockResponse);
-
- verify(mockResponse).setHeader(headerNameCaptor.capture(), headerValueCaptor.capture());
- assertEquals("Content-Security-Policy", headerNameCaptor.getValue());
- String cspValue = headerValueCaptor.getValue();
- for (String endpoint : CSP_URLS) {
- assertTrue("Expected CSP value to contain " + endpoint, cspValue.contains(endpoint));
- }
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- super.tearDown();
- }
-}
diff --git a/solr/packaging/build.gradle b/solr/packaging/build.gradle
index 31d4904ba91..7320f2ebe2b 100644
--- a/solr/packaging/build.gradle
+++ b/solr/packaging/build.gradle
@@ -282,6 +282,7 @@ task integrationTests(type: BatsTask) {
}
inputs.dir(distDir)
+ inputs.dir('test') // Track .bats test files and helper as inputs
outputs.dir(integrationTestOutput)
doFirst {
@@ -311,7 +312,9 @@ task integrationTests(type: BatsTask) {
}
class BatsTask extends Exec {
- @InputDirectory
+ // Note: testDir is marked @Internal because it's a String configuration property, not a trackable input.
+ // The task using BatsTask should explicitly declare inputs.dir(testDir) in its configuration.
+ @Internal
String testDir = 'test'
@Input
diff --git a/solr/packaging/test/test_adminconsole_urls.bats b/solr/packaging/test/test_adminconsole_urls.bats
index ad0d2112c83..bc468a75470 100644
--- a/solr/packaging/test/test_adminconsole_urls.bats
+++ b/solr/packaging/test/test_adminconsole_urls.bats
@@ -28,11 +28,37 @@ teardown() {
solr stop --all >/dev/null 2>&1
}
+
@test "assert able to launch solr admin console" {
run solr start
run curl -s -o /dev/null -w "%{http_code}" http://localhost:${SOLR_PORT}/solr/
+ assert_output "200"
+
+ run curl -s -I http://localhost:${SOLR_PORT}/solr/
+ assert_output --partial "text/html"
- # Check if the HTTP response code is 200 (OK)
+ run curl -s http://localhost:${SOLR_PORT}/solr/
+ assert_output --partial "<html"
+
+ run curl -s -o /dev/null -w "%{http_code}" http://localhost:${SOLR_PORT}/solr/css/angular/chosen.css
assert_output "200"
+
+ run curl -s -I http://localhost:${SOLR_PORT}/solr/css/angular/chosen.css
+ assert_output --partial "text/css"
+
+ run curl -s http://localhost:${SOLR_PORT}/solr/css/angular/chosen.css
+ assert_output --partial "{"
+}
+
+@test "assert CSP header contains custom connect src URLs" {
+ # Set custom CSP connect-src URLs via system property
+ local csp_urls="http://example1.com/token,https://example2.com/path/uri1,http://example3.com/oauth2/uri2"
+
+ run solr start -Dsolr.ui.headers.csp.connect-src.urls="${csp_urls}"
+
+ run curl -s -I http://localhost:${SOLR_PORT}/solr/
+ assert_output --partial "http://example1.com/token"
+ assert_output --partial "https://example2.com/path/uri1"
+ assert_output --partial "http://example3.com/oauth2/uri2"
}
diff --git a/solr/solrj/build.gradle b/solr/solrj/build.gradle
index 098ad3e8c33..fbf02869a2b 100644
--- a/solr/solrj/build.gradle
+++ b/solr/solrj/build.gradle
@@ -75,7 +75,6 @@ dependencies {
testRuntimeOnly(libs.eclipse.jetty.alpnjavaserver, {
exclude group: "org.eclipse.jetty.alpn", module: "alpn-api"
})
- testImplementation libs.eclipse.jetty.session
testImplementation(libs.mockito.core, {
exclude group: "net.bytebuddy", module: "byte-buddy-agent"
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/JettyWebappTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/JettyWebappTest.java
deleted file mode 100644
index 25d21e01001..00000000000
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/JettyWebappTest.java
+++ /dev/null
@@ -1,102 +0,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.
- */
-package org.apache.solr.client.solrj.embedded;
-
-import java.io.InputStream;
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Locale;
-import java.util.Random;
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.apache.HttpClientUtil;
-import org.apache.solr.util.ExternalPaths;
-import org.eclipse.jetty.ee10.webapp.WebAppContext;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.session.DefaultSessionIdManager;
-
-/**
- * @since solr 1.3
- */
-public class JettyWebappTest extends SolrTestCaseJ4 {
- int port = 0;
-
- Server server;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- System.setProperty("solr.solr.home", createTempDir().toString());
- System.setProperty("tests.shardhandler.randomSeed", Long.toString(random().nextLong()));
- System.setProperty("solr.tests.doContainerStreamCloseAssert", "false");
-
- Path dataDir = createTempDir();
- Files.createDirectories(dataDir);
-
- System.setProperty("solr.data.dir", dataDir.toRealPath().toString());
- String path = ExternalPaths.WEBAPP_HOME.toString();
-
- server = new Server(port);
- // insecure: only use for tests!!!!
- server.addBean(new DefaultSessionIdManager(server, new Random(random().nextLong())));
- server.setHandler(new WebAppContext(path, "/solr"));
-
- ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory());
- connector.setIdleTimeout(1000 * 60 * 60);
- connector.setPort(0);
- server.setConnectors(new Connector[] {connector});
- server.setStopAtShutdown(true);
-
- server.start();
- port = connector.getLocalPort();
- }
-
- @Override
- public void tearDown() throws Exception {
- try {
- server.stop();
- } catch (Exception ex) {
- }
- super.tearDown();
- }
-
- public void testAdminUI() throws Exception {
- // Not an extensive test, but it does connect to Solr and verify the Admin ui shows up.
- String adminPath = "http://127.0.0.1:" + port + "/solr/";
- try (InputStream is = URI.create(adminPath).toURL().openStream()) {
- assertNotNull(is.readAllBytes()); // real error will be an exception
- }
-
- try (CloseableHttpClient client = HttpClients.createDefault()) {
- HttpRequestBase m = new HttpGet(adminPath);
- HttpResponse response = client.execute(m, HttpClientUtil.createNewHttpClientRequestContext());
- assertEquals(200, response.getStatusLine().getStatusCode());
- Header header = response.getFirstHeader("X-Frame-Options");
- assertEquals("DENY", header.getValue().toUpperCase(Locale.ROOT));
- m.releaseConnection();
- }
- }
-}
diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
index 5ecabf8abf9..1c6405675ee 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
@@ -65,7 +65,6 @@ import org.apache.solr.core.CoreContainer;
import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.servlet.AuthenticationFilter;
import org.apache.solr.servlet.CoreContainerProvider;
-import org.apache.solr.servlet.PathExclusionFilter;
import org.apache.solr.servlet.RateLimitFilter;
import org.apache.solr.servlet.RequiredSolrRequestFilter;
import org.apache.solr.servlet.SolrServlet;
@@ -118,7 +117,6 @@ public class JettySolrRunner implements SolrBackend {
private Server server;
volatile FilterHolder debugFilter;
- volatile FilterHolder pathExcludeFilter;
volatile FilterHolder requiredFilter;
volatile FilterHolder rateLimitFilter;
volatile FilterHolder authFilter;
@@ -135,9 +133,6 @@ public class JettySolrRunner implements SolrBackend {
private List<FilterHolder> extraFilters;
- private static final String excludePatterns =
- "/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+,/templates/.+";
-
private int proxyPort = -1;
private final boolean enableProxy;
@@ -415,12 +410,6 @@ public class JettySolrRunner implements SolrBackend {
// TODO: This needs to be driven by a parsing of web.xml eventually
// though we still want to avoid classpath scanning.
- // this path excludes filter isn't actually necessary for any tests, but it's being
- // added for parity with the live application.
- pathExcludeFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
- pathExcludeFilter.setHeldClass(PathExclusionFilter.class);
- pathExcludeFilter.setInitParameter("excludePatterns", excludePatterns);
-
// required request setup
requiredFilter = root.getServletHandler().newFilterHolder(Source.EMBEDDED);
requiredFilter.setHeldClass(RequiredSolrRequestFilter.class);
@@ -438,7 +427,6 @@ public class JettySolrRunner implements SolrBackend {
authFilter.setHeldClass(AuthenticationFilter.class);
// Map filters in same path as in web.xml
- root.addFilter(pathExcludeFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(requiredFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(rateLimitFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
root.addFilter(tracingFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
diff --git a/solr/webapp/web/WEB-INF/web.xml b/solr/webapp/web/WEB-INF/web.xml
index 201a88156c3..04f949e2f6b 100644
--- a/solr/webapp/web/WEB-INF/web.xml
+++ b/solr/webapp/web/WEB-INF/web.xml
@@ -16,89 +16,107 @@
limitations under the License.
-->
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
- version="6.0"
- metadata-complete="true">
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
+ version="6.0"
+ metadata-complete="true">
<listener>
- <listener-class>org.apache.solr.servlet.CoreContainerProvider</listener-class>
+ <listener-class>org.apache.solr.servlet.CoreContainerProvider</listener-class>
</listener>
- <!-- Any path (name) registered in solrconfig.xml will be sent to that filter -->
- <filter>
- <filter-name>PathExclusionsFilter</filter-name>
- <filter-class>org.apache.solr.servlet.PathExclusionFilter</filter-class>
- <!--
- Exclude patterns is a list of directories that would be short-circuited by this
- Filter. It includes all Admin UI related static content.
- NOTE: It is NOT a pattern but only matches the start of the HTTP ServletPath.
- -->
- <init-param>
- <param-name>excludePatterns</param-name>
- <param-value>/partials/.+,/libs/.+,/css/.+,/js/.+,/img/.+,/templates/.+,/ui/.*</param-value>
- </init-param>
- </filter>
-
- <filter-mapping>
- <filter-name>PathExclusionsFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
<filter>
<filter-name>SolrFilter</filter-name>
<filter-class>org.apache.solr.servlet.RequiredSolrRequestFilter</filter-class>
</filter>
-
<filter-mapping>
<filter-name>SolrFilter</filter-name>
- <url-pattern>/*</url-pattern>
+ <servlet-name>SolrServlet</servlet-name>
</filter-mapping>
<filter>
<filter-name>RateLimitFilter</filter-name>
<filter-class>org.apache.solr.servlet.RateLimitFilter</filter-class>
</filter>
-
<filter-mapping>
<filter-name>RateLimitFilter</filter-name>
- <url-pattern>/*</url-pattern>
+ <servlet-name>SolrServlet</servlet-name>
</filter-mapping>
<filter>
<filter-name>TracingFilter</filter-name>
<filter-class>org.apache.solr.servlet.TracingFilter</filter-class>
</filter>
-
<filter-mapping>
<filter-name>TracingFilter</filter-name>
- <url-pattern>/*</url-pattern>
+ <servlet-name>SolrServlet</servlet-name>
</filter-mapping>
<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>org.apache.solr.servlet.AuthenticationFilter</filter-class>
</filter>
-
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
- <url-pattern>/*</url-pattern>
+ <servlet-name>SolrServlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>SolrServlet</servlet-name>
<servlet-class>org.apache.solr.servlet.SolrServlet</servlet-class>
</servlet>
-
<servlet-mapping>
<servlet-name>SolrServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
+ <!-- Define servlet for static Admin UI resources -->
+ <servlet>
+ <servlet-name>static</servlet-name>
+ <servlet-class>org.eclipse.jetty.ee10.servlet.ResourceServlet</servlet-class>
+ <init-param>
+ <param-name>pathInfoOnly</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ <init-param>
+ <param-name>dirAllowed</param-name>
+ <param-value>false</param-value>
+ </init-param>
+ </servlet>
+ <!-- Map static Admin UI resources to the static servlet -->
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/partials/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/libs/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/css/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/js/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/img/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/templates/*</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>static</servlet-name>
+ <url-pattern>/ui/*</url-pattern>
+ </servlet-mapping>
+
<servlet>
<servlet-name>LoadAdminUI</servlet-name>
<servlet-class>org.apache.solr.servlet.LoadAdminUiServlet</servlet-class>
</servlet>
-
<servlet-mapping>
<servlet-name>LoadAdminUI</servlet-name>
<url-pattern>/index.html</url-pattern>