Bug 56890 - getRealPath returns null
Summary: getRealPath returns null
Status: NEEDINFO
Alias: None
Product: Tomcat 8
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 8.0.11
Hardware: All All
: P2 major (vote)
Target Milestone: ----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-08-26 23:29 UTC by f.bantner
Modified: 2017-05-28 16:18 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description f.bantner 2014-08-26 23:29:41 UTC
There is more than one report for this kind of error.

since #55837 and #55345 are marked as fixed I have to add another one for 8.0.11.

Calling

		servletContext.getRealPath( "" )     -> some path
		servletContext.getRealPath( "." )    -> null
		servletContext.getRealPath( "./" )   -> null
		servletContext.getRealPath( "/" )    -> same path s.a.
		servletContext.getRealPath( "test" ) -> null

At least the third one worked under tomcat 7.0.54 as I was using it in order to determine where tomcat had it's webapp directory (this isn't so clear when e.g. running from eclipse)

I think most (if not all) of the null results are bugs and the behaviour was perfectly fine under tomcat <8.

(Note that this code has worked in Tomcat 5, 6, 7)

I would expect that this method to just do translation of the path.

As I read the other bug reports it seems that tomcat tries to check if this file exists. But I think this is wrong because the concept of Java File doesn't imply that a File must exist. (It weekly implies that it could exist). So instead File has a method 'createNewFile' which clearly wouldn't be there if the file already existed. Furthermore if I wanted to check if a paricular file existed I would use file.exists(), ...canRead() and so on.

Finally the specs state that this method could return under any circumstances. If taken literally this would lead to the question what good it is for anyway if it could just return null no matter what. As I read e.g. https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/ServletRequest.html the 'null' result is there to indicate that the path to this (potential) file is not leading into the filesystem and therefor makes no sense at all.

Best regards and keep up the great work

Scheintod
Comment 1 Konstantin Kolinko 2014-08-27 13:23:40 UTC
Resource paths are expected to start with a "/" (per javadoc of ServletContext.getResource()). [1]

The behaviour in Tomcat 8 is caused by a more strict underlying Resources implementation.

That said,
- I would expect "strict compliance" option (or more specifically GET_RESOURCE_REQUIRE_SLASH option) to control this behaviour both in this and in older versions. Apparently it is not so. [2]

- I wonder whether passing "" instead of "/" to getRealPath("") is valid.


[1] http://docs.oracle.com/javaee/7/api/javax/servlet/ServletContext.html#getResource%28java.lang.String%29

[2] http://tomcat.apache.org/tomcat-7.0-doc/config/systemprops.html#Specification
Comment 2 Mark Thomas 2014-08-27 13:48:47 UTC
I'm leaning towards resolving this as invalid.

While the spec and the Javadoc could be clearer, it seems pretty obvious that a 'virtual path' is 'servletPath + pathInfo' along the lines of section 3.5 of the Servlet spec.

Therefore, requiring that the virtual path is either empty or starts with '/' seems reasonable.

It is not unusual for one major verison of Tomcat to interpret a specification more strictly than the previous major version. Such changes are to be expected across major versions and certainly do not qualify as bugs. If these changes are causing problems, then they can be added to the migration guide.

An argument could be made that the virtual path, if it doesn't start with '/', should be taken as being relative to the ServletContext root - i.e. append '/' and then process it. I'm not convinced that that argument is valid since there is nothing I see in the specs or the Javadoc to support it.
Comment 3 f.bantner 2014-08-28 21:29:37 UTC
I have no strong opinion on this. Just a few thoughts:

1. getResource() is fundamental different to getRealPath(). Above all getResource( "/in_war/something" ) would return data whereas getRealPath( "/in_war/something" ) would return null.

2. There is nothing in the spec which tell that getRealPath() must start with a slash. At least not as far as I can see. (IMHO the spec for the whole servlet api is fubar anyway...) So the new behavior is not more strict but just different.

3. The leading slash in the path of urls is kind of inexistent anyway. (In complete urls there is no "leading slash". Just a separator between the host and the path)

4. The main advantage of not answering to requests for path's without a leading slash would be imho that the developer (say me) who is already confused about the **** servlet api is remindet that this is an absolute path. On the other hand throwing an exception would be the right thing to take in this case.

