Merge branch 'jetty-9.4.x'

This commit is contained in:
Joakim Erdfelt 2016-05-13 11:59:17 -07:00
commit 6c47951fae
8 changed files with 373 additions and 104 deletions

View File

@ -70,6 +70,7 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
@ -1065,9 +1066,14 @@ public class AsyncMiddleManServletTest
{ {
Assert.assertFalse(paths.iterator().hasNext()); Assert.assertFalse(paths.iterator().hasNext());
} }
try (DirectoryStream<Path> paths = Files.newDirectoryStream(targetTestsDir, outputPrefix + "*.*"))
// File deletion is delayed on windows, testing for deletion is not going to work
if(!OS.IS_WINDOWS)
{ {
Assert.assertFalse(paths.iterator().hasNext()); try (DirectoryStream<Path> paths = Files.newDirectoryStream(targetTestsDir, outputPrefix + "*.*"))
{
Assert.assertFalse(paths.iterator().hasNext());
}
} }
} }

View File

@ -53,6 +53,9 @@ public class AllowSymLinkAliasChecker implements AliasCheck
Path path = pathResource.getPath(); Path path = pathResource.getPath();
Path alias = pathResource.getAliasPath(); Path alias = pathResource.getAliasPath();
if (path.equals(alias))
return false; // Unknown why this is an alias
// is the file itself a symlink? // is the file itself a symlink?
if (Files.isSymbolicLink(path)) if (Files.isSymbolicLink(path))
{ {

View File

@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeTrue;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
@ -43,6 +44,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Assert; import org.junit.Assert;
@ -167,6 +169,7 @@ public class GracefulStopTest
@Test @Test
public void testGracefulComplete() throws Exception public void testGracefulComplete() throws Exception
{ {
assumeTrue(!OS.IS_WINDOWS);
Server server= new Server(); Server server= new Server();
server.setStopTimeout(10000); server.setStopTimeout(10000);

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.server.handler; package org.eclipse.jetty.server.handler;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -321,15 +321,15 @@ public class ContextHandlerGetResourceTest
String path="//subdir/data.txt"; String path="//subdir/data.txt";
Resource resource=context.getResource(path); Resource resource=context.getResource(path);
assertThat("Resource: " + resource, resource.getFile(), is(expected)); assertThat("Resource: " + resource, resource, nullValue());
URL url=context.getServletContext().getResource(path); URL url=context.getServletContext().getResource(path);
assertThat("Resource: " + url, url, is(expectedUrl)); assertThat("Resource: " + url, url, nullValue());
path="/subdir//data.txt"; path="/subdir//data.txt";
resource=context.getResource(path); resource=context.getResource(path);
assertThat("Resource: " + resource, resource.getFile(), is(expected)); assertThat("Resource: " + resource, resource, nullValue());
url=context.getServletContext().getResource(path); url=context.getServletContext().getResource(path);
assertThat("Resource: " + url, url, is(expectedUrl)); assertThat("Resource: " + url, url, nullValue());
} }
@Test @Test

View File

@ -854,6 +854,3 @@ public class URIUtil
return URI.create(buf.toString()); return URI.create(buf.toString());
} }
} }

View File

