diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index e6e1616b24c..84b89e3f52c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -2231,7 +2231,7 @@ public class Request implements HttpServletRequest _async=new AsyncContextState(state); AsyncContextEvent event = new AsyncContextEvent(_context,_async,state,this,servletRequest,servletResponse); event.setDispatchContext(getServletContext()); - event.setDispatchPath(URIUtil.addPaths(getServletPath(),getPathInfo())); + event.setDispatchPath(URIUtil.encodePath(URIUtil.addPaths(getServletPath(),getPathInfo()))); state.startAsync(event); return _async; } diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java new file mode 100644 index 00000000000..77ed22b0f52 --- /dev/null +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/EncodedURITest.java @@ -0,0 +1,206 @@ +// +// ======================================================================== +// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.servlet; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.GenericServlet; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestWrapper; +import javax.servlet.ServletResponse; +import javax.servlet.ServletResponseWrapper; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + +import org.eclipse.jetty.server.Dispatcher; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.UrlEncoded; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class EncodedURITest +{ + private Server _server; + private LocalConnector _connector; + private ContextHandlerCollection _contextCollection; + private ServletContextHandler _context0; + private ServletContextHandler _context1; + private ResourceHandler _resourceHandler; + + @Before + public void init() throws Exception + { + _server = new Server(); + _connector = new LocalConnector(_server); + _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false); + _connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false); + _server.addConnector( _connector ); + + _contextCollection = new ContextHandlerCollection(); + _server.setHandler(_contextCollection); + + _context0 = new ServletContextHandler(); + _context0.setContextPath("/context path"); + _contextCollection.addHandler(_context0); + _context0.addFilter(AsyncFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST)); + _context0.addServlet(TestServlet.class,"/test servlet/*"); + _context0.addServlet(AsyncServlet.class,"/async servlet/*"); + + _context1 = new ServletContextHandler(); + _context1.setContextPath("/redirecting context"); + _contextCollection.addHandler(_context1); + + _server.start(); + } + + @After + public void destroy() throws Exception + { + _server.stop(); + _server.join(); + } + + @Test + public void testTestServlet() throws Exception + { + String response = _connector.getResponse("GET /context%20path/test%20servlet/path%20info HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 ")); + assertThat(response,Matchers.containsString("requestURI=/context%20path/test%20servlet/path%20info")); + assertThat(response,Matchers.containsString("servletPath=/test servlet")); + assertThat(response,Matchers.containsString("contextPath=/context path")); + assertThat(response,Matchers.containsString("pathInfo=/path info")); + } + + @Test + public void testAsyncFilterTestServlet() throws Exception + { + String response = _connector.getResponse("GET /context%20path/test%20servlet/path%20info?async=true HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 ")); + assertThat(response,Matchers.containsString("requestURI=/context%20path/test%20servlet/path%20info")); + assertThat(response,Matchers.containsString("servletPath=/test servlet")); + assertThat(response,Matchers.containsString("contextPath=/context path")); + assertThat(response,Matchers.containsString("pathInfo=/path info")); + } + + @Test + public void testAsyncFilterWrapTestServlet() throws Exception + { + String response = _connector.getResponse("GET /context%20path/test%20servlet/path%20info?async=true&wrap=true HTTP/1.0\n\n"); + assertThat(response,startsWith("HTTP/1.1 200 ")); + // TODO this contextPath should be encoded + assertThat(response,Matchers.containsString("requestURI=/context path/test%20servlet/path%20info")); + assertThat(response,Matchers.containsString("servletPath=/test servlet")); + assertThat(response,Matchers.containsString("contextPath=/context path")); + assertThat(response,Matchers.containsString("pathInfo=/path info")); + } + + + public static class TestServlet extends HttpServlet + { + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + response.setContentType("text/plain"); + response.getWriter().println("requestURI="+request.getRequestURI()); + response.getWriter().println("contextPath="+request.getContextPath()); + response.getWriter().println("servletPath="+request.getServletPath()); + response.getWriter().println("pathInfo="+request.getPathInfo()); + } + } + + + public static class AsyncServlet extends HttpServlet + { + @Override + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + AsyncContext async = Boolean.parseBoolean(request.getParameter("wrap")) + ?request.startAsync(request,response) + :request.startAsync(); + + if (Boolean.parseBoolean(request.getParameter("encode"))) + async.dispatch("/test%20servlet"+URLEncoder.encode(request.getPathInfo(),"UTF-8")); + else + async.dispatch("/test servlet/path info"+request.getPathInfo()); + return; + } + } + + public static class AsyncFilter implements Filter + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + if (Boolean.parseBoolean(request.getParameter("async")) && !Boolean.parseBoolean((String)request.getAttribute("async"))) + { + request.setAttribute("async","true"); + AsyncContext async = Boolean.parseBoolean(request.getParameter("wrap")) + ?request.startAsync(request,response) + :request.startAsync(); + async.dispatch(); + return; + } + chain.doFilter(request,response); + } + + @Override + public void destroy() + { + } + } + +}