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()).

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

Reply via email to