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 dcd3aaed957..95ecf5739ba 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 @@ -1016,16 +1016,9 @@ public class Request implements HttpServletRequest if (_queryParameters == null) extractQueryParameters(); - if (_queryParameters==NO_PARAMS || _queryParameters.size()==0) - _parameters=_contentParameters; - else if (_contentParameters==NO_PARAMS || _contentParameters.size()==0) - _parameters=_queryParameters; - else - { - _parameters = new MultiMap<>(); - _parameters.addAllValues(_queryParameters); - _parameters.addAllValues(_contentParameters); - } + _parameters = new MultiMap<>(); + _parameters.addAllValues(_queryParameters); + _parameters.addAllValues(_contentParameters); } /* ------------------------------------------------------------ */ 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 554109db655..762f76c18b1 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 @@ -2156,6 +2156,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu if (url == null) return null; Resource r = Resource.newResource(url); + // Cannot serve directories as an InputStream + if(r.isDirectory()) + return null; return r.getInputStream(); } catch (Exception e) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextResourcesTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextResourcesTest.java new file mode 100644 index 00000000000..3a30bed3747 --- /dev/null +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextResourcesTest.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// 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.containsString; +import static org.junit.Assert.assertThat; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.IO; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ServletContextResourcesTest +{ + public static class ResourceAsStreamServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + String pathInfo = req.getPathInfo(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try (InputStream in = req.getServletContext().getResourceAsStream(pathInfo)) + { + if (in == null) + { + out.write("".getBytes(StandardCharsets.UTF_8)); + } + else + { + IO.copy(in, out); + } + } + + String resourceContents = new String(out.toByteArray(), StandardCharsets.UTF_8); + resp.getWriter().printf("Resource '%s': %s", pathInfo, resourceContents); + } + } + + private Server server; + private LocalConnector connector; + private ServletContextHandler context; + + @Before + public void init() throws Exception + { + server = new Server(); + + connector = new LocalConnector(server); + connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false); + + Path resBase = MavenTestingUtils.getTestResourcePathDir("contextResources"); + + context = new ServletContextHandler(); + context.setContextPath("/context"); + context.setResourceBase(resBase.toFile().toURI().toASCIIString()); + + server.setHandler(context); + server.addConnector(connector); + + server.start(); + } + + @After + public void destroy() throws Exception + { + server.stop(); + server.join(); + } + + @Test + public void testGetResourceAsStream_Root() throws Exception + { + context.addServlet(ResourceAsStreamServlet.class, "/*"); + + StringBuffer req1 = new StringBuffer(); + req1.append("GET /context/ HTTP/1.1\r\n"); + req1.append("Host: local\r\n"); + req1.append("Connection: close\r\n"); + req1.append("\r\n"); + + String response = connector.getResponses(req1.toString()); + assertThat("Response", response, containsString("Resource '/': ")); + } + + @Test + public void testGetResourceAsStream_Content() throws Exception + { + context.addServlet(ResourceAsStreamServlet.class, "/*"); + + StringBuffer req1 = new StringBuffer(); + req1.append("GET /context/content.txt HTTP/1.1\r\n"); + req1.append("Host: local\r\n"); + req1.append("Connection: close\r\n"); + req1.append("\r\n"); + + String response = connector.getResponses(req1.toString()); + assertThat("Response", response, containsString("Resource '/content.txt': content goes here")); + } +} diff --git a/jetty-servlet/src/test/resources/contextResources/content.txt b/jetty-servlet/src/test/resources/contextResources/content.txt new file mode 100644 index 00000000000..815d4403267 --- /dev/null +++ b/jetty-servlet/src/test/resources/contextResources/content.txt @@ -0,0 +1 @@ +content goes here \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java index f24c22ea13e..62129514ff1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java @@ -377,6 +377,13 @@ public class PathResource extends Resource @Override public InputStream getInputStream() throws IOException { + /* Mimic behavior from old FileResource class and its + * usage of java.io.FileInputStream(File) which will trigger + * an IOException on construction if the path is a directory + */ + if (Files.isDirectory(path)) + throw new IOException(path + " is a directory"); + return Files.newInputStream(path,StandardOpenOption.READ); } diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java index ad5e0085525..229f6aa3bff 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrExtension.java @@ -93,6 +93,23 @@ public class JsrExtension implements Extension return parameters; } + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JsrExtension that = (JsrExtension) o; + + return name != null ? name.equals(that.name) : that.name == null; + } + + @Override + public int hashCode() + { + return name != null ? name.hashCode() : 0; + } + @Override public String toString() {