Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/main/java/cz/smarteon/loxone/LoxoneEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,34 @@ public LoxoneEndpoint(@NotNull final String address) {

/**
* Create new instance of given host (only the host without protocol or port part is expected) and port.
* @param host loxone address host
* @param address loxone address (can include path like "dns.loxonecloud.com/783JDJ38DJ83JD")
* @param port loxone address port
*/
public LoxoneEndpoint(@NotNull final String host, final int port) {
this(host, port, false);
public LoxoneEndpoint(@NotNull final String address, final int port) {
this(
checkAndParseHost(requireNonNull(address, "address can't be null")),
address.contains(SLASH) ? null : port,
address.contains(SLASH),
address.contains(SLASH) ? address.substring(address.indexOf(SLASH)) : ""
);
}

/**
* Create new instance of given host (only the host without protocol or port part is expected), port
* and sets whether to use SSL.
* BEWARE: Loxone miniserver (in version 10) doesn't support SSL natively. So the {@code useSsl} make sense only
* when accessing miniserver through some reverse HTTPS proxy.
* @param host loxone address host
* @param address loxone address (can include path like "dns.loxonecloud.com/3D8UDJ83")
* @param port loxone address port
* @param useSsl whether to use SSL
*/
public LoxoneEndpoint(@NotNull final String host, final int port, final boolean useSsl) {
this(host, port, useSsl, "");
public LoxoneEndpoint(@NotNull final String address, final int port, final boolean useSsl) {
this(
checkAndParseHost(requireNonNull(address, "address can't be null")),
address.contains(SLASH) ? null : port,
address.contains(SLASH) || useSsl,
address.contains(SLASH) ? address.substring(address.indexOf(SLASH)) : ""
);
}

/**
Expand Down
33 changes: 23 additions & 10 deletions src/main/java/cz/smarteon/loxone/LoxoneHttp.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,33 @@ <T> T get(URL url, Command.Type type, Map<String, String> properties, Class<T> r
try {
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(connectionTimeout);
connection.setInstanceFollowRedirects(false);

for (Map.Entry<String, String> property : properties.entrySet()) {
connection.setRequestProperty(property.getKey(), property.getValue());
}

final int responseCode = connection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|| responseCode == 307
|| responseCode == 308) {

if (requestContext.get().redirects > MAX_REDIRECTS) {
throw new IllegalStateException("Too many redirects!");
}

final String locationHeader = connection.getHeaderField("Location");
if (locationHeader != null) {
final URL location = new URL(locationHeader);
requestContext.get().redirect(location);
LOG.debug("Redirecting to: {}", location);
return get(location, type, properties, responseType);
}
}

if (responseCode == HttpURLConnection.HTTP_OK) {
requestContext.get().lastUrl = connection.getURL();
try (InputStream is = connection.getInputStream()) {
Expand All @@ -86,16 +109,6 @@ <T> T get(URL url, Command.Type type, Map<String, String> properties, Class<T> r
throw new IllegalStateException("Unknown command type " + type);
}
}
} else if (responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|| responseCode == 307) {
if (requestContext.get().redirects > MAX_REDIRECTS) {
throw new IllegalStateException("Too many redirects!");
}
final URL location = new URL(connection.getHeaderField("Location"));
requestContext.get().redirect(location);
return get(location, type, properties, responseType);
} else {
throw new LoxoneException("Loxone command responded by status " + responseCode);
}
Expand Down
28 changes: 28 additions & 0 deletions src/test/kotlin/LoxoneEndpointTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import strikt.api.expectCatching
import strikt.api.expectThat
import strikt.assertions.isEqualTo
import strikt.assertions.isFailure
import kotlin.toString

class LoxoneEndpointTest {

Expand Down Expand Up @@ -73,4 +74,31 @@ class LoxoneEndpointTest {
fun `should verify equals`() {
EqualsVerifier.forClass(LoxoneEndpoint::class.java).withNonnullFields("host", "path").verify()
}

enum class TestDnsEndpoint(
val endpoint: LoxoneEndpoint,
val expectedHttp: String,
val expectedWs: String
) {
DnsSimple(
LoxoneEndpoint("dns.loxonecloud.com/5039IR9JFSF"),
"https://dns.loxonecloud.com/5039IR9JFSF/jdev",
"wss://dns.loxonecloud.com/ws/rfc6455"
),
DnsWithPath(
LoxoneEndpoint("dns.loxonecloud.com/5039IR9JFSF/extra/path"),
"https://dns.loxonecloud.com/5039IR9JFSF/extra/path/jdev",
"wss://dns.loxonecloud.com/ws/rfc6455"
)
}

@ParameterizedTest
@EnumSource(TestDnsEndpoint::class)
fun `should create DNS endpoint with path`(testParameters: TestDnsEndpoint) {
expectThat(testParameters.endpoint) {
get { httpUrl("jdev").toString() }.isEqualTo(testParameters.expectedHttp)
get { webSocketUri().toString() }.isEqualTo(testParameters.expectedWs)
}
}

}
36 changes: 36 additions & 0 deletions src/test/kotlin/LoxoneHttpTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import org.junit.jupiter.api.Test
import strikt.api.expectThat
import strikt.assertions.isEqualTo
import java.net.URL
import kotlin.text.get
import kotlin.toString

class LoxoneHttpTest {
private lateinit var loxoneHttp: LoxoneHttp
Expand Down Expand Up @@ -97,4 +99,38 @@ class LoxoneHttpTest {
isEqualTo("testString")
}
}

@Test
fun `should follow redirect without auth for DNS endpoint`() {
val finalLocation = "http://localhost:${Jadler.port()}/final"

onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/5039IR9JFSF/jdev/sps/io/test/value")
.respond()
.withStatus(302)
.withHeader("Location", finalLocation)

onRequest()
.havingMethodEqualTo("GET")
.havingPathEqualTo("/final")
.respond()
.withStatus(200)
.withBody("\"redirectSuccess\"")

expectThat(
loxoneHttp.get(
Command(
"/5039IR9JFSF/jdev/sps/io/test/value",
Command.Type.JSON,
String::class.java,
true,
false,
MiniserverType.KNOWN
)
)
).isEqualTo("redirectSuccess")

expectThat(loxoneHttp.lastUrl.toString()).isEqualTo(finalLocation)
}
}
Loading