diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CachingContentFactory.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CachingContentFactory.java index 31355a0c8d6..1c1320ea9ec 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CachingContentFactory.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CachingContentFactory.java @@ -177,6 +177,7 @@ public class CachingContentFactory implements HttpContent.ContentFactory else removeFromCache(cachingHttpContent); } + HttpContent httpContent = _authority.getContent(path, maxBuffer); // Do not cache directories or files that are too big if (httpContent != null && !httpContent.getResource().isDirectory() && httpContent.getContentLengthValue() <= _maxCachedFileSize) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java index 05d2726b9d9..467bdf1ae85 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java @@ -19,7 +19,6 @@ import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.InvalidPathException; -import java.nio.file.Path; import java.util.Collections; import java.util.Enumeration; import java.util.List; @@ -43,7 +42,6 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.URIUtil; -import org.eclipse.jetty.util.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,7 +57,6 @@ public class ResourceService private static final int NO_CONTENT_LENGTH = -1; private static final int USE_KNOWN_CONTENT_LENGTH = -2; - private boolean _pathInfoOnly = false; private CompressedContentFormat[] _precompressedFormats = new CompressedContentFormat[0]; private WelcomeFactory _welcomeFactory; private boolean _redirectWelcome = false; @@ -392,7 +389,7 @@ public class ResourceService } } - // look for a welcome file + // process optional Welcome behaviors if (welcome(request, response, callback)) return; @@ -400,41 +397,81 @@ public class ResourceService sendDirectory(request, response, content, callback, pathInContext); } + public enum WelcomeActionType + { + REDIRECT, + SERVE + } + + /** + * Behavior for a potential welcome action + * as determined by {@link ResourceService#processWelcome(Request, Response)} + * + *

