Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions api/src/main/java/io/grpc/Uri.java
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,13 @@ public Builder setRawQuery(@Nullable String query) {
* <p>The fragment can contain any string of codepoints. Codepoints that can't be encoded
* literally will be percent-encoded for you as UTF-8.
*
* <p>NB: Choose carefully between this method and {@link #setRawFragment(String)}. Many URI
* schemes embed further structure in the fragment that isn't part of the RFC 3986 generic
* syntax. These schemes often use internal delimiters that must be carefully percent-encoded in
* ways that this method doesn't understand. See {@link #getFragment()} for an example. In that
* case, callers should percent-encode externally then call {@link #setRawFragment(String)}
* instead.
*
* <p>This field is optional.
*
* @param fragment the new fragment component, or null to clear this field
Expand All @@ -832,9 +839,27 @@ public Builder setFragment(@Nullable String fragment) {
return this;
}

/**
* Specifies the fragment component of the new URI, already percent-encoded, exactly as it will
* appear after the '#' delimiter in the string form of the built URI.
*
* <p>NB: Choose carefully between this method and {@link #setFragment(String)}. {@code
* fragment} must only contain codepoints from RFC 3986's "fragment" character class. Use
* percent-encoding and UTF-8 to represent anything else. In certain cases, you can use {@link
* #setFragment(String)} to have the fragment percent-encoded for you instead, but see that
* method's Javadoc for its limitations.
*
* <p>This field is optional.
*
* @param fragment the new fragment component, or null to clear this field
* @return this, for fluent building
* @throws IllegalArgumentException if 'fragment' contains forbidden characters
*/
@CanIgnoreReturnValue
Builder setRawFragment(String fragment) {
checkPercentEncodedArg(fragment, "fragment", fragmentChars);
public Builder setRawFragment(@Nullable String fragment) {
if (fragment != null) {
checkPercentEncodedArg(fragment, "fragment", fragmentChars);
}
this.fragment = fragment;
return this;
}
Expand Down
40 changes: 40 additions & 0 deletions api/src/test/java/io/grpc/UriTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,46 @@ public void builder_setRawQuery_null() {
assertThat(uri.toString()).isEqualTo("http://host");
}

@Test
public void builder_setRawFragment() {
Uri uri = Uri.newBuilder().setScheme("http").setHost("host").setRawFragment("a%20b").build();
assertThat(uri.getRawFragment()).isEqualTo("a%20b");
assertThat(uri.getFragment()).isEqualTo("a b");
assertThat(uri.toString()).isEqualTo("http://host#a%20b");
}

@Test
public void builder_setRawFragment_null() {
Uri uri =
Uri.newBuilder()
.setScheme("http")
.setHost("host")
.setRawFragment("a%20b")
.setRawFragment(null)
.build();
assertThat(uri.getRawFragment()).isNull();
assertThat(uri.getFragment()).isNull();
assertThat(uri.toString()).isEqualTo("http://host");
}

@Test
public void builder_setRawFragment_invalidCharacters_throws() {
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class,
() -> Uri.newBuilder().setRawFragment("f[]rag"));
assertThat(e).hasMessageThat().contains("Invalid character in fragment");
}

@Test
public void builder_setRawFragment_invalidPercentEncoding_throws() {
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class,
() -> Uri.newBuilder().setRawFragment("f%XXragment"));
assertThat(e).hasMessageThat().contains("Invalid");
}

@Test
public void builder_canClearAuthorityComponents() {
Uri uri = Uri.create("s://user@host:80/path").toBuilder().setRawAuthority(null).build();
Expand Down
Loading