Skip to content

Commit ecba563

Browse files
committed
api: Make io.grpc.Uri's setRawFragment public with warnings
1 parent 4b9d186 commit ecba563

2 files changed

Lines changed: 68 additions & 3 deletions

File tree

api/src/main/java/io/grpc/Uri.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ public Builder setRawPath(String path) {
786786
}
787787

788788
/**
789-
* Specifies the query component of the new URI, possibly percent-encoded, exactly as it will
789+
* Specifies the query component of the new URI, already percent-encoded, exactly as it will
790790
* appear in the string form of the built URI.
791791
*
792792
* <p>'query' must only contain codepoints from RFC 3986's "query" character class. Any other
@@ -805,6 +805,7 @@ public Builder setRawPath(String path) {
805805
*
806806
* @param query the new query component, or null to clear this field
807807
* @return this, for fluent building
808+
* @throws IllegalArgumentException if 'query' contains forbidden characters
808809
*/
809810
@CanIgnoreReturnValue
810811
public Builder setRawQuery(@Nullable String query) {
@@ -821,6 +822,13 @@ public Builder setRawQuery(@Nullable String query) {
821822
* <p>The fragment can contain any string of codepoints. Codepoints that can't be encoded
822823
* literally will be percent-encoded for you as UTF-8.
823824
*
825+
* <p>NB: Choose carefully between this method and {@link #setRawFragment(String)}. Many URI
826+
* schemes embed further structure in the fragment that isn't part of the RFC 3986 generic
827+
* syntax. These schemes often use internal delimiters that must be carefully percent-encoded in
828+
* ways that this method doesn't understand. See {@link #getFragment()} for an example. In this
829+
* case, callers should percent-encode externally then call {@link #setRawFragment(String)}
830+
* instead.
831+
*
824832
* <p>This field is optional.
825833
*
826834
* @param fragment the new fragment component, or null to clear this field
@@ -832,9 +840,26 @@ public Builder setFragment(@Nullable String fragment) {
832840
return this;
833841
}
834842

843+
/**
844+
* Specifies the fragment component of the new URI, already percent-encoded, exactly as it will
845+
* appear after the '#' delimiter in the string form of the built URI.
846+
*
847+
* <p>NB: Choose carefully between this method and {@link #setFragment(String)}. {@code
848+
* fragment} must only contain codepoints from RFC 3986's "fragment" character class. Use
849+
* percent-encoding and UTF-8 to represent any other characters, or call {@link
850+
* #setFragment(String)} to automatically percent-encode just the characters that need it.
851+
*
852+
* <p>This field is optional.
853+
*
854+
* @param fragment the new fragment component, or null to clear this field
855+
* @return this, for fluent building
856+
* @throws IllegalArgumentException if 'fragment' contains forbidden characters
857+
*/
835858
@CanIgnoreReturnValue
836-
Builder setRawFragment(String fragment) {
837-
checkPercentEncodedArg(fragment, "fragment", fragmentChars);
859+
public Builder setRawFragment(@Nullable String fragment) {
860+
if (fragment != null) {
861+
checkPercentEncodedArg(fragment, "fragment", fragmentChars);
862+
}
838863
this.fragment = fragment;
839864
return this;
840865
}

api/src/test/java/io/grpc/UriTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,46 @@ public void builder_setRawQuery_null() {
643643
assertThat(uri.toString()).isEqualTo("http://host");
644644
}
645645

646+
@Test
647+
public void builder_setRawFragment() {
648+
Uri uri = Uri.newBuilder().setScheme("http").setHost("host").setRawFragment("a%20b").build();
649+
assertThat(uri.getRawFragment()).isEqualTo("a%20b");
650+
assertThat(uri.getFragment()).isEqualTo("a b");
651+
assertThat(uri.toString()).isEqualTo("http://host#a%20b");
652+
}
653+
654+
@Test
655+
public void builder_setRawFragment_null() {
656+
Uri uri =
657+
Uri.newBuilder()
658+
.setScheme("http")
659+
.setHost("host")
660+
.setRawFragment("a%20b")
661+
.setRawFragment(null)
662+
.build();
663+
assertThat(uri.getRawFragment()).isNull();
664+
assertThat(uri.getFragment()).isNull();
665+
assertThat(uri.toString()).isEqualTo("http://host");
666+
}
667+
668+
@Test
669+
public void builder_setRawFragment_invalidCharacters_throws() {
670+
IllegalArgumentException e =
671+
assertThrows(
672+
IllegalArgumentException.class,
673+
() -> Uri.newBuilder().setRawFragment("f[]rag"));
674+
assertThat(e).hasMessageThat().contains("Invalid character in fragment");
675+
}
676+
677+
@Test
678+
public void builder_setRawFragment_invalidPercentEncoding_throws() {
679+
IllegalArgumentException e =
680+
assertThrows(
681+
IllegalArgumentException.class,
682+
() -> Uri.newBuilder().setRawFragment("f%XXragment"));
683+
assertThat(e).hasMessageThat().contains("Invalid");
684+
}
685+
646686
@Test
647687
public void builder_canClearAuthorityComponents() {
648688
Uri uri = Uri.create("s://user@host:80/path").toBuilder().setRawAuthority(null).build();

0 commit comments

Comments
 (0)