[http] fix missing escaping of URLs (#9618)

Signed-off-by: Jan N. Klug <jan.n.klug@rub.de>
This commit is contained in:
J-N-K 2020-12-31 16:55:03 +01:00 committed by GitHub
parent ede94354f3
commit 27afb1f9f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 24 deletions

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.http.internal; package org.openhab.binding.http.internal;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.*; import java.util.*;
@ -302,10 +303,10 @@ public class HttpThingHandler extends BaseThingHandler {
private void sendHttpValue(String commandUrl, String command, boolean isRetry) { private void sendHttpValue(String commandUrl, String command, boolean isRetry) {
try { try {
// format URL // format URL
URI finalUrl = new URI(String.format(commandUrl, new Date(), command)); URI uri = Util.uriFromString(String.format(commandUrl, new Date(), command));
// build request // build request
Request request = httpClient.newRequest(finalUrl).timeout(config.timeout, TimeUnit.MILLISECONDS) Request request = httpClient.newRequest(uri).timeout(config.timeout, TimeUnit.MILLISECONDS)
.method(config.commandMethod); .method(config.commandMethod);
if (config.commandMethod != HttpMethod.GET) { if (config.commandMethod != HttpMethod.GET) {
final String contentType = config.contentType; final String contentType = config.contentType;
@ -326,30 +327,30 @@ public class HttpThingHandler extends BaseThingHandler {
}); });
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Sending to '{}': {}", finalUrl, Util.requestToLogString(request)); logger.trace("Sending to '{}': {}", uri, Util.requestToLogString(request));
} }
CompletableFuture<@Nullable Content> f = new CompletableFuture<>(); CompletableFuture<@Nullable Content> f = new CompletableFuture<>();
f.exceptionally(e -> { f.exceptionally(e -> {
if (e instanceof HttpAuthException) { if (e instanceof HttpAuthException) {
if (isRetry) { if (isRetry) {
logger.warn("Retry after authentication failure failed again for '{}', failing here", finalUrl); logger.warn("Retry after authentication failure failed again for '{}', failing here", uri);
} else { } else {
AuthenticationStore authStore = httpClient.getAuthenticationStore(); AuthenticationStore authStore = httpClient.getAuthenticationStore();
Authentication.Result authResult = authStore.findAuthenticationResult(finalUrl); Authentication.Result authResult = authStore.findAuthenticationResult(uri);
if (authResult != null) { if (authResult != null) {
authStore.removeAuthenticationResult(authResult); authStore.removeAuthenticationResult(authResult);
logger.debug("Cleared authentication result for '{}', retrying immediately", finalUrl); logger.debug("Cleared authentication result for '{}', retrying immediately", uri);
sendHttpValue(commandUrl, command, true); sendHttpValue(commandUrl, command, true);
} else { } else {
logger.warn("Could not find authentication result for '{}', failing here", finalUrl); logger.warn("Could not find authentication result for '{}', failing here", uri);
} }
} }
} }
return null; return null;
}); });
request.send(new HttpResponseListener(f, null, config.bufferSize)); request.send(new HttpResponseListener(f, null, config.bufferSize));
} catch (IllegalArgumentException | URISyntaxException e) { } catch (IllegalArgumentException | URISyntaxException | MalformedURLException e) {
logger.warn("Creating request for '{}' failed: {}", commandUrl, e.getMessage()); logger.warn("Creating request for '{}' failed: {}", commandUrl, e.getMessage());
} }
} }

View File

@ -12,6 +12,7 @@
*/ */
package org.openhab.binding.http.internal; package org.openhab.binding.http.internal;
import java.net.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
@ -23,13 +24,18 @@ import org.eclipse.jetty.http.HttpField;
/** /**
* The {@link Util} is a utility class * The {@link Util} is a utility class
* channels
* *
* @author Jan N. Klug - Initial contribution * @author Jan N. Klug - Initial contribution
*/ */
@NonNullByDefault @NonNullByDefault
public class Util { public class Util {
/**
* create a log string from a {@link org.eclipse.jetty.client.api.Request}
*
* @param request the request to log
* @return the string representing the request
*/
public static String requestToLogString(Request request) { public static String requestToLogString(Request request) {
ContentProvider contentProvider = request.getContent(); ContentProvider contentProvider = request.getContent();
String contentString = contentProvider == null ? "null" String contentString = contentProvider == null ? "null"
@ -41,4 +47,18 @@ public class Util {
return logString; return logString;
} }
/**
* create an URI from a string, escaping all necessary characters
*
* @param s the URI as unescaped string
* @return URI correspondign to the input string
* @throws MalformedURLException
* @throws URISyntaxException
*/
public static URI uriFromString(String s) throws MalformedURLException, URISyntaxException {
URL url = new URL(s);
return new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(),
url.getQuery(), url.getRef());
}
} }

