jetty.project
jetty.project copied to clipboard
500 response when trying to display symlinked directory
Jetty version(s) 12
Enhancement Description
Scenario:
The contexts base resource is /foo, there is a symlink at /foo/bar which points to /other which is a directory. Inside /other you have another directory called files.
If you try to access the path /foo/bar/other, and the server will generate a 500 response.
jakarta.servlet.ServletException: java.io.IOException: Is a directory
at org.eclipse.jetty.ee10.servlet.DefaultServlet.doGet(DefaultServlet.java:539)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:527)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
at org.eclipse.jetty.ee10.servlet.ServletHolder.handle(ServletHolder.java:736)
at org.eclipse.jetty.ee10.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1614)
at org.eclipse.jetty.ee10.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1547)
at org.eclipse.jetty.ee10.servlet.ServletChannel.dispatch(ServletChannel.java:799)
at org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:428)
at org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:464)
at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:761)
at org.eclipse.jetty.server.handler.HotSwapHandler.handle(HotSwapHandler.java:92)
at org.eclipse.jetty.server.Server.handle(Server.java:179)
at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:594)
at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:424)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:971)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1201)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1156)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.io.IOException: Is a directory
at java.base/sun.nio.ch.UnixFileDispatcherImpl.read0(Native Method)
at java.base/sun.nio.ch.UnixFileDispatcherImpl.read(UnixFileDispatcherImpl.java:51)
at java.base/sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:340)
at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:294)
at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:283)
at java.base/sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:234)
at org.eclipse.jetty.server.ResourceService$ContentWriterIteratingCallback.process(ResourceService.java:947)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:250)
at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:231)
at org.eclipse.jetty.server.ResourceService.writeHttpContent(ResourceService.java:722)
at org.eclipse.jetty.server.ResourceService.sendData(ResourceService.java:666)
at org.eclipse.jetty.server.ResourceService.doGet(ResourceService.java:205)
at org.eclipse.jetty.ee10.servlet.DefaultServlet.doGet(DefaultServlet.java:534)
... 20 more
This is because ResourceService does content.getResource().isDirectory() to determine whether to do the directory listing. This is implemented by PathResource as Files.isDirectory(getPath(), LinkOption.NOFOLLOW_LINKS) and /foo/bar is a symlink file not a directory when you don't follow links.
Interestingly this does not affect the path /foo/bar/files, for some reason Files.isDirectory("/foo/bar/files", LinkOption.NOFOLLOW_LINKS) would return true, even though /foo/bar is a symlink.
@joakime thoughts?
I wonder if the Resource.isDirectory() protection on LinkOption.NOFOLLOW_LINKS is sane here.
I don't think it is.
I cannot think of a sane reason we use NOFOLLOW_LINKS for this Resource.isDirectory() test.
We have other mechanisms for determining if the Resource is an alias.