+ * For {@link WelcomeActionType#REDIRECT} this is the resulting `Location` response header. + * For {@link WelcomeActionType#SERVE} this is the resulting path to for welcome serve, note that + * this is just a path, and can point to a real file, or a dynamic handler for + * welcome processing (such as Jetty core Handler, or EE Servlet), it's up + * to the implementation of {@link ResourceService#welcome(Request, Response, Callback)} + * to handle the various action types. + *

+ * @param type the type of action + * @param target The target URI path of the action. + */ + public record WelcomeAction(WelcomeActionType type, String target) {} + protected boolean welcome(Request request, Response response, Callback callback) throws IOException { - String pathInContext = request.getPathInContext(); - String welcome = _welcomeFactory == null ? null : _welcomeFactory.getWelcomeFile(pathInContext); - if (welcome != null) + WelcomeAction welcomeAction = processWelcome(request, response); + if (welcomeAction == null) + return false; + + switch (welcomeAction.type) { - String contextPath = request.getContext().getContextPath(); - - if (_pathInfoOnly) - welcome = URIUtil.addPaths(contextPath, welcome); - - if (LOG.isDebugEnabled()) - LOG.debug("welcome={}", welcome); - - if (_redirectWelcome) + case REDIRECT -> { - // Redirect to the index response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, 0); - // TODO need URI util that handles param and query without reconstructing entire URI with scheme and authority - HttpURI.Mutable uri = HttpURI.build(request.getHttpURI()); - String parameter = uri.getParam(); - uri.path(URIUtil.addPaths(request.getContext().getContextPath(), welcome)); - uri.param(parameter); - Response.sendRedirect(request, response, callback, uri.getPathQuery()); + Response.sendRedirect(request, response, callback, welcomeAction.target); + return true; + } + case SERVE -> + { + // TODO output buffer size???? + HttpContent c = _contentFactory.getContent(welcomeAction.target, 16 * 1024); + sendData(request, response, callback, c, null); return true; } - - // Serve welcome file - HttpContent c = _contentFactory.getContent(welcome, 16 * 1024); // TODO output buffer size???? - sendData(request, response, callback, c, null); - return true; } return false; } + protected WelcomeAction processWelcome(Request request, Response response) throws IOException + { + String welcomeTarget = _welcomeFactory.getWelcomeTarget(request); + if (welcomeTarget == null) + return null; + + String contextPath = request.getContext().getContextPath(); + + if (LOG.isDebugEnabled()) + LOG.debug("welcome={}", welcomeTarget); + + if (_redirectWelcome) + { + // Redirect to the index + response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, 0); + // TODO need URI util that handles param and query without reconstructing entire URI with scheme and authority + HttpURI.Mutable uri = HttpURI.build(request.getHttpURI()); + String parameter = uri.getParam(); + uri.path(URIUtil.addPaths(request.getContext().getContextPath(), welcomeTarget)); + uri.param(parameter); + return new WelcomeAction(WelcomeActionType.REDIRECT, uri.getPathQuery()); + } + + // Serve welcome file + return new WelcomeAction(WelcomeActionType.SERVE, welcomeTarget); + } + private void sendDirectory(Request request, Response response, HttpContent httpContent, Callback callback, String pathInContext) throws IOException { if (!_dirAllowed) @@ -678,14 +715,6 @@ public class ResourceService return _precompressedFormats; } - /** - * @return true, only the path info will be applied to the resourceBase - */ - public boolean isPathInfoOnly() - { - return _pathInfoOnly; - } - /** * @return If true, welcome files are redirected rather than forwarded to. */ @@ -761,14 +790,6 @@ public class ResourceService return _encodingCacheSize; } - /** - * @param pathInfoOnly true, only the path info will be applied to the resourceBase - */ - public void setPathInfoOnly(boolean pathInfoOnly) - { - _pathInfoOnly = pathInfoOnly; - } - /** * @param redirectWelcome If true, welcome files are redirected rather than forwarded to. * redirection is always used if the ResourceHandler is not scoped by @@ -786,14 +807,14 @@ public class ResourceService public interface WelcomeFactory { - /** - * Finds a matching welcome file for the supplied {@link Resource}. - * TODO would be better to pass back a URI or a Resource - * @param pathInContext the path of the request - * @return The path of the matching welcome file in context or null. + * Finds a matching welcome target URI path for the request. + * + * @param request the request to use to determine the matching welcome target from. + * @return The URI path of the matching welcome target in context or null + * (null means no welcome target was found) */ - String getWelcomeFile(String pathInContext) throws IOException; + String getWelcomeTarget(Request request) throws IOException; } private static class ContentWriterIteratingCallback extends IteratingCallback diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java index 0b5e778be19..c34bd00e2cb 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java @@ -81,15 +81,15 @@ public class ResourceHandler extends Handler.Wrapper { HttpContent.ContentFactory contentFactory = new CachingContentFactory(new ResourceContentFactory(_resourceBase, _mimeTypes, _resourceService.getPrecompressedFormats())); _resourceService.setContentFactory(contentFactory); - _resourceService.setWelcomeFactory(pathInContext -> + _resourceService.setWelcomeFactory(request -> { if (_welcomes == null) return null; for (String welcome : _welcomes) { - String welcomeInContext = URIUtil.addPaths(pathInContext, welcome); - Resource welcomePath = _resourceBase.resolve(pathInContext).resolve(welcome); + String welcomeInContext = URIUtil.addPaths(request.getPathInContext(), welcome); + Resource welcomePath = _resourceBase.resolve(request.getPathInContext()).resolve(welcome); if (welcomePath != null && welcomePath.exists()) return welcomeInContext; } @@ -204,14 +204,6 @@ public class ResourceHandler extends Handler.Wrapper return _resourceService.getPrecompressedFormats(); } - /** - * @return true, only the path info will be applied to the resourceBase - */ - public boolean isPathInfoOnly() - { - return _resourceService.isPathInfoOnly(); - } - /** * @return If true, welcome files are redirected rather than forwarded to. */ @@ -296,14 +288,6 @@ public class ResourceHandler extends Handler.Wrapper setupContentFactory(); } - /** - * @param pathInfoOnly true, only the path info will be applied to the resourceBase - */ - public void setPathInfoOnly(boolean pathInfoOnly) - { - _resourceService.setPathInfoOnly(pathInfoOnly); - } - /** * @param redirectWelcome If true, welcome files are redirected rather than forwarded to. * redirection is always used if the ResourceHandler is not scoped by diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java index f5f7b560fa6..4bb0f910855 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/DefaultServlet.java @@ -75,10 +75,13 @@ public class DefaultServlet extends HttpServlet private boolean _welcomeExactServlets = false; private Resource.Mount _resourceBaseMount; + private Resource _baseResource; private Resource.Mount _stylesheetMount; private Resource _stylesheet; private boolean _useFileMappedBuffer = false; + private boolean _isPathInfoOnly = false; + @Override public void init() throws ServletException { @@ -86,7 +89,7 @@ public class DefaultServlet extends HttpServlet _resourceService = new ServletResourceService(servletContextHandler); _resourceService.setWelcomeFactory(_resourceService); - Resource baseResource = servletContextHandler.getResourceBase(); + _baseResource = servletContextHandler.getResourceBase(); String rb = getInitParameter("resourceBase"); if (rb != null) { @@ -94,7 +97,7 @@ public class DefaultServlet extends HttpServlet { URI resourceUri = Resource.toURI(rb); _resourceBaseMount = Resource.mountIfNeeded(resourceUri); - baseResource = Resource.newResource(resourceUri); + _baseResource = Resource.newResource(resourceUri); } catch (Exception e) { @@ -109,7 +112,7 @@ public class DefaultServlet extends HttpServlet CompressedContentFormat[] precompressedFormats = new CompressedContentFormat[0]; _useFileMappedBuffer = getInitBoolean("useFileMappedBuffer", _useFileMappedBuffer); - ResourceContentFactory resourceContentFactory = new ResourceContentFactory(baseResource, mimeTypes, precompressedFormats); + ResourceContentFactory resourceContentFactory = new ResourceContentFactory(_baseResource, mimeTypes, precompressedFormats); CachingContentFactory cached = new CachingContentFactory(resourceContentFactory, _useFileMappedBuffer); int maxCacheSize = getInitInt("maxCacheSize", -2); @@ -145,9 +148,10 @@ public class DefaultServlet extends HttpServlet _resourceService.setDirAllowed(getInitBoolean("dirAllowed", _resourceService.isDirAllowed())); _resourceService.setRedirectWelcome(getInitBoolean("redirectWelcome", _resourceService.isRedirectWelcome())); _resourceService.setPrecompressedFormats(parsePrecompressedFormats(getInitParameter("precompressed"), getInitBoolean("gzip"), _resourceService.getPrecompressedFormats())); - _resourceService.setPathInfoOnly(getInitBoolean("pathInfoOnly", _resourceService.isPathInfoOnly())); _resourceService.setEtags(getInitBoolean("etags", _resourceService.isEtags())); + _isPathInfoOnly = getInitBoolean("pathInfoOnly", _isPathInfoOnly); + if ("exact".equals(getInitParameter("welcomeServlets"))) { _welcomeExactServlets = true; @@ -216,7 +220,7 @@ public class DefaultServlet extends HttpServlet // TODO: remove? _servletHandler = _contextHandler.getChildHandlerByClass(ServletHandler.class); if (LOG.isDebugEnabled()) - LOG.debug("base resource = {}", baseResource); + LOG.debug("base resource = {}", _baseResource); } @Override @@ -302,10 +306,19 @@ public class DefaultServlet extends HttpServlet servletContext.getClass().getName() + " is not " + ContextHandler.Context.class.getName()); } + protected boolean isPathInfoOnly() + { + return _isPathInfoOnly; + } + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - String pathInContext = _resourceService.isPathInfoOnly() ? req.getPathInfo() : URIUtil.addPaths(req.getServletPath(), req.getPathInfo()); + // TODO: need special handling here for included if request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) is not null? + // Handling things like RequestDispatcher.INCLUDE_PATH_INFO? + // or is this handled elsewhere now? + + String pathInContext = isPathInfoOnly() ? req.getPathInfo() : URIUtil.addPaths(req.getServletPath(), req.getPathInfo()); HttpContent content = _resourceService.getContent(pathInContext, resp.getBufferSize()); if (content == null) { @@ -776,20 +789,23 @@ public class DefaultServlet extends HttpServlet } @Override - public String getWelcomeFile(String pathInContext) throws IOException + public String getWelcomeTarget(Request coreRequest) throws IOException { String[] welcomes = _servletContextHandler.getWelcomeFiles(); if (welcomes == null) return null; - // TODO this feels inefficient - Resource base = _servletContextHandler.getResourceBase().resolve(pathInContext); + HttpServletRequest request = getServletRequest(coreRequest); + + String requestTarget = isPathInfoOnly() ? request.getPathInfo() : coreRequest.getPathInContext(); + + Resource base = _baseResource.resolve(requestTarget); String welcomeServlet = null; for (String welcome : welcomes) { Resource welcomePath = base.resolve(welcome); - String welcomeInContext = URIUtil.addPaths(pathInContext, welcome); + String welcomeInContext = URIUtil.addPaths(coreRequest.getPathInContext(), welcome); if (welcomePath != null && welcomePath.exists()) return welcomeInContext; @@ -806,81 +822,92 @@ public class DefaultServlet extends HttpServlet } @Override - protected boolean welcome(Request rq, Response rs, Callback callback) throws IOException + protected boolean welcome(Request coreRequest, Response coreResponse, Callback callback) throws IOException { - // TODO The contract of this method is very confused: it has a callback a return and throws? - - // TODO, this unwrapping is fragile - HttpServletRequest request = ((ServletCoreRequest)rq)._request; - HttpServletResponse response = ((ServletCoreResponse)rs)._response; - String pathInContext = rq.getPathInContext(); - WelcomeFactory welcomeFactory = getWelcomeFactory(); - String welcome = welcomeFactory == null ? null : welcomeFactory.getWelcomeFile(pathInContext); + HttpServletRequest request = getServletRequest(coreRequest); + HttpServletResponse response = getServletResponse(coreResponse); boolean included = request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null; - if (welcome == null) + WelcomeAction welcomeAction = super.processWelcome(coreRequest, coreResponse); + if (welcomeAction == null) return false; - String servletPath = included ? (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH) - : request.getServletPath(); - - if (isPathInfoOnly()) - welcome = URIUtil.addPaths(servletPath, welcome); + String welcome = welcomeAction.target(); if (LOG.isDebugEnabled()) LOG.debug("welcome={}", welcome); ServletContext context = request.getServletContext(); - - if (isRedirectWelcome() || context == null) + switch (welcomeAction.type()) { - // Redirect to the index - response.setContentLength(0); - // TODO need URI util that handles param and query without reconstructing entire URI with scheme and authority - HttpURI.Mutable uri = HttpURI.build(rq.getHttpURI()); - String parameter = uri.getParam(); - uri.path(URIUtil.addPaths(rq.getContext().getContextPath(), welcome)); - uri.param(parameter); - response.sendRedirect(response.encodeRedirectURL(uri.getPathQuery())); - callback.succeeded(); - return true; - } - - RequestDispatcher dispatcher = context.getRequestDispatcher(URIUtil.encodePath(welcome)); - if (dispatcher != null) - { - // Forward to the index - try + case REDIRECT -> { - if (included) + if (isRedirectWelcome() || context == null) { - dispatcher.include(request, response); + String servletPath = included ? (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH) + : request.getServletPath(); + + if (isPathInfoOnly()) + welcome = URIUtil.addPaths(servletPath, welcome); + + response.setContentLength(0); + response.sendRedirect(welcome); + callback.succeeded(); + return true; } - else - { - request.setAttribute("org.eclipse.jetty.server.welcome", welcome); - dispatcher.forward(request, response); - } - callback.succeeded(); return true; } - catch (ServletException e) + case SERVE -> { - callback.failed(e); + RequestDispatcher dispatcher = context.getRequestDispatcher(welcome); + if (dispatcher != null) + { + // Forward to the index + try + { + if (included) + { + dispatcher.include(request, response); + } + else + { + request.setAttribute("org.eclipse.jetty.server.welcome", welcomeAction.target()); + dispatcher.forward(request, response); + } + callback.succeeded(); + return true; + } + catch (ServletException e) + { + callback.failed(e); + return true; + } + } return true; } } - return false; } @Override protected boolean passConditionalHeaders(Request request, Response response, HttpContent content, Callback callback) throws IOException { - boolean included = ((ServletCoreRequest)request)._request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null; + boolean included = getServletRequest(request).getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null; if (included) return true; return super.passConditionalHeaders(request, response, content, callback); } + + private HttpServletRequest getServletRequest(Request request) + { + // TODO, this unwrapping is fragile + return ((ServletCoreRequest)request)._request; + } + + private HttpServletResponse getServletResponse(Response response) + { + // TODO, this unwrapping is fragile + return ((ServletCoreResponse)response)._response; + } } } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java index bec1423de6f..7d0ed465b9a 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DefaultServletTest.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.ee10.servlet; import java.io.File; import java.io.IOException; -import java.io.OutputStream; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; @@ -153,7 +152,7 @@ public class DefaultServletTest response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.NOT_FOUND_404)); - createFile(file, "How now brown cow"); + Files.writeString(file, "How now brown cow", UTF_8); rawResponse = connector.getResponse(""" GET /context/file.txt HTTP/1.1\r @@ -418,7 +417,6 @@ public class DefaultServletTest } @Test - @Disabled public void testListingProperUrlEncoding() throws Exception { ServletHolder defholder = context.addServlet(DefaultServlet.class, "/*"); @@ -710,7 +708,7 @@ public class DefaultServletTest /* create some content in the docroot */ Path index = docRoot.resolve("index.html"); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); if (!OS.WINDOWS.isCurrentOs()) { @@ -725,7 +723,7 @@ public class DefaultServletTest Path sekret = workDir.getPath().resolve("sekret"); FS.ensureDirExists(sekret); Path pass = sekret.resolve("pass"); - createFile(pass, "Sssh, you shouldn't be seeing this"); + Files.writeString(pass, "Sssh, you shouldn't be seeing this", UTF_8); /* At this point we have the following * testListingContextBreakout/ @@ -814,11 +812,11 @@ public class DefaultServletTest FS.ensureDirExists(two); FS.ensureDirExists(three); - createFile(one.resolve("index.htm"), "

Hello Inde

"); - createFile(two.resolve("index.html"), "

Hello Index

"); + Files.writeString(one.resolve("index.htm"), "

Hello Inde

", UTF_8); + Files.writeString(two.resolve("index.html"), "

Hello Index

", UTF_8); - createFile(three.resolve("index.html"), "

Three Index

"); - createFile(three.resolve("index.htm"), "

Three Inde

"); + Files.writeString(three.resolve("index.html"), "

Three Index

", UTF_8); + Files.writeString(three.resolve("index.htm"), "

Three Inde

", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -841,8 +839,7 @@ public class DefaultServletTest } @Test - @Disabled - public void testWelcomeMultipleBasesBase() throws Exception + public void testWelcomeMultipleDefaultServletsDifferentBases() throws Exception { Path dir = docRoot.resolve("dir"); FS.ensureDirExists(dir); @@ -892,9 +889,9 @@ public class DefaultServletTest """); response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.MOVED_TEMPORARILY_302)); - assertThat(response, containsHeaderValue("Location", "http://0.0.0.0/context/other/")); + assertThat(response, containsHeaderValue("Location", "http://local/context/other/")); - // Test alt default + // Test alt default, should see no directory listing output (dirAllowed == false per config) rawResponse = connector.getResponse(""" GET /context/alt/dir/ HTTP/1.1\r Host: local\r @@ -904,7 +901,9 @@ public class DefaultServletTest response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.FORBIDDEN_403)); - createFile(altIndex, "

Alt Index

"); + // Test alt welcome file, there's no index.html here yet, so let's create it and try + // accessing it directly + Files.writeString(altIndex, "

Alt Index

", UTF_8); rawResponse = connector.getResponse(""" GET /context/alt/dir/index.html HTTP/1.1\r Host: local\r @@ -915,6 +914,8 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Alt Index

")); + // Test alt welcome file, there now exists an index.html, lets try accessing + // it via the welcome file behaviors rawResponse = connector.getResponse(""" GET /context/alt/dir/ HTTP/1.1\r Host: local\r @@ -925,7 +926,10 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Alt Index

")); - createFile(altInde, "

Alt Inde

"); + // Let's create an index.htm (no 'l') and see if the welcome file logic holds, + // we should still see the original `index.html` as that's the first welcome + // file listed + Files.writeString(altInde, "

Alt Inde

", UTF_8); rawResponse = connector.getResponse(""" GET /context/alt/dir/ HTTP/1.1\r Host: local\r @@ -936,8 +940,12 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Alt Index

")); + // Let's try deleting the `index.html` and accesing the welcome file at `index.htm` + // We skip this section of the test if the OS or filesystem doesn't support instantaneous delete + // such as what happens on Microsoft Windows. if (deleteFile(altIndex)) { + // Access welcome file `index.htm` via the directory request. rawResponse = connector.getResponse(""" GET /context/alt/dir/ HTTP/1.1\r Host: local\r @@ -948,6 +956,8 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Alt Inde

")); + // Delete the welcome file `index.htm`, and access the directory. + // We should see no directory listing output (dirAllowed == false per config) if (deleteFile(altInde)) { rawResponse = connector.getResponse(""" @@ -971,7 +981,7 @@ public class DefaultServletTest response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.FORBIDDEN_403)); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); rawResponse = connector.getResponse(""" GET /context/dir/ HTTP/1.1\r Host: local\r @@ -982,7 +992,7 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Hello Index

")); - createFile(inde, "

Hello Inde

"); + Files.writeString(inde, "

Hello Inde

", UTF_8); rawResponse = connector.getResponse(""" GET /context/dir/ HTTP/1.1\r Host: local\r @@ -1060,7 +1070,7 @@ public class DefaultServletTest // should be used assertThat(response.toString(), response.getStatus(), is(HttpStatus.INTERNAL_SERVER_ERROR_500)); - createFile(altIndex, "

Alt Index

"); + Files.writeString(altIndex, "

Alt Index

", UTF_8); rawResponse = connector.getResponse(""" GET /context/gateway HTTP/1.1\r Host: local\r @@ -1105,7 +1115,7 @@ public class DefaultServletTest response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.FORBIDDEN_403)); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); rawResponse = connector.getResponse(""" GET /context/dir/ HTTP/1.1\r Host: local\r @@ -1116,7 +1126,7 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.MOVED_TEMPORARILY_302)); assertThat(response, headerValue("Location", "http://local/context/dir/index.html")); - createFile(inde, "

Hello Inde

"); + Files.writeString(inde, "

Hello Inde

", UTF_8); rawResponse = connector.getResponse(""" GET /context/dir HTTP/1.1\r Host: local\r @@ -1169,7 +1179,7 @@ public class DefaultServletTest Path dir = docRoot.resolve("dir"); FS.ensureDirExists(dir); Path index = dir.resolve("index.html"); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); context.addAliasCheck((p, r) -> true); connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setRelativeRedirectAllowed(true); @@ -1228,7 +1238,7 @@ public class DefaultServletTest Path dir = assumeMkDirSupported(docRoot, "dir?"); Path index = dir.resolve("index.html"); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -1280,7 +1290,7 @@ public class DefaultServletTest Path dir = assumeMkDirSupported(docRoot, "dir;"); Path index = dir.resolve("index.html"); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -1299,7 +1309,7 @@ public class DefaultServletTest """); response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.MOVED_TEMPORARILY_302)); - assertThat(response, containsHeaderValue("Location", "http://0.0.0.0/context/dir%3B/")); + assertThat(response, containsHeaderValue("Location", "http://local/context/dir%3B/")); rawResponse = connector.getResponse(""" GET /context/dir%3B/ HTTP/1.1\r @@ -1309,7 +1319,7 @@ public class DefaultServletTest """); response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.MOVED_TEMPORARILY_302)); - assertThat(response, containsHeaderValue("Location", "http://0.0.0.0/context/dir%3B/index.html")); + assertThat(response, containsHeaderValue("Location", "http://local/context/dir%3B/index.html")); } @Test @@ -1340,7 +1350,7 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.INTERNAL_SERVER_ERROR_500)); assertThat(response.getContent(), containsString("JSP support not configured")); - createFile(index, "

Hello Index

"); + Files.writeString(index, "

Hello Index

", UTF_8); rawResponse = connector.getResponse(""" GET /context/ HTTP/1.1\r Host: local\r @@ -1351,7 +1361,7 @@ public class DefaultServletTest assertThat(response.toString(), response.getStatus(), is(HttpStatus.OK_200)); assertThat(response.getContent(), containsString("

Hello Index

")); - createFile(inde, "

Hello Inde

"); + Files.writeString(inde, "

Hello Inde

", UTF_8); rawResponse = connector.getResponse(""" GET /context/ HTTP/1.1\r Host: local\r @@ -1401,7 +1411,7 @@ public class DefaultServletTest Path foobar = dir.resolve("foobar.txt"); Path link = dir.resolve("link.txt"); Path rLink = dir.resolve("rlink.txt"); - createFile(foobar, "Foo Bar"); + Files.writeString(foobar, "Foo Bar", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("gzip", "false"); @@ -1579,11 +1589,11 @@ public class DefaultServletTest FS.ensureDirExists(two); FS.ensureDirExists(three); - createFile(one.resolve("index.htm"), "

Hello Inde

"); - createFile(two.resolve("index.html"), "

Hello Index

"); + Files.writeString(one.resolve("index.htm"), "

Hello Inde

", UTF_8); + Files.writeString(two.resolve("index.html"), "

Hello Index

", UTF_8); - createFile(three.resolve("index.html"), "

Three Index

"); - createFile(three.resolve("index.htm"), "

Three Inde

"); + Files.writeString(three.resolve("index.html"), "

Three Index

", UTF_8); + Files.writeString(three.resolve("index.htm"), "

Three Inde

", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -1607,7 +1617,7 @@ public class DefaultServletTest { FS.ensureDirExists(docRoot); Path index = docRoot.resolve("index.html"); - createFile(index, "

Hello World

"); + Files.writeString(index, "

Hello World

", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "true"); @@ -1883,11 +1893,11 @@ public class DefaultServletTest { FS.ensureDirExists(docRoot); Path data = docRoot.resolve("data.txt"); - createFile(data, "01234567890123456789012345678901234567890123456789012345678901234567890123456789"); + Files.writeString(data, "01234567890123456789012345678901234567890123456789012345678901234567890123456789", UTF_8); // test a range request with a file with no suffix, therefore no mimetype Path nofilesuffix = docRoot.resolve("nofilesuffix"); - createFile(nofilesuffix, "01234567890123456789012345678901234567890123456789012345678901234567890123456789"); + Files.writeString(nofilesuffix, "01234567890123456789012345678901234567890123456789012345678901234567890123456789", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -1909,9 +1919,9 @@ public class DefaultServletTest { FS.ensureDirExists(docRoot); Path file0 = docRoot.resolve("data0.txt"); - createFile(file0, "Hello Text 0"); + Files.writeString(file0, "Hello Text 0", UTF_8); Path image = docRoot.resolve("image.jpg"); - createFile(image, "not an image"); + Files.writeString(image, "not an image", UTF_8); server.stop(); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); @@ -1994,9 +2004,9 @@ public class DefaultServletTest { FS.ensureDirExists(docRoot); Path file0 = docRoot.resolve("data0.txt"); - createFile(file0, "Hello Text 0"); + Files.writeString(file0, "Hello Text 0", UTF_8); Path file0gz = docRoot.resolve("data0.txt.gz"); - createFile(file0gz, "fake gzip"); + Files.writeString(file0gz, "fake gzip", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -2147,9 +2157,9 @@ public class DefaultServletTest { FS.ensureDirExists(docRoot); Path file0 = docRoot.resolve("data0.txt"); - createFile(file0, "Hello Text 0"); + Files.writeString(file0, "Hello Text 0", UTF_8); Path file0gz = docRoot.resolve("data0.txt.gz"); - createFile(file0gz, "fake gzip"); + Files.writeString(file0gz, "fake gzip", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -2273,8 +2283,8 @@ public class DefaultServletTest @Disabled public void testBrotli() throws Exception { - createFile(docRoot.resolve("data0.txt"), "Hello Text 0"); - createFile(docRoot.resolve("data0.txt.br"), "fake brotli"); + Files.writeString(docRoot.resolve("data0.txt"), "Hello Text 0", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.br"), "fake brotli", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -2413,8 +2423,8 @@ public class DefaultServletTest @Disabled public void testCachedBrotli() throws Exception { - createFile(docRoot.resolve("data0.txt"), "Hello Text 0"); - createFile(docRoot.resolve("data0.txt.br"), "fake brotli"); + Files.writeString(docRoot.resolve("data0.txt"), "Hello Text 0", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.br"), "fake brotli", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("dirAllowed", "false"); @@ -2538,9 +2548,9 @@ public class DefaultServletTest @Disabled public void testDefaultBrotliOverGzip() throws Exception { - createFile(docRoot.resolve("data0.txt"), "Hello Text 0"); - createFile(docRoot.resolve("data0.txt.br"), "fake brotli"); - createFile(docRoot.resolve("data0.txt.gz"), "fake gzip"); + Files.writeString(docRoot.resolve("data0.txt"), "Hello Text 0", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.br"), "fake brotli", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.gz"), "fake gzip", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("precompressed", "true"); @@ -2587,10 +2597,10 @@ public class DefaultServletTest @Disabled public void testCustomCompressionFormats() throws Exception { - createFile(docRoot.resolve("data0.txt"), "Hello Text 0"); - createFile(docRoot.resolve("data0.txt.br"), "fake brotli"); - createFile(docRoot.resolve("data0.txt.gz"), "fake gzip"); - createFile(docRoot.resolve("data0.txt.bz2"), "fake bzip2"); + Files.writeString(docRoot.resolve("data0.txt"), "Hello Text 0", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.br"), "fake brotli", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.gz"), "fake gzip", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.bz2"), "fake bzip2", UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); defholder.setInitParameter("precompressed", "bzip2=.bz2,gzip=.gz,br=.br"); @@ -2638,10 +2648,10 @@ public class DefaultServletTest @Disabled public void testProgrammaticCustomCompressionFormats() throws Exception { - createFile(docRoot.resolve("data0.txt"), "Hello Text 0"); - createFile(docRoot.resolve("data0.txt.br"), "fake brotli"); - createFile(docRoot.resolve("data0.txt.gz"), "fake gzip"); - createFile(docRoot.resolve("data0.txt.bz2"), "fake bzip2"); + Files.writeString(docRoot.resolve("data0.txt"), "Hello Text 0", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.br"), "fake brotli", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.gz"), "fake gzip", UTF_8); + Files.writeString(docRoot.resolve("data0.txt.bz2"), "fake bzip2", UTF_8); ResourceService resourceService = new ResourceService(); resourceService.setPrecompressedFormats(new CompressedContentFormat[]{ @@ -2739,7 +2749,7 @@ public class DefaultServletTest response = HttpTester.parseResponse(rawResponse); assertThat(response.toString(), response.getStatus(), is(HttpStatus.NOT_FOUND_404)); - createFile(file, content); + Files.writeString(file, content, UTF_8); rawResponse = connector.getResponse(""" GET /context/file.txt HTTP/1.1\r @@ -2812,7 +2822,7 @@ public class DefaultServletTest @Disabled public void testIfETag(String content) throws Exception { - createFile(docRoot.resolve("file.txt"), content); + Files.writeString(docRoot.resolve("file.txt"), content, UTF_8); ServletHolder defholder = context.addServlet(DefaultServlet.class, "/"); @@ -2927,7 +2937,7 @@ public class DefaultServletTest // Create file with UTF-8 NFC format String filename = "swedish-" + new String(StringUtil.fromHexString("C3A5"), UTF_8) + ".txt"; - createFile(docRoot.resolve(filename), "hi a-with-circle"); + Files.writeString(docRoot.resolve(filename), "hi a-with-circle", UTF_8); // Using filesystem, attempt to access via NFD format Path nfdPath = docRoot.resolve("swedish-a" + new String(StringUtil.fromHexString("CC8A"), UTF_8) + ".txt"); @@ -2977,7 +2987,7 @@ public class DefaultServletTest // Create file with UTF-8 NFD format String filename = "swedish-a" + new String(StringUtil.fromHexString("CC8A"), UTF_8) + ".txt"; - createFile(docRoot.resolve(filename), "hi a-with-circle"); + Files.writeString(docRoot.resolve(filename), "hi a-with-circle", UTF_8); // Using filesystem, attempt to access via NFC format Path nfcPath = docRoot.resolve("swedish-" + new String(StringUtil.fromHexString("C3A5"), UTF_8) + ".txt"); @@ -3057,15 +3067,6 @@ public class DefaultServletTest } } - private void createFile(Path path, String str) throws IOException - { - try (OutputStream out = Files.newOutputStream(path)) - { - out.write(str.getBytes(UTF_8)); - out.flush(); - } - } - private boolean deleteFile(Path file) throws IOException { if (!Files.exists(file)) diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java index 3ed4bce2139..e99d6c1201d 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/DispatcherTest.java @@ -97,7 +97,6 @@ public class DispatcherTest _resourceHandler = new ResourceHandler(); Path basePath = MavenTestingUtils.getTestResourcePathDir("dispatchResourceTest"); _resourceHandler.setBaseResource(Resource.newResource(basePath)); - _resourceHandler.setPathInfoOnly(true); ContextHandler resourceContextHandler = new ContextHandler("/resource"); resourceContextHandler.setHandler(_resourceHandler); _contextCollection.addHandler(resourceContextHandler);