2014-06-05 22:04 GMT+04:00 Jimmy Royer <jimlero...@gmail.com>:
> Hello,
>
> I am using this combo of components:
>
> * Apache Tomcat 8.0.8
> * Apache CXF 2.7.11
> * Servlet 3.0
> * JAX-RS 2.0
> * JDK 1.7.0_45
> * Windows 7
> * Chrome browser with the Advanced REST Client plug-in
>
> I developed some web services using REST that leverages CXF ability to
> do asynchronous methods, and under the hood, that uses Apache Tomcat.
>
> This is working fine overall, the setup and configuration are all
> good. There is one exception though. This is when I make a request to
> an async web service that uses a space in the URL, encoded to a %20.
>
> The encoding itself works fine, but internally, when Tomcat resumes
> the Servlet 3 continuation, it passes to some class the previously
> decoded path and sets it on the request URL. The request is then
> passed to the CXF layer, that expects a valid URL with no space and
> tries to instantiate a URL object from it, and fails. Here is the
> stack trace I got:
>
>
>
> """
> 05-Jun-2014 12:33:37.426 SEVERE [http-apr-8080-exec-10]
> org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service()
> for servlet [CXFServlet] in context with path [] threw exception
> [java.lang.RuntimeException: java.lang.IllegalArgumentException:
> Illegal character in path at index 134:
> http://127.0.0.1:8080/qlikview11/bi-service/c4aeb78f-109a-49a3-9716-10d83272a845/folders/13e5f0b4-90e2-4d11-bc5f-4f688e53bed2/Software
> Division/documents] with root cause
>  java.net.URISyntaxException: Illegal character in path at index 134:
> http://127.0.0.1:8080/qlikview11/bi-service/c4aeb78f-109a-49a3-9716-10d83272a845/folders/13e5f0b4-90e2-4d11-bc5f-4f688e53bed2/Software
> Division/documents
> at java.net.URI$Parser.fail(URI.java:2829)
> at java.net.URI$Parser.checkChars(URI.java:3002)
> at java.net.URI$Parser.parseHierarchical(URI.java:3086)
> at java.net.URI$Parser.parse(URI.java:3034)
> at java.net.URI.<init>(URI.java:595)
> at java.net.URI.create(URI.java:857)
> at 
> org.apache.cxf.transport.servlet.BaseUrlHelper.getBaseURL(BaseUrlHelper.java:49)
> at 
> org.apache.cxf.transport.servlet.ServletController.getBaseURL(ServletController.java:78)
> at 
> org.apache.cxf.transport.servlet.ServletController.updateDestination(ServletController.java:87)
> at 
> org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:200)
> at 
> org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
> at 
> org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:171)
> at 
> org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
> at 
> org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:211)
> at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
> at 
> org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
> at 
> org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
> at 
> org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
> at 
> org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:721)
> at 
> org.apache.catalina.core.ApplicationDispatcher.doDispatch(ApplicationDispatcher.java:639)
> at 
> org.apache.catalina.core.ApplicationDispatcher.dispatch(ApplicationDispatcher.java:605)
> at org.apache.catalina.core.AsyncContextImpl$1.run(AsyncContextImpl.java:208)
> at 
> org.apache.catalina.core.AsyncContextImpl.doInternalDispatch(AsyncContextImpl.java:363)
> at 
> org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
> at 
> org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
> at 
> org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
> at 
> org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
> at 
> org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:78)
> at 
> org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
> at 
> org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
> at 
> org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:405)
> at 
> org.apache.coyote.http11.AbstractHttp11Processor.asyncDispatch(AbstractHttp11Processor.java:1636)
> at 
> org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:646)
> at 
> org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:277)
> at 
> org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2451)
> at 
> org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2440)
> at 
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
> at 
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
> at 
> org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
> at java.lang.Thread.run(Thread.java:744)
> """
>
>
>
> The URL is perfectly encoded when it gets in the Tomcat machinery, but
> it gets decoded along the way, and the information is not re-encoded
> along the way. It's hard for me to say what should be the proper logic
> as I am not familiar with the Tomcat code base, but here is the
> workflow of the key classes, methods and URI values that got to this
> situation:
>
>>  AsyncContextImpl#dispatch: The request path info used to be dispatched. 
>> This path was previously decoded during the previous operations.
>
> -->  ApplicationContext#getRequestDispatcher: The decoded path is
> eventually sent to this method. The path is normalized and appended to
> a variable uriCC meant to represent an URI. The value of this variable
> is never re-encoded nor validated to a valid URI. A new
> ApplicationDispatcher is returned that contains a non-encoded URI.
>
>   --> ApplicationDispatcher#doDispatch: The previously created
> application dispatcher now has to dispatch the request. It overwrites
> the request URL from the incoming request (which is properly encoded)
> with the previously computed path that is non-encoded.
>
>     --> BaseUrlHelper#getBaseURL: This CXF method eventually gets
> called with a request that contains a non-valid URI. The code that
> triggers the exception is equivalent to:
> URI.create(request.getRequestURL().toString()).
>

