Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<mvc:resources> does not handles directories well [SPR-12999] #17591

Closed
spring-issuemaster opened this issue May 8, 2015 · 7 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented May 8, 2015

Michael Osipov opened SPR-12999 and commented

Say I have this mapping:

<mvc:resources mapping="/docs/**" location="classpath:/docs/" />

Request docs/ or any other subdirectory ends up in a HTTP 500:

19:27:23.669 [http-8081-2] DEBUG o.s.web.servlet.DispatcherServlet - Could not complete request
java.io.FileNotFoundException: class path resource [docs/rest] cannot be opened because it does not exist
	at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172) ~[spring-core-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.writeContent(ResourceHttpRequestHandler.java:407) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpRequestHandler.java:241) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) ~[spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) [servlet-api.jar:na]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.6.RELEASE.jar:4.1.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:723) [servlet-api.jar:na]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) [catalina.jar:6.0.41]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) [catalina.jar:6.0.41]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) [catalina.jar:6.0.41]
	at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:191) [catalina.jar:6.0.41]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java) [catalina.jar:6.0.41]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:470) [catalina.jar:6.0.41]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [catalina.jar:6.0.41]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) [catalina.jar:6.0.41]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [catalina.jar:6.0.41]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) [catalina.jar:6.0.41]
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861) [tomcat-coyote.jar:6.0.41]
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606) [tomcat-coyote.jar:6.0.41]
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) [tomcat-coyote.jar:6.0.41]
	at java.lang.Thread.run(Thread.java:745) [na:1.7.0_67]

I would rather expect here 200 or better yet 204. The directory does exist but WebMVC does not know how to handle. I have debugged the implementation and Resource is retrieved but getInputStream fails. Moreover, content length is 0.

I don't expect an extensive handling like in Apaches mod_dir` but at least not a 500.

Meanwhile, I have applied this workaround:

<urlrewrite>

	<rule>
		<name>mod_dir fake</name>
        <from>^/docs(/rest)?$</from>
        <to type="permanent-redirect" last="true">%{context-path}/docs$1/</to>
    </rule>

    <rule>
        <name>mod_dir fake</name>
        <from>^/docs(.+)/$</from>
        <to last="true">/docs$1/index.html</to>
    </rule>

</urlrewrite>

Affects: 4.1.6

Referenced from: commits 9e8e7aa, c99cc53, 4d5fca5

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 13, 2015

Brian Clozel commented

Hello Michael Osipov

I've been trying to reproduce this error and couldn't.

Right now, the Resource handling chain checks that the resolved resource exists (true for a folder) and that it's readable (false for a folder). In that case, it's returning HTTP 404.

While HTTP 404 may still not be ideal, it's still more consistent than a server error.

I've created this repro project; could you take a look at it?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 13, 2015

Michael Osipov commented

Hi Brian, thanks a lot for the quick test project. I won't get back to work before Monday so I need to ask you to be patient. What I can say now is that I have a multimodule project where documentation is a Maven module and webapp is a separate one.I did even had the docs/ from WEB-INF/. All is running on Tomcat 6.0.41. Anyway, I will clone your test project modify it to have a clean room example.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 19, 2015

Michael Osipov commented

Brian,

here is my change to the issue project: https://github.com/michael-o/spring-framework-issues/commit/9a701f5356839469ebc8e426dac641b45e119266

Cargo does not work for me because I am behind a proxy, though Jetty does its job.

Invoke mvn clean package jetty:run and open up http://localhost:8080/maven/log4j

You'll see in Jetty:

HTTP ERROR 500

Problem accessing /maven/log4j. Reason:

    Server Error

Caused by:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:717)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1644)
	at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:171)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1615)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:550)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:568)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:221)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1112)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:479)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1046)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:199)
	at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
	at org.eclipse.jetty.server.Server.handle(Server.java:459)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:281)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:232)
	at org.eclipse.jetty.io.AbstractConnection$1.run(AbstractConnection.java:505)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:607)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:536)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
	at java.io.FilterInputStream.close(FilterInputStream.java:181)
	at sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream.close(JarURLConnection.java:108)
	at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.writeContent(ResourceHttpRequestHandler.java:413)
	at org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpRequestHandler.java:241)
	at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
	... 27 more

and in Tomcat:

Type Exception report

Message class path resource [META-INF/maven/log4j/log4j] cannot be opened because it does not exist

Description The server encountered an unexpected condition which prevented it from fulfilling the request.

Exception

java.io.FileNotFoundException: class path resource [META-INF/maven/log4j/log4j] cannot be opened because it does not exist
	org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)
	org.springframework.web.servlet.resource.ResourceHttpRequestHandler.writeContent(ResourceHttpRequestHandler.java:407)
	org.springframework.web.servlet.resource.ResourceHttpRequestHandler.handleRequest(ResourceHttpRequestHandler.java:241)
	org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:723)

The difference is mentioned in the commit message.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 20, 2015

Brian Clozel commented

Hi Michael Osipov

I managed to reproduce this issue, but only using jetty; tomcat + cargo works fine.
Your initial report shows a tomcat stacktrace - could you tell me which tomcat version you were using?

I'm trying to narrow down the cause of this issue.

Thanks

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 20, 2015

Michael Osipov commented

Testing with Eclipse was done with Apache Tomcat 6.0.41.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 21, 2015

Brian Clozel commented

Thanks Michael Osipov for this report.

This is now fixed with more defensive checks in the ResourceHttpRequestHandler.
This all comes from the way servlet containers deal with directories within JAR files - a behavior that we can track down to webapp ClassLoader implementations.

Please note that:

  • the same issue can be reproduced in Tomcat6x, but not Tomcat7x+
  • Jetty9x throws a different exception (a NullPointerException) for the same use case
  • for folders located on the filesystem, the behavior is a bit different as HTTP 404 responses are sent

The commit message has more details about this.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 21, 2015

Michael Osipov commented

Hi Brian, thanks for that fix, looking forward to. This isn't perfect, probably never will but better than having unpredictable expections flying around.

Do you think it is worth filing issues in Tomcat 6 and Jetty 9?. We use Tomcat 6 exclusively, but still.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.