Create a wrapper around URIs to fix issues with spaces

This commit is contained in:
comp500 2019-08-11 14:36:08 +01:00
parent 465e4973ba
commit 5a54a90f59
14 changed files with 168 additions and 97 deletions

View File

@ -2,6 +2,7 @@ package link.infra.packwiz.installer;
import link.infra.packwiz.installer.metadata.IndexFile; import link.infra.packwiz.installer.metadata.IndexFile;
import link.infra.packwiz.installer.metadata.ManifestFile; import link.infra.packwiz.installer.metadata.ManifestFile;
import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource; import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
import link.infra.packwiz.installer.metadata.hash.Hash; import link.infra.packwiz.installer.metadata.hash.Hash;
import link.infra.packwiz.installer.metadata.hash.HashUtils; import link.infra.packwiz.installer.metadata.hash.HashUtils;
@ -12,7 +13,6 @@ import okio.Okio;
import okio.Source; import okio.Source;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -74,7 +74,7 @@ class DownloadTask implements IOptionDetails, IExceptionDetails {
} }
} }
void downloadMetadata(IndexFile parentIndexFile, URI indexUri) { void downloadMetadata(IndexFile parentIndexFile, SpaceSafeURI indexUri) {
if (failure != null) return; if (failure != null) return;
if (metadataRequired) { if (metadataRequired) {
try { try {
@ -101,7 +101,7 @@ class DownloadTask implements IOptionDetails, IExceptionDetails {
} }
} }
void download(String packFolder, URI indexUri) { void download(String packFolder, SpaceSafeURI indexUri) {
if (failure != null) return; if (failure != null) return;
// Ensure it is removed // Ensure it is removed

View File

@ -1,5 +1,6 @@
package link.infra.packwiz.installer; package link.infra.packwiz.installer;
import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import link.infra.packwiz.installer.ui.CLIHandler; import link.infra.packwiz.installer.ui.CLIHandler;
import link.infra.packwiz.installer.ui.IUserInterface; import link.infra.packwiz.installer.ui.IUserInterface;
import link.infra.packwiz.installer.ui.InstallWindow; import link.infra.packwiz.installer.ui.InstallWindow;
@ -7,7 +8,6 @@ import org.apache.commons.cli.*;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
public class Main { public class Main {
@ -37,7 +37,7 @@ public class Main {
} }
} }
protected void startup(String[] args) { private void startup(String[] args) {
Options options = new Options(); Options options = new Options();
addNonBootstrapOptions(options); addNonBootstrapOptions(options);
addBootstrapOptions(options); addBootstrapOptions(options);
@ -99,7 +99,7 @@ public class Main {
} }
try { try {
uOptions.downloadURI = new URI(unparsedArgs[0]); uOptions.downloadURI = new SpaceSafeURI(unparsedArgs[0]);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
// TODO: better error message? // TODO: better error message?
ui.handleExceptionAndExit(e); ui.handleExceptionAndExit(e);
@ -124,6 +124,7 @@ public class Main {
} }
// Called by packwiz-installer-bootstrap to set up the help command // Called by packwiz-installer-bootstrap to set up the help command
@SuppressWarnings("WeakerAccess")
public static void addNonBootstrapOptions(Options options) { public static void addNonBootstrapOptions(Options options) {
options.addOption("s", "side", true, "Side to install mods from (client/server, defaults to client)"); options.addOption("s", "side", true, "Side to install mods from (client/server, defaults to client)");
options.addOption(null, "title", true, "Title of the installer window"); options.addOption(null, "title", true, "Title of the installer window");

View File

@ -9,6 +9,7 @@ import com.moandjiezana.toml.Toml;
import link.infra.packwiz.installer.metadata.IndexFile; import link.infra.packwiz.installer.metadata.IndexFile;
import link.infra.packwiz.installer.metadata.ManifestFile; import link.infra.packwiz.installer.metadata.ManifestFile;
import link.infra.packwiz.installer.metadata.PackFile; import link.infra.packwiz.installer.metadata.PackFile;
import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource; import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
import link.infra.packwiz.installer.metadata.hash.Hash; import link.infra.packwiz.installer.metadata.hash.Hash;
import link.infra.packwiz.installer.metadata.hash.HashUtils; import link.infra.packwiz.installer.metadata.hash.HashUtils;
@ -20,7 +21,6 @@ import okio.Okio;
import okio.Source; import okio.Source;
import java.io.*; import java.io.*;
import java.net.URI;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
@ -35,7 +35,7 @@ public class UpdateManager {
private boolean cancelledStartGame = false; private boolean cancelledStartGame = false;
public static class Options { public static class Options {
URI downloadURI = null; SpaceSafeURI downloadURI = null;
String manifestFile = "packwiz.json"; // TODO: make configurable String manifestFile = "packwiz.json"; // TODO: make configurable
String packFolder = "."; String packFolder = ".";
Side side = Side.CLIENT; Side side = Side.CLIENT;
@ -136,9 +136,9 @@ public class UpdateManager {
ui.submitProgress(new InstallProgress("Checking local files...")); ui.submitProgress(new InstallProgress("Checking local files..."));
// Invalidation checking must be done here, as it must happen before pack/index hashes are checked // Invalidation checking must be done here, as it must happen before pack/index hashes are checked
List<URI> invalidatedUris = new ArrayList<>(); List<SpaceSafeURI> invalidatedUris = new ArrayList<>();
if (manifest.cachedFiles != null) { if (manifest.cachedFiles != null) {
for (Map.Entry<URI, ManifestFile.File> entry : manifest.cachedFiles.entrySet()) { for (Map.Entry<SpaceSafeURI, ManifestFile.File> entry : manifest.cachedFiles.entrySet()) {
boolean invalid = false; boolean invalid = false;
// if isn't optional, or is optional but optionValue == true // if isn't optional, or is optional but optionValue == true
if (!entry.getValue().isOptional || entry.getValue().optionValue) { if (!entry.getValue().isOptional || entry.getValue().optionValue) {
@ -152,7 +152,7 @@ public class UpdateManager {
} }
} }
if (invalid) { if (invalid) {
URI fileUri = entry.getKey(); SpaceSafeURI fileUri = entry.getKey();
System.out.println("File " + fileUri.toString() + " invalidated, marked for redownloading"); System.out.println("File " + fileUri.toString() + " invalidated, marked for redownloading");
invalidatedUris.add(fileUri); invalidatedUris.add(fileUri);
} }
@ -202,7 +202,7 @@ public class UpdateManager {
// TODO: implement // TODO: implement
} }
private void processIndex(URI indexUri, Hash indexHash, String hashFormat, ManifestFile manifest, List<URI> invalidatedUris) { private void processIndex(SpaceSafeURI indexUri, Hash indexHash, String hashFormat, ManifestFile manifest, List<SpaceSafeURI> invalidatedUris) {
if (manifest.indexFileHash != null && manifest.indexFileHash.equals(indexHash) && invalidatedUris.isEmpty()) { if (manifest.indexFileHash != null && manifest.indexFileHash.equals(indexHash) && invalidatedUris.isEmpty()) {
System.out.println("Modpack files are already up to date!"); System.out.println("Modpack files are already up to date!");
return; return;
@ -238,9 +238,9 @@ public class UpdateManager {
} }
ui.submitProgress(new InstallProgress("Checking local files...")); ui.submitProgress(new InstallProgress("Checking local files..."));
Iterator<Map.Entry<URI, ManifestFile.File>> it = manifest.cachedFiles.entrySet().iterator(); Iterator<Map.Entry<SpaceSafeURI, ManifestFile.File>> it = manifest.cachedFiles.entrySet().iterator();
while (it.hasNext()) { while (it.hasNext()) {
Map.Entry<URI, ManifestFile.File> entry = it.next(); Map.Entry<SpaceSafeURI, ManifestFile.File> entry = it.next();
if (entry.getValue().cachedLocation != null) { if (entry.getValue().cachedLocation != null) {
boolean alreadyDeleted = false; boolean alreadyDeleted = false;
// Delete if option value has been set to false // Delete if option value has been set to false

View File

@ -1,6 +1,5 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.moandjiezana.toml.Toml; import com.moandjiezana.toml.Toml;
import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource; import link.infra.packwiz.installer.metadata.hash.GeneralHashingSource;
@ -10,7 +9,6 @@ import link.infra.packwiz.installer.request.HandlerManager;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
import java.net.URI;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
@ -20,20 +18,19 @@ public class IndexFile {
public List<File> files; public List<File> files;
public static class File { public static class File {
@JsonAdapter(SpaceSafeURIParser.class) public SpaceSafeURI file;
public URI file;
@SerializedName("hash-format") @SerializedName("hash-format")
public String hashFormat; public String hashFormat;
public String hash; public String hash;
public URI alias; public SpaceSafeURI alias;
public boolean metafile; public boolean metafile;
public boolean preserve; public boolean preserve;
public transient ModFile linkedFile; public transient ModFile linkedFile;
public transient URI linkedFileURI; public transient SpaceSafeURI linkedFileURI;
public transient boolean optionValue = true; public transient boolean optionValue = true;
public void downloadMeta(IndexFile parentIndexFile, URI indexUri) throws Exception { public void downloadMeta(IndexFile parentIndexFile, SpaceSafeURI indexUri) throws Exception {
if (!metafile) { if (!metafile) {
return; return;
} }
@ -51,14 +48,14 @@ public class IndexFile {
} }
} }
public Source getSource(URI indexUri) throws Exception { public Source getSource(SpaceSafeURI indexUri) throws Exception {
if (metafile) { if (metafile) {
if (linkedFile == null) { if (linkedFile == null) {
throw new Exception("Linked file doesn't exist!"); throw new Exception("Linked file doesn't exist!");
} }
return linkedFile.getSource(linkedFileURI); return linkedFile.getSource(linkedFileURI);
} else { } else {
URI newLoc = HandlerManager.getNewLoc(indexUri, file); SpaceSafeURI newLoc = HandlerManager.getNewLoc(indexUri, file);
if (newLoc == null) { if (newLoc == null) {
throw new Exception("Index file URI is invalid"); throw new Exception("Index file URI is invalid");
} }
@ -94,13 +91,13 @@ public class IndexFile {
return "Invalid file"; return "Invalid file";
} }
public URI getDestURI() { public SpaceSafeURI getDestURI() {
if (alias != null) { if (alias != null) {
return alias; return alias;
} }
if (metafile && linkedFile != null) { if (metafile && linkedFile != null) {
// TODO: URIs are bad // TODO: URIs are bad
return file.resolve(linkedFile.filename.replace(" ", "%20")); return file.resolve(linkedFile.filename);
} else { } else {
return file; return file;
} }

View File

@ -3,13 +3,12 @@ package link.infra.packwiz.installer.metadata;
import link.infra.packwiz.installer.UpdateManager; import link.infra.packwiz.installer.UpdateManager;
import link.infra.packwiz.installer.metadata.hash.Hash; import link.infra.packwiz.installer.metadata.hash.Hash;
import java.net.URI;
import java.util.Map; import java.util.Map;
public class ManifestFile { public class ManifestFile {
public Hash packFileHash = null; public Hash packFileHash = null;
public Hash indexFileHash = null; public Hash indexFileHash = null;
public Map<URI, File> cachedFiles; public Map<SpaceSafeURI, File> cachedFiles;
// If the side changes, EVERYTHING invalidates. FUN!!! // If the side changes, EVERYTHING invalidates. FUN!!!
public UpdateManager.Options.Side cachedSide = UpdateManager.Options.Side.CLIENT; public UpdateManager.Options.Side cachedSide = UpdateManager.Options.Side.CLIENT;

View File

@ -1,17 +1,14 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
import java.net.URI;
import java.util.Map;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import link.infra.packwiz.installer.UpdateManager.Options.Side; import link.infra.packwiz.installer.UpdateManager.Options.Side;
import link.infra.packwiz.installer.metadata.hash.Hash; import link.infra.packwiz.installer.metadata.hash.Hash;
import link.infra.packwiz.installer.metadata.hash.HashUtils; import link.infra.packwiz.installer.metadata.hash.HashUtils;
import link.infra.packwiz.installer.request.HandlerManager; import link.infra.packwiz.installer.request.HandlerManager;
import okio.Source; import okio.Source;
import java.util.Map;
public class ModFile { public class ModFile {
public String name; public String name;
public String filename; public String filename;
@ -19,8 +16,7 @@ public class ModFile {
public Download download; public Download download;
public static class Download { public static class Download {
@JsonAdapter(SpaceSafeURIParser.class) public SpaceSafeURI url;
public URI url;
@SerializedName("hash-format") @SerializedName("hash-format")
public String hashFormat; public String hashFormat;
public String hash; public String hash;
@ -36,14 +32,14 @@ public class ModFile {
public boolean defaultValue; public boolean defaultValue;
} }
public Source getSource(URI baseLoc) throws Exception { public Source getSource(SpaceSafeURI baseLoc) throws Exception {
if (download == null) { if (download == null) {
throw new Exception("Metadata file doesn't have download"); throw new Exception("Metadata file doesn't have download");
} }
if (download.url == null) { if (download.url == null) {
throw new Exception("Metadata file doesn't have a download URI"); throw new Exception("Metadata file doesn't have a download URI");
} }
URI newLoc = HandlerManager.getNewLoc(baseLoc, download.url); SpaceSafeURI newLoc = HandlerManager.getNewLoc(baseLoc, download.url);
if (newLoc == null) { if (newLoc == null) {
throw new Exception("Metadata file URI is invalid"); throw new Exception("Metadata file URI is invalid");
} }

View File

@ -1,18 +1,15 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
import java.net.URI;
import java.util.Map;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.util.Map;
public class PackFile { public class PackFile {
public String name; public String name;
public IndexFileLoc index; public IndexFileLoc index;
public static class IndexFileLoc { public static class IndexFileLoc {
@JsonAdapter(SpaceSafeURIParser.class) public SpaceSafeURI file;
public URI file;
@SerializedName("hash-format") @SerializedName("hash-format")
public String hashFormat; public String hashFormat;
public String hash; public String hash;

View File

@ -0,0 +1,83 @@
package link.infra.packwiz.installer.metadata;
import com.google.gson.annotations.JsonAdapter;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
// The world's worst URI wrapper
@JsonAdapter(SpaceSafeURIParser.class)
public class SpaceSafeURI implements Comparable<SpaceSafeURI>, Serializable {
private final URI u;
public SpaceSafeURI(String str) throws URISyntaxException {
u = new URI(str.replace(" ", "%20"));
}
public SpaceSafeURI(URI uri) {
this.u = uri;
}
public SpaceSafeURI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException {
// TODO: do all components need to be replaced?
scheme = scheme.replace(" ", "%20");
authority = authority.replace(" ", "%20");
path = path.replace(" ", "%20");
query = query.replace(" ", "%20");
fragment = fragment.replace(" ", "%20");
u = new URI(scheme, authority, path, query, fragment);
}
public String getPath() {
return u.getPath().replace("%20", " ");
}
public String toString() {
return u.toString().replace("%20", " ");
}
@SuppressWarnings("WeakerAccess")
public SpaceSafeURI resolve(String path) {
return new SpaceSafeURI(u.resolve(path.replace(" ", "%20")));
}
public SpaceSafeURI resolve(SpaceSafeURI loc) {
return new SpaceSafeURI(u.resolve(loc.u));
}
public SpaceSafeURI relativize(SpaceSafeURI loc) {
return new SpaceSafeURI(u.relativize(loc.u));
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SpaceSafeURI) {
return u.equals(((SpaceSafeURI) obj).u);
}
return false;
}
@Override
public int compareTo(SpaceSafeURI uri) {
return u.compareTo(uri.u);
}
public String getScheme() {
return u.getScheme();
}
public String getAuthority() {
return u.getAuthority();
}
public String getHost() {
return u.getHost();
}
public URL toURL() throws MalformedURLException {
return u.toURL();
}
}

View File

@ -1,26 +1,24 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.net.URISyntaxException;
/** /**
* This class encodes spaces before parsing the URI, so the URI can actually be * This class encodes spaces before parsing the URI, so the URI can actually be
* parsed. * parsed.
*/ */
class SpaceSafeURIParser implements JsonDeserializer<URI> { class SpaceSafeURIParser implements JsonDeserializer<SpaceSafeURI> {
@Override @Override
public URI deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) public SpaceSafeURI deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException { throws JsonParseException {
String uriString = json.getAsString().replace(" ", "%20");
try { try {
return new URI(uriString); return new SpaceSafeURI(json.getAsString());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new JsonParseException("Failed to parse URI", e); throw new JsonParseException("Failed to parse URI", e);
} }

View File

@ -1,23 +1,23 @@
package link.infra.packwiz.installer.request; package link.infra.packwiz.installer.request;
import java.net.URI; import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import java.util.ArrayList;
import java.util.List;
import link.infra.packwiz.installer.request.handlers.RequestHandlerGithub; import link.infra.packwiz.installer.request.handlers.RequestHandlerGithub;
import link.infra.packwiz.installer.request.handlers.RequestHandlerHTTP; import link.infra.packwiz.installer.request.handlers.RequestHandlerHTTP;
import okio.Source; import okio.Source;
import java.util.ArrayList;
import java.util.List;
public abstract class HandlerManager { public abstract class HandlerManager {
public static List<IRequestHandler> handlers = new ArrayList<IRequestHandler>(); private static List<IRequestHandler> handlers = new ArrayList<>();
static { static {
handlers.add(new RequestHandlerGithub()); handlers.add(new RequestHandlerGithub());
handlers.add(new RequestHandlerHTTP()); handlers.add(new RequestHandlerHTTP());
} }
public static URI getNewLoc(URI base, URI loc) { public static SpaceSafeURI getNewLoc(SpaceSafeURI base, SpaceSafeURI loc) {
if (loc == null) return null; if (loc == null) return null;
if (base != null) { if (base != null) {
loc = base.resolve(loc); loc = base.resolve(loc);
@ -35,7 +35,7 @@ public abstract class HandlerManager {
// Zip handler discards once read, requesting multiple times on other handlers would cause multiple downloads // Zip handler discards once read, requesting multiple times on other handlers would cause multiple downloads
// Caching system? Copy from already downloaded files? // Caching system? Copy from already downloaded files?
public static Source getFileSource(URI loc) throws Exception { public static Source getFileSource(SpaceSafeURI loc) throws Exception {
for (IRequestHandler handler : handlers) { for (IRequestHandler handler : handlers) {
if (handler.matchesHandler(loc)) { if (handler.matchesHandler(loc)) {
Source src = handler.getFileSource(loc); Source src = handler.getFileSource(loc);

View File

@ -1,17 +1,16 @@
package link.infra.packwiz.installer.request; package link.infra.packwiz.installer.request;
import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import okio.Source; import okio.Source;
import java.net.URI;
/** /**
* IRequestHandler handles requests for locations specified in modpack metadata. * IRequestHandler handles requests for locations specified in modpack metadata.
*/ */
public interface IRequestHandler { public interface IRequestHandler {
boolean matchesHandler(URI loc); boolean matchesHandler(SpaceSafeURI loc);
default URI getNewLoc(URI loc) { default SpaceSafeURI getNewLoc(SpaceSafeURI loc) {
return loc; return loc;
} }
@ -20,8 +19,8 @@ public interface IRequestHandler {
* It is assumed that each location is read only once for the duration of an IRequestHandler. * It is assumed that each location is read only once for the duration of an IRequestHandler.
* @param loc The location to be read * @param loc The location to be read
* @return The Source containing the data of the file * @return The Source containing the data of the file
* @throws Exception * @throws Exception Exception if it failed to download a file!!!
*/ */
Source getFileSource(URI loc) throws Exception; Source getFileSource(SpaceSafeURI loc) throws Exception;
} }

View File

@ -1,6 +1,7 @@
package link.infra.packwiz.installer.request.handlers; package link.infra.packwiz.installer.request.handlers;
import java.net.URI; import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@ -14,16 +15,16 @@ public class RequestHandlerGithub extends RequestHandlerZip {
} }
@Override @Override
public URI getNewLoc(URI loc) { public SpaceSafeURI getNewLoc(SpaceSafeURI loc) {
return loc; return loc;
} }
// TODO: is caching really needed, if HTTPURLConnection follows redirects correctly? // TODO: is caching really needed, if HTTPURLConnection follows redirects correctly?
private Map<String, URI> zipUriMap = new HashMap<>(); private Map<String, SpaceSafeURI> zipUriMap = new HashMap<>();
private final ReentrantReadWriteLock zipUriLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock zipUriLock = new ReentrantReadWriteLock();
private static Pattern repoMatcherPattern = Pattern.compile("/([\\w.-]+/[\\w.-]+).*"); private static Pattern repoMatcherPattern = Pattern.compile("/([\\w.-]+/[\\w.-]+).*");
private String getRepoName(URI loc) { private String getRepoName(SpaceSafeURI loc) {
Matcher matcher = repoMatcherPattern.matcher(loc.getPath()); Matcher matcher = repoMatcherPattern.matcher(loc.getPath());
if (matcher.matches()) { if (matcher.matches()) {
return matcher.group(1); return matcher.group(1);
@ -33,22 +34,22 @@ public class RequestHandlerGithub extends RequestHandlerZip {
} }
@Override @Override
protected URI getZipUri(URI loc) throws Exception { protected SpaceSafeURI getZipUri(SpaceSafeURI loc) throws Exception {
String repoName = getRepoName(loc); String repoName = getRepoName(loc);
String branchName = getBranch(loc); String branchName = getBranch(loc);
zipUriLock.readLock().lock(); zipUriLock.readLock().lock();
URI zipUri = zipUriMap.get(repoName + "/" + branchName); SpaceSafeURI zipUri = zipUriMap.get(repoName + "/" + branchName);
zipUriLock.readLock().unlock(); zipUriLock.readLock().unlock();
if (zipUri != null) { if (zipUri != null) {
return zipUri; return zipUri;
} }
zipUri = new URI("https://api.github.com/repos/" + repoName + "/zipball/" + branchName); zipUri = new SpaceSafeURI("https://api.github.com/repos/" + repoName + "/zipball/" + branchName);
zipUriLock.writeLock().lock(); zipUriLock.writeLock().lock();
// If another thread sets the value concurrently, use the value of the // If another thread sets the value concurrently, use the value of the
// thread that first acquired the lock. // thread that first acquired the lock.
URI zipUriInserted = zipUriMap.putIfAbsent(repoName + "/" + branchName, zipUri); SpaceSafeURI zipUriInserted = zipUriMap.putIfAbsent(repoName + "/" + branchName, zipUri);
if (zipUriInserted != null) { if (zipUriInserted != null) {
zipUri = zipUriInserted; zipUri = zipUriInserted;
} }
@ -58,7 +59,7 @@ public class RequestHandlerGithub extends RequestHandlerZip {
private static Pattern branchMatcherPattern = Pattern.compile("/[\\w.-]+/[\\w.-]+/blob/([\\w.-]+).*"); private static Pattern branchMatcherPattern = Pattern.compile("/[\\w.-]+/[\\w.-]+/blob/([\\w.-]+).*");
private String getBranch(URI loc) { private String getBranch(SpaceSafeURI loc) {
Matcher matcher = branchMatcherPattern.matcher(loc.getPath()); Matcher matcher = branchMatcherPattern.matcher(loc.getPath());
if (matcher.matches()) { if (matcher.matches()) {
return matcher.group(1); return matcher.group(1);
@ -68,13 +69,13 @@ public class RequestHandlerGithub extends RequestHandlerZip {
} }
@Override @Override
protected URI getLocationInZip(URI loc) throws Exception { protected SpaceSafeURI getLocationInZip(SpaceSafeURI loc) throws Exception {
String path = "/" + getRepoName(loc) + "/blob/" + getBranch(loc); String path = "/" + getRepoName(loc) + "/blob/" + getBranch(loc);
return new URI(loc.getScheme(), loc.getAuthority(), path, null, null).relativize(loc); return new SpaceSafeURI(loc.getScheme(), loc.getAuthority(), path, null, null).relativize(loc);
} }
@Override @Override
public boolean matchesHandler(URI loc) { public boolean matchesHandler(SpaceSafeURI loc) {
String scheme = loc.getScheme(); String scheme = loc.getScheme();
if (!("http".equals(scheme) || "https".equals(scheme))) { if (!("http".equals(scheme) || "https".equals(scheme))) {
return false; return false;

View File

@ -1,22 +1,22 @@
package link.infra.packwiz.installer.request.handlers; package link.infra.packwiz.installer.request.handlers;
import java.net.URI; import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import java.net.URLConnection;
import link.infra.packwiz.installer.request.IRequestHandler; import link.infra.packwiz.installer.request.IRequestHandler;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
import java.net.URLConnection;
public class RequestHandlerHTTP implements IRequestHandler { public class RequestHandlerHTTP implements IRequestHandler {
@Override @Override
public boolean matchesHandler(URI loc) { public boolean matchesHandler(SpaceSafeURI loc) {
String scheme = loc.getScheme(); String scheme = loc.getScheme();
return "http".equals(scheme) || "https".equals(scheme); return "http".equals(scheme) || "https".equals(scheme);
} }
@Override @Override
public Source getFileSource(URI loc) throws Exception { public Source getFileSource(SpaceSafeURI loc) throws Exception {
URLConnection conn = loc.toURL().openConnection(); URLConnection conn = loc.toURL().openConnection();
// TODO: when do we send specific headers??? should there be a way to signal this? // TODO: when do we send specific headers??? should there be a way to signal this?
// github *sometimes* requires it, sometimes not! // github *sometimes* requires it, sometimes not!

View File

@ -1,12 +1,12 @@
package link.infra.packwiz.installer.request.handlers; package link.infra.packwiz.installer.request.handlers;
import link.infra.packwiz.installer.metadata.SpaceSafeURI;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.Okio; import okio.Okio;
import okio.Source; import okio.Source;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -35,7 +35,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
private class ZipReader { private class ZipReader {
private final ZipInputStream zis; private final ZipInputStream zis;
private final Map<URI, Buffer> readFiles = new HashMap<>(); private final Map<SpaceSafeURI, Buffer> readFiles = new HashMap<>();
// Write lock implies access to ZipInputStream - only 1 thread must read at a time! // Write lock implies access to ZipInputStream - only 1 thread must read at a time!
final ReentrantLock filesLock = new ReentrantLock(); final ReentrantLock filesLock = new ReentrantLock();
private ZipEntry entry; private ZipEntry entry;
@ -55,14 +55,14 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
} }
// File lock must be obtained before calling this function // File lock must be obtained before calling this function
private Buffer findFile(URI loc) throws IOException, URISyntaxException { private Buffer findFile(SpaceSafeURI loc) throws IOException, URISyntaxException {
while (true) { while (true) {
entry = zis.getNextEntry(); entry = zis.getNextEntry();
if (entry == null) { if (entry == null) {
return null; return null;
} }
Buffer data = readCurrFile(); Buffer data = readCurrFile();
URI fileLoc = new URI(removeFolder(entry.getName())); SpaceSafeURI fileLoc = new SpaceSafeURI(removeFolder(entry.getName()));
if (loc.equals(fileLoc)) { if (loc.equals(fileLoc)) {
return data; return data;
} else { } else {
@ -71,7 +71,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
} }
} }
Source getFileSource(URI loc) throws Exception { Source getFileSource(SpaceSafeURI loc) throws Exception {
filesLock.lock(); filesLock.lock();
// Assume files are only read once, allow GC by removing // Assume files are only read once, allow GC by removing
Buffer file = readFiles.remove(loc); Buffer file = readFiles.remove(loc);
@ -84,10 +84,10 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
filesLock.unlock(); filesLock.unlock();
return file; return file;
} }
URI findInZip(Predicate<URI> matches) throws Exception { SpaceSafeURI findInZip(Predicate<SpaceSafeURI> matches) throws Exception {
filesLock.lock(); filesLock.lock();
for (URI file : readFiles.keySet()) { for (SpaceSafeURI file : readFiles.keySet()) {
if (matches.test(file)) { if (matches.test(file)) {
filesLock.unlock(); filesLock.unlock();
return file; return file;
@ -101,7 +101,7 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
return null; return null;
} }
Buffer data = readCurrFile(); Buffer data = readCurrFile();
URI fileLoc = new URI(removeFolder(entry.getName())); SpaceSafeURI fileLoc = new SpaceSafeURI(removeFolder(entry.getName()));
readFiles.put(fileLoc, data); readFiles.put(fileLoc, data);
if (matches.test(fileLoc)) { if (matches.test(fileLoc)) {
filesLock.unlock(); filesLock.unlock();
@ -112,19 +112,19 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
} }
private final Map<URI, ZipReader> cache = new HashMap<>(); private final Map<SpaceSafeURI, ZipReader> cache = new HashMap<>();
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
protected abstract URI getZipUri(URI loc) throws Exception; protected abstract SpaceSafeURI getZipUri(SpaceSafeURI loc) throws Exception;
protected abstract URI getLocationInZip(URI loc) throws Exception; protected abstract SpaceSafeURI getLocationInZip(SpaceSafeURI loc) throws Exception;
@Override @Override
public abstract boolean matchesHandler(URI loc); public abstract boolean matchesHandler(SpaceSafeURI loc);
@Override @Override
public Source getFileSource(URI loc) throws Exception { public Source getFileSource(SpaceSafeURI loc) throws Exception {
URI zipUri = getZipUri(loc); SpaceSafeURI zipUri = getZipUri(loc);
cacheLock.readLock().lock(); cacheLock.readLock().lock();
ZipReader zr = cache.get(zipUri); ZipReader zr = cache.get(zipUri);
cacheLock.readLock().unlock(); cacheLock.readLock().unlock();
@ -147,8 +147,8 @@ public abstract class RequestHandlerZip extends RequestHandlerHTTP {
return zr.getFileSource(getLocationInZip(loc)); return zr.getFileSource(getLocationInZip(loc));
} }
protected URI findInZip(URI loc, Predicate<URI> matches) throws Exception { protected SpaceSafeURI findInZip(SpaceSafeURI loc, Predicate<SpaceSafeURI> matches) throws Exception {
URI zipUri = getZipUri(loc); SpaceSafeURI zipUri = getZipUri(loc);
cacheLock.readLock().lock(); cacheLock.readLock().lock();
ZipReader zr = cache.get(zipUri); ZipReader zr = cache.get(zipUri);
cacheLock.readLock().unlock(); cacheLock.readLock().unlock();