The CXF source code:
http://cxf.apache.org/source-repository.html
https://git-wip-us.apache.org/repos/asf?p=cxf.git

The code of BaseUrlHelper is in "2.7.x-fixes" branch.
https://git-wip-us.apache.org/repos/asf?p=cxf.git;a=tree;h=refs/heads/2.7.x-fixes;hb=2.7.x-fixes

https://git-wip-us.apache.org/repos/asf?p=cxf.git;a=blob;f=rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/BaseUrlHelper.java;h=db80a075c464da83637e2399c19dbdb42fc24aeb;hb=2.7.x-fixes

[[[
 33     /**
34      * Returns base URL which includes scheme, host, port, Servlet
context and servlet paths
35      * @param request current HttpServletRequest
36      * @return base URL
37      */
38     public static String getBaseURL(HttpServletRequest request) {
39         String reqPrefix = request.getRequestURL().toString();
40         String pathInfo = request.getPathInfo() == null ? "" :
request.getPathInfo();
41         //fix for CXF-898
42         if (!"/".equals(pathInfo) || reqPrefix.endsWith("/")) {
43             StringBuilder sb = new StringBuilder();
44             // request.getScheme(), request.getLocalName() and
request.getLocalPort()
45             // should be marginally cheaper - provided
request.getLocalName() does
46             // return the actual name used in request URI as
opposed to localhost
47             // consistently across the Servlet stacks
48
49             URI uri = URI.create(reqPrefix);
50             
sb.append(uri.getScheme()).append("://").append(uri.getRawAuthority());
51             
sb.append(request.getContextPath()).append(request.getServletPath());
52
53             reqPrefix = sb.toString();
54         }
55         return reqPrefix;
56     }
]]]

Servlet API javadocs:
http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html

It does not mention explicitly whether getRequestURL() returns a
non-decoded or decoded URL,

but as our implementation in o.a.c.connector.Request does
"url.append(getRequestURI());" I would say that it is expected to
return a non-decoded String.


On the other hand I think ServletContext.getRequestDispatcher()
expects a decoded URI, as it expects a "resource path", not a
%-encoded URI.

I think it is OK to file an issue into Bugzilla.

Best regards,
Konstantin Kolinko

> I came across a somewhat similar bug in the CXF Jira (where the cause
> was different). The CXF folks really expect the URL to be properly
> encoded. In my case, it seems that this might not be properly handed
> by Tomcat to CXF.
>
> As I said, I'm not familiar with the code base of both Tomcat and CXF,
> so please tell me if that could be something wrong with my setup, or
> if that is a bug.
>
> To reproduce this behavior, I guess that these steps would do it:
>
> 1- Develop an async web method for Tomcat (I'm using Apache CXF JAX-RS
> async support for that).
> 2- Send a request to this web method that contains an encoded %20
> space, make sure that async support is in and servlet 3 continuations
> are used.
>
> That would be the bare minimum I guess, and not the exact setup I have!
>
> Best regards,
> Jimmy Royer
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to