From 2a943887b66e8dc88383d4cf8a30c09df3377859 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 25 Jun 2014 06:52:18 -0700 Subject: [PATCH] 437800 - URLs with single quote and spaces return 404 + Expanded FileResourceTest + Added PathResource + PathResourceTest to show java.nio.file alternative approach (currently POC) --- .../jetty/util/resource/PathResource.java | 331 +++++++++++++ .../eclipse/jetty/util/resource/Resource.java | 4 + .../eclipse/jetty/util/CollectionAssert.java | 137 ++++++ .../util/resource/AbstractFSResourceTest.java | 447 ++++++++++++++++++ .../jetty/util/resource/FileResourceTest.java | 128 +---- .../jetty/util/resource/PathResourceTest.java | 38 ++ 6 files changed, 964 insertions(+), 121 deletions(-) create mode 100644 jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/CollectionAssert.java create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java 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 new file mode 100644 index 00000000000..2a4f8d0c5c1 --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java @@ -0,0 +1,331 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.util.resource; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/** + * Java NIO Path equivalent of FileResource. + */ +public class PathResource extends Resource +{ + private static final Logger LOG = Log.getLogger(PathResource.class); + + private final Path path; + private final URI uri; + private LinkOption linkOptions[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS }; + + public PathResource(File file) + { + this(file.toPath()); + } + + public PathResource(Path path) + { + this.path = path; + this.uri = this.path.toUri(); + } + + public PathResource(URI uri) throws IOException + { + if (!uri.isAbsolute()) + { + throw new IllegalArgumentException("not an absolute uri"); + } + + if (!uri.getScheme().equalsIgnoreCase("file")) + { + throw new IllegalArgumentException("not file: scheme"); + } + + Path path; + try + { + path = new File(uri).toPath(); + } + catch (InvalidPathException e) + { + throw e; + } + catch (IllegalArgumentException e) + { + throw e; + } + catch (Exception e) + { + LOG.ignore(e); + throw new IOException("Unable to build Path from: " + uri,e); + } + + this.path = path; + this.uri = path.toUri(); + } + + public PathResource(URL url) throws IOException, URISyntaxException + { + this(url.toURI()); + } + + @Override + public Resource addPath(String apath) throws IOException, MalformedURLException + { + return new PathResource(this.path.resolve(apath)); + } + + @Override + public void close() + { + // not applicable for FileSytem / Path + } + + @Override + public boolean delete() throws SecurityException + { + try + { + return Files.deleteIfExists(path); + } + catch (IOException e) + { + LOG.ignore(e); + return false; + } + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + PathResource other = (PathResource)obj; + if (path == null) + { + if (other.path != null) + { + return false; + } + } + else if (!path.equals(other.path)) + { + return false; + } + return true; + } + + @Override + public boolean exists() + { + return Files.exists(path,linkOptions); + } + + @Override + public File getFile() throws IOException + { + return path.toFile(); + } + + public boolean getFollowLinks() + { + return (linkOptions != null) && (linkOptions.length > 0) && (linkOptions[0] == LinkOption.NOFOLLOW_LINKS); + } + + @Override + public InputStream getInputStream() throws IOException + { + return Files.newInputStream(path,StandardOpenOption.READ); + } + + @Override + public String getName() + { + return path.toAbsolutePath().toString(); + } + + @Override + public ReadableByteChannel getReadableByteChannel() throws IOException + { + return FileChannel.open(path,StandardOpenOption.READ); + } + + @Override + public URI getURI() + { + return this.uri; + } + + @Override + public URL getURL() + { + try + { + return path.toUri().toURL(); + } + catch (MalformedURLException e) + { + return null; + } + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = (prime * result) + ((path == null)?0:path.hashCode()); + return result; + } + + @Override + public boolean isContainedIn(Resource r) throws MalformedURLException + { + // not applicable for FileSystem / path + return false; + } + + @Override + public boolean isDirectory() + { + return Files.isDirectory(path,linkOptions); + } + + @Override + public long lastModified() + { + try + { + FileTime ft = Files.getLastModifiedTime(path,linkOptions); + return ft.toMillis(); + } + catch (IOException e) + { + LOG.ignore(e); + return 0; + } + } + + @Override + public long length() + { + try + { + return Files.size(path); + } + catch (IOException e) + { + // in case of error, use File.length logic of 0L + return 0L; + } + } + + @Override + public String[] list() + { + try + { + List entries = new ArrayList<>(); + try (DirectoryStream dir = Files.newDirectoryStream(path)) + { + for (Path entry : dir) + { + String name = entry.getFileName().toString(); + + if (Files.isDirectory(entry)) + { + name += "/"; + } + + entries.add(name); + } + } + int size = entries.size(); + return entries.toArray(new String[size]); + } + catch (IOException e) + { + LOG.ignore(e); + } + return null; + } + + @Override + public boolean renameTo(Resource dest) throws SecurityException + { + if (dest instanceof PathResource) + { + PathResource destRes = (PathResource)dest; + try + { + Path result = Files.move(path,destRes.path,StandardCopyOption.ATOMIC_MOVE); + return Files.exists(result,linkOptions); + } + catch (IOException e) + { + LOG.ignore(e); + return false; + } + } + else + { + return false; + } + } + + public void setFollowLinks(boolean followLinks) + { + if (followLinks) + { + linkOptions = new LinkOption[0]; + } + else + { + linkOptions = new LinkOption[] { LinkOption.NOFOLLOW_LINKS }; + } + } +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index b36a9355823..181ccf0d2af 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -352,6 +352,7 @@ public abstract class Resource implements ResourceFactory, Closeable /** * Returns an URL representing the given resource */ + // TODO: should deprecate this one and only use getURI() public abstract URL getURL(); /* ------------------------------------------------------------ */ @@ -405,6 +406,7 @@ public abstract class Resource implements ResourceFactory, Closeable /** * Deletes the given resource */ + // TODO: can throw IOException public abstract boolean delete() throws SecurityException; @@ -412,6 +414,7 @@ public abstract class Resource implements ResourceFactory, Closeable /** * Rename the given resource */ + // TODO: can throw IOException public abstract boolean renameTo( Resource dest) throws SecurityException; @@ -420,6 +423,7 @@ public abstract class Resource implements ResourceFactory, Closeable * Returns a list of resource names contained in the given resource * The resource names are not URL encoded. */ + // TODO: can throw IOException public abstract String[] list(); /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/CollectionAssert.java b/jetty-util/src/test/java/org/eclipse/jetty/util/CollectionAssert.java new file mode 100644 index 00000000000..9ddba2aab32 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/CollectionAssert.java @@ -0,0 +1,137 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.junit.Assert; + +public class CollectionAssert +{ + public static void assertContainsUnordered(String msg, Collection expectedSet, Collection actualSet) + { + // same size? + boolean mismatch = expectedSet.size() != actualSet.size(); + + // test content + Set missing = new HashSet<>(); + for (String expected : expectedSet) + { + if (!actualSet.contains(expected)) + { + missing.add(expected); + } + } + + if (mismatch || missing.size() > 0) + { + // build up detailed error message + StringWriter message = new StringWriter(); + PrintWriter err = new PrintWriter(message); + + err.printf("%s: Assert Contains (Unordered)",msg); + if (mismatch) + { + err.print(" [size mismatch]"); + } + if (missing.size() >= 0) + { + err.printf(" [%d entries missing]",missing.size()); + } + err.println(); + err.printf("Actual Entries (size: %d)%n",actualSet.size()); + for (String actual : actualSet) + { + char indicator = expectedSet.contains(actual)?' ':'>'; + err.printf("%s| %s%n",indicator,actual); + } + err.printf("Expected Entries (size: %d)%n",expectedSet.size()); + for (String expected : expectedSet) + { + char indicator = actualSet.contains(expected)?' ':'>'; + err.printf("%s| %s%n",indicator,expected); + } + err.flush(); + Assert.fail(message.toString()); + } + } + + public static void assertOrdered(String msg, List expectedList, List actualList) + { + // same size? + boolean mismatch = expectedList.size() != actualList.size(); + + // test content + List badEntries = new ArrayList<>(); + int min = Math.min(expectedList.size(),actualList.size()); + int max = Math.max(expectedList.size(),actualList.size()); + for (int i = 0; i < min; i++) + { + if (!expectedList.get(i).equals(actualList.get(i))) + { + badEntries.add(i); + } + } + for (int i = min; i < max; i++) + { + badEntries.add(i); + } + + if (mismatch || badEntries.size() > 0) + { + // build up detailed error message + StringWriter message = new StringWriter(); + PrintWriter err = new PrintWriter(message); + + err.printf("%s: Assert Contains (Unordered)",msg); + if (mismatch) + { + err.print(" [size mismatch]"); + } + if (badEntries.size() >= 0) + { + err.printf(" [%d entries not matched]",badEntries.size()); + } + err.println(); + err.printf("Actual Entries (size: %d)%n",actualList.size()); + for (int i = 0; i < actualList.size(); i++) + { + String actual = actualList.get(i); + char indicator = badEntries.contains(i)?'>':' '; + err.printf("%s[%d] %s%n",indicator,i,actual); + } + + err.printf("Expected Entries (size: %d)%n",expectedList.size()); + for (int i = 0; i < expectedList.size(); i++) + { + String expected = expectedList.get(i); + char indicator = badEntries.contains(i)?'>':' '; + err.printf("%s[%d] %s%n",indicator,i,expected); + } + err.flush(); + Assert.fail(message.toString()); + } + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java new file mode 100644 index 00000000000..a00ec851bca --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/AbstractFSResourceTest.java @@ -0,0 +1,447 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.util.resource; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.junit.Assume.*; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.net.URI; +import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.InvalidPathException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.OS; +import org.eclipse.jetty.toolchain.test.TestingDir; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.CollectionAssert; +import org.junit.Rule; +import org.junit.Test; + +public abstract class AbstractFSResourceTest +{ + @Rule + public TestingDir testdir = new TestingDir(); + + public abstract Resource newResource(URI uri) throws IOException; + + public abstract Resource newResource(File file) throws IOException; + + private URI createEmptyFile(String name) throws IOException + { + File file = testdir.getFile(name); + file.createNewFile(); + return file.toURI(); + } + + @Test(expected = IllegalArgumentException.class) + public void testNonAbsoluteURI() throws Exception + { + newResource(new URI("path/to/resource")); + } + + @Test(expected = IllegalArgumentException.class) + public void testNotFileURI() throws Exception + { + newResource(new URI("http://www.eclipse.org/jetty/")); + } + + @Test(expected = IllegalArgumentException.class) + public void testBogusFilename() throws Exception + { + if (OS.IS_UNIX) + { + newResource(new URI("file://Z:/:")); + } + else if (OS.IS_WINDOWS) + { + newResource(new URI("file:/dev/null")); + } + } + + @Test + public void testIsContainedIn() throws Exception + { + createEmptyFile("foo"); + + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("is contained in",res.isContainedIn(base),is(false)); + } + } + + @Test + public void testIsDirectory() throws Exception + { + File dir = testdir.getDir(); + createEmptyFile("foo"); + + File subdir = new File(dir,"sub"); + FS.ensureDirExists(subdir); + + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("foo.isDirectory",res.isDirectory(),is(false)); + + Resource sub = base.addPath("sub"); + assertThat("sub/.isDirectory",sub.isDirectory(),is(true)); + } + } + + @Test + public void testLastModified() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + long expected = file.lastModified(); + + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("foo.lastModified",res.lastModified(),is(expected)); + } + } + + @Test + public void testLastModified_NotExists() throws Exception + { + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("foo.lastModified",res.lastModified(),is(0L)); + } + } + + @Test + public void testLength() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + try (StringReader reader = new StringReader("foo"); FileWriter writer = new FileWriter(file)) + { + IO.copy(reader,writer); + } + + long expected = file.length(); + + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("foo.length",res.length(),is(expected)); + } + } + + @Test + public void testLength_NotExists() throws Exception + { + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo"); + assertThat("foo.length",res.length(),is(0L)); + } + } + + @Test + public void testDelete() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + try (Resource base = newResource(testdir.getDir())) + { + // Is it there? + Resource res = base.addPath("foo"); + assertThat("foo.exists",res.exists(),is(true)); + // delete it + assertThat("foo.delete",res.delete(),is(true)); + // is it there? + assertThat("foo.exists",res.exists(),is(false)); + } + } + + @Test + public void testDelete_NotExists() throws Exception + { + try (Resource base = newResource(testdir.getDir())) + { + // Is it there? + Resource res = base.addPath("foo"); + assertThat("foo.exists",res.exists(),is(false)); + // delete it + assertThat("foo.delete",res.delete(),is(false)); + // is it there? + assertThat("foo.exists",res.exists(),is(false)); + } + } + + @Test + public void testName() throws Exception + { + String expected = testdir.getDir().getAbsolutePath(); + + try (Resource base = newResource(testdir.getDir())) + { + assertThat("base.name",base.getName(),is(expected)); + } + } + + @Test + public void testInputStream() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + String content = "Foo is here"; + + try (StringReader reader = new StringReader(content); FileWriter writer = new FileWriter(file)) + { + IO.copy(reader,writer); + } + + try (Resource base = newResource(testdir.getDir())) + { + Resource foo = base.addPath("foo"); + try (InputStream stream = foo.getInputStream(); InputStreamReader reader = new InputStreamReader(stream); StringWriter writer = new StringWriter()) + { + IO.copy(reader,writer); + assertThat("Stream",writer.toString(),is(content)); + } + } + } + + @Test + public void testReadableByteChannel() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + String content = "Foo is here"; + + try (StringReader reader = new StringReader(content); FileWriter writer = new FileWriter(file)) + { + IO.copy(reader,writer); + } + + try (Resource base = newResource(testdir.getDir())) + { + Resource foo = base.addPath("foo"); + try (ReadableByteChannel channel = foo.getReadableByteChannel()) + { + ByteBuffer buf = ByteBuffer.allocate(256); + channel.read(buf); + buf.flip(); + String actual = BufferUtil.toUTF8String(buf); + assertThat("ReadableByteChannel content",actual,is(content)); + } + } + } + + @Test + public void testGetURI() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + URI expected = file.toURI(); + + try (Resource base = newResource(testdir.getDir())) + { + Resource foo = base.addPath("foo"); + assertThat("getURI",foo.getURI(),is(expected)); + } + } + + @Test + public void testGetURL() throws Exception + { + File file = testdir.getFile("foo"); + file.createNewFile(); + + URL expected = file.toURI().toURL(); + + try (Resource base = newResource(testdir.getDir())) + { + Resource foo = base.addPath("foo"); + assertThat("getURL",foo.getURL(),is(expected)); + } + } + + @Test + public void testList() throws Exception + { + File dir = testdir.getDir(); + FS.touch(new File(dir, "foo")); + FS.touch(new File(dir, "bar")); + FS.ensureDirExists(new File(dir, "tick")); + FS.ensureDirExists(new File(dir, "tock")); + + List expected = new ArrayList<>(); + expected.add("foo"); + expected.add("bar"); + expected.add("tick/"); + expected.add("tock/"); + + try (Resource base = newResource(testdir.getDir())) + { + String list[] = base.list(); + List actual = Arrays.asList(list); + + CollectionAssert.assertContainsUnordered("Resource Directory Listing", + expected,actual); + } + } + + @Test + public void testSemicolon() throws Exception + { + assumeTrue(!OS.IS_WINDOWS); + createEmptyFile("foo;"); + + try (Resource base = newResource(testdir.getDir())) + { + Resource res = base.addPath("foo;"); + assertThat("Alias: " + res,res.getAlias(),nullValue()); + } + } + + @Test + public void testExist_Normal() throws Exception + { + createEmptyFile("a.jsp"); + + URI ref = testdir.getDir().toURI().resolve("a.jsp"); + try (Resource fileres = newResource(ref)) + { + assertThat("Resource: " + fileres,fileres.exists(),is(true)); + } + } + + @Test + public void testSingleQuoteInFileName() throws Exception + { + createEmptyFile("foo's.txt"); + createEmptyFile("f o's.txt"); + + URI refQuoted = testdir.getDir().toURI().resolve("foo's.txt"); + + try (Resource fileres = newResource(refQuoted)) + { + assertThat("Exists: " + refQuoted,fileres.exists(),is(true)); + assertThat("Alias: " + refQuoted,fileres.getAlias(),nullValue()); + } + + URI refEncoded = testdir.getDir().toURI().resolve("foo%27s.txt"); + + try (Resource fileres = newResource(refEncoded)) + { + assertThat("Exists: " + refEncoded,fileres.exists(),is(true)); + assertThat("Alias: " + refEncoded,fileres.getAlias(),nullValue()); + } + + URI refQuoteSpace = testdir.getDir().toURI().resolve("f%20o's.txt"); + + try (Resource fileres = newResource(refQuoteSpace)) + { + assertThat("Exists: " + refQuoteSpace,fileres.exists(),is(true)); + assertThat("Alias: " + refQuoteSpace,fileres.getAlias(),nullValue()); + } + + URI refEncodedSpace = testdir.getDir().toURI().resolve("f%20o%27s.txt"); + + try (Resource fileres = newResource(refEncodedSpace)) + { + assertThat("Exists: " + refEncodedSpace,fileres.exists(),is(true)); + assertThat("Alias: " + refEncodedSpace,fileres.getAlias(),nullValue()); + } + + URI refA = testdir.getDir().toURI().resolve("foo's.txt"); + URI refB = testdir.getDir().toURI().resolve("foo%27s.txt"); + + StringBuilder msg = new StringBuilder(); + msg.append("URI[a].equals(URI[b])").append(System.lineSeparator()); + msg.append("URI[a] = ").append(refA).append(System.lineSeparator()); + msg.append("URI[b] = ").append(refB); + + // show that simple URI.equals() doesn't work + assertThat(msg.toString(),refA.equals(refB),is(false)); + + // now show that Resource.equals() does work + try (Resource a = newResource(refA); Resource b = newResource(refB);) + { + assertThat("A.equals(B)",a.equals(b),is(true)); + } + } + + @Test + public void testExist_BadNull() throws Exception + { + createEmptyFile("a.jsp"); + + try + { + // request with null at end + URI ref = testdir.getDir().toURI().resolve("a.jsp%00"); + assertThat("Null URI",ref,notNullValue()); + + newResource(ref); + fail("Should have thrown " + InvalidPathException.class); + } + catch (InvalidPathException e) + { + // Expected path + } + } + + @Test + public void testExist_BadNullX() throws Exception + { + createEmptyFile("a.jsp"); + + try + { + // request with null and x at end + URI ref = testdir.getDir().toURI().resolve("a.jsp%00x"); + assertThat("NullX URI",ref,notNullValue()); + + newResource(ref); + fail("Should have thrown " + InvalidPathException.class); + } + catch (InvalidPathException e) + { + // Expected path + } + } +} diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java index e77f241b49b..71b02db6caf 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java @@ -18,150 +18,36 @@ package org.eclipse.jetty.util.resource; -import static org.hamcrest.Matchers.is; -import static org.junit.Assume.assumeTrue; - import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import org.eclipse.jetty.toolchain.test.OS; -import org.eclipse.jetty.toolchain.test.TestingDir; -import org.eclipse.jetty.util.UrlEncoded; -import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Ignore; -import org.junit.Rule; import org.junit.Test; -public class FileResourceTest +public class FileResourceTest extends AbstractFSResourceTest { - @Rule - public TestingDir testdir = new TestingDir(); - - private URI createDummyFile(String name) throws IOException + @Override + public Resource newResource(URI uri) throws IOException { - File file = testdir.getFile(name); - file.createNewFile(); - return file.toURI(); + return new FileResource(uri); } - private URL decode(URL url) throws MalformedURLException + @Override + public Resource newResource(File file) throws IOException { - String raw = url.toExternalForm(); - String decoded = UrlEncoded.decodeString(raw,0,raw.length(), StandardCharsets.UTF_8); - return new URL(decoded); + return new FileResource(file); } - @Test - public void testSemicolon() throws Exception - { - assumeTrue(!OS.IS_WINDOWS); - createDummyFile("foo;"); - - try(Resource base = new FileResource(testdir.getDir());) - { - Resource res = base.addPath("foo;"); - Assert.assertNull(res.getAlias()); - } - } - - @Test - public void testExist_Normal() throws Exception - { - createDummyFile("a.jsp"); - - URI ref = testdir.getDir().toURI().resolve("a.jsp"); - try(FileResource fileres = new FileResource(decode(ref.toURL()));) - { - Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(true)); - } - } - - @Test - public void testSingleQuoteInFileName() throws Exception - { - createDummyFile("foo's.txt"); - createDummyFile("f o's.txt"); - - URI refQuoted = testdir.getDir().toURI().resolve("foo's.txt"); - - try(FileResource fileres = new FileResource(refQuoted)) - { - Assert.assertThat(fileres.exists(),is(true)); - Assert.assertThat(fileres.getAlias(),Matchers.nullValue()); - } - - URI refEncoded = testdir.getDir().toURI().resolve("foo%27s.txt"); - - try(FileResource fileres = new FileResource(refEncoded)) - { - Assert.assertThat(fileres.exists(),is(true)); - Assert.assertThat(fileres.getAlias(),Matchers.nullValue()); - } - - URI refQuoteSpace = testdir.getDir().toURI().resolve("f%20o's.txt"); - - try(FileResource fileres = new FileResource(refQuoteSpace)) - { - Assert.assertThat(fileres.exists(),is(true)); - Assert.assertThat(fileres.getAlias(),Matchers.nullValue()); - } - - URI refEncodedSpace = testdir.getDir().toURI().resolve("f%20o%27s.txt"); - - try(FileResource fileres = new FileResource(refEncodedSpace)) - { - Assert.assertThat(fileres.exists(),is(true)); - Assert.assertThat(fileres.getAlias(),Matchers.nullValue()); - } - } - - - @Ignore("Cannot get null to be seen by FileResource") @Test public void testExist_BadNull() throws Exception { - createDummyFile("a.jsp"); - - try - { - // request with null at end - URI ref = testdir.getDir().toURI().resolve("a.jsp%00"); - try(FileResource fileres = new FileResource(decode(ref.toURL()));) - { - Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false)); - } - } - catch(URISyntaxException e) - { - // Valid path - } } @Ignore("Validation shouldn't be done in FileResource") @Test public void testExist_BadNullX() throws Exception { - createDummyFile("a.jsp"); - - try - { - // request with null and x at end - URI ref = testdir.getDir().toURI().resolve("a.jsp%00x"); - try(FileResource fileres = new FileResource(decode(ref.toURL()));) - { - Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false)); - } - } - catch(URISyntaxException e) - { - // Valid path - } } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java new file mode 100644 index 00000000000..6e059a68c2a --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.util.resource; + +import java.io.File; +import java.io.IOException; +import java.net.URI; + +public class PathResourceTest extends AbstractFSResourceTest +{ + @Override + public Resource newResource(URI uri) throws IOException + { + return new PathResource(uri); + } + + @Override + public Resource newResource(File file) throws IOException + { + return new PathResource(file); + } +}