diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f839750..2b6033562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.0.1](https://github.com/googleapis/google-http-java-client/compare/v2.0.0...v2.0.1) (2025-09-24) + + +### Bug Fixes + +* UriTemplate expansion reserved ("+") and fragment("#") should not encode already percent encoded parts ([#2108](https://github.com/googleapis/google-http-java-client/issues/2108)) ([30766a8](https://github.com/googleapis/google-http-java-client/commit/30766a8a74df49c37e80ec41f1021d4ad69a8fda)) + ## [2.0.0](https://github.com/googleapis/google-http-java-client/compare/v1.47.1...v2.0.0) (2025-08-08) diff --git a/google-http-client-android-test/pom.xml b/google-http-client-android-test/pom.xml index a7df0a44a..5364010b6 100644 --- a/google-http-client-android-test/pom.xml +++ b/google-http-client-android-test/pom.xml @@ -4,7 +4,7 @@ google-http-client google-http-client-android-test Test project for google-http-client-android. - 2.0.0 + 2.0.1 apk @@ -53,7 +53,7 @@ com.google.http-client google-http-client-android - 2.0.0 + 2.0.1 android @@ -72,7 +72,7 @@ com.google.http-client google-http-client-test - 2.0.0 + 2.0.1 junit diff --git a/google-http-client-android/pom.xml b/google-http-client-android/pom.xml index 1105e1c55..450dc2b7b 100644 --- a/google-http-client-android/pom.xml +++ b/google-http-client-android/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-android - 2.0.0 + 2.0.1 Android Platform Extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client-apache-v2/pom.xml b/google-http-client-apache-v2/pom.xml index e8816150a..4717196ae 100644 --- a/google-http-client-apache-v2/pom.xml +++ b/google-http-client-apache-v2/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-apache-v2 - 2.0.0 + 2.0.1 Apache HTTP transport v2 for the Google HTTP Client Library for Java. diff --git a/google-http-client-apache-v5/pom.xml b/google-http-client-apache-v5/pom.xml index 552372d68..6973ee524 100644 --- a/google-http-client-apache-v5/pom.xml +++ b/google-http-client-apache-v5/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-apache-v5 - 2.0.0 + 2.0.1 Apache HTTP transport v5 for the Google HTTP Client Library for Java. diff --git a/google-http-client-appengine/pom.xml b/google-http-client-appengine/pom.xml index dec87e73e..63c96b131 100644 --- a/google-http-client-appengine/pom.xml +++ b/google-http-client-appengine/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-appengine - 2.0.0 + 2.0.1 Google App Engine extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client-assembly/pom.xml b/google-http-client-assembly/pom.xml index 9859e0f54..2cd239148 100644 --- a/google-http-client-assembly/pom.xml +++ b/google-http-client-assembly/pom.xml @@ -4,12 +4,12 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml com.google.http-client google-http-client-assembly - 2.0.0 + 2.0.1 pom Assembly for the Google HTTP Client Library for Java diff --git a/google-http-client-bom/pom.xml b/google-http-client-bom/pom.xml index ac7e2369d..60772596d 100644 --- a/google-http-client-bom/pom.xml +++ b/google-http-client-bom/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.google.http-client google-http-client-bom - 2.0.0 + 2.0.1 pom Google HTTP Client Library for Java BOM @@ -63,57 +63,57 @@ com.google.http-client google-http-client - 2.0.0 + 2.0.1 com.google.http-client google-http-client-android - 2.0.0 + 2.0.1 com.google.http-client google-http-client-apache-v2 - 2.0.0 + 2.0.1 com.google.http-client google-http-client-apache-v5 - 2.0.0 + 2.0.1 com.google.http-client google-http-client-appengine - 2.0.0 + 2.0.1 com.google.http-client google-http-client-findbugs - 2.0.0 + 2.0.1 com.google.http-client google-http-client-gson - 2.0.0 + 2.0.1 com.google.http-client google-http-client-jackson2 - 2.0.0 + 2.0.1 com.google.http-client google-http-client-protobuf - 2.0.0 + 2.0.1 com.google.http-client google-http-client-test - 2.0.0 + 2.0.1 com.google.http-client google-http-client-xml - 2.0.0 + 2.0.1 diff --git a/google-http-client-findbugs/pom.xml b/google-http-client-findbugs/pom.xml index 603a086aa..fd781d240 100644 --- a/google-http-client-findbugs/pom.xml +++ b/google-http-client-findbugs/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-findbugs - 2.0.0 + 2.0.1 Google APIs Client Library Findbugs custom plugin. diff --git a/google-http-client-gson/pom.xml b/google-http-client-gson/pom.xml index 6ddfe96bc..1ddbd7018 100644 --- a/google-http-client-gson/pom.xml +++ b/google-http-client-gson/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-gson - 2.0.0 + 2.0.1 GSON extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client-jackson2/pom.xml b/google-http-client-jackson2/pom.xml index cf2431de7..cac5191d4 100644 --- a/google-http-client-jackson2/pom.xml +++ b/google-http-client-jackson2/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-jackson2 - 2.0.0 + 2.0.1 Jackson 2 extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client-protobuf/pom.xml b/google-http-client-protobuf/pom.xml index 27245dbf0..7d2df7317 100644 --- a/google-http-client-protobuf/pom.xml +++ b/google-http-client-protobuf/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-protobuf - 2.0.0 + 2.0.1 Protocol Buffer extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client-test/pom.xml b/google-http-client-test/pom.xml index d31da5e48..75456117a 100644 --- a/google-http-client-test/pom.xml +++ b/google-http-client-test/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-test - 2.0.0 + 2.0.1 Shared classes used for testing of artifacts in the Google HTTP Client Library for Java. diff --git a/google-http-client-xml/pom.xml b/google-http-client-xml/pom.xml index 91f62316f..2c4d8748f 100644 --- a/google-http-client-xml/pom.xml +++ b/google-http-client-xml/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client-xml - 2.0.0 + 2.0.1 XML extensions to the Google HTTP Client Library for Java. diff --git a/google-http-client/pom.xml b/google-http-client/pom.xml index e725116af..aefa21738 100644 --- a/google-http-client/pom.xml +++ b/google-http-client/pom.xml @@ -4,11 +4,11 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../pom.xml google-http-client - 2.0.0 + 2.0.1 Google HTTP Client Library for Java Google HTTP Client Library for Java. Functionality that works on all supported Java platforms, diff --git a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java index 6a1e2e23c..d6095c4b4 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java @@ -29,8 +29,8 @@ /** * Expands URI Templates. * - *

This Class supports Level 1 templates and all Level 4 composite templates as described in: RFC 6570. + *

This class supports URI Template Level 1, partial support for Levels 2 and 3, and Level 4 + * composite templates as described in: RFC 6570. * *

Specifically, for the variables: var := "value" list := ["red", "green", "blue"] keys := * [("semi", ";"),("dot", "."),("comma", ",")] @@ -159,7 +159,7 @@ private String getEncodedValue(String value) { String encodedValue; if (reservedExpansion) { // Reserved expansion allows percent-encoded triplets and characters in the reserved set. - encodedValue = CharEscapers.escapeUriPathWithoutReserved(value); + encodedValue = CharEscapers.escapeUriPathWithoutReservedAndPercentEncoded(value); } else { encodedValue = CharEscapers.escapeUriConformant(value); } diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java b/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java index 4350f2711..d434403dc 100644 --- a/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/CharEscapers.java @@ -44,6 +44,9 @@ public final class CharEscapers { private static final Escaper URI_QUERY_STRING_ESCAPER = new PercentEscaper(PercentEscaper.SAFEQUERYSTRINGCHARS_URLENCODER); + private static final Escaper URI_RESERVED_AND_PERCENT_ENCODED_ESCAPER = + new PercentEncodedEscaper(URI_RESERVED_ESCAPER); + /** * Escapes the string value so it can be safely included in application/x-www-form-urlencoded * data. This is not appropriate for generic URI escaping. In particular it encodes the space @@ -184,6 +187,15 @@ public static String escapeUriPathWithoutReserved(String value) { return URI_RESERVED_ESCAPER.escape(value); } + /** + * Escapes a URI path but retains all reserved and percent-encoded characters. That is the same as + * {@link #escapeUriPathWithoutReserved(String)} except that it also escapes percent encoded + * parts. + */ + public static String escapeUriPathWithoutReservedAndPercentEncoded(String value) { + return URI_RESERVED_AND_PERCENT_ENCODED_ESCAPER.escape(value); + } + /** * Escapes the string value so it can be safely included in URI user info part. For details on * escaping URIs, see RFC 3986 - section diff --git a/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java new file mode 100644 index 000000000..6de6a0c1d --- /dev/null +++ b/google-http-client/src/main/java/com/google/api/client/util/escape/PercentEncodedEscaper.java @@ -0,0 +1,57 @@ +package com.google.api.client.util.escape; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * An {@link Escaper} implementation that preserves percent-encoded sequences in the input string. + * + *

This escaper applies the provided {@link Escaper} to all parts of the input string except for + * valid percent-encoded sequences (e.g., %20), which are left unchanged. + */ +final class PercentEncodedEscaper extends Escaper { + + /** Pattern to match valid percent-encoded sequences (e.g., %20). */ + static final Pattern PCT_ENCODE_PATTERN = Pattern.compile("%[0-9A-Fa-f]{2}"); + + private final Escaper escaper; + + public PercentEncodedEscaper(Escaper escaper) { + if (escaper == null) { + throw new NullPointerException("Escaper cannot be null"); + } + this.escaper = escaper; + } + + /** + * Escapes the input string using the provided {@link Escaper}, preserving valid percent-encoded + * sequences. + * + * @param string the input string to escape + * @return the escaped string with percent-encoded sequences left unchanged + */ + @Override + public String escape(String string) { + if (string == null || string.isEmpty()) { + return string; + } + + Matcher matcher = PCT_ENCODE_PATTERN.matcher(string); + StringBuilder sb = new StringBuilder(); + + int lastEnd = 0; + while (matcher.find()) { + sb.append(escaper.escape(string.substring(lastEnd, matcher.start()))); + + sb.append(string.substring(matcher.start(), matcher.end())); + + lastEnd = matcher.end(); + } + + if (lastEnd < string.length()) { + sb.append(escaper.escape(string.substring(lastEnd))); + } + + return sb.toString(); + } +} diff --git a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java index 73492d3c4..9fc90ba85 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java @@ -380,4 +380,57 @@ public void testExpandTemplates_reservedExpansion_mustNotEscapeUnreservedCharSet unReservedSet, UriTemplate.expand("{+var}", requestMap, false)); } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_alreadyEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("id", "admin%2F"); + assertEquals("admin%252F", UriTemplate.expand("{id}", variables, false)); + assertEquals("admin%2F", UriTemplate.expand("{+id}", variables, false)); + assertEquals("#admin%2F", UriTemplate.expand("{#id}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_notEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("not_pct", "%foo"); + assertEquals("%25foo", UriTemplate.expand("{not_pct}", variables, false)); + assertEquals("%25foo", UriTemplate.expand("{+not_pct}", variables, false)); + assertEquals("#%25foo", UriTemplate.expand("{#not_pct}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json + public void testExpandTemplates_reservedExpansion_listExpansionWithMixedEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + variables.put("list", Arrays.asList("red%25", "%2Fgreen", "blue ")); + assertEquals("red%2525,%252Fgreen,blue%20", UriTemplate.expand("{list}", variables, false)); + assertEquals("red%25,%2Fgreen,blue%20", UriTemplate.expand("{+list}", variables, false)); + assertEquals("#red%25,%2Fgreen,blue%20", UriTemplate.expand("{#list}", variables, false)); + } + + @Test + // These tests are from the uri-template test suite + // https://github.com/uri-templates/uritemplate-test/blob/master/extended-tests.json with an + // additional map entry + public void testExpandTemplates_reservedExpansion_mapWithMixedEncodedInput() { + Map variables = Maps.newLinkedHashMap(); + Map keys = Maps.newLinkedHashMap(); + keys.put("key1", "val1%2F"); + keys.put("key2", "val2%2F"); + keys.put("key3", "val "); + variables.put("keys", keys); + assertEquals( + "key1,val1%252F,key2,val2%252F,key3,val%20", + UriTemplate.expand("{keys}", variables, false)); + assertEquals( + "key1,val1%2F,key2,val2%2F,key3,val%20", UriTemplate.expand("{+keys}", variables, false)); + assertEquals( + "#key1,val1%2F,key2,val2%2F,key3,val%20", UriTemplate.expand("{#keys}", variables, false)); + } } diff --git a/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java new file mode 100644 index 000000000..9eff5c0f7 --- /dev/null +++ b/google-http-client/src/test/java/com/google/api/client/util/escape/PercentEncodedEscaperTest.java @@ -0,0 +1,32 @@ +package com.google.api.client.util.escape; + +import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class PercentEncodedEscaperTest extends TestCase { + @Test + public void testEscape() { + PercentEncodedEscaper escaper = + new PercentEncodedEscaper( + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER)); + String input = "Hello%20World+/?#[]"; + + String actual = escaper.escape(input); + assertEquals(input, actual); // No change expected since it's already percent-encoded + } + + @Test + public void testEscapeEncode() { + PercentEncodedEscaper escaper = + new PercentEncodedEscaper( + new PercentEscaper(PercentEscaper.SAFE_PLUS_RESERVED_CHARS_URLENCODER)); + String input = "Hello World%"; + String expected = "Hello%20World%25"; + + String actual = escaper.escape(input); + assertEquals(expected, actual); + } +} diff --git a/pom.xml b/pom.xml index a35530848..b62193526 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 pom Parent for the Google HTTP Client Library for Java Google HTTP Client Library for Java @@ -547,7 +547,7 @@ - google-api-java-client/google-api-client-assembly/android-properties (make the filenames match the version here) - Internally, update the default features.json file --> - 2.0.0 + 2.0.1 2.0.32 UTF-8 3.0.2 diff --git a/samples/dailymotion-simple-cmdline-sample/pom.xml b/samples/dailymotion-simple-cmdline-sample/pom.xml index 6362bbcae..975ae1411 100644 --- a/samples/dailymotion-simple-cmdline-sample/pom.xml +++ b/samples/dailymotion-simple-cmdline-sample/pom.xml @@ -4,7 +4,7 @@ com.google.http-client google-http-client-parent - 2.0.0 + 2.0.1 ../../pom.xml dailymotion-simple-cmdline-sample diff --git a/versions.txt b/versions.txt index 08350581c..3783a5310 100644 --- a/versions.txt +++ b/versions.txt @@ -1,18 +1,18 @@ # Format: # module:released-version:current-version -google-http-client:2.0.0:2.0.0 -google-http-client-bom:2.0.0:2.0.0 -google-http-client-parent:2.0.0:2.0.0 -google-http-client-android:2.0.0:2.0.0 -google-http-client-android-test:2.0.0:2.0.0 -google-http-client-apache-v2:2.0.0:2.0.0 -google-http-client-apache-v5:2.0.0:2.0.0 -google-http-client-appengine:2.0.0:2.0.0 -google-http-client-assembly:2.0.0:2.0.0 -google-http-client-findbugs:2.0.0:2.0.0 -google-http-client-gson:2.0.0:2.0.0 -google-http-client-jackson2:2.0.0:2.0.0 -google-http-client-protobuf:2.0.0:2.0.0 -google-http-client-test:2.0.0:2.0.0 -google-http-client-xml:2.0.0:2.0.0 +google-http-client:2.0.1:2.0.1 +google-http-client-bom:2.0.1:2.0.1 +google-http-client-parent:2.0.1:2.0.1 +google-http-client-android:2.0.1:2.0.1 +google-http-client-android-test:2.0.1:2.0.1 +google-http-client-apache-v2:2.0.1:2.0.1 +google-http-client-apache-v5:2.0.1:2.0.1 +google-http-client-appengine:2.0.1:2.0.1 +google-http-client-assembly:2.0.1:2.0.1 +google-http-client-findbugs:2.0.1:2.0.1 +google-http-client-gson:2.0.1:2.0.1 +google-http-client-jackson2:2.0.1:2.0.1 +google-http-client-protobuf:2.0.1:2.0.1 +google-http-client-test:2.0.1:2.0.1 +google-http-client-xml:2.0.1:2.0.1