Skip to content

Commit cc02c37

Browse files
authored
Feature/custom url fix (#29)
* Extract function to create Url from host (domain), add tests and implement logic similar to iOS * Was using Roboelectric because original implementation used function from Android SDK, not the case anymore * Remove unused functions
1 parent 832e89d commit cc02c37

File tree

4 files changed

+150
-17
lines changed

4 files changed

+150
-17
lines changed

GenericApp/app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ dependencies {
7474
implementation 'com.google.firebase:firebase-messaging-ktx'
7575
implementation 'com.google.android.material:material:1.12.0'
7676
implementation project(':ORLib')
77+
78+
// Unit testing framework
79+
testImplementation 'junit:junit:4.13.2'
7780
}
7881

7982
task sourcesJar(type: Jar) {

GenericApp/app/src/main/java/io/openremote/app/ui/HostSelectionFragment.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,7 @@ class HostSelectionFragment : Fragment() {
5151

5252
private fun connectToHost(host: String) {
5353
parentActivity.binding.progressBar.visibility = View.VISIBLE
54-
val url = when {
55-
URLUtil.isValidUrl(host) -> host.plus("/api/master")
56-
UrlUtils.isIpAddress(host) -> "https://${host}/api/master"
57-
!UrlUtils.startsWithHttp(host) && UrlUtils.endsWithTld(host) -> "https://${host}/api/master"
58-
else -> "https://${host}.openremote.app/api/master"
59-
}
54+
val url = UrlUtils.hostToUrl(host).plus("/api/master")
6055
parentActivity.apiManager = ApiManager(url)
6156
parentActivity.apiManager.getConsoleConfig { statusCode, consoleConfig, error ->
6257
when (statusCode) {
Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
package io.openremote.app.util
22

33
object UrlUtils {
4-
fun isIpAddress(url: String): Boolean {
5-
val ipPattern = Regex(
6-
"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:\\d{1,5})?$"
4+
fun isIpV6NoScheme(url: String): Boolean {
5+
val ipv6Pattern = Regex(
6+
"^(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6}))$"
77
)
8-
return ipPattern.matches(url)
8+
return ipv6Pattern.matches(url)
99
}
10-
fun startsWithHttp(url: String): Boolean {
11-
return url.startsWith("http://") || url.startsWith("https://")
10+
11+
fun startsWithScheme(url: String): Boolean {
12+
val schemePattern = Regex("^[a-zA-Z]+://.*$")
13+
return schemePattern.matches(url)
1214
}
1315

14-
fun endsWithTld(url: String): Boolean {
15-
val tldPattern = Regex(
16-
"(?:[a-zA-Z]*\\.)+([a-zA-Z]+)(?:\\/.*)?"
17-
)
18-
return tldPattern.matches(url)
16+
fun hostToUrl(host: String): String {
17+
return when {
18+
isIpV6NoScheme(host) -> "https://[${host}]"
19+
startsWithScheme(host) ->
20+
if (host.contains(".") || host.contains("[")) host else "${host}.openremote.app"
21+
(host.contains(".") || host.contains("[")) -> "https://${host}"
22+
else -> "https://${host}.openremote.app"
23+
}
1924
}
2025
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import io.openremote.app.util.UrlUtils
2+
import org.junit.Test
3+
import org.junit.Assert.assertEquals
4+
5+
class UrlUtilsTest {
6+
7+
@Test
8+
fun fqdnWithScheme() {
9+
assertEquals("http://www.example.com", UrlUtils.hostToUrl("http://www.example.com"))
10+
assertEquals("https://www.example.com", UrlUtils.hostToUrl("https://www.example.com"))
11+
}
12+
13+
@Test
14+
fun fqdnWithNonWebScheme() {
15+
assertEquals("ftp://www.example.com", UrlUtils.hostToUrl("ftp://www.example.com"))
16+
}
17+
18+
@Test
19+
fun fqdnNoScheme() {
20+
assertEquals("https://www.example.com", UrlUtils.hostToUrl("www.example.com"))
21+
}
22+
23+
@Test
24+
fun fqdnAndPortWithScheme() {
25+
assertEquals("http://www.example.com:8080", UrlUtils.hostToUrl("http://www.example.com:8080"))
26+
assertEquals("https://www.example.com:443", UrlUtils.hostToUrl("https://www.example.com:443"))
27+
}
28+
29+
@Test
30+
fun fqdnAndPortWithNonWebScheme() {
31+
assertEquals("ftp://www.example.com:21", UrlUtils.hostToUrl("ftp://www.example.com:21"))
32+
}
33+
34+
@Test
35+
fun fqdnAndPortNoScheme() {
36+
assertEquals("https://www.example.com:8080", UrlUtils.hostToUrl("www.example.com:8080"))
37+
}
38+
39+
@Test
40+
fun hostnameNoScheme() {
41+
assertEquals("https://example.openremote.app", UrlUtils.hostToUrl("example"))
42+
}
43+
44+
@Test
45+
fun ipAddressWithScheme() {
46+
assertEquals("http://192.168.1.1", UrlUtils.hostToUrl("http://192.168.1.1"))
47+
}
48+
49+
@Test
50+
fun ipAddressWithNonWebScheme() {
51+
assertEquals("ftp://192.168.1.1", UrlUtils.hostToUrl("ftp://192.168.1.1"))
52+
}
53+
54+
@Test
55+
fun ipAddressAndPortWithScheme() {
56+
assertEquals("http://192.168.1.1:8080", UrlUtils.hostToUrl("http://192.168.1.1:8080"))
57+
}
58+
59+
@Test
60+
fun ipAddressAndPortWithNonWebScheme() {
61+
assertEquals("ftp://192.168.1.1:25", UrlUtils.hostToUrl("ftp://192.168.1.1:25"))
62+
}
63+
64+
@Test
65+
fun ipAddressAndInvalidPortWithScheme() {
66+
assertEquals("http://192.168.1.1:InvalidPort", UrlUtils.hostToUrl("http://192.168.1.1:InvalidPort"))
67+
}
68+
69+
@Test
70+
fun ipAddressNoScheme() {
71+
assertEquals("https://192.168.1.1", UrlUtils.hostToUrl("192.168.1.1"))
72+
}
73+
74+
@Test
75+
fun ipAddressAndPortNoScheme() {
76+
assertEquals("https://192.168.1.1:8080", UrlUtils.hostToUrl("192.168.1.1:8080"))
77+
}
78+
79+
@Test
80+
fun ipv6AddressWithScheme() {
81+
assertEquals("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
82+
UrlUtils.hostToUrl("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"))
83+
}
84+
85+
@Test
86+
fun ipv6AddressAndPortWithScheme() {
87+
assertEquals("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080",
88+
UrlUtils.hostToUrl("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"))
89+
}
90+
91+
@Test
92+
fun ipv6AddressNoScheme() {
93+
assertEquals("https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
94+
UrlUtils.hostToUrl("2001:0db8:85a3:0000:0000:8a2e:0370:7334"))
95+
assertEquals("https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
96+
UrlUtils.hostToUrl("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"))
97+
}
98+
99+
@Test
100+
fun ipv6AddressAndPortNoScheme() {
101+
assertEquals("https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080",
102+
UrlUtils.hostToUrl("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"))
103+
}
104+
105+
@Test
106+
fun ipv6CompressedAddressWithScheme() {
107+
assertEquals("http://[2001:db8:85a3::8a2e:370:7334]",
108+
UrlUtils.hostToUrl("http://[2001:db8:85a3::8a2e:370:7334]"))
109+
}
110+
111+
@Test
112+
fun ipv6CompressedAddressAndPortWithScheme() {
113+
assertEquals("http://[2001:db8:85a3::8a2e:370:7334]:8080",
114+
UrlUtils.hostToUrl("http://[2001:db8:85a3::8a2e:370:7334]:8080"))
115+
}
116+
117+
@Test
118+
fun ipv6CompressedAddressNoScheme() {
119+
assertEquals("https://[2001:db8:85a3::8a2e:370:7334]",
120+
UrlUtils.hostToUrl("2001:db8:85a3::8a2e:370:7334"))
121+
assertEquals("https://[2001:db8:85a3::8a2e:370:7334]",
122+
UrlUtils.hostToUrl("[2001:db8:85a3::8a2e:370:7334]"))
123+
}
124+
125+
@Test
126+
fun ipv6CompressedAddressAndPortNoScheme() {
127+
assertEquals("https://[2001:db8:85a3::8a2e:370:7334]:8080",
128+
UrlUtils.hostToUrl("[2001:db8:85a3::8a2e:370:7334]:8080"))
129+
}
130+
}

0 commit comments

Comments
 (0)