View File

@ -12,8 +12,7 @@
*/ */
package org.openhab.binding.http.internal.http; package org.openhab.binding.http.internal.http;
import java.net.URI; import java.net.*;
import java.net.URISyntaxException;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -76,11 +75,10 @@ public class RefreshingUrlCache {
// format URL // format URL
try { try {
URI finalUrl = new URI(String.format(this.url, new Date())); URI uri = Util.uriFromString(String.format(this.url, new Date()));
logger.trace("Requesting refresh (retry={}) from '{}' with timeout {}ms", isRetry, uri, timeout);
logger.trace("Requesting refresh (retry={}) from '{}' with timeout {}ms", isRetry, finalUrl, timeout); httpClient.newRequest(uri).thenAccept(request -> {
httpClient.newRequest(finalUrl).thenAccept(request -> {
request.timeout(timeout, TimeUnit.MILLISECONDS); request.timeout(timeout, TimeUnit.MILLISECONDS);
headers.forEach(header -> { headers.forEach(header -> {
@ -96,17 +94,16 @@ public class RefreshingUrlCache {
response.exceptionally(e -> { response.exceptionally(e -> {
if (e instanceof HttpAuthException) { if (e instanceof HttpAuthException) {
if (isRetry) { if (isRetry) {
logger.warn("Retry after authentication failure failed again for '{}', failing here", logger.warn("Retry after authentication failure failed again for '{}', failing here", uri);
finalUrl);
} else { } else {
AuthenticationStore authStore = httpClient.getAuthenticationStore(); AuthenticationStore authStore = httpClient.getAuthenticationStore();
Authentication.Result authResult = authStore.findAuthenticationResult(finalUrl); Authentication.Result authResult = authStore.findAuthenticationResult(uri);
if (authResult != null) { if (authResult != null) {
authStore.removeAuthenticationResult(authResult); authStore.removeAuthenticationResult(authResult);
logger.debug("Cleared authentication result for '{}', retrying immediately", finalUrl); logger.debug("Cleared authentication result for '{}', retrying immediately", uri);
refresh(true); refresh(true);
} else { } else {
logger.warn("Could not find authentication result for '{}', failing here", finalUrl); logger.warn("Could not find authentication result for '{}', failing here", uri);
} }
} }
} }
@ -114,19 +111,19 @@ public class RefreshingUrlCache {
}).thenAccept(this::processResult); }).thenAccept(this::processResult);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Sending to '{}': {}", finalUrl, Util.requestToLogString(request)); logger.trace("Sending to '{}': {}", uri, Util.requestToLogString(request));
} }
request.send(new HttpResponseListener(response, fallbackEncoding, bufferSize)); request.send(new HttpResponseListener(response, fallbackEncoding, bufferSize));
}).exceptionally(e -> { }).exceptionally(e -> {
if (e instanceof CancellationException) { if (e instanceof CancellationException) {
logger.debug("Request to URL {} was cancelled by thing handler.", finalUrl); logger.debug("Request to URL {} was cancelled by thing handler.", uri);
} else { } else {
logger.warn("Request to URL {} failed: {}", finalUrl, e.getMessage()); logger.warn("Request to URL {} failed: {}", uri, e.getMessage());
} }
return null; return null;
}); });
} catch (IllegalArgumentException | URISyntaxException e) { } catch (IllegalArgumentException | URISyntaxException | MalformedURLException e) {
logger.warn("Creating request for '{}' failed: {}", url, e.getMessage()); logger.warn("Creating request for '{}' failed: {}", url, e.getMessage());
} }
} }