5. The spec says: "This method returns null if the servlet container cannot translate the virtual path to a real path [...]". The key here is "cannot". How it stands right now it is a "doesn't want to". (The server could translate it if it wanted to)

6. To be consistent getRealPath( "" ) should return null, too.

7. Why fix what's not broken?

8. Finally: For my part I replaced my "./" string (after some heavy searching for the problem) with "/" and everything worked again. (I won't change this back anyway). But this was the first time I had to fix something for Tomcat upgrades since Tomcat 5. And I liked that very much ;)

Best Regards 

Scheintod
Comment 4 Remy Maucherat 2014-08-28 21:41:14 UTC
+1 for invalid.
Comment 5 Mark Thomas 2014-09-01 08:43:28 UTC
(In reply to f.bantner from comment #3)
> I have no strong opinion on this. Just a few thoughts:
> 
> 1. getResource() is fundamental different to getRealPath().

Yes and no. The purpose is very different but they both take a path relative to the base of the servlet context.

> 2. There is nothing in the spec which tell that getRealPath() must start
> with a slash.

I think it is strongly implied but I agree it could be clearer.

> 3. The leading slash in the path of urls is kind of inexistent anyway.

Regardless, the servlet spec uses leading '/'

> 4. The main advantage of not answering to requests for path's without a
> leading slash would be imho that the developer (say me) who is already
> confused about the **** servlet api is remindet that this is an absolute
> path. On the other hand throwing an exception would be the right thing to
> take in this case.

The Javadoc for getRealPath() strongly suggests returning null rather than throwing an exception.

> 5. The spec says: "This method returns null if the servlet container cannot
> translate the virtual path to a real path [...]". The key here is "cannot".
> How it stands right now it is a "doesn't want to". (The server could
> translate it if it wanted to)

At this point, I really do wish the spec was clearer. I'll raise a bug and see if we can get this addressed for Servlet 3.2. When a later spec clarifies something, Tomcat often back-ports the clarification to earlier versions.

I've re-read the Javadoc for getRealPath() several times. It could be read as "Take the value of path, append it to 'http://<host>:<port>/<contextPath>' and return the absolute path on the file system that that request would be mapped to or null if that is not possible."

The 'interesting' part here is that path gets appended to something that does not end in '/'. Taking your 5 examples from earlier, that gives us:
getRealPath("")     -> http://<host>:<port>/<contextPath>
getRealPath(".")    -> http://<host>:<port>/<contextPath>.
getRealPath("./")   -> http://<host>:<port>/<contextPath>./
getRealPath("/")    -> http://<host>:<port>/<contextPath>/
getRealPath("test") -> http://<host>:<port>/<contextPath>test

The first and fourth examples should be OK (assuming the web app is expanded). The others will fail.

This is actually (more by luck than judegment) how Tomcat is behaving now. I'm not entirely comfortable with this. I'd be happier with either a requirement that the path must start with / (else an IAE is thrown) or a requirement the '/' is prepended if not present.

> 6. To be consistent getRealPath( "" ) should return null, too.

It depends what you are being consistent with. See my response to point 5 that demonstrates that the current behaviour is consistent with at least one point of view.

> 7. Why fix what's not broken?

The resource handling was almost impossible to maintain. It was horribly fragile and implementing what - then - was expected to be in Servlet 3.1 in terms of overlays would have been a nightmare. The new resource handling is much more robust and provides much more consistent behaviour. What we have here is yet another grey area in the Servlet spec that needs some clarification.

> 8. Finally: For my part I replaced my "./" string (after some heavy
> searching for the problem) with "/" and everything worked again. (I won't
> change this back anyway). But this was the first time I had to fix something
> for Tomcat upgrades since Tomcat 5. And I liked that very much ;)

I'm fairly sure that however the Servlet EG clarifies this, that starting with "./" will end up being invalid so I think you were going to have to change this sooner or later. It is nice to know that for well written applicaitons that follow the spec the upgrade process is as smooth as it is meant to be.

I'm going to leave this in the NEEDINFO state until we get some clarification from the Servlet EG.
Comment 6 Mark Thomas 2014-09-01 08:52:59 UTC
https://java.net/jira/browse/SERVLET_SPEC-105
Comment 7 Mark Thomas 2017-05-28 16:18:18 UTC
Updated location for the Servlet-spec issue.

https://github.com/javaee/servlet-spec/issues/105