Jetty 12 - Simplification of aliases in `PathResource` (Take 2) (#8734)
* simplify the PathResource.resolveTargetPath code * changes to how PathResource handles aliases * fix usages of Resource.getTargetUri() * fixes for FileSystemResourceTest * update javadoc for Resource.getTargetURI() * rename getTargetURI to getCanonicalURI * let resolveCanonicalPath return null if resource does not exist * add test in PathResourceTest for broken symlinks * some changes from review + optimization for exists() * restore name to getTargetUri in Resource * fix some tests related to PathResource changes * revert changes to PathResource equals and hashcode * also compare URI in PathResource * checkAlias to resolveAlias * PathResource cleanup + Adding comments about class fields. + Removing normalization from input/output/comparison flows. + Collapsing `resolveTargetPath` into `resolveAlias` to react accordingly to the exceptions that can flow out of Path.toRealPath(). + Failure on Path.toRealPath() is never an alias, as the resource cannot ever be served anyway. + More comments in `resolveAlias()` + Failed / Bad / Nonexistent / Inaccessible resources are not aliases to anything. * Renames of targetPath/targetUri `targetPath` to `realPath` `targetURI` to `realURI` * Cleanup alias/aliasResolved booleans * More testcase cleanup around not-exist * Don't resolve alias on Error during toRealPath * Add test to check how Alias check behaves if non-existent resource exists later Signed-off-by: Lachlan Roberts <lachlan@webtide.com> Co-authored-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
6ba81ce10d
commit
9061348ec4
|
@ -867,7 +867,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
if (resource.isAlias())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: {} -> {}", resource, resource.getTargetURI());
|
||||
LOG.debug("Aliased resource: {} -> {}", resource, resource.getRealURI());
|
||||
|
||||
// alias checks
|
||||
for (AliasCheck check : _aliasChecks)
|
||||
|
|
|
@ -344,7 +344,7 @@ public class ContextHandlerGetResourceTest
|
|||
|
||||
Resource resource = context.getResource(path);
|
||||
assertNotNull(resource);
|
||||
assertEquals(context.getResource("/subdir/TextFile.Long.txt").getURI(), resource.getTargetURI());
|
||||
assertEquals(context.getResource("/subdir/TextFile.Long.txt").getURI(), resource.getRealURI());
|
||||
|
||||
URL url = context.getServletContext().getResource(path);
|
||||
assertNotNull(url);
|
||||
|
|
|
@ -47,10 +47,14 @@ public class PathResource extends Resource
|
|||
.with("jrt")
|
||||
.build();
|
||||
|
||||
// The path object represented by this instance
|
||||
private final Path path;
|
||||
// The as-requested URI for this path object
|
||||
private final URI uri;
|
||||
private boolean targetResolved = false;
|
||||
private Path targetPath;
|
||||
// True / False to indicate if this is an alias of something else, or null if the alias hasn't been resolved
|
||||
private Boolean alias;
|
||||
// The Path representing the real-path of this PathResource instance. (populated during alias checking)
|
||||
private Path realPath;
|
||||
|
||||
/**
|
||||
* Test if the paths are the same name.
|
||||
|
@ -142,9 +146,7 @@ public class PathResource extends Resource
|
|||
|
||||
PathResource(URI uri, boolean bypassAllowedSchemeCheck)
|
||||
{
|
||||
// normalize to referenced location, Paths.get() doesn't like "/bar/../foo/text.txt" style references
|
||||
// and will return a Path that will not be found with `Files.exists()` or `Files.isDirectory()`
|
||||
this(Paths.get(uri.normalize()), uri, bypassAllowedSchemeCheck);
|
||||
this(Paths.get(uri), uri, bypassAllowedSchemeCheck);
|
||||
}
|
||||
|
||||
PathResource(Path path)
|
||||
|
@ -152,6 +154,13 @@ public class PathResource extends Resource
|
|||
this(path, path.toUri(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PathResource.
|
||||
*
|
||||
* @param path the Path object
|
||||
* @param uri the as-requested URI for the resource
|
||||
* @param bypassAllowedSchemeCheck true to bypass the allowed schemes check
|
||||
*/
|
||||
PathResource(Path path, URI uri, boolean bypassAllowedSchemeCheck)
|
||||
{
|
||||
if (!uri.isAbsolute())
|
||||
|
@ -159,9 +168,12 @@ public class PathResource extends Resource
|
|||
if (!bypassAllowedSchemeCheck && !ALLOWED_SCHEMES.contains(uri.getScheme()))
|
||||
throw new IllegalArgumentException("not an allowed scheme: " + uri);
|
||||
|
||||
if (Files.isDirectory(path))
|
||||
{
|
||||
String uriString = uri.toASCIIString();
|
||||
if (Files.isDirectory(path) && !uriString.endsWith(URIUtil.SLASH))
|
||||
if (!uriString.endsWith(URIUtil.SLASH))
|
||||
uri = URIUtil.correctFileURI(URI.create(uriString + URIUtil.SLASH));
|
||||
}
|
||||
|
||||
this.path = path;
|
||||
this.uri = uri;
|
||||
|
@ -170,36 +182,38 @@ public class PathResource extends Resource
|
|||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
return Files.exists(targetPath != null ? targetPath : path);
|
||||
if (alias == null)
|
||||
{
|
||||
// no alias check performed
|
||||
return Files.exists(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (realPath == null)
|
||||
return false;
|
||||
return Files.exists(realPath);
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
return Objects.equals(path, other.path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Path} of the resource
|
||||
*/
|
||||
public Path getPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
public Path getRealPath()
|
||||
{
|
||||
resolveAlias();
|
||||
return realPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getRealURI()
|
||||
{
|
||||
Path realPath = getRealPath();
|
||||
return (realPath == null) ? null : realPath.toUri();
|
||||
}
|
||||
|
||||
public List<Resource> list()
|
||||
{
|
||||
if (!isDirectory())
|
||||
|
@ -220,6 +234,13 @@ public class PathResource extends Resource
|
|||
return List.of(); // empty
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlias()
|
||||
{
|
||||
resolveAlias();
|
||||
return alias != null && alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
|
@ -241,12 +262,6 @@ public class PathResource extends Resource
|
|||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isContainedIn(Resource r)
|
||||
{
|
||||
|
@ -255,88 +270,141 @@ public class PathResource extends Resource
|
|||
return r.getClass() == PathResource.class && path.startsWith(r.getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getTargetURI()
|
||||
{
|
||||
if (!targetResolved)
|
||||
{
|
||||
targetPath = resolveTargetPath();
|
||||
targetResolved = true;
|
||||
}
|
||||
if (targetPath == null)
|
||||
return null;
|
||||
return targetPath.toUri();
|
||||
}
|
||||
|
||||
private Path resolveTargetPath()
|
||||
{
|
||||
Path abs = path;
|
||||
|
||||
// TODO: is this a valid shortcut?
|
||||
// If the path doesn't exist, then there's no alias to reference
|
||||
if (!Files.exists(path))
|
||||
return null;
|
||||
|
||||
/* Catch situation where the Path class has already normalized
|
||||
* the URI eg. input path "aa./foo.txt"
|
||||
* from an #resolve(String) is normalized away during
|
||||
* the creation of a Path object reference.
|
||||
* If the URI is different from the Path.toUri() then
|
||||
* we will just use the original URI to construct the
|
||||
* alias reference Path.
|
||||
/**
|
||||
* <p>
|
||||
* Perform a check of the original Path and as-requested URI to determine
|
||||
* if this resource is an alias to another name/location.
|
||||
* </p>
|
||||
*
|
||||
* We use the method `toUri(Path)` here, instead of
|
||||
* Path.toUri() to ensure that the path contains
|
||||
* a trailing slash if it's a directory, (something
|
||||
* not all FileSystems seem to support)
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>path</th>
|
||||
* <th>realPath</th>
|
||||
* <th>uri-as-requested</th>
|
||||
* <th>uri-from-realPath</th>
|
||||
* <th>alias</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td><code>C:/temp/aa./foo.txt</code></td>
|
||||
* <td><code>C:/temp/aa/foo.txt</code></td>
|
||||
* <td><code>file:///C:/temp/aa./foo.txt</code></td>
|
||||
* <td><code>file:///C:/temp/aa./foo.txt</code></td>
|
||||
* <td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/tmp/foo-symlink</code></td>
|
||||
* <td><code>/tmp/bar.txt</code></td>
|
||||
* <td><code>file:///tmp/foo-symlink</code></td>
|
||||
* <td><code>file:///tmp/bar.txt</code></td>
|
||||
* <td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>C:/temp/aa.txt</code></td>
|
||||
* <td><code>C:/temp/AA.txt</code></td>
|
||||
* <td><code>file:///C:/temp/aa.txt</code></td>
|
||||
* <td><code>file:///C:/temp/AA.txt</code></td>
|
||||
* <td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/tmp/bar-exists/../foo.txt</code></td>
|
||||
* <td><code>/tmp/foo.txt</code></td>
|
||||
* <td><code>file:///tmp/bar-exists/../foo.txt</code></td>
|
||||
* <td><code>file:///tmp/foo.txt</code></td>
|
||||
* <td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/tmp/doesnt-exist.txt</code></td>
|
||||
* <td>null (does not exist)</td>
|
||||
* <td><code>file:///tmp/doesnt-exist.txt</code></td>
|
||||
* <td>null (does not exist)</td>
|
||||
* <td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/tmp/doesnt-exist/../foo.txt</code></td>
|
||||
* <td>null (intermediate does not exist)</td>
|
||||
* <td><code>file:///tmp/doesnt-exist/../foo.txt</code></td>
|
||||
* <td>null (intermediate does not exist)</td>
|
||||
* <td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/var/protected/config.xml</code></td>
|
||||
* <td>null (no permissions)</td>
|
||||
* <td><code>file:///var/protected/config.xml</code></td>
|
||||
* <td>null (no permission)</td>
|
||||
* <td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>/tmp/foo-symlink</code></td>
|
||||
* <td>null (broken symlink, doesn't point to anything)</td>
|
||||
* <td><code>file:///tmp/foo-symlink</code></td>
|
||||
* <td>null (broken symlink, doesn't point to anything)</td>
|
||||
* <td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><code>C:/temp/cannot:be:referenced</code></td>
|
||||
* <td>null (illegal filename)</td>
|
||||
* <td><code>file:///C:/temp/cannot:be:referenced</code></td>
|
||||
* <td>null (illegal filename)</td>
|
||||
* <td>false</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*/
|
||||
if (!URIUtil.equalsIgnoreEncodings(uri, toUri(path)))
|
||||
private void resolveAlias()
|
||||
{
|
||||
if (alias == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use normalized path to get past navigational references like "/bar/../foo/test.txt"
|
||||
Path ref = Paths.get(uri.normalize());
|
||||
return ref.toRealPath();
|
||||
}
|
||||
catch (IOException ioe)
|
||||
{
|
||||
// If the toRealPath() call fails, then let
|
||||
// the alias checking routines continue on
|
||||
// to other techniques.
|
||||
LOG.trace("IGNORED", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
if (!abs.isAbsolute())
|
||||
abs = path.toAbsolutePath();
|
||||
|
||||
// Any normalization difference means it's an alias,
|
||||
// and we don't want to bother further to follow
|
||||
// symlinks as it's an alias anyway.
|
||||
Path normal = path.normalize();
|
||||
if (!isSameName(abs, normal))
|
||||
return normal;
|
||||
|
||||
try
|
||||
{
|
||||
if (Files.isSymbolicLink(path))
|
||||
return path.getParent().resolve(Files.readSymbolicLink(path));
|
||||
if (Files.exists(path))
|
||||
{
|
||||
Path real = abs.toRealPath();
|
||||
if (!isSameName(abs, real))
|
||||
return real;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
// Default behavior is to follow symlinks.
|
||||
// We don't want to use the NO_FOLLOW_LINKS parameter as that takes this call from
|
||||
// being filesystem aware, and using FileSystem specific techniques to find
|
||||
// the real file, to being done in-API (which doesn't work reliably on
|
||||
// filesystems that have different names for the same file.
|
||||
// eg: case-insensitive file systems, unicode name normalization,
|
||||
// alternate names, etc)
|
||||
// We also don't want to use Path.normalize() here as that eliminates
|
||||
// the knowledge of what directories are being navigated through.
|
||||
realPath = path.toRealPath();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e instanceof IOException)
|
||||
LOG.trace("IGNORED", e);
|
||||
else
|
||||
LOG.warn("bad alias ({} {}) for {}", e.getClass().getName(), e.getMessage(), path);
|
||||
// Not possible to serve this resource.
|
||||
// - This resource doesn't exist.
|
||||
// - No access rights to this resource.
|
||||
// - Unable to read the file or directory.
|
||||
// - Navigation segments (eg: "foo/../test.txt") would go through something that doesn't exist, or not accessible.
|
||||
// - FileSystem doesn't support toRealPath.
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the path and realPath are the same, also check
|
||||
* The as-requested URI as it will represent what was
|
||||
* URI created this PathResource.
|
||||
* e.g. the input of `resolve("aa./foo.txt")
|
||||
* on windows would resolve the path, but the Path.toUri() would
|
||||
* not always show this extension-less access.
|
||||
* The as-requested URI will retain this extra '.' and be used
|
||||
* to evaluate if the realPath.toUri() is the same as the as-requested URI.
|
||||
*
|
||||
* // On Windows
|
||||
* PathResource resource = PathResource("C:/temp");
|
||||
* PathResource child = resource.resolve("aa./foo.txt");
|
||||
* child.exists() == true
|
||||
* child.isAlias() == true
|
||||
* child.toUri() == "file:///C:/temp/aa./foo.txt"
|
||||
* child.getPath().toUri() == "file:///C:/temp/aa/foo.txt"
|
||||
* child.getRealURI() == "file:///C:/temp/aa/foo.txt"
|
||||
*/
|
||||
alias = !isSameName(path, realPath) || !Objects.equals(uri, toUri(realPath));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -373,6 +441,25 @@ public class PathResource extends Resource
|
|||
return pathUri;
|
||||
}
|
||||
|
||||
@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;
|
||||
return Objects.equals(path, other.path) && Objects.equals(uri, other.uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(path, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -297,19 +297,20 @@ public abstract class Resource implements Iterable<Resource>
|
|||
*/
|
||||
public boolean isAlias()
|
||||
{
|
||||
return getTargetURI() != null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this Resource is an alias pointing to a different location,
|
||||
* return the target location as URI.
|
||||
* <p>The real URI of the resource.</p>
|
||||
* <p>If this Resource is an alias, ({@link #isAlias()}), this
|
||||
* URI will be different from {@link #getURI()}, and will point to the real name/location
|
||||
* of the Resource.</p>
|
||||
*
|
||||
* @return The target URI location of this resource,
|
||||
* or null if there is no target URI location (eg: not an alias, or a symlink)
|
||||
* @return The real URI location of this resource.
|
||||
*/
|
||||
public URI getTargetURI()
|
||||
public URI getRealURI()
|
||||
{
|
||||
return null;
|
||||
return getURI();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.util.ssl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -48,16 +47,15 @@ public class KeyStoreScanner extends ContainerLifeCycle implements Scanner.Discr
|
|||
{
|
||||
this.sslContextFactory = sslContextFactory;
|
||||
Resource keystoreResource = sslContextFactory.getKeyStoreResource();
|
||||
Path monitoredFile = keystoreResource.getPath();
|
||||
if (monitoredFile == null || !Files.exists(monitoredFile))
|
||||
if (!keystoreResource.exists())
|
||||
throw new IllegalArgumentException("keystore file does not exist");
|
||||
if (Files.isDirectory(monitoredFile))
|
||||
if (keystoreResource.isDirectory())
|
||||
throw new IllegalArgumentException("expected keystore file not directory");
|
||||
|
||||
// Use real location of keystore (if different), so that change monitoring can work properly
|
||||
URI targetURI = keystoreResource.getTargetURI();
|
||||
if (targetURI != null)
|
||||
monitoredFile = Paths.get(targetURI);
|
||||
Path monitoredFile = keystoreResource.getPath();
|
||||
if (keystoreResource.isAlias())
|
||||
monitoredFile = Paths.get(keystoreResource.getRealURI());
|
||||
|
||||
keystoreFile = monitoredFile;
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -55,13 +55,15 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
@ -86,32 +88,55 @@ public class FileSystemResourceTest
|
|||
assertThat(FileSystemPool.INSTANCE.mounts(), empty());
|
||||
}
|
||||
|
||||
private Matcher<Resource> hasNoTargetURI()
|
||||
private Matcher<Resource> isAlias()
|
||||
{
|
||||
return new BaseMatcher<>()
|
||||
{
|
||||
@Override
|
||||
public boolean matches(Object item)
|
||||
{
|
||||
final Resource res = (Resource)item;
|
||||
return res.getTargetURI() == null;
|
||||
return ((Resource)item).isAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description)
|
||||
{
|
||||
description.appendText("getTargetURI should return null");
|
||||
description.appendText("isAlias() should return true");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatch(Object item, Description description)
|
||||
{
|
||||
description.appendText("was ").appendValue(((Resource)item).getTargetURI());
|
||||
description.appendText("was ").appendValue(((Resource)item).getRealURI());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Matcher<Resource> isTargetFor(final Resource resource)
|
||||
private Matcher<Resource> isNotAlias()
|
||||
{
|
||||
return new BaseMatcher<>()
|
||||
{
|
||||
@Override
|
||||
public boolean matches(Object item)
|
||||
{
|
||||
return !((Resource)item).isAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description)
|
||||
{
|
||||
description.appendText("isAlias() should return false");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatch(Object item, Description description)
|
||||
{
|
||||
description.appendText("was ").appendValue(((Resource)item).getRealURI());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Matcher<Resource> isRealResourceFor(final Resource resource)
|
||||
{
|
||||
return new BaseMatcher<>()
|
||||
{
|
||||
|
@ -119,27 +144,19 @@ public class FileSystemResourceTest
|
|||
public boolean matches(Object item)
|
||||
{
|
||||
final Resource ritem = (Resource)item;
|
||||
final URI alias = ritem.getTargetURI();
|
||||
if (alias == null)
|
||||
{
|
||||
return resource.getTargetURI() == null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return alias.equals(resource.getURI());
|
||||
}
|
||||
return ritem.getRealURI().equals(resource.getRealURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description)
|
||||
{
|
||||
description.appendText("getTargetURI should return ").appendValue(resource.getURI());
|
||||
description.appendText("getRealURI should return ").appendValue(resource.getURI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeMismatch(Object item, Description description)
|
||||
{
|
||||
description.appendText("was ").appendValue(((Resource)item).getTargetURI());
|
||||
description.appendText("was ").appendValue(((Resource)item).getRealURI());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -596,13 +613,13 @@ public class FileSystemResourceTest
|
|||
// Access to the same resource, but via a symlink means that they are not equivalent
|
||||
assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
|
||||
|
||||
assertThat("resource.targetURI", resFoo, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resFoo.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resFoo.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resFoo, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resFoo.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resFoo.getPath()), isNotAlias());
|
||||
|
||||
assertThat("targetURI", resBar, isTargetFor(resFoo));
|
||||
assertThat("uri.targetURI", ResourceFactory.root().newResource(resBar.getURI()), isTargetFor(resFoo));
|
||||
assertThat("file.targetURI", ResourceFactory.root().newResource(resBar.getPath()), isTargetFor(resFoo));
|
||||
assertThat("targetURI", resBar, isRealResourceFor(resFoo));
|
||||
assertThat("uri.targetURI", ResourceFactory.root().newResource(resBar.getURI()), isRealResourceFor(resFoo));
|
||||
assertThat("file.targetURI", ResourceFactory.root().newResource(resBar.getPath()), isRealResourceFor(resFoo));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -618,6 +635,7 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
Files.createSymbolicLink(bar, foo);
|
||||
// Now a "bar" symlink exists, pointing to a "foo" that doesn't exist
|
||||
symlinkSupported = true;
|
||||
}
|
||||
catch (UnsupportedOperationException | FileSystemException e)
|
||||
|
@ -636,13 +654,20 @@ public class FileSystemResourceTest
|
|||
// Access to the same resource, but via a symlink means that they are not equivalent
|
||||
assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
|
||||
|
||||
assertThat("resource.targetURI", resFoo, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resFoo.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resFoo.getPath()), hasNoTargetURI());
|
||||
// This is not an alias because the file does not exist.
|
||||
assertFalse(resFoo.exists());
|
||||
assertFalse(Files.exists(resFoo.getPath()));
|
||||
assertFalse(resFoo.isAlias());
|
||||
assertNotNull(resFoo.getURI());
|
||||
assertNull(resFoo.getRealURI());
|
||||
|
||||
assertThat("targetURI", resBar, isTargetFor(resFoo));
|
||||
assertThat("uri.targetURI", ResourceFactory.root().newResource(resBar.getURI()), isTargetFor(resFoo));
|
||||
assertThat("file.targetURI", ResourceFactory.root().newResource(resBar.getPath()), isTargetFor(resFoo));
|
||||
// This is alias because the target file does not exist even though the symlink file does exist.
|
||||
// This resource cannot be served, so it should not exist, nor have an alias
|
||||
assertFalse(resBar.exists());
|
||||
assertFalse(Files.exists(resBar.getPath()));
|
||||
assertFalse(resBar.isAlias());
|
||||
assertNotNull(resBar.getURI());
|
||||
assertNull(resBar.getRealURI());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -657,9 +682,9 @@ public class FileSystemResourceTest
|
|||
// Reference to actual resource that exists
|
||||
Resource resource = base.resolve("file");
|
||||
|
||||
assertThat("resource.targetURI", resource, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resource, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), isNotAlias());
|
||||
|
||||
// On some case-insensitive file systems, lets see if an alternate
|
||||
// case for the filename results in an alias reference
|
||||
|
@ -667,9 +692,9 @@ public class FileSystemResourceTest
|
|||
if (alias.exists())
|
||||
{
|
||||
// If it exists, it must be an alias
|
||||
assertThat("targetURI", alias, isTargetFor(resource));
|
||||
assertThat("targetURI.uri", ResourceFactory.root().newResource(alias.getURI()), isTargetFor(resource));
|
||||
assertThat("targetURI.file", ResourceFactory.root().newResource(alias.getPath()), isTargetFor(resource));
|
||||
assertThat("targetURI", alias, isRealResourceFor(resource));
|
||||
assertThat("targetURI.uri", ResourceFactory.root().newResource(alias.getURI()), isRealResourceFor(resource));
|
||||
assertThat("targetURI.file", ResourceFactory.root().newResource(alias.getPath()), isRealResourceFor(resource));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -695,9 +720,9 @@ public class FileSystemResourceTest
|
|||
// Long filename
|
||||
Resource resource = base.resolve("TextFile.Long.txt");
|
||||
|
||||
assertThat("resource.targetURI", resource, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resource, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), isNotAlias());
|
||||
|
||||
// On some versions of Windows, the long filename can be referenced
|
||||
// via a short 8.3 equivalent filename.
|
||||
|
@ -705,9 +730,9 @@ public class FileSystemResourceTest
|
|||
if (alias.exists())
|
||||
{
|
||||
// If it exists, it must be an alias
|
||||
assertThat("targetURI", alias, isTargetFor(resource));
|
||||
assertThat("targetURI.uri", ResourceFactory.root().newResource(alias.getURI()), isTargetFor(resource));
|
||||
assertThat("targetURI.file", ResourceFactory.root().newResource(alias.getPath()), isTargetFor(resource));
|
||||
assertThat("targetURI", alias, isRealResourceFor(resource));
|
||||
assertThat("targetURI.uri", ResourceFactory.root().newResource(alias.getURI()), isRealResourceFor(resource));
|
||||
assertThat("targetURI.file", ResourceFactory.root().newResource(alias.getPath()), isRealResourceFor(resource));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,9 +756,9 @@ public class FileSystemResourceTest
|
|||
Resource base = ResourceFactory.root().newResource(dir);
|
||||
Resource resource = base.resolve("testfile");
|
||||
|
||||
assertThat("resource.targetURI", resource, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resource, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), isNotAlias());
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -742,9 +767,9 @@ public class FileSystemResourceTest
|
|||
if (alias.exists())
|
||||
{
|
||||
// If it exists, it must be an alias
|
||||
assertThat("resource.targetURI", alias, isTargetFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isTargetFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isTargetFor(resource));
|
||||
assertThat("resource.targetURI", alias, isRealResourceFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isRealResourceFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isRealResourceFor(resource));
|
||||
}
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
|
@ -773,9 +798,9 @@ public class FileSystemResourceTest
|
|||
Resource base = ResourceFactory.root().newResource(dir);
|
||||
Resource resource = base.resolve("testfile");
|
||||
|
||||
assertThat("resource.targetURI", resource, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resource, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), isNotAlias());
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -786,9 +811,9 @@ public class FileSystemResourceTest
|
|||
assumeTrue(alias.getURI().getScheme().equals("file"));
|
||||
|
||||
// If it exists, it must be an alias
|
||||
assertThat("resource.targetURI", alias, isTargetFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isTargetFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isTargetFor(resource));
|
||||
assertThat("resource.targetURI", alias, isRealResourceFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isRealResourceFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isRealResourceFor(resource));
|
||||
}
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
|
@ -817,9 +842,9 @@ public class FileSystemResourceTest
|
|||
Resource base = ResourceFactory.root().newResource(dir);
|
||||
Resource resource = base.resolve("testfile");
|
||||
|
||||
assertThat("resource.targetURI", resource, hasNoTargetURI());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), hasNoTargetURI());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), hasNoTargetURI());
|
||||
assertThat("resource.targetURI", resource, isNotAlias());
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(resource.getURI()), isNotAlias());
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(resource.getPath()), isNotAlias());
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -828,9 +853,9 @@ public class FileSystemResourceTest
|
|||
if (alias.exists())
|
||||
{
|
||||
// If it exists, it must be an alias
|
||||
assertThat("resource.targetURI", alias, isTargetFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isTargetFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isTargetFor(resource));
|
||||
assertThat("resource.targetURI", alias, isRealResourceFor(resource));
|
||||
assertThat("resource.uri.targetURI", ResourceFactory.root().newResource(alias.getURI()), isRealResourceFor(resource));
|
||||
assertThat("resource.file.targetURI", ResourceFactory.root().newResource(alias.getPath()), isRealResourceFor(resource));
|
||||
}
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
|
@ -857,7 +882,7 @@ public class FileSystemResourceTest
|
|||
|
||||
Resource base = ResourceFactory.root().newResource(dir);
|
||||
Resource res = base.resolve("foo;");
|
||||
assertThat("Target URI: " + res, res, hasNoTargetURI());
|
||||
assertThat("Target URI: " + res, res, isNotAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1052,25 +1077,25 @@ public class FileSystemResourceTest
|
|||
|
||||
Resource fileres = ResourceFactory.root().newResource(refQuoted);
|
||||
assertThat("Exists: " + refQuoted, fileres.exists(), is(true));
|
||||
assertThat("Target URI: " + refQuoted, fileres, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + refQuoted, fileres, isNotAlias());
|
||||
|
||||
URI refEncoded = dir.toUri().resolve("foo%27s.txt");
|
||||
|
||||
fileres = ResourceFactory.root().newResource(refEncoded);
|
||||
assertThat("Exists: " + refEncoded, fileres.exists(), is(true));
|
||||
assertThat("Target URI: " + refEncoded, fileres, hasNoTargetURI());
|
||||
assertThat("Is Alias: " + refEncoded, fileres, isAlias());
|
||||
|
||||
URI refQuoteSpace = dir.toUri().resolve("f%20o's.txt");
|
||||
|
||||
fileres = ResourceFactory.root().newResource(refQuoteSpace);
|
||||
assertThat("Exists: " + refQuoteSpace, fileres.exists(), is(true));
|
||||
assertThat("Target URI: " + refQuoteSpace, fileres, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + refQuoteSpace, fileres, isNotAlias());
|
||||
|
||||
URI refEncodedSpace = dir.toUri().resolve("f%20o%27s.txt");
|
||||
|
||||
fileres = ResourceFactory.root().newResource(refEncodedSpace);
|
||||
assertThat("Exists: " + refEncodedSpace, fileres.exists(), is(true));
|
||||
assertThat("Target URI: " + refEncodedSpace, fileres, hasNoTargetURI());
|
||||
assertThat("Is Alias: " + refEncodedSpace, fileres, isAlias());
|
||||
|
||||
URI refA = dir.toUri().resolve("foo's.txt");
|
||||
URI refB = dir.toUri().resolve("foo%27s.txt");
|
||||
|
@ -1081,10 +1106,14 @@ public class FileSystemResourceTest
|
|||
"URI[b] = " + refB;
|
||||
assertThat(msg, refA.equals(refB), is(false));
|
||||
|
||||
// now show that Resource.equals() does work
|
||||
// These resources are not equal because they have different request URIs.
|
||||
Resource a = ResourceFactory.root().newResource(refA);
|
||||
Resource b = ResourceFactory.root().newResource(refB);
|
||||
assertThat("A.equals(B)", a.equals(b), is(true));
|
||||
assertThat("A.equals(B)", a.equals(b), is(false));
|
||||
assertThat(a.getPath(), equalTo(b.getPath()));
|
||||
assertThat(a.getRealURI(), equalTo(b.getRealURI()));
|
||||
assertFalse(a.isAlias());
|
||||
assertTrue(b.isAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1156,7 +1185,7 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
assertThat("Exists: " + basePath, base.exists(), is(true));
|
||||
assertThat("Target URI: " + basePath, base, hasNoTargetURI());
|
||||
assertThat("Target URI: " + basePath, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("aa%5C/foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa%5C/foo.txt"));
|
||||
|
@ -1165,8 +1194,8 @@ public class FileSystemResourceTest
|
|||
{
|
||||
assertThat("getPath().toString()", r.getPath().toString(), containsString("aa\\foo.txt"));
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getTargetURI()", r.getTargetURI(), notNullValue());
|
||||
assertThat("getTargetURI()", r.getTargetURI().toASCIIString(), containsString("aa/foo.txt"));
|
||||
assertThat("getRealURI()", r.getRealURI(), notNullValue());
|
||||
assertThat("getRealURI()", r.getRealURI().toASCIIString(), containsString("aa/foo.txt"));
|
||||
assertThat("Exists: " + r, r.exists(), is(true));
|
||||
}
|
||||
else
|
||||
|
@ -1200,7 +1229,7 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
assertThat("Exists: " + basePath, base.exists(), is(true));
|
||||
assertThat("Target URI: " + basePath, base, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + basePath, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("aa./foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa./foo.txt"));
|
||||
|
@ -1208,8 +1237,8 @@ public class FileSystemResourceTest
|
|||
if (WINDOWS.isCurrentOs())
|
||||
{
|
||||
assertThat("isAlias()", r.isAlias(), is(true));
|
||||
assertThat("getTargetURI()", r.getTargetURI(), notNullValue());
|
||||
assertThat("getTargetURI()", r.getTargetURI().toASCIIString(), containsString("aa/foo.txt"));
|
||||
assertThat("getRealURI()", r.getRealURI(), notNullValue());
|
||||
assertThat("getRealURI()", r.getRealURI().toASCIIString(), containsString("aa/foo.txt"));
|
||||
assertThat("Exists: " + r, r.exists(), is(true));
|
||||
}
|
||||
else
|
||||
|
@ -1240,7 +1269,7 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
assertThat("Exists: " + basePath, base.exists(), is(true));
|
||||
assertThat("Target URI: " + basePath, base, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + basePath, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("/foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("/foo.txt"));
|
||||
|
@ -1270,13 +1299,13 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
assertThat("Exists: " + basePath, base.exists(), is(true));
|
||||
assertThat("Target URI: " + basePath, base, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + basePath, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("//foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("/foo.txt"));
|
||||
|
||||
assertThat("isAlias()", r.isAlias(), is(false));
|
||||
assertThat("getTargetURI()", r.getTargetURI(), nullValue());
|
||||
assertThat("Is Not Alias: " + r.getPath(), r, isNotAlias());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
|
@ -1302,13 +1331,13 @@ public class FileSystemResourceTest
|
|||
try
|
||||
{
|
||||
assertThat("Exists: " + basePath, base.exists(), is(true));
|
||||
assertThat("Target URI: " + basePath, base, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + basePath, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("aa//foo.txt");
|
||||
assertThat("getURI()", r.getURI().toASCIIString(), containsString("aa/foo.txt"));
|
||||
|
||||
assertThat("isAlias()", r.isAlias(), is(false));
|
||||
assertThat("getTargetURI()", r.getTargetURI(), nullValue());
|
||||
assertThat("Is Not Alias: " + r.getPath(), r, isNotAlias());
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
|
@ -1356,11 +1385,11 @@ public class FileSystemResourceTest
|
|||
|
||||
Resource base = ResourceFactory.root().newResource(utf8Dir);
|
||||
assertThat("Exists: " + utf8Dir, base.exists(), is(true));
|
||||
assertThat("Target URI: " + utf8Dir, base, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + utf8Dir, base, isNotAlias());
|
||||
|
||||
Resource r = base.resolve("file.txt");
|
||||
assertThat("Exists: " + r, r.exists(), is(true));
|
||||
assertThat("Target URI: " + r, r, hasNoTargetURI());
|
||||
assertThat("Is Not Alias: " + r, r, isNotAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1371,7 +1400,7 @@ public class FileSystemResourceTest
|
|||
Resource resource = base.resolve("WEB-INF/");
|
||||
assertThat("getURI()", resource.getURI().toASCIIString(), containsString("path/WEB-INF/"));
|
||||
assertThat("isAlias()", resource.isAlias(), is(false));
|
||||
assertThat("getTargetURI()", resource.getTargetURI(), nullValue());
|
||||
assertThat("Is Not Alias: " + resource.getPath(), resource, isNotAlias());
|
||||
}
|
||||
|
||||
private String toString(Resource resource) throws IOException
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Map;
|
|||
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -38,6 +39,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
@ -303,37 +305,37 @@ public class PathResourceTest
|
|||
// Test not alias paths
|
||||
Resource resource = resourceFactory.newResource(file);
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toAbsolutePath());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toUri());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toUri().toString());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = archiveResource.resolve("test.txt");
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
|
||||
// Test alias paths
|
||||
resource = resourceFactory.newResource(file0);
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toAbsolutePath());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toUri());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toUri().toString());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
|
||||
resource = archiveResource.resolve("test.txt\0");
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
{
|
||||
|
@ -402,9 +404,9 @@ public class PathResourceTest
|
|||
assertThat("resource.uri.alias", resourceFactory.newResource(resFoo.getURI()).isAlias(), is(false));
|
||||
assertThat("resource.file.alias", resourceFactory.newResource(resFoo.getPath()).isAlias(), is(false));
|
||||
|
||||
assertThat("targetURI", resBar.getTargetURI(), is(resFoo.getURI()));
|
||||
assertThat("uri.targetURI", resourceFactory.newResource(resBar.getURI()).getTargetURI(), is(resFoo.getURI()));
|
||||
assertThat("file.targetURI", resourceFactory.newResource(resBar.getPath()).getTargetURI(), is(resFoo.getURI()));
|
||||
assertThat("targetURI", resBar.getRealURI(), is(resFoo.getURI()));
|
||||
assertThat("uri.targetURI", resourceFactory.newResource(resBar.getURI()).getRealURI(), is(resFoo.getURI()));
|
||||
assertThat("file.targetURI", resourceFactory.newResource(resBar.getPath()).getRealURI(), is(resFoo.getURI()));
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
{
|
||||
|
@ -413,6 +415,43 @@ public class PathResourceTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrokenSymlink(WorkDir workDir) throws Exception
|
||||
{
|
||||
Path testDir = workDir.getEmptyPathDir();
|
||||
Path resourcePath = testDir.resolve("resource.txt");
|
||||
IO.copy(MavenTestingUtils.getTestResourcePathFile("resource.txt").toFile(), resourcePath.toFile());
|
||||
Path symlinkPath = Files.createSymbolicLink(testDir.resolve("symlink.txt"), resourcePath);
|
||||
|
||||
PathResource fileResource = new PathResource(resourcePath);
|
||||
assertTrue(fileResource.exists());
|
||||
PathResource symlinkResource = new PathResource(symlinkPath);
|
||||
assertTrue(symlinkResource.exists());
|
||||
|
||||
// Their paths are not equal but not their canonical paths are.
|
||||
assertThat(fileResource.getPath(), not(equalTo(symlinkResource.getPath())));
|
||||
assertThat(fileResource.getPath(), equalTo(symlinkResource.getRealPath()));
|
||||
assertFalse(fileResource.isAlias());
|
||||
assertTrue(symlinkResource.isAlias());
|
||||
assertTrue(fileResource.exists());
|
||||
assertTrue(symlinkResource.exists());
|
||||
|
||||
// After deleting file the Resources do not exist even though symlink file exists.
|
||||
assumeTrue(Files.deleteIfExists(resourcePath));
|
||||
assertFalse(fileResource.exists());
|
||||
assertFalse(symlinkResource.exists());
|
||||
|
||||
// Re-create and test the resources now that the file has been deleted.
|
||||
fileResource = new PathResource(resourcePath);
|
||||
assertFalse(fileResource.exists());
|
||||
assertNull(fileResource.getRealPath());
|
||||
assertTrue(symlinkResource.isAlias());
|
||||
symlinkResource = new PathResource(symlinkPath);
|
||||
assertFalse(symlinkResource.exists());
|
||||
assertNull(symlinkResource.getRealPath());
|
||||
assertFalse(symlinkResource.isAlias());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNavigation(WorkDir workDir) throws Exception
|
||||
{
|
||||
|
@ -421,15 +460,22 @@ public class PathResourceTest
|
|||
Path dir = docroot.resolve("dir");
|
||||
Files.createDirectory(dir);
|
||||
|
||||
Path foo = docroot.resolve("foo");
|
||||
Files.createDirectory(foo);
|
||||
|
||||
Path testText = dir.resolve("test.txt");
|
||||
Files.createFile(testText);
|
||||
|
||||
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
|
||||
{
|
||||
Resource rootRes = resourceFactory.newResource(docroot);
|
||||
// This is the heart of the test, we should support this
|
||||
Resource fileRes = rootRes.resolve("bar/../dir/test.txt");
|
||||
assertTrue(fileRes.exists());
|
||||
// Test navigation through a directory that doesn't exist
|
||||
Resource fileResViaBar = rootRes.resolve("bar/../dir/test.txt");
|
||||
assertFalse(fileResViaBar.exists());
|
||||
|
||||
// Test navigation through a directory that does exist
|
||||
Resource fileResViaFoo = rootRes.resolve("foo/../dir/test.txt");
|
||||
assertTrue(fileResViaFoo.exists());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
|||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
|
@ -62,16 +60,34 @@ public class ResourceAliasTest
|
|||
@Test
|
||||
public void testAliasNavigation() throws IOException
|
||||
{
|
||||
Path baseDir = workDir.getEmptyPathDir();
|
||||
Path docroot = workDir.getEmptyPathDir();
|
||||
|
||||
Path foo = baseDir.resolve("foo");
|
||||
Files.createDirectories(foo);
|
||||
Files.writeString(foo.resolve("test.txt"), "Contents of test.txt", StandardCharsets.UTF_8);
|
||||
Path dir = docroot.resolve("dir");
|
||||
Files.createDirectory(dir);
|
||||
|
||||
Resource resource = ResourceFactory.root().newResource(baseDir);
|
||||
Resource test = resource.resolve("/bar/../foo/test.txt");
|
||||
assertTrue(test.exists(), "Should exist");
|
||||
assertTrue(test.isAlias(), "Should be an alias");
|
||||
Path foo = docroot.resolve("foo");
|
||||
Files.createDirectory(foo);
|
||||
|
||||
Path testText = dir.resolve("test.txt");
|
||||
Files.writeString(testText, "Contents of test.txt", StandardCharsets.UTF_8);
|
||||
|
||||
try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable())
|
||||
{
|
||||
Resource rootRes = resourceFactory.newResource(docroot);
|
||||
// Test navigation through a directory that doesn't exist
|
||||
Resource fileResViaBar = rootRes.resolve("bar/../dir/test.txt");
|
||||
assertFalse(fileResViaBar.exists(), "Should not exist");
|
||||
assertFalse(fileResViaBar.isAlias(), "Should not be an alias");
|
||||
|
||||
Files.createDirectory(docroot.resolve("bar"));
|
||||
assertTrue(fileResViaBar.exists(), "Should exist");
|
||||
assertTrue(fileResViaBar.isAlias(), "Should be an alias");
|
||||
|
||||
// Test navigation through a directory that does exist
|
||||
Resource fileResViaFoo = rootRes.resolve("foo/../dir/test.txt");
|
||||
assertTrue(fileResViaFoo.exists(), "Should exist");
|
||||
assertTrue(fileResViaFoo.isAlias(), "Should be an alias");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -132,37 +148,37 @@ public class ResourceAliasTest
|
|||
// Test not alias paths
|
||||
Resource resource = resourceFactory.newResource(file);
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toAbsolutePath());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toUri());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file.toUri().toString());
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
resource = dir.resolve("test.txt");
|
||||
assertTrue(resource.exists());
|
||||
assertNull(resource.getTargetURI());
|
||||
assertFalse(resource.isAlias());
|
||||
|
||||
// Test alias paths
|
||||
resource = resourceFactory.newResource(file0);
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toAbsolutePath());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toUri());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
resource = resourceFactory.newResource(file0.toUri().toString());
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
|
||||
resource = dir.resolve("test.txt\0");
|
||||
assertTrue(resource.exists());
|
||||
assertNotNull(resource.getTargetURI());
|
||||
assertTrue(resource.isAlias());
|
||||
}
|
||||
catch (InvalidPathException e)
|
||||
{
|
||||
|
|
|
@ -380,7 +380,7 @@ public class ResourceTest
|
|||
assertNotNull(dot);
|
||||
assertTrue(dot.exists());
|
||||
assertTrue(dot.isAlias(), "Reference to '.' is an alias to itself");
|
||||
assertTrue(Files.isSameFile(dot.getPath(), Paths.get(dot.getTargetURI())));
|
||||
assertTrue(Files.isSameFile(dot.getPath(), Paths.get(dot.getRealURI())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -402,12 +402,13 @@ public class ResourceTest
|
|||
FS.ensureDirExists(dir);
|
||||
Path file = dir.resolve("bar.txt");
|
||||
FS.touch(file);
|
||||
assertTrue(Files.exists(file));
|
||||
Resource resource = resourceFactory.newResource(file);
|
||||
Resource dot = resource.resolve(".");
|
||||
// We are now pointing to a resource at ".../testDotAliasFileExists/foo/bar.txt/."
|
||||
assertNotNull(dot);
|
||||
assertTrue(dot.exists());
|
||||
assertTrue(dot.isAlias(), "Reference to '.' is an alias to itself");
|
||||
assertTrue(Files.isSameFile(dot.getPath(), Paths.get(dot.getTargetURI())));
|
||||
assertFalse(dot.exists());
|
||||
assertFalse(dot.isAlias(), "Reference to '.' against a file is not an alias");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -420,9 +421,10 @@ public class ResourceTest
|
|||
assertFalse(Files.exists(file));
|
||||
Resource resource = resourceFactory.newResource(file);
|
||||
Resource dot = resource.resolve(".");
|
||||
// We are now pointing to a resource at ".../testDotAliasFileDoesNotExists/foo/bar.txt/."
|
||||
assertNotNull(dot);
|
||||
assertFalse(dot.exists());
|
||||
assertFalse(dot.isAlias(), "Reference to '.' is not an alias as file doesn't exist");
|
||||
assertFalse(dot.isAlias(), "Reference to '.' against a file is not an alias (the file also does not exist)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -74,7 +74,6 @@ import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
|
|||
import org.eclipse.jetty.ee10.servlet.security.SecurityHandler;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
|
@ -1081,7 +1080,7 @@ public class ServletContextHandler extends ContextHandler implements Graceful
|
|||
Resource baseResource = getBaseResource();
|
||||
if (baseResource != null && baseResource.isAlias())
|
||||
LOG.warn("BaseResource {} is aliased to {} in {}. May not be supported in future releases.",
|
||||
baseResource, baseResource.getTargetURI(), this);
|
||||
baseResource, baseResource.getRealURI(), this);
|
||||
|
||||
if (_logger == null)
|
||||
_logger = LoggerFactory.getLogger(ContextHandler.class.getName() + getLogNameSuffix());
|
||||
|
|
|
@ -603,6 +603,7 @@ public class MetaInfConfiguration extends AbstractConfiguration
|
|||
* @return the list of tlds found
|
||||
* @throws IOException if unable to scan the directory
|
||||
*/
|
||||
// TODO: Needs to use resource.
|
||||
public Collection<URL> getTlds(Path dir) throws IOException
|
||||
{
|
||||
if (dir == null || !Files.isDirectory(dir))
|
||||
|
|
|
@ -292,14 +292,14 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
throw new IllegalStateException("No resourceBase or war set for context");
|
||||
|
||||
// Use real location (if different) for WAR file, so that change/modification monitoring can work.
|
||||
URI targetURI = webApp.getTargetURI();
|
||||
if (targetURI != null)
|
||||
if (webApp.isAlias())
|
||||
{
|
||||
URI realURI = webApp.getRealURI();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} anti-aliased to {}", webApp, targetURI);
|
||||
Resource targetWebApp = context.newResource(targetURI);
|
||||
if (targetWebApp != null && targetWebApp.exists())
|
||||
webApp = targetWebApp;
|
||||
LOG.debug("{} anti-aliased to {}", webApp, realURI);
|
||||
Resource realWebApp = context.newResource(realURI);
|
||||
if (realWebApp != null && realWebApp.exists())
|
||||
webApp = realWebApp;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
|
@ -231,7 +231,7 @@ public class CachedContentFactory implements HttpContent.ContentFactory
|
|||
{
|
||||
compressedContent = null;
|
||||
Resource compressedResource = _factory.newResource(compressedPathInContext);
|
||||
if (compressedResource.exists() && ResourceContentFactory.newerThanOrEqual(compressedResource, resource) &&
|
||||
if (compressedResource != null && compressedResource.exists() && ResourceContentFactory.newerThanOrEqual(compressedResource, resource) &&
|
||||
compressedResource.length() < resource.length())
|
||||
{
|
||||
compressedContent = new CachedHttpContent(compressedPathInContext, compressedResource, null);
|
||||
|
|
|
@ -1383,11 +1383,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
// addPath with accept non-canonical paths that don't go above the root,
|
||||
// but will treat them as aliases. So unless allowed by an AliasChecker
|
||||
// they will be rejected below.
|
||||
Resource resource = baseResource.resolve(pathInContext);
|
||||
|
||||
if (checkAlias(pathInContext, resource))
|
||||
return resource;
|
||||
return null;
|
||||
return baseResource.resolve(pathInContext);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -1408,7 +1404,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
if (resource.isAlias())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Alias resource {} for {}", resource, resource.getTargetURI());
|
||||
LOG.debug("Alias resource {} for {}", resource, resource.getRealURI());
|
||||
|
||||
// alias checks
|
||||
for (AliasCheck check : getAliasChecks())
|
||||
|
|
|
@ -253,6 +253,15 @@ public class ResourceService
|
|||
return response.isCommitted();
|
||||
}
|
||||
|
||||
ContextHandler contextHandler = ContextHandler.getContextHandler(request.getServletContext());
|
||||
if (contextHandler != null && !contextHandler.checkAlias(pathInContext, content.getResource()))
|
||||
{
|
||||
if (included)
|
||||
throw new FileNotFoundException("!" + pathInContext);
|
||||
notFound(request, response);
|
||||
return response.isCommitted();
|
||||
}
|
||||
|
||||
// Directory?
|
||||
if (content.getResource().isDirectory())
|
||||
{
|
||||
|
@ -289,6 +298,10 @@ public class ResourceService
|
|||
HttpContent precompressedContent = precompressedContents.get(precompressedContentEncoding);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("precompressed={}", precompressedContent);
|
||||
|
||||
if (contextHandler != null && !contextHandler.checkAlias(pathInContext, precompressedContent.getResource()))
|
||||
content = null;
|
||||
|
||||
content = precompressedContent;
|
||||
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), precompressedContentEncoding.getEncoding());
|
||||
}
|
||||
|
|
|
@ -457,8 +457,6 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
|
|||
if (_baseResource != null)
|
||||
{
|
||||
r = _baseResource.resolve(subUriPath);
|
||||
if (!_contextHandler.checkAlias(subUriPath, r))
|
||||
r = null;
|
||||
}
|
||||
else if (_servletContext instanceof ContextHandler.APIContext)
|
||||
{
|
||||
|
|
|
@ -290,12 +290,14 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
throw new IllegalStateException("No resourceBase or war set for context");
|
||||
|
||||
// Use real location (if different) for WAR file, so that change/modification monitoring can work.
|
||||
URI targetURI = webApp.getTargetURI();
|
||||
if (targetURI != null)
|
||||
if (webApp.isAlias())
|
||||
{
|
||||
URI realURI = webApp.getRealURI();
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} anti-aliased to {}", webApp, targetURI);
|
||||
webApp = context.newResource(targetURI);
|
||||
LOG.debug("{} anti-aliased to {}", webApp, realURI);
|
||||
Resource realWebApp = context.newResource(realURI);
|
||||
if (realWebApp != null && realWebApp.exists())
|
||||
webApp = realWebApp;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
|
|
Loading…
Reference in New Issue