Skip to content

Commit

Permalink
Issue #8474 - Jetty 12 - Resource.list() API change + `Resource.get…
Browse files Browse the repository at this point in the history
…FileName()` addition (#8582)

* Issue #8474 - Resource.list and Resource.getFileName work
* Correcting signature of Resource.list and use it
* Introduce Resource.getFileName and use it
* Adding URIUtil.addPath() test to show file+str behavior

Signed-off-by: Joakim Erdfelt <[email protected]>
  • Loading branch information
joakime authored Sep 14, 2022
1 parent 045af3d commit 9668d61
Show file tree
Hide file tree
Showing 29 changed files with 550 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@

package org.eclipse.jetty.server;

import java.nio.file.Path;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.StringUtil;
Expand Down Expand Up @@ -56,7 +59,9 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S
if (base == null || !resource.isDirectory())
return null;

List<Resource> listing = new ArrayList<>(resource.list().stream().map(URIUtil::encodePath).map(resource::resolve).toList());
List<Resource> listing = resource.list().stream()
.filter(distinctBy(Resource::getFileName))
.collect(Collectors.toCollection(ArrayList::new));

boolean sortOrderAscending = true;
String sortColumn = "N"; // name (or "M" for Last Modified, or "S" for Size)
Expand Down Expand Up @@ -208,14 +213,11 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S
for (Resource item : listing)
{
// Listings always return non-composite Resource entries
Path filePath = item.getPath();
if (filePath == null)
continue; // skip, can't represent this in a listing anyway.

String name = filePath.getFileName().toString();
String name = item.getFileName();
if (StringUtil.isBlank(name))
continue;
continue; // a resource either not backed by a filename (eg: MemoryResource), or has no filename (eg: a segment-less root "/")

// Ensure name has a slash if it's a directory
if (item.isDirectory() && !name.endsWith("/"))
name += URIUtil.SLASH;

Expand Down Expand Up @@ -252,6 +254,13 @@ public static String getAsHTML(Resource resource, String base, boolean parent, S
return buf.toString();
}

/* TODO: see if we can use {@link Collectors#groupingBy} */
private static <T> Predicate<T> distinctBy(Function<? super T, Object> keyExtractor)
{
HashSet<Object> map = new HashSet<>();
return t -> map.add(keyExtractor.apply(t));
}

/**
* Encode any characters that could break the URI string in an HREF.
* Such as <a href="/path/to;<script>Window.alert("XSS"+'%20'+"here");</script>">Link</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public void testUncacheable() throws Exception
@Override
public boolean isCacheable(Resource resource)
{
return super.isCacheable(resource) && resource.getName().indexOf("2.txt") < 0;
return super.isCacheable(resource) && !resource.getFileName().equals("2.txt");
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,51 @@ public static boolean isArchive(URI uri)
return (ext.equals(".jar") || ext.equals(".war") || ext.equals(".zip"));
}

/**
* Test if Path is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param path the path to test
* @return true if path is a file, and an extension of {@code .jar}, or {@code .zip}
* @see #getExtension(Path)
*/
public static boolean isLibArchive(Path path)
{
String ext = getExtension(path);
if (ext == null)
return false;
return (ext.equals(".jar") || ext.equals(".zip"));
}

/**
* Test if filename is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param filename the filename to test
* @return true if path is a file and name ends with {@code .jar}, or {@code .zip}
* @see #getExtension(String)
*/
public static boolean isLibArchive(String filename)
{
String ext = getExtension(filename);
if (ext == null)
return false;
return (ext.equals(".jar") || ext.equals(".zip"));
}

/**
* Test if URI is any supported Java Library Archive type (suitable to use as a library in a classpath/classloader)
*
* @param uri the URI to test
* @return true if the URI has a path that seems to point to a ({@code .jar}, or {@code .zip}).
* @see #getExtension(URI)
*/
public static boolean isLibArchive(URI uri)
{
String ext = getExtension(uri);
if (ext == null)
return false;
return (ext.equals(".jar") || ext.equals(".zip"));
}

/**
* Predicate to select all class files
*
Expand Down Expand Up @@ -365,7 +410,7 @@ public static boolean isNotModuleInfoClass(Path path)
* Is the path a TLD File
*
* @param path the path to test.
* @return True if a .war file.
* @return True if a .tld file.
*/
public static boolean isTld(Path path)
{
Expand All @@ -387,6 +432,17 @@ public static boolean isWebArchive(Path path)
return ".war".equals(getExtension(path));
}

/**
* Is the path a Web Archive File (not directory)
*
* @param uri the uri to test.
* @return True if a .war file.
*/
public static boolean isWebArchive(URI uri)
{
return ".war".equals(getExtension(uri));
}

/**
* Is the filename a WAR file.
*
Expand Down Expand Up @@ -419,4 +475,26 @@ public static boolean isXml(String filename)
{
return ".xml".equals(getExtension(filename));
}

/**
* Is the Path a file that ends in ZIP?
*
* @param path the path to test
* @return true if a .zip, false otherwise
*/
public static boolean isZip(Path path)
{
return ".zip".equals(getExtension(path));
}

/**
* Is the Path a file that ends in ZIP?
*
* @param filename the filename to test
* @return true if a .zip, false otherwise
*/
public static boolean isZip(String filename)
{
return ".zip".equals(getExtension(filename));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1922,7 +1922,7 @@ public static List<URI> split(String str)
{
listStream
.filter(Files::isRegularFile)
.filter(FileID::isArchive)
.filter(FileID::isLibArchive)
.sorted(Comparator.naturalOrder())
.forEach(path -> uris.add(toJarFileUri(path.toUri())));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.eclipse.jetty.util.IO;
Expand Down Expand Up @@ -73,7 +75,22 @@ public URI getURI()
@Override
public String getName()
{
return getPath().toAbsolutePath().toString();
Path p = getPath();
if (p == null)
return null;
return p.toAbsolutePath().toString();
}

@Override
public String getFileName()
{
Path p = getPath();
if (p == null)
return null;
Path fn = p.getFileName();
if (fn == null)
return ""; // no segments, so no filename
return fn.toString();
}

@Override
Expand Down Expand Up @@ -106,6 +123,18 @@ public boolean exists()
return true;
}

@Override
public List<Resource> list()
{
return List.of(); // empty
}

@Override
public Collection<Resource> getAllResources()
{
return List.of(); // empty
}

@Override
public String toString()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,16 @@ public Path getContainerPath()
{
return containerUri == null ? null : Path.of(containerUri);
}

@Override
public String getName()
{
Path abs = getPath();
// If a "jar:file:" based path, we should normalize here, as the toAbsolutePath() does not resolve "/../" style segments in all cases
if ("jar".equalsIgnoreCase(abs.toUri().getScheme()))
abs = abs.normalize();
// Get the absolute path
abs = abs.toAbsolutePath();
return abs.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.URIUtil;
Expand Down Expand Up @@ -207,27 +211,31 @@ public static boolean isSameName(Path pathA, Path pathB)
}

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

PathResource(Path path)
{
this(path, path.toUri(), true);
}

PathResource(Path path, URI uri, boolean bypassAllowedSchemeCheck)
{
if (!uri.isAbsolute())
throw new IllegalArgumentException("not an absolute uri: " + uri);
if (!bypassAllowedSchemeCheck && !ALLOWED_SCHEMES.contains(uri.getScheme()))
throw new IllegalArgumentException("not an allowed scheme: " + uri);

try
{
// 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.path = Paths.get(uri.normalize());
String uriString = uri.toString();
if (Files.isDirectory(path) && !uriString.endsWith(URIUtil.SLASH))
uri = URIUtil.correctFileURI(URI.create(uriString + URIUtil.SLASH));
this.uri = uri;
this.alias = checkAliasPath();
}
catch (FileSystemNotFoundException e)
{
throw new IllegalStateException("No FileSystem mounted for : " + uri, e);
}
String uriString = uri.toASCIIString();
if (Files.isDirectory(path) && !uriString.endsWith(URIUtil.SLASH))
uri = URIUtil.correctFileURI(URI.create(uriString + URIUtil.SLASH));

this.path = path;
this.uri = uri;
this.alias = checkAliasPath();
}

@Override
Expand Down Expand Up @@ -263,12 +271,41 @@ public Path getPath()
return path;
}

public List<Resource> list()
{
if (!isDirectory())
return List.of(); // empty

try (Stream<Path> dirStream = Files.list(getPath()))
{
return dirStream.map(PathResource::new).collect(Collectors.toCollection(ArrayList::new));
}
catch (DirectoryIteratorException e)
{
LOG.debug("Directory list failure", e);
}
catch (IOException e)
{
LOG.debug("Directory list access failure", e);
}
return List.of(); // empty
}

@Override
public String getName()
{
return path.toAbsolutePath().toString();
}

@Override
public String getFileName()
{
Path fn = path.getFileName();
if (fn == null) // if path has no segments (eg "/")
return "";
return fn.toString();
}

@Override
public URI getURI()
{
Expand Down
Loading

0 comments on commit 9668d61

Please sign in to comment.