import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@NonNullByDefault
public class Util {
+ public static final Pattern FORMAT_REPLACE_PATTERN = Pattern.compile("%\\d\\$[^%]+");
+
/**
- * create a log string from a {@link org.eclipse.jetty.client.api.Request}
+ * 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
}
/**
- * create an URI from a string, escaping all necessary characters
+ * Create a URI from a string, escaping all necessary characters
*
* @param s the URI as unescaped string
* @return URI corresponding to the input string
- * @throws MalformedURLException if parameter is not an URL
- * @throws URISyntaxException if parameter could not be converted to an URI
+ * @throws MalformedURLException if parameter is not a URL
+ * @throws URISyntaxException if parameter could not be converted to a URI
*/
public static URI uriFromString(String s) throws MalformedURLException, URISyntaxException {
URL url = new URL(s);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(),
url.getPath(), url.getQuery(), url.getRef());
- return URI.create(uri.toASCIIString());
+ return URI.create(uri.toASCIIString().replace("+", "%2B"));
+ }
+
+ /**
+ * Format a string using {@link String#format(String, Object...)} but allow non-format percent characters
+ *
+ * The {@param inputString} is checked for format patterns ({@code %<index>$<format>}) and passes only those to the
+ * {@link String#format(String, Object...)} method. This avoids format errors due to other percent characters in the
+ * string.
+ *
+ * @param inputString the input string, potentially containing format instructions
+ * @param params an array of parameters to be passed to the splitted input string
+ * @return the formatted string
+ */
+ public static String wrappedStringFormat(String inputString, Object... params) {
+ Matcher replaceMatcher = FORMAT_REPLACE_PATTERN.matcher(inputString);
+ return replaceMatcher.replaceAll(matchResult -> String.format(matchResult.group(), params));
}
}
*/
package org.openhab.binding.http;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import java.net.MalformedURLException;
import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.openhab.binding.http.internal.Util;
@Test
public void uriUTF8InHostnameEncodeTest() throws MalformedURLException, URISyntaxException {
String s = "https://foöo.bar/zhu.html?str=zin&tzz=678";
- Assertions.assertEquals("https://xn--foo-tna.bar/zhu.html?str=zin&tzz=678", Util.uriFromString(s).toString());
+ assertEquals("https://xn--foo-tna.bar/zhu.html?str=zin&tzz=678", Util.uriFromString(s).toString());
}
@Test
public void uriUTF8InPathEncodeTest() throws MalformedURLException, URISyntaxException {
String s = "https://foo.bar/zül.html?str=zin";
- Assertions.assertEquals("https://foo.bar/z%C3%BCl.html?str=zin", Util.uriFromString(s).toString());
+ assertEquals("https://foo.bar/z%C3%BCl.html?str=zin", Util.uriFromString(s).toString());
}
@Test
public void uriUTF8InQueryEncodeTest() throws MalformedURLException, URISyntaxException {
String s = "https://foo.bar/zil.html?str=zän";
- Assertions.assertEquals("https://foo.bar/zil.html?str=z%C3%A4n", Util.uriFromString(s).toString());
+ assertEquals("https://foo.bar/zil.html?str=z%C3%A4n", Util.uriFromString(s).toString());
}
@Test
public void uriSpaceInPathEncodeTest() throws MalformedURLException, URISyntaxException {
String s = "https://foo.bar/z l.html?str=zun";
- Assertions.assertEquals("https://foo.bar/z%20l.html?str=zun", Util.uriFromString(s).toString());
+ assertEquals("https://foo.bar/z%20l.html?str=zun", Util.uriFromString(s).toString());
}
@Test
public void uriSpaceInQueryEncodeTest() throws MalformedURLException, URISyntaxException {
String s = "https://foo.bar/zzl.html?str=z n";
- Assertions.assertEquals("https://foo.bar/zzl.html?str=z%20n", Util.uriFromString(s).toString());
+ assertEquals("https://foo.bar/zzl.html?str=z%20n", Util.uriFromString(s).toString());
+ }
+
+ @Test
+ public void uriPlusInQueryEncodeTest() throws MalformedURLException, URISyntaxException {
+ String s = "https://foo.bar/zzl.html?str=z+n";
+ assertEquals("https://foo.bar/zzl.html?str=z%2Bn", Util.uriFromString(s).toString());
+ }
+
+ @Test
+ public void uriAlreadyPartlyEscapedTest() throws MalformedURLException, URISyntaxException {
+ String s = "https://foo.bar/zzl.html?p=field%2Bvalue&foostatus=This is a test String&date=2024- 07-01";
+ assertEquals(
+ "https://foo.bar/zzl.html?p=field%252Bvalue&foostatus=This%20is%20a%20test%20String&date=2024-%20%2007-01",
+ Util.uriFromString(s).toString());
+ }
+
+ @Test
+ public void wrappedStringFormatDateTest() {
+ String formatString = "https://foo.bar/zzl.html?p=field%2Bvalue&date=%1$tY-%1$4tm-%1$td";
+ Date testDate = Date.from(Instant.parse("2024-07-01T10:00:00.000Z"));
+ assertEquals("https://foo.bar/zzl.html?p=field%2Bvalue&date=2024- 07-01",
+ Util.wrappedStringFormat(formatString, testDate));
+ }
+
+ @Test
+ public void wrappedStringFormatDateAndCommandTest() {
+ String formatString = "https://foo.bar/zzl.html?p=field%2Bvalue&foostatus=%2$s&date=%1$tY-%1$4tm-%1$td";
+ Date testDate = Date.from(Instant.parse("2024-07-01T10:00:00.000Z"));
+ String testCommand = "This is a test String";
+ assertEquals("https://foo.bar/zzl.html?p=field%2Bvalue&foostatus=This is a test String&date=2024- 07-01",
+ Util.wrappedStringFormat(formatString, testDate, testCommand));
}
}