diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java index c3866cd5f01..d9b980efc8d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java @@ -829,19 +829,19 @@ public class ResourceService Response r = (Response)response; r.putHeaders(content, contentLength, _etags); HttpFields f = r.getHttpFields(); - if (_acceptRanges) + if (_acceptRanges && !response.containsHeader(HttpHeader.ACCEPT_RANGES.asString())) f.put(ACCEPT_RANGES); - if (_cacheControl != null) + if (_cacheControl != null && !response.containsHeader(HttpHeader.CACHE_CONTROL.asString())) f.put(_cacheControl); } else { Response.putHeaders(response, content, contentLength, _etags); - if (_acceptRanges) + if (_acceptRanges && !response.containsHeader(HttpHeader.ACCEPT_RANGES.name())) response.setHeader(ACCEPT_RANGES.getName(), ACCEPT_RANGES.getValue()); - if (_cacheControl != null) + if (_cacheControl != null && !response.containsHeader(HttpHeader.CACHE_CONTROL.name())) response.setHeader(_cacheControl.getName(), _cacheControl.getValue()); } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java index 1aaf297215d..101a292a86f 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java @@ -283,7 +283,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc _resourceService.setContentFactory(contentFactory); _resourceService.setWelcomeFactory(this); - List gzipEquivalentFileExtensions = new ArrayList(); + List gzipEquivalentFileExtensions = new ArrayList<>(); String otherGzipExtensions = getInitParameter("otherGzipFileExtensions"); if (otherGzipExtensions != null) { diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/CacheControlHeaderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/CacheControlHeaderTest.java new file mode 100644 index 00000000000..72eb82a5ea6 --- /dev/null +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/CacheControlHeaderTest.java @@ -0,0 +1,157 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.servlet; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.EnumSet; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class CacheControlHeaderTest +{ + private Server server; + private LocalConnector connector; + + public static class ForceCacheControlFilter implements Filter + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + HttpServletResponse httpResponse = (HttpServletResponse)response; + httpResponse.setHeader(HttpHeader.CACHE_CONTROL.asString(), "max-age=0,private"); + chain.doFilter(request, response); + } + + @Override + public void destroy() + { + } + } + + public void startServer(boolean forceFilter) throws Exception + { + server = new Server(); + + HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(new HttpConfiguration()); + connector = new LocalConnector(server, null, null, null, -1, httpConnectionFactory); + + ServletContextHandler context = new ServletContextHandler(); + + ServletHolder servletHolder = new ServletHolder(); + servletHolder.setServlet(new DefaultServlet()); + servletHolder.setInitParameter("cacheControl", "max-age=3600,public"); + Path resBase = MavenTestingUtils.getTestResourcePathDir("contextResources"); + servletHolder.setInitParameter("resourceBase", resBase.toFile().toURI().toASCIIString()); + context.addServlet(servletHolder, "/*"); + if (forceFilter) + { + context.addFilter(ForceCacheControlFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); + } + server.setHandler(context); + server.addConnector(connector); + + server.start(); + } + + public void stopServer() throws Exception + { + if (server != null && server.isRunning()) + { + server.stop(); + } + } + + @Test + public void testCacheControlFilterOverride() throws Exception + { + try + { + startServer(true); + StringBuffer req1 = new StringBuffer(); + req1.append("GET /content.txt HTTP/1.1\r\n"); + req1.append("Host: local\r\n"); + req1.append("Accept: */*\r\n"); + req1.append("Connection: close\r\n"); + req1.append("\r\n"); + + String response = connector.getResponse(req1.toString()); + assertThat("Response status", + response, + containsString("HTTP/1.1 200 OK")); + assertThat("Response headers", + response, + containsString(HttpHeader.CACHE_CONTROL.asString() + ": max-age=0,private")); + } + finally + { + stopServer(); + } + } + + @Test + public void testCacheControlDefaultServlet() throws Exception + { + try + { + startServer(false); + StringBuffer req1 = new StringBuffer(); + req1.append("GET /content.txt HTTP/1.1\r\n"); + req1.append("Host: local\r\n"); + req1.append("Accept: */*\r\n"); + req1.append("Connection: close\r\n"); + req1.append("\r\n"); + + String response = connector.getResponse(req1.toString()); + assertThat("Response status", + response, + containsString("HTTP/1.1 200 OK")); + assertThat("Response headers", + response, + containsString(HttpHeader.CACHE_CONTROL.asString() + ": max-age=3600,public")); + } + finally + { + stopServer(); + } + } + +}