import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
private @Nullable String restApiVersion;
private Map<String, @Nullable String> apiEndPointsUrls = new HashMap<>();
private @Nullable String topicNamespace;
+ private boolean authenticateAnyway;
private String accessToken;
+ private String credentialToken;
private boolean trustedCertificate;
private boolean connected;
private boolean completed;
this.eventSourceFactory = eventSourceFactory;
this.jsonParser = jsonParser;
this.accessToken = "";
+ this.credentialToken = "";
}
public void setHttpClient(HttpClient httpClient) {
this.restUrl = restUrl;
}
- public void setAccessToken(String accessToken) {
+ public void setAuthenticationData(boolean authenticateAnyway, String accessToken, String username,
+ String password) {
+ this.authenticateAnyway = authenticateAnyway;
this.accessToken = accessToken;
+ if (username.isBlank() || password.isBlank()) {
+ this.credentialToken = "";
+ } else {
+ String token = username + ":" + password;
+ this.credentialToken = Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8));
+ }
}
public void setTrustedCertificate(boolean trustedCertificate) {
public void tryApi() throws RemoteopenhabException {
try {
- String jsonResponse = executeGetUrl(getRestUrl(), "application/json", false);
+ String jsonResponse = executeGetUrl(getRestUrl(), "application/json", false, false);
if (jsonResponse.isEmpty()) {
throw new RemoteopenhabException("JSON response is empty");
}
url += "&fields=" + fields;
}
boolean asyncReading = fields == null || Arrays.asList(fields.split(",")).contains("state");
- String jsonResponse = executeGetUrl(url, "application/json", asyncReading);
+ String jsonResponse = executeGetUrl(url, "application/json", false, asyncReading);
if (jsonResponse.isEmpty()) {
throw new RemoteopenhabException("JSON response is empty");
}
public String getRemoteItemState(String itemName) throws RemoteopenhabException {
try {
String url = String.format("%s/%s/state", getRestApiUrl("items"), itemName);
- return executeGetUrl(url, "text/plain", true);
+ return executeGetUrl(url, "text/plain", false, true);
} catch (RemoteopenhabException e) {
throw new RemoteopenhabException("Failed to get the state of remote item " + itemName
+ " using the items REST API: " + e.getMessage(), e);
public void sendCommandToRemoteItem(String itemName, Command command) throws RemoteopenhabException {
try {
String url = String.format("%s/%s", getRestApiUrl("items"), itemName);
- executeUrl(HttpMethod.POST, url, "application/json", command.toFullString(), "text/plain", false, true);
+ executeUrl(HttpMethod.POST, url, "application/json", command.toFullString(), "text/plain", false, false,
+ true);
} catch (RemoteopenhabException e) {
throw new RemoteopenhabException("Failed to send command to the remote item " + itemName
+ " using the items REST API: " + e.getMessage(), e);
public List<RemoteopenhabThing> getRemoteThings() throws RemoteopenhabException {
try {
- String jsonResponse = executeGetUrl(getRestApiUrl("things"), "application/json", false);
+ String jsonResponse = executeGetUrl(getRestApiUrl("things"), "application/json", true, false);
if (jsonResponse.isEmpty()) {
throw new RemoteopenhabException("JSON response is empty");
}
public RemoteopenhabThing getRemoteThing(String uid) throws RemoteopenhabException {
try {
String url = String.format("%s/%s", getRestApiUrl("things"), uid);
- String jsonResponse = executeGetUrl(url, "application/json", false);
+ String jsonResponse = executeGetUrl(url, "application/json", true, false);
if (jsonResponse.isEmpty()) {
throw new RemoteopenhabException("JSON response is empty");
}
private String getRestApiUrl(String endPoint) throws RemoteopenhabException {
String url = apiEndPointsUrls.get(endPoint);
- return url != null ? url : getRestUrl() + "/" + endPoint;
+ if (url == null) {
+ url = getRestUrl();
+ if (!url.endsWith("/")) {
+ url += "/";
+ }
+ url += endPoint;
+ }
+ return url;
}
public String getTopicNamespace() {
}
private SseEventSource createEventSource(String restSseUrl) {
+ String credentialToken = restSseUrl.startsWith("https:") || authenticateAnyway ? this.credentialToken : "";
Client client;
// Avoid a timeout exception after 1 minute by setting the read timeout to 0 (infinite)
if (trustedCertificate) {
public boolean verify(@Nullable String hostname, @Nullable SSLSession session) {
return true;
}
- }).readTimeout(0, TimeUnit.SECONDS).register(new RemoteopenhabStreamingRequestFilter(accessToken))
- .build();
+ }).readTimeout(0, TimeUnit.SECONDS)
+ .register(new RemoteopenhabStreamingRequestFilter(credentialToken)).build();
} else {
client = clientBuilder.readTimeout(0, TimeUnit.SECONDS)
- .register(new RemoteopenhabStreamingRequestFilter(accessToken)).build();
+ .register(new RemoteopenhabStreamingRequestFilter(credentialToken)).build();
}
SseEventSource eventSource = eventSourceFactory.newSource(client.target(restSseUrl));
eventSource.register(this::onEvent, this::onError, this::onComplete);
return parts[2];
}
- public String executeGetUrl(String url, String acceptHeader, boolean asyncReading) throws RemoteopenhabException {
- return executeUrl(HttpMethod.GET, url, acceptHeader, null, null, asyncReading, true);
+ public String executeGetUrl(String url, String acceptHeader, boolean provideAccessToken, boolean asyncReading)
+ throws RemoteopenhabException {
+ return executeUrl(HttpMethod.GET, url, acceptHeader, null, null, provideAccessToken, asyncReading, true);
}
public String executeUrl(HttpMethod httpMethod, String url, String acceptHeader, @Nullable String content,
- @Nullable String contentType, boolean asyncReading, boolean retryIfEOF) throws RemoteopenhabException {
- final Request request = httpClient.newRequest(url).method(httpMethod).timeout(REQUEST_TIMEOUT,
- TimeUnit.MILLISECONDS);
-
- request.header(HttpHeaders.ACCEPT, acceptHeader);
- if (!accessToken.isEmpty()) {
- request.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
+ @Nullable String contentType, boolean provideAccessToken, boolean asyncReading, boolean retryIfEOF)
+ throws RemoteopenhabException {
+ final Request request = httpClient.newRequest(url).method(httpMethod)
+ .timeout(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS).followRedirects(false)
+ .header(HttpHeaders.ACCEPT, acceptHeader);
+
+ if (url.startsWith("https:") || authenticateAnyway) {
+ boolean useAlternativeHeader = false;
+ if (!credentialToken.isEmpty()) {
+ request.header(HttpHeaders.AUTHORIZATION, "Basic " + credentialToken);
+ useAlternativeHeader = true;
+ }
+ if (provideAccessToken && !accessToken.isEmpty()) {
+ if (useAlternativeHeader) {
+ request.header("X-OPENHAB-TOKEN", accessToken);
+ } else {
+ request.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
+ }
+ }
}
if (content != null && (HttpMethod.POST.equals(httpMethod) || HttpMethod.PUT.equals(httpMethod))
request.content(new StringContentProvider(content), contentType);
}
+ logger.debug("Request {} {}", request.getMethod(), request.getURI());
+
try {
if (asyncReading) {
InputStreamResponseListener listener = new InputStreamResponseListener();
} else {
ContentResponse response = request.send();
int statusCode = response.getStatus();
- if (statusCode >= HttpStatus.BAD_REQUEST_400) {
+ if (statusCode == HttpStatus.MOVED_PERMANENTLY_301 || statusCode == HttpStatus.FOUND_302) {
+ String locationHeader = response.getHeaders().get(HttpHeaders.LOCATION);
+ if (locationHeader != null && !locationHeader.isBlank()) {
+ logger.debug("The remopte server redirected the request to this URL: {}", locationHeader);
+ return executeUrl(httpMethod, locationHeader, acceptHeader, content, contentType,
+ provideAccessToken, asyncReading, retryIfEOF);
+ } else {
+ String statusLine = statusCode + " " + response.getReason();
+ throw new RemoteopenhabException("HTTP call failed: " + statusLine);
+ }
+ } else if (statusCode >= HttpStatus.BAD_REQUEST_400) {
String statusLine = statusCode + " " + response.getReason();
throw new RemoteopenhabException("HTTP call failed: " + statusLine);
}
Throwable cause = e.getCause();
if (retryIfEOF && cause instanceof EOFException) {
logger.debug("EOFException - retry the request");
- return executeUrl(httpMethod, url, acceptHeader, content, contentType, asyncReading, false);
+ return executeUrl(httpMethod, url, acceptHeader, content, contentType, provideAccessToken, asyncReading,
+ false);
} else {
throw new RemoteopenhabException(e);
}