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:
Joakim Erdfelt 2022-10-19 11:17:23 -05:00 committed by GitHub
parent 6ba81ce10d
commit 9061348ec4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 456 additions and 268 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);
String uriString = uri.toASCIIString();
if (Files.isDirectory(path) && !uriString.endsWith(URIUtil.SLASH))
uri = URIUtil.correctFileURI(URI.create(uriString + URIUtil.SLASH));
if (Files.isDirectory(path))
{
String uriString = uri.toASCIIString();
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()
/**
* <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>
*
* <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>
*/
private void resolveAlias()
{
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.
*
* 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)
*/
if (!URIUtil.equalsIgnoreEncodings(uri, toUri(path)))
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();
// 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 (IOException ioe)
catch (Exception e)
{
// If the toRealPath() call fails, then let
// the alias checking routines continue on
// to other techniques.
LOG.trace("IGNORED", ioe);
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 (!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;
}
/* 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));
}
catch (IOException e)
{
LOG.trace("IGNORED", e);
}
catch (Exception e)
{
LOG.warn("bad alias ({} {}) for {}", e.getClass().getName(), e.getMessage(), path);
}
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()
{

View File

@ -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();
}
/**

View File

@ -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())

View File

@ -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

View File

@ -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());
}
}

View File

@ -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)
{

View File

@ -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

View File

@ -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());

View File

@ -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))

View File

@ -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())

View File

@ -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);

View File

@ -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())

View File

@ -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());
}

View File

@ -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)
{

View File

@ -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())