diff options
author | ByteHamster <info@bytehamster.com> | 2022-11-06 11:34:24 +0100 |
---|---|---|
committer | ByteHamster <info@bytehamster.com> | 2022-11-06 12:28:30 +0100 |
commit | b140d7297a82dc45037030d4842b60bc2d69df02 (patch) | |
tree | 7faa47ffb3fdbc147c3c30c8fe591463506ea380 /net/common | |
parent | e4d4c69519c7854f1852d80a3b47eebe30e6a3d5 (diff) | |
download | AntennaPod-b140d7297a82dc45037030d4842b60bc2d69df02.zip |
Move DownloadService-Interface to new module
Diffstat (limited to 'net/common')
-rw-r--r-- | net/common/README.md | 3 | ||||
-rw-r--r-- | net/common/build.gradle | 12 | ||||
-rw-r--r-- | net/common/src/main/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | net/common/src/main/java/de/danoeh/antennapod/net/common/UrlChecker.java | 139 | ||||
-rw-r--r-- | net/common/src/test/java/de/danoeh/antennapod/net/common/UrlCheckerTest.java | 175 |
5 files changed, 330 insertions, 0 deletions
diff --git a/net/common/README.md b/net/common/README.md new file mode 100644 index 000000000..3bd8b232e --- /dev/null +++ b/net/common/README.md @@ -0,0 +1,3 @@ +# :net:common + +This module contains general network related utilities. diff --git a/net/common/build.gradle b/net/common/build.gradle new file mode 100644 index 000000000..c519aa653 --- /dev/null +++ b/net/common/build.gradle @@ -0,0 +1,12 @@ +plugins { + id("com.android.library") +} +apply from: "../../common.gradle" + +dependencies { + annotationProcessor "androidx.annotation:annotation:$annotationVersion" + implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" + + testImplementation "junit:junit:$junitVersion" + testImplementation "org.robolectric:robolectric:$robolectricVersion" +} diff --git a/net/common/src/main/AndroidManifest.xml b/net/common/src/main/AndroidManifest.xml new file mode 100644 index 000000000..702944daa --- /dev/null +++ b/net/common/src/main/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="de.danoeh.antennapod.net.common" /> diff --git a/net/common/src/main/java/de/danoeh/antennapod/net/common/UrlChecker.java b/net/common/src/main/java/de/danoeh/antennapod/net/common/UrlChecker.java new file mode 100644 index 000000000..4eb1fd6a5 --- /dev/null +++ b/net/common/src/main/java/de/danoeh/antennapod/net/common/UrlChecker.java @@ -0,0 +1,139 @@ +package de.danoeh.antennapod.net.common; + +import android.net.Uri; +import android.text.TextUtils; +import androidx.annotation.NonNull; +import android.util.Log; + +import okhttp3.HttpUrl; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Provides methods for checking and editing a URL. + */ +public final class UrlChecker { + + /** + * Class shall not be instantiated. + */ + private UrlChecker() { + } + + /** + * Logging tag. + */ + private static final String TAG = "UrlChecker"; + + private static final String AP_SUBSCRIBE = "antennapod-subscribe://"; + private static final String AP_SUBSCRIBE_DEEPLINK = "antennapod.org/deeplink/subscribe"; + + /** + * Checks if URL is valid and modifies it if necessary. + * + * @param url The url which is going to be prepared + * @return The prepared url + */ + public static String prepareUrl(@NonNull String url) { + url = url.trim(); + String lowerCaseUrl = url.toLowerCase(Locale.ROOT); // protocol names are case insensitive + if (lowerCaseUrl.startsWith("feed://")) { + Log.d(TAG, "Replacing feed:// with http://"); + return prepareUrl(url.substring("feed://".length())); + } else if (lowerCaseUrl.startsWith("pcast://")) { + Log.d(TAG, "Removing pcast://"); + return prepareUrl(url.substring("pcast://".length())); + } else if (lowerCaseUrl.startsWith("pcast:")) { + Log.d(TAG, "Removing pcast:"); + return prepareUrl(url.substring("pcast:".length())); + } else if (lowerCaseUrl.startsWith("itpc")) { + Log.d(TAG, "Replacing itpc:// with http://"); + return prepareUrl(url.substring("itpc://".length())); + } else if (lowerCaseUrl.startsWith(AP_SUBSCRIBE)) { + Log.d(TAG, "Removing antennapod-subscribe://"); + return prepareUrl(url.substring(AP_SUBSCRIBE.length())); + } else if (lowerCaseUrl.contains(AP_SUBSCRIBE_DEEPLINK)) { + Log.d(TAG, "Removing " + AP_SUBSCRIBE_DEEPLINK); + String removedWebsite = url.substring(url.indexOf("?url=") + "?url=".length()); + try { + return prepareUrl(URLDecoder.decode(removedWebsite, "UTF-8")); + } catch (UnsupportedEncodingException e) { + return prepareUrl(removedWebsite); + } + } else if (!(lowerCaseUrl.startsWith("http://") || lowerCaseUrl.startsWith("https://"))) { + Log.d(TAG, "Adding http:// at the beginning of the URL"); + return "http://" + url; + } else { + return url; + } + } + + /** + * Checks if URL is valid and modifies it if necessary. + * This method also handles protocol relative URLs. + * + * @param url The url which is going to be prepared + * @param base The url against which the (possibly relative) url is applied. If this is null, + * the result of prepareURL(url) is returned instead. + * @return The prepared url + */ + public static String prepareUrl(String url, String base) { + if (base == null) { + return prepareUrl(url); + } + url = url.trim(); + base = prepareUrl(base); + Uri urlUri = Uri.parse(url); + Uri baseUri = Uri.parse(base); + if (urlUri.isRelative() && baseUri.isAbsolute()) { + return urlUri.buildUpon().scheme(baseUri.getScheme()).build().toString(); + } else { + return prepareUrl(url); + } + } + + public static boolean containsUrl(List<String> list, String url) { + for (String item : list) { + if (urlEquals(item, url)) { + return true; + } + } + return false; + } + + public static boolean urlEquals(String string1, String string2) { + HttpUrl url1 = HttpUrl.parse(string1); + HttpUrl url2 = HttpUrl.parse(string2); + if (!url1.host().equals(url2.host())) { + return false; + } + List<String> pathSegments1 = normalizePathSegments(url1.pathSegments()); + List<String> pathSegments2 = normalizePathSegments(url2.pathSegments()); + if (!pathSegments1.equals(pathSegments2)) { + return false; + } + if (TextUtils.isEmpty(url1.query())) { + return TextUtils.isEmpty(url2.query()); + } + return url1.query().equals(url2.query()); + } + + /** + * Removes empty segments and converts all to lower case. + * @param input List of path segments + * @return Normalized list of path segments + */ + private static List<String> normalizePathSegments(List<String> input) { + List<String> result = new ArrayList<>(); + for (String string : input) { + if (!TextUtils.isEmpty(string)) { + result.add(string.toLowerCase(Locale.ROOT)); + } + } + return result; + } +} diff --git a/net/common/src/test/java/de/danoeh/antennapod/net/common/UrlCheckerTest.java b/net/common/src/test/java/de/danoeh/antennapod/net/common/UrlCheckerTest.java new file mode 100644 index 000000000..ba9f1dcbb --- /dev/null +++ b/net/common/src/test/java/de/danoeh/antennapod/net/common/UrlCheckerTest.java @@ -0,0 +1,175 @@ +package de.danoeh.antennapod.net.common; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test class for {@link UrlChecker} + */ +@RunWith(RobolectricTestRunner.class) +public class UrlCheckerTest { + + @Test + public void testCorrectURLHttp() { + final String in = "http://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals(in, out); + } + + @Test + public void testCorrectURLHttps() { + final String in = "https://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals(in, out); + } + + @Test + public void testMissingProtocol() { + final String in = "example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testFeedProtocol() { + final String in = "feed://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testPcastProtocolNoScheme() { + final String in = "pcast://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testItpcProtocol() { + final String in = "itpc://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testItpcProtocolWithScheme() { + final String in = "itpc://https://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("https://example.com", out); + } + + @Test + public void testWhiteSpaceUrlShouldNotAppend() { + final String in = "\n http://example.com \t"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testWhiteSpaceShouldAppend() { + final String in = "\n example.com \t"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testAntennaPodSubscribeProtocolNoScheme() { + final String in = "antennapod-subscribe://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("http://example.com", out); + } + + @Test + public void testPcastProtocolWithScheme() { + final String in = "pcast://https://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("https://example.com", out); + } + + @Test + public void testAntennaPodSubscribeProtocolWithScheme() { + final String in = "antennapod-subscribe://https://example.com"; + final String out = UrlChecker.prepareUrl(in); + assertEquals("https://example.com", out); + } + + @Test + public void testAntennaPodSubscribeDeeplink() throws UnsupportedEncodingException { + final String feed = "http://example.org/podcast.rss"; + assertEquals(feed, UrlChecker.prepareUrl("https://antennapod.org/deeplink/subscribe?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("http://antennapod.org/deeplink/subscribe?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("http://antennapod.org/deeplink/subscribe/?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("https://www.antennapod.org/deeplink/subscribe?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("http://www.antennapod.org/deeplink/subscribe?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("http://www.antennapod.org/deeplink/subscribe/?url=" + feed)); + assertEquals(feed, UrlChecker.prepareUrl("http://www.antennapod.org/deeplink/subscribe?url=" + + URLEncoder.encode(feed, "UTF-8"))); + assertEquals(feed, UrlChecker.prepareUrl("http://www.antennapod.org/deeplink/subscribe?url=" + + "example.org/podcast.rss")); + } + + @Test + public void testProtocolRelativeUrlIsAbsolute() { + final String in = "https://example.com"; + final String inBase = "http://examplebase.com"; + final String out = UrlChecker.prepareUrl(in, inBase); + assertEquals(in, out); + } + + @Test + public void testProtocolRelativeUrlIsRelativeHttps() { + final String in = "//example.com"; + final String inBase = "https://examplebase.com"; + final String out = UrlChecker.prepareUrl(in, inBase); + assertEquals("https://example.com", out); + } + + @Test + public void testProtocolRelativeUrlIsHttpsWithApSubscribeProtocol() { + final String in = "//example.com"; + final String inBase = "antennapod-subscribe://https://examplebase.com"; + final String out = UrlChecker.prepareUrl(in, inBase); + assertEquals("https://example.com", out); + } + + @Test + public void testProtocolRelativeUrlBaseUrlNull() { + final String in = "example.com"; + final String out = UrlChecker.prepareUrl(in, null); + assertEquals("http://example.com", out); + } + + @Test + public void testUrlEqualsSame() { + assertTrue(UrlChecker.urlEquals("https://www.example.com/test", "https://www.example.com/test")); + assertTrue(UrlChecker.urlEquals("https://www.example.com/test", "https://www.example.com/test/")); + assertTrue(UrlChecker.urlEquals("https://www.example.com/test", "https://www.example.com//test")); + assertTrue(UrlChecker.urlEquals("https://www.example.com", "https://www.example.com/")); + assertTrue(UrlChecker.urlEquals("https://www.example.com", "http://www.example.com")); + assertTrue(UrlChecker.urlEquals("http://www.example.com/", "https://www.example.com/")); + assertTrue(UrlChecker.urlEquals("https://www.example.com/?id=42", "https://www.example.com/?id=42")); + assertTrue(UrlChecker.urlEquals("https://example.com/podcast%20test", "https://example.com/podcast test")); + assertTrue(UrlChecker.urlEquals("https://example.com/?a=podcast%20test", "https://example.com/?a=podcast test")); + assertTrue(UrlChecker.urlEquals("https://example.com/?", "https://example.com/")); + assertTrue(UrlChecker.urlEquals("https://example.com/?", "https://example.com")); + assertTrue(UrlChecker.urlEquals("https://Example.com", "https://example.com")); + assertTrue(UrlChecker.urlEquals("https://example.com/test", "https://example.com/Test")); + } + + @Test + public void testUrlEqualsDifferent() { + assertFalse(UrlChecker.urlEquals("https://www.example.com/test", "https://www.example2.com/test")); + assertFalse(UrlChecker.urlEquals("https://www.example.com/test", "https://www.example.de/test")); + assertFalse(UrlChecker.urlEquals("https://example.com/", "https://otherpodcast.example.com/")); + assertFalse(UrlChecker.urlEquals("https://www.example.com/?id=42&a=b", "https://www.example.com/?id=43&a=b")); + assertFalse(UrlChecker.urlEquals("https://example.com/podcast%25test", "https://example.com/podcast test")); + } +} |