diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 685a95084b1..1d2994139bc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -435,12 +435,21 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable LOG.ignore(e); path = _uri.getDecodedPath(StringUtil.__ISO_8859_1); } + String info = URIUtil.canonicalPath(path); if (info == null) { - info = "/"; - _request.setRequestURI(""); + if( path==null && _uri.getScheme()!=null &&_uri.getHost()!=null) + { + info = "/"; + _request.setRequestURI(""); + } + else + { + badMessage(400,null); + return true; + } } _request.setPathInfo(info); _version = version == null ? HttpVersion.HTTP_0_9 : version; 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 7dbe34be919..267a199de99 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 @@ -19,6 +19,7 @@ package org.eclipse.jetty.server; import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -72,6 +73,7 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context; import org.eclipse.jetty.server.session.AbstractSession; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.AttributesMap; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.MultiPartInputStreamParser; @@ -349,6 +351,7 @@ public class Request implements HttpServletRequest } } } + } if (_parameters == null) @@ -358,6 +361,28 @@ public class Request implements HttpServletRequest // Merge parameters (needed if parameters extracted after a forward). _parameters.addAllValues(_baseParameters); } + + if (content_type != null && content_type.length()>0 && content_type.startsWith("multipart/form-data") && getAttribute(__MULTIPART_CONFIG_ELEMENT)!=null) + { + try + { + getParts(); + } + catch (IOException e) + { + if (LOG.isDebugEnabled()) + LOG.warn(e); + else + LOG.warn(e.toString()); + } + catch (ServletException e) + { + if (LOG.isDebugEnabled()) + LOG.warn(e); + else + LOG.warn(e.toString()); + } + } } finally { @@ -2036,38 +2061,8 @@ public class Request implements HttpServletRequest @Override public Part getPart(String name) throws IOException, ServletException { - if (getContentType() == null || !getContentType().startsWith("multipart/form-data")) - throw new ServletException("Content-Type != multipart/form-data"); + getParts(); - if (_multiPartInputStream == null) - { - MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT); - - if (config == null) - throw new IllegalStateException("No multipart config for servlet"); - - _multiPartInputStream = new MultiPartInputStreamParser(getInputStream(), - getContentType(),config, - (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null)); - setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream); - setAttribute(__MULTIPART_CONTEXT, _context); - Collection parts = _multiPartInputStream.getParts(); //causes parsing - for (Part p:parts) - { - MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p; - if (mp.getContentDispositionFilename() == null && mp.getFile() == null) - { - //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params - String charset = null; - if (mp.getContentType() != null) - charset = MimeTypes.getCharsetFromContentType(mp.getContentType()); - - String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); - getParameter(""); //cause params to be evaluated - getParameters().add(mp.getName(), content); - } - } - } return _multiPartInputStream.getPart(name); } @@ -2078,6 +2073,9 @@ public class Request implements HttpServletRequest if (getContentType() == null || !getContentType().startsWith("multipart/form-data")) throw new ServletException("Content-Type != multipart/form-data"); + if (_multiPartInputStream == null) + _multiPartInputStream = (MultiPartInputStreamParser)getAttribute(__MULTIPART_INPUT_STREAM); + if (_multiPartInputStream == null) { MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT); @@ -2095,19 +2093,32 @@ public class Request implements HttpServletRequest for (Part p:parts) { MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p; - if (mp.getContentDispositionFilename() == null && mp.getFile() == null) + if (mp.getContentDispositionFilename() == null) { //Servlet Spec 3.0 pg 23, parts without filenames must be put into init params String charset = null; if (mp.getContentType() != null) charset = MimeTypes.getCharsetFromContentType(mp.getContentType()); - String content=new String(mp.getBytes(),charset==null?StringUtil.__UTF8:charset); - getParameter(""); //cause params to be evaluated - getParameters().add(mp.getName(), content); + ByteArrayOutputStream os = null; + InputStream is = mp.getInputStream(); //get the bytes regardless of being in memory or in temp file + try + { + os = new ByteArrayOutputStream(); + IO.copy(is, os); + String content=new String(os.toByteArray(),charset==null?StringUtil.__UTF8:charset); + getParameter(""); //cause params to be evaluated + getParameters().add(mp.getName(), content); + } + finally + { + IO.close(os); + IO.close(is); + } } } } + return _multiPartInputStream.getParts(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 04fc9f67c81..918eb5bb470 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -1825,9 +1825,12 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu // uriInContext = uriInContext.substring(0,q); String pathInContext = URIUtil.canonicalPath(URIUtil.decodePath(uriInContext)); - String uri = URIUtil.addPaths(getContextPath(),uriInContext); - ContextHandler context = ContextHandler.this; - return new Dispatcher(context,uri,pathInContext,query); + if (pathInContext!=null) + { + String uri = URIUtil.addPaths(getContextPath(),uriInContext); + ContextHandler context = ContextHandler.this; + return new Dispatcher(context,uri,pathInContext,query); + } } catch (Exception e) { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java index 5869d352ebd..6e7a6d5e338 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java @@ -138,6 +138,66 @@ public class HttpConnectionTest checkContains(response,offset,"pathInfo=/"); } + @Test + public void testBadNoPath() throws Exception + { + String response=connector.getResponses("GET http://localhost:80/../cheat HTTP/1.1\n"+ + "Host: localhost:80\n"+ + "\n"); + int offset=0; + offset = checkContains(response,offset,"HTTP/1.1 400"); + } + + @Test + public void testOKPathDotDotPath() throws Exception + { + String response=connector.getResponses("GET /ooops/../path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 200 OK"); + checkContains(response,0,"pathInfo=/path"); + } + + @Test + public void testBadPathDotDotPath() throws Exception + { + String response=connector.getResponses("GET /ooops/../../path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 400 Bad Request"); + } + + @Test + public void testOKPathEncodedDotDotPath() throws Exception + { + String response=connector.getResponses("GET /ooops/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 200 OK"); + checkContains(response,0,"pathInfo=/path"); + } + + @Test + public void testBadPathEncodedDotDotPath() throws Exception + { + String response=connector.getResponses("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 400 Bad Request"); + } + + @Test + public void testBadDotDotPath() throws Exception + { + String response=connector.getResponses("GET ../path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 400 Bad Request"); + } + + @Test + public void testBadSlashDotDotPath() throws Exception + { + String response=connector.getResponses("GET /../path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 400 Bad Request"); + } + + @Test + public void testEncodedBadDotDotPath() throws Exception + { + String response=connector.getResponses("GET %2e%2e/path HTTP/1.0\nHost: localhost:80\n\n"); + checkContains(response,0,"HTTP/1.1 400 Bad Request"); + } @Test public void testEmpty() throws Exception diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java index b1c9bde8968..3249d964b82 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java @@ -1094,10 +1094,12 @@ public class RequestTest MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2); request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce); + String field1 = request.getParameter("field1"); + assertNotNull(field1); + Part foo = request.getPart("stuff"); assertNotNull(foo); assertTrue(foo.getSize() > 0); - response.setStatus(200); } catch (IllegalStateException e) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java index 2b27158ec13..58971c923ea 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/DispatcherTest.java @@ -220,6 +220,19 @@ public class DispatcherTest assertEquals(expected, responses); } + @Test + public void testServletForwardDotDot() throws Exception + { + _contextHandler.addServlet(DispatchServletServlet.class, "/dispatch/*"); + _contextHandler.addServlet(RogerThatServlet.class, "/roger/that"); + + String requests="GET /context/dispatch/test?forward=%2e%2e/roger/that HTTP/1.0\n" + "Host: localhost\n\n"; + + String responses = _connector.getResponses(requests); + + assertThat(responses,startsWith("HTTP/1.1 404 ")); + } + @Test public void testServletInclude() throws Exception { @@ -412,7 +425,10 @@ public class DispatcherTest else if(request.getParameter("forward")!=null) { dispatcher = getServletContext().getRequestDispatcher(request.getParameter("forward")); - dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response)); + if (dispatcher!=null) + dispatcher.forward(new ServletRequestWrapper(request), new ServletResponseWrapper(response)); + else + response.sendError(404); } }