diff --git a/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java b/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java index de8333f..664b125 100644 --- a/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java +++ b/src/main/java/moe/yushi/authlibinjector/AuthlibInjector.java @@ -14,17 +14,22 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Base64; +import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; +import moe.yushi.authlibinjector.httpd.DefaultURLRedirector; +import moe.yushi.authlibinjector.httpd.LegacySkinAPIFilter; +import moe.yushi.authlibinjector.httpd.URLFilter; +import moe.yushi.authlibinjector.httpd.URLProcessor; import moe.yushi.authlibinjector.transform.AuthlibLogInterceptor; import moe.yushi.authlibinjector.transform.ClassTransformer; +import moe.yushi.authlibinjector.transform.ConstantURLTransformUnit; import moe.yushi.authlibinjector.transform.DumpClassListener; import moe.yushi.authlibinjector.transform.SkinWhitelistTransformUnit; -import moe.yushi.authlibinjector.transform.LocalYggdrasilApiTransformUnit; -import moe.yushi.authlibinjector.transform.RemoteYggdrasilTransformUnit; import moe.yushi.authlibinjector.transform.YggdrasilKeyTransformUnit; import moe.yushi.authlibinjector.util.Logging; @@ -54,11 +59,6 @@ public final class AuthlibInjector { */ public static final String PROP_PREFETCHED_DATA_OLD = "org.to2mbn.authlibinjector.config.prefetched"; - /** - * Whether to disable the local httpd server. - */ - public static final String PROP_DISABLE_HTTPD = "authlibinjector.httpd.disable"; - /** * The name of loggers to have debug level turned on. */ @@ -267,9 +267,22 @@ public final class AuthlibInjector { } } - private static ClassTransformer createTransformer(YggdrasilConfiguration config) { - ClassTransformer transformer = new ClassTransformer(); + private static URLProcessor createURLProcessor(YggdrasilConfiguration config) { + List filters = new ArrayList<>(); + if (Boolean.TRUE.equals(config.getMeta().get("feature.legacy_skin_api"))) { + Logging.CONFIG.info("Disabled local redirect for legacy skin API, as the remote Yggdrasil server supports it"); + } else { + filters.add(new LegacySkinAPIFilter(config)); + } + + return new URLProcessor(filters, new DefaultURLRedirector(config)); + } + + private static ClassTransformer createTransformer(YggdrasilConfiguration config) { + URLProcessor urlProcessor = createURLProcessor(config); + + ClassTransformer transformer = new ClassTransformer(); for (String ignore : nonTransformablePackages) { transformer.ignores.add(ignore); } @@ -282,11 +295,7 @@ public final class AuthlibInjector { transformer.units.add(new AuthlibLogInterceptor()); } - if (!"true".equals(System.getProperty(PROP_DISABLE_HTTPD))) { - transformer.units.add(new LocalYggdrasilApiTransformUnit(config)); - } - - transformer.units.add(new RemoteYggdrasilTransformUnit(config.getApiRoot())); + transformer.units.add(new ConstantURLTransformUnit(urlProcessor)); transformer.units.add(new SkinWhitelistTransformUnit(config.getSkinDomains().toArray(new String[0]))); diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/DefaultURLRedirector.java b/src/main/java/moe/yushi/authlibinjector/httpd/DefaultURLRedirector.java new file mode 100644 index 0000000..f7be300 --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/httpd/DefaultURLRedirector.java @@ -0,0 +1,37 @@ +package moe.yushi.authlibinjector.httpd; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import moe.yushi.authlibinjector.YggdrasilConfiguration; + +public class DefaultURLRedirector implements URLRedirector { + + private Map domainMapping = new HashMap<>(); + private String apiRoot; + + public DefaultURLRedirector(YggdrasilConfiguration config) { + initDomainMapping(); + + apiRoot = config.getApiRoot(); + } + + private void initDomainMapping() { + domainMapping.put("api.mojang.com", "api"); + domainMapping.put("authserver.mojang.com", "authserver"); + domainMapping.put("sessionserver.mojang.com", "sessionserver"); + domainMapping.put("skins.minecraft.net", "skins"); + } + + @Override + public Optional redirect(String domain, String path) { + String subdirectory = domainMapping.get(domain); + if (subdirectory == null) { + return Optional.empty(); + } + + return Optional.of(apiRoot + subdirectory + path); + } + +} diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHttpd.java b/src/main/java/moe/yushi/authlibinjector/httpd/LegacySkinAPIFilter.java similarity index 86% rename from src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHttpd.java rename to src/main/java/moe/yushi/authlibinjector/httpd/LegacySkinAPIFilter.java index 0793a89..32a15c7 100644 --- a/src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHttpd.java +++ b/src/main/java/moe/yushi/authlibinjector/httpd/LegacySkinAPIFilter.java @@ -1,10 +1,12 @@ package moe.yushi.authlibinjector.httpd; +import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singleton; import static java.util.Optional.empty; import static java.util.Optional.of; import static java.util.Optional.ofNullable; +import static moe.yushi.authlibinjector.util.IOUtils.CONTENT_TYPE_JSON; import static moe.yushi.authlibinjector.util.IOUtils.asString; import static moe.yushi.authlibinjector.util.IOUtils.getURL; import static moe.yushi.authlibinjector.util.IOUtils.newUncheckedIOException; @@ -13,6 +15,7 @@ import static moe.yushi.authlibinjector.util.JsonUtils.asJsonArray; import static moe.yushi.authlibinjector.util.JsonUtils.asJsonObject; import static moe.yushi.authlibinjector.util.JsonUtils.asJsonString; import static moe.yushi.authlibinjector.util.JsonUtils.parseJson; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UncheckedIOException; @@ -21,7 +24,9 @@ import java.util.Optional; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; -import fi.iki.elonen.NanoHTTPD; + +import fi.iki.elonen.NanoHTTPD.IHTTPSession; +import fi.iki.elonen.NanoHTTPD.Response; import fi.iki.elonen.NanoHTTPD.Response.Status; import moe.yushi.authlibinjector.YggdrasilConfiguration; import moe.yushi.authlibinjector.internal.org.json.simple.JSONArray; @@ -29,28 +34,28 @@ import moe.yushi.authlibinjector.internal.org.json.simple.JSONObject; import moe.yushi.authlibinjector.util.JsonUtils; import moe.yushi.authlibinjector.util.Logging; -public class LocalYggdrasilHttpd extends NanoHTTPD { +public class LegacySkinAPIFilter implements URLFilter { - public static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8"; - - private static final Pattern URL_SKINS = Pattern.compile("^/skins/MinecraftSkins/(?[^/]+)\\.png$"); + private static final Pattern PATH_SKINS = Pattern.compile("^/MinecraftSkins/(?[^/]+)\\.png$"); private YggdrasilConfiguration configuration; - public LocalYggdrasilHttpd(int port, YggdrasilConfiguration configuration) { - super("127.0.0.1", port); + public LegacySkinAPIFilter(YggdrasilConfiguration configuration) { this.configuration = configuration; } @Override - public Response serve(IHTTPSession session) { - return processAsSkin(session) - .orElseGet(() -> super.serve(session)); + public boolean canHandle(String domain, String path) { + return domain.equals("skins.minecraft.net"); } - private Optional processAsSkin(IHTTPSession session) { - Matcher matcher = URL_SKINS.matcher(session.getUri()); - if (!matcher.find()) return empty(); + @Override + public Optional handle(String domain, String path, IHTTPSession session) { + if (!domain.equals("skins.minecraft.net")) + return empty(); + Matcher matcher = PATH_SKINS.matcher(path); + if (!matcher.find()) + return empty(); String username = matcher.group("username"); Optional skinUrl; @@ -138,5 +143,4 @@ public class LocalYggdrasilHttpd extends NanoHTTPD { .map(JsonUtils::asJsonString) .orElseThrow(() -> newUncheckedIOException("Invalid JSON: Missing texture url"))); } - } diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHandle.java b/src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHandle.java deleted file mode 100644 index c50393f..0000000 --- a/src/main/java/moe/yushi/authlibinjector/httpd/LocalYggdrasilHandle.java +++ /dev/null @@ -1,44 +0,0 @@ -package moe.yushi.authlibinjector.httpd; - -import java.io.IOException; -import moe.yushi.authlibinjector.YggdrasilConfiguration; -import moe.yushi.authlibinjector.util.Logging; - -public class LocalYggdrasilHandle { - - private boolean started = false; - private YggdrasilConfiguration configuration; - private LocalYggdrasilHttpd httpd; - - private final Object _lock = new Object(); - - public LocalYggdrasilHandle(YggdrasilConfiguration configuration) { - this.configuration = configuration; - } - - public void ensureStarted() { - if (started) - return; - synchronized (_lock) { - if (started) - return; - if (configuration == null) - throw new IllegalStateException("Configuration hasn't been set yet"); - httpd = new LocalYggdrasilHttpd(0, configuration); - try { - httpd.start(); - } catch (IOException e) { - throw new IllegalStateException("Httpd failed to start"); - } - Logging.HTTPD.info("Httpd is running on port " + getLocalApiPort()); - started = true; - } - } - - public int getLocalApiPort() { - if (httpd == null) - return -1; - return httpd.getListeningPort(); - } - -} diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/URLFilter.java b/src/main/java/moe/yushi/authlibinjector/httpd/URLFilter.java new file mode 100644 index 0000000..d6ae9b2 --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/httpd/URLFilter.java @@ -0,0 +1,13 @@ +package moe.yushi.authlibinjector.httpd; + +import java.util.Optional; + +import fi.iki.elonen.NanoHTTPD.IHTTPSession; +import fi.iki.elonen.NanoHTTPD.Response; + +public interface URLFilter { + + boolean canHandle(String domain, String path); + + Optional handle(String domain, String path, IHTTPSession session); +} diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/URLProcessor.java b/src/main/java/moe/yushi/authlibinjector/httpd/URLProcessor.java new file mode 100644 index 0000000..7e63c8f --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/httpd/URLProcessor.java @@ -0,0 +1,108 @@ +package moe.yushi.authlibinjector.httpd; + +import static fi.iki.elonen.NanoHTTPD.Response.Status.INTERNAL_ERROR; +import static fi.iki.elonen.NanoHTTPD.Response.Status.NOT_FOUND; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import fi.iki.elonen.NanoHTTPD; +import moe.yushi.authlibinjector.util.Logging; + +public class URLProcessor { + + private static final Pattern URL_REGEX = Pattern.compile("^https?:\\/\\/(?[^\\/]+)(?\\/.*)$"); + private static final Pattern LOCAL_URL_REGEX = Pattern.compile("^/(?[^\\/]+)(?\\/.*)$"); + + private List filters; + private URLRedirector redirector; + + public URLProcessor(List filters, URLRedirector redirector) { + this.filters = filters; + this.redirector = redirector; + } + + public Optional transformURL(String inputUrl) { + Matcher matcher = URL_REGEX.matcher(inputUrl); + if (!matcher.find()) { + return Optional.empty(); + } + String domain = matcher.group("domain"); + String path = matcher.group("path"); + + Optional result = transform(domain, path); + if (result.isPresent()) { + Logging.TRANSFORM.fine("Transformed url [" + inputUrl + "] to [" + result.get() + "]"); + } + return result; + } + + private Optional transform(String domain, String path) { + boolean handleLocally = false; + for (URLFilter filter : filters) { + if (filter.canHandle(domain, path)) { + handleLocally = true; + break; + } + } + + if (handleLocally) { + return Optional.of("http://127.0.0.1:" + getLocalApiPort() + "/" + domain + path); + } + + return redirector.redirect(domain, path); + } + + private volatile NanoHTTPD httpd; + private final Object httpdLock = new Object(); + + private int getLocalApiPort() { + synchronized (httpdLock) { + if (httpd == null) { + httpd = createHttpd(); + try { + httpd.start(); + } catch (IOException e) { + throw new IllegalStateException("Httpd failed to start"); + } + Logging.HTTPD.info("Httpd is running on port " + httpd.getListeningPort()); + } + return httpd.getListeningPort(); + } + } + + private NanoHTTPD createHttpd() { + return new NanoHTTPD("127.0.0.1", 0) { + @Override + public Response serve(IHTTPSession session) { + Matcher matcher = LOCAL_URL_REGEX.matcher(session.getUri()); + if (matcher.find()) { + String domain = matcher.group("domain"); + String path = matcher.group("path"); + for (URLFilter filter : filters) { + if (filter.canHandle(domain, path)) { + Optional result; + try { + result = filter.handle(domain, path, session); + } catch (Throwable e) { + Logging.HTTPD.log(Level.WARNING, "An error occurred while processing request [" + session.getUri() + "]", e); + return newFixedLengthResponse(INTERNAL_ERROR, null, null); + } + + if (result.isPresent()) { + Logging.HTTPD.fine("Request to [" + session.getUri() + "] is handled by [" + filter + "]"); + return result.get(); + } + } + } + } + + Logging.HTTPD.fine("No handler is found for [" + session.getUri() + "]"); + return newFixedLengthResponse(NOT_FOUND, MIME_PLAINTEXT, "Not Found"); + } + }; + } +} diff --git a/src/main/java/moe/yushi/authlibinjector/httpd/URLRedirector.java b/src/main/java/moe/yushi/authlibinjector/httpd/URLRedirector.java new file mode 100644 index 0000000..6688f64 --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/httpd/URLRedirector.java @@ -0,0 +1,7 @@ +package moe.yushi.authlibinjector.httpd; + +import java.util.Optional; + +public interface URLRedirector { + Optional redirect(String domain, String path); +} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/ConstantURLTransformUnit.java b/src/main/java/moe/yushi/authlibinjector/transform/ConstantURLTransformUnit.java new file mode 100644 index 0000000..9a824aa --- /dev/null +++ b/src/main/java/moe/yushi/authlibinjector/transform/ConstantURLTransformUnit.java @@ -0,0 +1,24 @@ +package moe.yushi.authlibinjector.transform; + +import java.util.Optional; + +import moe.yushi.authlibinjector.httpd.URLProcessor; + +public class ConstantURLTransformUnit extends LdcTransformUnit { + + private URLProcessor urlProcessor; + + public ConstantURLTransformUnit(URLProcessor urlProcessor) { + this.urlProcessor = urlProcessor; + } + + @Override + protected Optional transformLdc(String input) { + return urlProcessor.transformURL(input); + } + + @Override + public String toString() { + return "Constant URL Transformer"; + } +} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/DomainBasedTransformUnit.java b/src/main/java/moe/yushi/authlibinjector/transform/DomainBasedTransformUnit.java deleted file mode 100644 index 704ca36..0000000 --- a/src/main/java/moe/yushi/authlibinjector/transform/DomainBasedTransformUnit.java +++ /dev/null @@ -1,38 +0,0 @@ -package moe.yushi.authlibinjector.transform; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public abstract class DomainBasedTransformUnit extends LdcTransformUnit { - - private static final Pattern URL_REGEX = Pattern.compile("^https?:\\/\\/(?[^\\/]+)(?\\/.*)$"); - - private Map domainMapping = new ConcurrentHashMap<>(); - - public Map getDomainMapping() { - return domainMapping; - } - - @Override - public Optional transformLdc(String input) { - Matcher matcher = URL_REGEX.matcher(input); - if (!matcher.find()) { - return Optional.empty(); - } - - String domain = matcher.group("domain"); - String subdirectory = domainMapping.get(domain); - if (subdirectory == null) { - return Optional.empty(); - } - - String path = matcher.group("path"); - - return Optional.of(getApiRoot() + subdirectory + path); - } - - protected abstract String getApiRoot(); -} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/LocalYggdrasilApiTransformUnit.java b/src/main/java/moe/yushi/authlibinjector/transform/LocalYggdrasilApiTransformUnit.java deleted file mode 100644 index f2dc9c5..0000000 --- a/src/main/java/moe/yushi/authlibinjector/transform/LocalYggdrasilApiTransformUnit.java +++ /dev/null @@ -1,34 +0,0 @@ -package moe.yushi.authlibinjector.transform; - -import java.util.Map; - -import moe.yushi.authlibinjector.YggdrasilConfiguration; -import moe.yushi.authlibinjector.httpd.LocalYggdrasilHandle; -import moe.yushi.authlibinjector.util.Logging; - -public class LocalYggdrasilApiTransformUnit extends DomainBasedTransformUnit { - - private LocalYggdrasilHandle handle; - - public LocalYggdrasilApiTransformUnit(YggdrasilConfiguration config) { - handle = new LocalYggdrasilHandle(config); - - Map mapping = getDomainMapping(); - if (Boolean.TRUE.equals(config.getMeta().get("feature.legacy_skin_api"))) { - Logging.CONFIG.info("Disabled local redirect for legacy skin API, as the remote Yggdrasil server supports it"); - } else { - mapping.put("skins.minecraft.net", "skins"); - } - } - - @Override - protected String getApiRoot() { - handle.ensureStarted(); - return "http://127.0.0.1:" + handle.getLocalApiPort() + "/"; - } - - @Override - public String toString() { - return "Local Yggdrasil API Transformer"; - } -} diff --git a/src/main/java/moe/yushi/authlibinjector/transform/RemoteYggdrasilTransformUnit.java b/src/main/java/moe/yushi/authlibinjector/transform/RemoteYggdrasilTransformUnit.java deleted file mode 100644 index e4efdc0..0000000 --- a/src/main/java/moe/yushi/authlibinjector/transform/RemoteYggdrasilTransformUnit.java +++ /dev/null @@ -1,28 +0,0 @@ -package moe.yushi.authlibinjector.transform; - -import java.util.Map; - -public class RemoteYggdrasilTransformUnit extends DomainBasedTransformUnit { - - private String apiRoot; - - public RemoteYggdrasilTransformUnit(String apiRoot) { - this.apiRoot = apiRoot; - - Map mapping = getDomainMapping(); - mapping.put("api.mojang.com", "api"); - mapping.put("authserver.mojang.com", "authserver"); - mapping.put("sessionserver.mojang.com", "sessionserver"); - mapping.put("skins.minecraft.net", "skins"); - } - - @Override - protected String getApiRoot() { - return apiRoot; - } - - @Override - public String toString() { - return "Yggdrasil API Transformer"; - } -} diff --git a/src/main/java/moe/yushi/authlibinjector/util/IOUtils.java b/src/main/java/moe/yushi/authlibinjector/util/IOUtils.java index 52407de..b8ed853 100644 --- a/src/main/java/moe/yushi/authlibinjector/util/IOUtils.java +++ b/src/main/java/moe/yushi/authlibinjector/util/IOUtils.java @@ -11,6 +11,8 @@ import java.net.URL; public final class IOUtils { + public static final String CONTENT_TYPE_JSON = "application/json; charset=utf-8"; + public static byte[] getURL(String url) throws IOException { try (InputStream in = new URL(url).openStream()) { return asBytes(in); diff --git a/src/test/java/moe/yushi/authlibinjector/test/DefaultURLRedirectorTest.java b/src/test/java/moe/yushi/authlibinjector/test/DefaultURLRedirectorTest.java new file mode 100644 index 0000000..25b8f14 --- /dev/null +++ b/src/test/java/moe/yushi/authlibinjector/test/DefaultURLRedirectorTest.java @@ -0,0 +1,90 @@ +package moe.yushi.authlibinjector.test; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.junit.Assert.assertEquals; + +import java.util.Optional; + +import org.junit.Test; + +import moe.yushi.authlibinjector.YggdrasilConfiguration; +import moe.yushi.authlibinjector.httpd.DefaultURLRedirector; + +public class DefaultURLRedirectorTest { + + private String apiRoot = "https://yggdrasil.example.com/"; + private DefaultURLRedirector redirector = new DefaultURLRedirector(new YggdrasilConfiguration(apiRoot, emptyList(), emptyMap(), Optional.empty())); + + private void testTransform(String domain, String path, String output) { + assertEquals(redirector.redirect(domain, path).get(), output); + } + + @Test + public void testReplace() { + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository + "api.mojang.com", "/profiles/", + "https://yggdrasil.example.com/api/profiles/"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService + "sessionserver.mojang.com", "/session/minecraft/join", + "https://yggdrasil.example.com/sessionserver/session/minecraft/join"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService + "sessionserver.mojang.com", "/session/minecraft/hasJoined", + "https://yggdrasil.example.com/sessionserver/session/minecraft/hasJoined"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication + "authserver.mojang.com", "/authenticate", + "https://yggdrasil.example.com/authserver/authenticate"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication + "authserver.mojang.com", "/refresh", + "https://yggdrasil.example.com/authserver/refresh"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication + "authserver.mojang.com", "/validate", + "https://yggdrasil.example.com/authserver/validate"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication + "authserver.mojang.com", "/invalidate", + "https://yggdrasil.example.com/authserver/invalidate"); + + testTransform( + // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication + "authserver.mojang.com", "/signout", + "https://yggdrasil.example.com/authserver/signout"); + + testTransform( + // from: [mcp940]/net.minecraft.client.entity.AbstractClientPlayer + // issue: yushijinhun/authlib-injector#7 + "skins.minecraft.net", "/MinecraftSkins/%s.png", + "https://yggdrasil.example.com/skins/MinecraftSkins/%s.png"); + + testTransform( + // from: [bungeecord@806a6dfacaadb7538860889f8a50612bb496a2d3]/net.md_5.bungee.connection.InitialHandler + // url: https://github.com/SpigotMC/BungeeCord/blob/806a6dfacaadb7538860889f8a50612bb496a2d3/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L409 + "sessionserver.mojang.com", "/session/minecraft/hasJoined?username=", + "https://yggdrasil.example.com/sessionserver/session/minecraft/hasJoined?username="); + + testTransform( + // from: [wiki.vg]/Mojang_API/Username -> UUID at time + // url: http://wiki.vg/Mojang_API#Username_-.3E_UUID_at_time + // issue: yushijinhun/authlib-injector#6 + "api.mojang.com", "/users/profiles/minecraft/", + "https://yggdrasil.example.com/api/users/profiles/minecraft/"); + } + + @Test + public void testEmpty() { + assertEquals(redirector.redirect("example.com", "/path"), Optional.empty()); + } + +} diff --git a/src/test/java/moe/yushi/authlibinjector/test/UrlReplaceTest.java b/src/test/java/moe/yushi/authlibinjector/test/UrlReplaceTest.java deleted file mode 100644 index b7c42bd..0000000 --- a/src/test/java/moe/yushi/authlibinjector/test/UrlReplaceTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package moe.yushi.authlibinjector.test; - -import static org.junit.Assert.assertEquals; -import java.util.Arrays; -import java.util.Collection; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; -import moe.yushi.authlibinjector.transform.RemoteYggdrasilTransformUnit; - -@RunWith(Parameterized.class) -public class UrlReplaceTest { - - private static final String apiRoot = "https://yggdrasil.example.com/"; - - @Parameters - public static Collection data() { - // @formatter:off - return Arrays.asList(new Object[][] { - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository - "https://api.mojang.com/profiles/", - "https://yggdrasil.example.com/api/profiles/" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService - "https://sessionserver.mojang.com/session/minecraft/join", - "https://yggdrasil.example.com/sessionserver/session/minecraft/join" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService - "https://sessionserver.mojang.com/session/minecraft/hasJoined", - "https://yggdrasil.example.com/sessionserver/session/minecraft/hasJoined" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication - "https://authserver.mojang.com/authenticate", - "https://yggdrasil.example.com/authserver/authenticate" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication - "https://authserver.mojang.com/refresh", - "https://yggdrasil.example.com/authserver/refresh" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication - "https://authserver.mojang.com/validate", - "https://yggdrasil.example.com/authserver/validate" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication - "https://authserver.mojang.com/invalidate", - "https://yggdrasil.example.com/authserver/invalidate" - }, - { - // from: [com.mojang:authlib:1.5.24]/com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication - "https://authserver.mojang.com/signout", - "https://yggdrasil.example.com/authserver/signout" - }, - { - // from: [mcp940]/net.minecraft.client.entity.AbstractClientPlayer - // issue: yushijinhun/authlib-injector#7 - "http://skins.minecraft.net/MinecraftSkins/%s.png", - "https://yggdrasil.example.com/skins/MinecraftSkins/%s.png" - }, - { - // from: [bungeecord@806a6dfacaadb7538860889f8a50612bb496a2d3]/net.md_5.bungee.connection.InitialHandler - // url: https://github.com/SpigotMC/BungeeCord/blob/806a6dfacaadb7538860889f8a50612bb496a2d3/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L409 - "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=", - "https://yggdrasil.example.com/sessionserver/session/minecraft/hasJoined?username=" - }, - { - // from: [wiki.vg]/Mojang_API/Username -> UUID at time - // url: http://wiki.vg/Mojang_API#Username_-.3E_UUID_at_time - // issue: yushijinhun/authlib-injector#6 - "https://api.mojang.com/users/profiles/minecraft/", - "https://yggdrasil.example.com/api/users/profiles/minecraft/" - } - }); - // @formatter:on - } - - @Parameter(0) - public String input; - - @Parameter(1) - public String output; - - @Test - public void test() { - RemoteYggdrasilTransformUnit transformer = new RemoteYggdrasilTransformUnit(apiRoot); - assertEquals(output, transformer.transformLdc(input).get()); - } - -}