From 18f53d52b7b955e76b1eb40dbc4972ec6e17383f Mon Sep 17 00:00:00 2001 From: Iscle Date: Mon, 20 Dec 2021 22:41:11 +0100 Subject: [PATCH 1/4] ApResolver: Removed unused private function return --- lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java index aefd3eea..fdea1b02 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java +++ b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java @@ -62,8 +62,7 @@ private static List getUrls(@NotNull JsonObject body, @NotNull String ty return list; } - @NotNull - private static Map> request(@NotNull String... types) throws IOException { + private static void request(@NotNull String... types) throws IOException { if (types.length == 0) throw new IllegalArgumentException(); StringBuilder url = new StringBuilder(BASE_URL + "?"); @@ -88,8 +87,6 @@ private static Map> request(@NotNull String... types) throw } LOGGER.info("Loaded aps into pool: " + pool); - - return map; } finally { conn.disconnect(); } From 704ca19c52b1360c07f7c7ac6ca5b39549a8adaa Mon Sep 17 00:00:00 2001 From: Iscle Date: Mon, 20 Dec 2021 23:39:45 +0100 Subject: [PATCH 2/4] ApResolver: Use OkHttpClient for requesting the list With the old implementation, the ap request wasn't going through the proxy (if there was one configured). Now, it uses the OkHttpClient we use in other parts of the app which will be configured to use the proxy if needed. --- .../xyz/gianlu/librespot/core/ApResolver.java | 72 ++++++++++--------- .../xyz/gianlu/librespot/core/Session.java | 25 ++++--- .../gianlu/librespot/dealer/ApiClient.java | 2 +- .../gianlu/librespot/dealer/DealerClient.java | 3 +- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java index fdea1b02..8c1f0973 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java +++ b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java @@ -20,15 +20,15 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.io.InputStreamReader; import java.io.Reader; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -40,29 +40,36 @@ */ public final class ApResolver { private static final String BASE_URL = "http://apresolve.spotify.com/"; - private static final Map> pool = new HashMap<>(3); private static final Logger LOGGER = LoggerFactory.getLogger(ApResolver.class); - private static volatile boolean poolReady = false; - public static void fillPool() throws IOException { - if (!poolReady) request("accesspoint", "dealer", "spclient"); + private final OkHttpClient client; + private final Map> pool = new HashMap<>(3); + private volatile boolean poolReady = false; + + public ApResolver(OkHttpClient client) throws IOException { + this.client = client; + fillPool(); } - public static void refreshPool() throws IOException { + private void fillPool() throws IOException { + request("accesspoint", "dealer", "spclient"); + } + + public void refreshPool() throws IOException { poolReady = false; pool.clear(); - request("accesspoint", "dealer", "spclient"); + fillPool(); } @NotNull - private static List getUrls(@NotNull JsonObject body, @NotNull String type) { + private List getUrls(@NotNull JsonObject body, @NotNull String type) { JsonArray aps = body.getAsJsonArray(type); List list = new ArrayList<>(aps.size()); for (JsonElement ap : aps) list.add(ap.getAsString()); return list; } - private static void request(@NotNull String... types) throws IOException { + private void request(@NotNull String... types) throws IOException { if (types.length == 0) throw new IllegalArgumentException(); StringBuilder url = new StringBuilder(BASE_URL + "?"); @@ -71,28 +78,29 @@ private static void request(@NotNull String... types) throws IOException { url.append("type=").append(types[i]); } - HttpURLConnection conn = (HttpURLConnection) new URL(url.toString()).openConnection(); - conn.connect(); - - try (Reader reader = new InputStreamReader(conn.getInputStream())) { - JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); - HashMap> map = new HashMap<>(); - for (String type : types) - map.put(type, getUrls(obj, type)); + Request request = new Request.Builder() + .url(url.toString()) + .build(); + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + try (Reader reader = response.body().charStream()) { + JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); + HashMap> map = new HashMap<>(); + for (String type : types) + map.put(type, getUrls(obj, type)); + + synchronized (pool) { + pool.putAll(map); + poolReady = true; + pool.notifyAll(); + } - synchronized (pool) { - pool.putAll(map); - poolReady = true; - pool.notifyAll(); + LOGGER.info("Loaded aps into pool: " + pool); } - - LOGGER.info("Loaded aps into pool: " + pool); - } finally { - conn.disconnect(); } } - private static void waitForPool() { + private void waitForPool() { if (!poolReady) { synchronized (pool) { try { @@ -105,7 +113,7 @@ private static void waitForPool() { } @NotNull - private static String getRandomOf(@NotNull String type) { + private String getRandomOf(@NotNull String type) { waitForPool(); List urls = pool.get(type); @@ -114,17 +122,17 @@ private static String getRandomOf(@NotNull String type) { } @NotNull - public static String getRandomDealer() { + public String getRandomDealer() { return getRandomOf("dealer"); } @NotNull - public static String getRandomSpclient() { + public String getRandomSpclient() { return getRandomOf("spclient"); } @NotNull - public static String getRandomAccesspoint() { + public String getRandomAccesspoint() { return getRandomOf("accesspoint"); } } diff --git a/lib/src/main/java/xyz/gianlu/librespot/core/Session.java b/lib/src/main/java/xyz/gianlu/librespot/core/Session.java index 6a37d70c..c232f35a 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/core/Session.java +++ b/lib/src/main/java/xyz/gianlu/librespot/core/Session.java @@ -100,6 +100,7 @@ public final class Session implements Closeable { (byte) 0xeb, (byte) 0x00, (byte) 0x06, (byte) 0xa2, (byte) 0x5a, (byte) 0xee, (byte) 0xa1, (byte) 0x1b, (byte) 0x13, (byte) 0x87, (byte) 0x3c, (byte) 0xd7, (byte) 0x19, (byte) 0xe6, (byte) 0x55, (byte) 0xbd }; + private final ApResolver apResolver; private final DiffieHellman keys; private final Inner inner; private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NameThreadFactory(r -> "session-scheduler-" + r.hashCode())); @@ -128,11 +129,13 @@ public final class Session implements Closeable { private volatile boolean closing = false; private volatile ScheduledFuture scheduledReconnect = null; - private Session(@NotNull Inner inner, @NotNull String addr) throws IOException { + private Session(@NotNull Inner inner) throws IOException { this.inner = inner; this.keys = new DiffieHellman(inner.random); - this.conn = ConnectionHolder.create(addr, inner.conf); this.client = createClient(inner.conf); + this.apResolver = new ApResolver(client); + String addr = apResolver.getRandomAccesspoint(); + this.conn = ConnectionHolder.create(addr, inner.conf); LOGGER.info("Created new session! {deviceId: {}, ap: {}, proxy: {}} ", inner.deviceId, addr, inner.conf.proxyEnabled); } @@ -566,6 +569,11 @@ public void send(Packet.Type cmd, byte[] payload) throws IOException { } } + @NotNull + public ApResolver apResolver() { + return apResolver; + } + @NotNull public MercuryClient mercury() { waitAuthLock(); @@ -710,14 +718,14 @@ private void reconnect() { } try { - ApResolver.refreshPool(); + apResolver.refreshPool(); if (conn != null) { conn.socket.close(); receiver.stop(); } - conn = ConnectionHolder.create(ApResolver.getRandomAccesspoint(), inner.conf); + conn = ConnectionHolder.create(apResolver.getRandomAccesspoint(), inner.conf); connect(); authenticatePartial(Authentication.LoginCredentials.newBuilder() .setUsername(apWelcome.getCanonicalUsername()) @@ -1021,10 +1029,9 @@ public Session create() throws IOException, GeneralSecurityException, SpotifyAut if (loginCredentials == null) throw new IllegalStateException("You must select an authentication method."); - ApResolver.fillPool(); TimeProvider.init(conf); - Session session = new Session(new Inner(deviceType, deviceName, deviceId, preferredLocale, conf), ApResolver.getRandomAccesspoint()); + Session session = new Session(new Inner(deviceType, deviceName, deviceId, preferredLocale, conf)); session.connect(); session.authenticate(loginCredentials); return session; @@ -1253,9 +1260,9 @@ private ConnectionHolder(@NotNull Socket socket) throws IOException { @NotNull static ConnectionHolder create(@NotNull String addr, @NotNull Configuration conf) throws IOException { - int colon = addr.indexOf(':'); - String apAddr = addr.substring(0, colon); - int apPort = Integer.parseInt(addr.substring(colon + 1)); + String[] split = addr.split(":"); + String apAddr = split[0]; + int apPort = Integer.parseInt(split[1]); if (!conf.proxyEnabled || conf.proxyType == Proxy.Type.DIRECT) return new ConnectionHolder(new Socket(apAddr, apPort)); diff --git a/lib/src/main/java/xyz/gianlu/librespot/dealer/ApiClient.java b/lib/src/main/java/xyz/gianlu/librespot/dealer/ApiClient.java index 88a1b40d..3e1fac63 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/dealer/ApiClient.java +++ b/lib/src/main/java/xyz/gianlu/librespot/dealer/ApiClient.java @@ -46,7 +46,7 @@ public final class ApiClient { public ApiClient(@NotNull Session session) { this.session = session; - this.baseUrl = "https://" + ApResolver.getRandomSpclient(); + this.baseUrl = "https://" + session.apResolver().getRandomSpclient(); } @NotNull diff --git a/lib/src/main/java/xyz/gianlu/librespot/dealer/DealerClient.java b/lib/src/main/java/xyz/gianlu/librespot/dealer/DealerClient.java index 005ab8eb..887b62c1 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/dealer/DealerClient.java +++ b/lib/src/main/java/xyz/gianlu/librespot/dealer/DealerClient.java @@ -31,7 +31,6 @@ import xyz.gianlu.librespot.common.BytesArrayList; import xyz.gianlu.librespot.common.NameThreadFactory; import xyz.gianlu.librespot.common.Utils; -import xyz.gianlu.librespot.core.ApResolver; import xyz.gianlu.librespot.core.Session; import xyz.gianlu.librespot.mercury.MercuryClient; @@ -76,7 +75,7 @@ private static Map getHeaders(@NotNull JsonObject obj) { */ public synchronized void connect() throws IOException, MercuryClient.MercuryException { conn = new ConnectionHolder(session, new Request.Builder() - .url(String.format("wss://%s/?access_token=%s", ApResolver.getRandomDealer(), session.tokens().get("playlist-read"))) + .url(String.format("wss://%s/?access_token=%s", session.apResolver().getRandomDealer(), session.tokens().get("playlist-read"))) .build()); } From dae67be307ee0dc52e66ed5416ee46090fd2a245 Mon Sep 17 00:00:00 2001 From: Iscle Date: Wed, 22 Dec 2021 22:22:10 +0100 Subject: [PATCH 3/4] ApResolver: Make getUrls static --- lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java index 8c1f0973..7618eaba 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java +++ b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java @@ -62,7 +62,7 @@ public void refreshPool() throws IOException { } @NotNull - private List getUrls(@NotNull JsonObject body, @NotNull String type) { + private static List getUrls(@NotNull JsonObject body, @NotNull String type) { JsonArray aps = body.getAsJsonArray(type); List list = new ArrayList<>(aps.size()); for (JsonElement ap : aps) list.add(ap.getAsString()); From d240c5a483f522f647d868dd106037db3f5a4d50 Mon Sep 17 00:00:00 2001 From: Iscle Date: Wed, 22 Dec 2021 22:24:13 +0100 Subject: [PATCH 4/4] ApResolver: Make request inside a try-with-resources block --- .../main/java/xyz/gianlu/librespot/core/ApResolver.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java index 7618eaba..a2b7dd65 100644 --- a/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java +++ b/lib/src/main/java/xyz/gianlu/librespot/core/ApResolver.java @@ -23,6 +23,7 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import okhttp3.ResponseBody; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,9 +82,10 @@ private void request(@NotNull String... types) throws IOException { Request request = new Request.Builder() .url(url.toString()) .build(); - Response response = client.newCall(request).execute(); - if (response.isSuccessful()) { - try (Reader reader = response.body().charStream()) { + try (Response response = client.newCall(request).execute()) { + ResponseBody body = response.body(); + if (body == null) throw new IOException("No body"); + try (Reader reader = body.charStream()) { JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); HashMap> map = new HashMap<>(); for (String type : types)