@ -57,12 +57,12 @@ public class FileResource extends Resource
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private final File _file; private final File _file;
private final String _uri; private final URI _uri;
private final URI _alias; private final URI _alias;
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
public FileResource(URL url) public FileResource(URL url)
throws IOException, URISyntaxException throws IOException, URISyntaxException
{ {
File file; File file;
try try
@ -103,7 +103,7 @@ public class FileResource extends Resource
_file=file; _file=file;
_uri=normalizeURI(_file,url.toURI()); _uri=normalizeURI(_file,url.toURI());
_alias=checkFileAlias(_file); _alias=checkFileAlias(_uri,_file);
} }
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
@ -111,15 +111,27 @@ public class FileResource extends Resource
{ {
File file=new File(uri); File file=new File(uri);
_file=file; _file=file;
URI file_uri=_file.toURI(); try
_uri=normalizeURI(_file,uri); {
assertValidPath(file.toString()); URI file_uri = _file.toURI();
_uri = normalizeURI(_file, uri);
assertValidPath(file.toString());
// Is it a URI alias? // Is it a URI alias?
if (!URIUtil.equalsIgnoreEncodings(_uri,file_uri.toString())) if (!URIUtil.equalsIgnoreEncodings(_uri.toASCIIString(), file_uri.toString()))
_alias=_file.toURI(); _alias = _file.toURI();
else else
_alias=checkFileAlias(_file); _alias = checkFileAlias(_uri, _file);
}
catch (URISyntaxException e)
{
throw new InvalidPathException(_file.toString(), e.getMessage())
{
{
initCause(e);
}
};
}
} }
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
@ -127,13 +139,59 @@ public class FileResource extends Resource
{ {
assertValidPath(file.toString()); assertValidPath(file.toString());
_file=file; _file=file;
_uri=normalizeURI(_file,_file.toURI()); try
_alias=checkFileAlias(_file); {
_uri = normalizeURI(_file, _file.toURI());
}
catch (URISyntaxException e)
{
throw new InvalidPathException(_file.toString(), e.getMessage())
{
{
initCause(e);
}
};
}
_alias=checkFileAlias(_uri,_file);
} }
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
private static String normalizeURI(File file, URI uri) public FileResource(File base, String childPath)
{ {
String encoded = URIUtil.encodePath(childPath);
_file = new File(base, childPath);
// The encoded path should be a suffix of the resource (give or take a directory / )
URI uri;
try
{
if (base.isDirectory())
{
// treat all paths being added as relative
uri=new URI(URIUtil.addPaths(base.toURI().toASCIIString(),encoded));
}
else
{
uri=new URI(base.toURI().toASCIIString()+encoded);
}
}
catch (final URISyntaxException e)
{
throw new InvalidPathException(base.toString() + childPath, e.getMessage())
{
{
initCause(e);
}
};
}
_uri=uri;
_alias=checkFileAlias(_uri,_file);
}
/* -------------------------------------------------------- */
private static URI normalizeURI(File file, URI uri) throws URISyntaxException {
String u =uri.toASCIIString(); String u =uri.toASCIIString();
if (file.isDirectory()) if (file.isDirectory())
{ {
@ -142,14 +200,20 @@ public class FileResource extends Resource
} }
else if (file.exists() && u.endsWith("/")) else if (file.exists() && u.endsWith("/"))
u=u.substring(0,u.length()-1); u=u.substring(0,u.length()-1);
return u; return new URI(u);
} }
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
private static URI checkFileAlias(File file) private static URI checkFileAlias(final URI uri, final File file)
{ {
try try
{ {
if (!URIUtil.equalsIgnoreEncodings(uri,file.toURI()))
{
// Return alias pointing to Java File normalized URI
return new File(uri).getAbsoluteFile().toURI();
}
String abs=file.getAbsolutePath(); String abs=file.getAbsolutePath();
String can=file.getCanonicalPath(); String can=file.getCanonicalPath();
@ -160,8 +224,7 @@ public class FileResource extends Resource
URI alias=new File(can).toURI(); URI alias=new File(can).toURI();
// Have to encode the path as File.toURI does not! // Have to encode the path as File.toURI does not!
String uri="file://"+URIUtil.encodePath(alias.getPath()); return new URI("file://"+URIUtil.encodePath(alias.getPath()));
return new URI(uri);
} }
} }
catch(Exception e) catch(Exception e)
@ -185,7 +248,7 @@ public class FileResource extends Resource
/* -------------------------------------------------------- */ /* -------------------------------------------------------- */
@Override @Override
public Resource addPath(String path) public Resource addPath(String path)
throws IOException, MalformedURLException throws IOException, MalformedURLException
{ {
assertValidPath(path); assertValidPath(path);
path = org.eclipse.jetty.util.URIUtil.canonicalPath(path); path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
@ -196,32 +259,7 @@ public class FileResource extends Resource
if ("/".equals(path)) if ("/".equals(path))
return this; return this;
path=URIUtil.encodePath(path); return new FileResource(_file, path);
// The encoded path should be a suffix of the resource (give or take a directory / )
URI uri;
try
{
if (_file.isDirectory())
{
// treat all paths being added as relative
uri=new URI(URIUtil.addPaths(_uri,path));
}
else
{
uri=new URI(_uri+path);
}
}
catch (final URISyntaxException e)
{
throw new InvalidPathException(path, e.getMessage())
{
{
initCause(e);
}
};
}
return new FileResource(uri);
} }
private void assertValidPath(String path) private void assertValidPath(String path)
@ -267,7 +305,7 @@ public class FileResource extends Resource
@Override @Override
public boolean isDirectory() public boolean isDirectory()
{ {
return _file.exists() && _file.isDirectory() || _uri.endsWith("/"); return _file.exists() && _file.isDirectory() || _uri.toASCIIString().endsWith("/");
} }
/* --------------------------------------------------------- */ /* --------------------------------------------------------- */
@ -325,7 +363,7 @@ public class FileResource extends Resource
*/ */
@Override @Override
public boolean delete() public boolean delete()
throws SecurityException throws SecurityException
{ {
return _file.delete(); return _file.delete();
} }
@ -336,7 +374,7 @@ public class FileResource extends Resource
*/ */
@Override @Override
public boolean renameTo( Resource dest) public boolean renameTo( Resource dest)
throws SecurityException throws SecurityException
{ {
if( dest instanceof FileResource) if( dest instanceof FileResource)
return _file.renameTo( ((FileResource)dest)._file); return _file.renameTo( ((FileResource)dest)._file);
@ -357,7 +395,7 @@ public class FileResource extends Resource
for (int i=list.length;i-->0;) for (int i=list.length;i-->0;)
{ {
if (new File(_file,list[i]).isDirectory() && if (new File(_file,list[i]).isDirectory() &&
!list[i].endsWith("/")) !list[i].endsWith("/"))
list[i]+="/"; list[i]+="/";
} }
return list; return list;
@ -388,13 +426,13 @@ public class FileResource extends Resource
@Override @Override
public int hashCode() public int hashCode()
{ {
return null == _file ? super.hashCode() : _file.hashCode(); return null == _file ? super.hashCode() : _file.hashCode();
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
public void copyTo(File destination) public void copyTo(File destination)
throws IOException throws IOException
{ {
if (isDirectory()) if (isDirectory())
{ {
@ -424,7 +462,7 @@ public class FileResource extends Resource
{ {
try try
{ {
return new URL(_uri); return _uri.toURL();
} }
catch (MalformedURLException e) catch (MalformedURLException e)
{ {
@ -435,13 +473,12 @@ public class FileResource extends Resource
@Override @Override
public URI getURI() public URI getURI()
{ {
return _file.toURI(); return _uri;
} }
@Override @Override
public String toString() public String toString()
{ {
return _uri; return _uri.toString();
} }
} }

View File

@ -57,9 +57,24 @@ public class PathResource extends Resource
private final Path alias; private final Path alias;
private final URI uri; private final URI uri;
private static final Path checkAliasPath(final Path path) private final Path checkAliasPath()
{ {
Path abs = path; Path abs = path;
/* Catch situation where the Path class has already normalized
* the URI eg. input path "aa./foo.txt"
* from an #addPath(String) is normalized away during
* the creation of a Path object reference.
* If the URI is different then the Path.toUri() then
* we will just use the original URI to construct the
* alias reference Path.
*/
if(!URIUtil.equalsIgnoreEncodings(uri,path.toUri()))
{
return new File(uri).toPath().toAbsolutePath();
}
if (!abs.isAbsolute()) if (!abs.isAbsolute())
{ {
abs = path.toAbsolutePath(); abs = path.toAbsolutePath();
@ -168,7 +183,27 @@ public class PathResource extends Resource
this.path = path.toAbsolutePath(); this.path = path.toAbsolutePath();
assertValidPath(path); assertValidPath(path);
this.uri = this.path.toUri(); this.uri = this.path.toUri();
this.alias = checkAliasPath(path); this.alias = checkAliasPath();
}
/**
* Construct a new PathResource from a parent PathResource
* and child sub path
*
* @param parent the parent path resource
* @param childPath the child sub path
*/
private PathResource(PathResource parent, String childPath) throws MalformedURLException
{
// Calculate the URI and the path separately, so that any aliasing done by
// FileSystem.getPath(path,childPath) is visiable as a difference to the URI
// obtained via URIUtil.addDecodedPath(uri,childPath)
this.path = parent.path.getFileSystem().getPath(parent.path.toString(), childPath);
if (isDirectory() &&!childPath.endsWith("/"))
childPath+="/";
this.uri = URIUtil.addDecodedPath(parent.uri,childPath);
this.alias = checkAliasPath();
} }
/** /**
@ -212,7 +247,7 @@ public class PathResource extends Resource
this.path = path.toAbsolutePath(); this.path = path.toAbsolutePath();
this.uri = path.toUri(); this.uri = path.toUri();
this.alias = checkAliasPath(path); this.alias = checkAliasPath();
} }
/** /**
@ -245,7 +280,7 @@ public class PathResource extends Resource
String cpath = URIUtil.canonicalPath(subpath); String cpath = URIUtil.canonicalPath(subpath);
if ((cpath == null) || (cpath.length() == 0)) if ((cpath == null) || (cpath.length() == 0))
throw new MalformedURLException(); throw new MalformedURLException(subpath);
if ("/".equals(cpath)) if ("/".equals(cpath))
return this; return this;
@ -254,7 +289,8 @@ public class PathResource extends Resource
// compensate for input subpaths like "/subdir" // compensate for input subpaths like "/subdir"
// where default resolve behavior would be // where default resolve behavior would be
// to treat that like an absolute path // to treat that like an absolute path
return new PathResource(this.path.getFileSystem().getPath(path.toString(), subpath));
return new PathResource(this, subpath);
} }
private void assertValidPath(Path path) private void assertValidPath(Path path)
@ -434,6 +470,10 @@ public class PathResource extends Resource
/** /**
* The Alias as a Path. * The Alias as a Path.
* <p>
* Note: this cannot return the alias as a DIFFERENT path in 100% of situations,
* due to Java's internal Path/File normalization.
* </p>
* *
* @return the alias as a path. * @return the alias as a path.
*/ */

View File

@ -18,9 +18,17 @@
package org.eclipse.jetty.util.resource; package org.eclipse.jetty.util.resource;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*; import static org.hamcrest.Matchers.is;
import static org.junit.Assume.*; import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeThat;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
@ -54,7 +62,6 @@ import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.BaseMatcher; import org.hamcrest.BaseMatcher;
import org.hamcrest.Description; import org.hamcrest.Description;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -274,12 +281,15 @@ public class FileSystemResourceTest
{ {
for (Path entry : dir) for (Path entry : dir)
{ {
if (Files.isDirectory(entry) && !Files.isHidden(entry)) if (Files.isDirectory(entry) && !Files.isHidden(entry) && !entry.getFileName().toString().contains("$"))
{ {
return entry.toAbsolutePath().toString(); return entry.toAbsolutePath().toString();
} }
} }
} }
catch(Exception e)
{
}
} }
return null; return null;
@ -1170,6 +1180,179 @@ public class FileSystemResourceTest
} }
} }
@Test
public void testAddPath_WindowsSlash() throws Exception
{
Path dir = testdir.getPath().normalize().toRealPath();
Files.createDirectories(dir);
Path basePath = dir.resolve("base");
FS.ensureDirExists(basePath);
Path dirPath = basePath.resolve("aa");
FS.ensureDirExists(dirPath);
Path filePath = dirPath.resolve("foo.txt");
Files.createFile(filePath);
try (Resource base = newResource(basePath.toFile()))
{
assertThat("Exists: " + basePath,base.exists(),is(true));
assertThat("Alias: " + basePath,base,hasNoAlias());
Resource r = base.addPath("aa\\/foo.txt");
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa%5C/foo.txt"));
if(OS.IS_WINDOWS)
{
assertThat("isAlias()", r.isAlias(), is(true));
assertThat("getAlias()", r.getAlias(), notNullValue());
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
assertThat("Exists: " + r, r.exists(), is(true));
}
else
{
assertThat("isAlias()", r.isAlias(), is(false));
assertThat("Exists: " + r, r.exists(), is(false));
}
}
catch (InvalidPathException e)
{
// Exception is acceptable
}
}
@Test
public void testAddPath_WindowsExtensionLess() throws Exception
{
Path dir = testdir.getPath().normalize().toRealPath();
Files.createDirectories(dir);
Path basePath = dir.resolve("base");
FS.ensureDirExists(basePath);
Path dirPath = basePath.resolve("aa");
FS.ensureDirExists(dirPath);
Path filePath = dirPath.resolve("foo.txt");
Files.createFile(filePath);
try (Resource base = newResource(basePath.toFile()))
{
assertThat("Exists: " + basePath,base.exists(),is(true));
assertThat("Alias: " + basePath,base,hasNoAlias());
Resource r = base.addPath("aa./foo.txt");
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa./foo.txt"));
if(OS.IS_WINDOWS)
{
assertThat("isAlias()", r.isAlias(), is(true));
assertThat("getAlias()", r.getAlias(), notNullValue());
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
assertThat("Exists: " + r, r.exists(), is(true));
}
else
{
assertThat("isAlias()", r.isAlias(), is(false));
assertThat("Exists: " + r, r.exists(), is(false));
}
}
catch (InvalidPathException e)
{
// Exception is acceptable
}
}
@Test
public void testAddInitialSlash() throws Exception
{
Path dir = testdir.getPath().normalize().toRealPath();
Files.createDirectories(dir);
Path basePath = dir.resolve("base");
FS.ensureDirExists(basePath);
Path filePath = basePath.resolve("foo.txt");
Files.createFile(filePath);
try (Resource base = newResource(basePath.toFile()))
{
assertThat("Exists: " + basePath,base.exists(),is(true));
assertThat("Alias: " + basePath,base,hasNoAlias());
Resource r = base.addPath("/foo.txt");
assertThat("getURI()", r.getURI().toASCIIString(), containsString("/foo.txt"));
assertThat("isAlias()", r.isAlias(), is(false));
assertThat("Exists: " + r, r.exists(), is(true));
}
catch (InvalidPathException e)
{
// Exception is acceptable
}
}
@Test
public void testAddInitialDoubleSlash() throws Exception
{
Path dir = testdir.getPath().normalize().toRealPath();
Files.createDirectories(dir);
Path basePath = dir.resolve("base");
FS.ensureDirExists(basePath);
Path filePath = basePath.resolve("foo.txt");
Files.createFile(filePath);
try (Resource base = newResource(basePath.toFile()))
{
assertThat("Exists: " + basePath,base.exists(),is(true));
assertThat("Alias: " + basePath,base,hasNoAlias());
Resource r = base.addPath("//foo.txt");
assertThat("getURI()", r.getURI().toASCIIString(), containsString("//foo.txt"));
assertThat("isAlias()", r.isAlias(), is(true));
assertThat("getAlias()", r.getAlias(), notNullValue());
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("/foo.txt"));
assertThat("Exists: " + r, r.exists(), is(true));
}
catch (InvalidPathException e)
{
// Exception is acceptable
}
}
@Test
public void testAddDoubleSlash() throws Exception
{
Path dir = testdir.getPath().normalize().toRealPath();
Files.createDirectories(dir);
Path basePath = dir.resolve("base");
FS.ensureDirExists(basePath);
Path dirPath = basePath.resolve("aa");
FS.ensureDirExists(dirPath);
Path filePath = dirPath.resolve("foo.txt");
Files.createFile(filePath);
try (Resource base = newResource(basePath.toFile()))
{
assertThat("Exists: " + basePath,base.exists(),is(true));
assertThat("Alias: " + basePath,base,hasNoAlias());
Resource r = base.addPath("aa//foo.txt");
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa//foo.txt"));
assertThat("isAlias()", r.isAlias(), is(true));
assertThat("getAlias()", r.getAlias(), notNullValue());
assertThat("getAlias()", r.getAlias().toASCIIString(), containsString("aa/foo.txt"));
assertThat("Exists: " + r, r.exists(), is(true));
}
catch (InvalidPathException e)
{
// Exception is acceptable
}
}
@Test @Test
public void testEncoding() throws Exception public void testEncoding() throws Exception
{ {