-
Notifications
You must be signed in to change notification settings - Fork 4k
Add RFC 3986 support to DnsNameResolverProvider #12602
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+163
−13
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -481,6 +481,15 @@ public String getPath() { | |
| * <p>Prefer this method over {@link #getPath()} because it preserves the distinction between | ||
| * segment separators and literal '/'s within a path segment. | ||
| * | ||
| * <p>A trailing '/' delimiter in the path results in the empty string as the last element in the | ||
| * returned list. For example, <code>file://localhost/foo/bar/</code> has path segments <code> | ||
| * ["foo", "bar", ""]</code> | ||
| * | ||
| * <p>A leading '/' delimiter cannot be detected using this method. For example, both <code> | ||
| * dns:example.com</code> and <code>dns:///example.com</code> have the same list of path segments: | ||
| * <code>["example.com"]</code>. Use {@link #isPathAbsolute()} or {@link #isPathRootless()} to | ||
| * distinguish these cases. | ||
| * | ||
| * <p>The returned list is immutable. | ||
| */ | ||
| public List<String> getPathSegments() { | ||
|
|
@@ -490,6 +499,44 @@ public List<String> getPathSegments() { | |
| return segmentsBuilder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns true iff this URI's path component starts with a path segment (rather than the '/' | ||
| * segment delimiter). | ||
| * | ||
| * <p>The path of an RFC 3986 URI is either empty, absolute (starts with the '/' segment | ||
| * delimiter) or rootless (starts with a path segment). For example, <code>tel:+1-206-555-1212 | ||
| * </code>, <code>mailto:[email protected]</code> and <code>urn:isbn:978-1492082798</code> all have | ||
| * rootless paths. <code>mailto:%2Fdev%[email protected]</code> is also rootless because its | ||
| * percent-encoded slashes are not segment delimiters but rather part of the first and only path | ||
| * segment. | ||
| * | ||
| * <p>Contrast rootless paths with absolute ones (see {@link #isPathAbsolute()}. | ||
| */ | ||
| public boolean isPathRootless() { | ||
| return !path.isEmpty() && !path.startsWith("/"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns true iff this URI's path component starts with the '/' segment delimiter (rather than a | ||
| * path segment). | ||
| * | ||
| * <p>The path of an RFC 3986 URI is either empty, absolute (starts with the '/' segment | ||
| * delimiter) or rootless (starts with a path segment). For example, <code>file:///resume.txt | ||
| * </code>, <code>file:/resume.txt</code> and <code>file://localhost/</code> all have absolute | ||
| * paths while <code>tel:+1-206-555-1212</code>'s path is not absolute. <code> | ||
| * mailto:%2Fdev%[email protected]</code> is also not absolute because its percent-encoded | ||
| * slashes are not segment delimiters but rather part of the first and only path segment. | ||
| * | ||
| * <p>Contrast absolute paths with rootless ones (see {@link #isPathRootless()}. | ||
| * | ||
| * <p>NB: The term "absolute" has two different meanings in RFC 3986 which are easily confused. | ||
| * This method tests for a property of this URI's path component. Contrast with {@link | ||
| * #isAbsolute()} which tests the URI itself for a different property. | ||
| */ | ||
| public boolean isPathAbsolute() { | ||
| return path.startsWith("/"); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the path component of this URI in its originally parsed, possibly percent-encoded form. | ||
| */ | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,8 @@ public void parse_allComponents() throws URISyntaxException { | |
| assertThat(uri.getFragment()).isEqualTo("fragment"); | ||
| assertThat(uri.toString()).isEqualTo("scheme://user@host:0443/path?query#fragment"); | ||
| assertThat(uri.isAbsolute()).isFalse(); // Has a fragment. | ||
| assertThat(uri.isPathAbsolute()).isTrue(); | ||
| assertThat(uri.isPathRootless()).isFalse(); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -127,6 +129,8 @@ public void parse_emptyPathWithAuthority() throws URISyntaxException { | |
| assertThat(uri.getFragment()).isNull(); | ||
| assertThat(uri.toString()).isEqualTo("scheme://authority"); | ||
| assertThat(uri.isAbsolute()).isTrue(); | ||
| assertThat(uri.isPathAbsolute()).isFalse(); | ||
| assertThat(uri.isPathRootless()).isFalse(); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -139,6 +143,8 @@ public void parse_rootless() throws URISyntaxException { | |
| assertThat(uri.getFragment()).isNull(); | ||
| assertThat(uri.toString()).isEqualTo("mailto:[email protected]?subject=raise"); | ||
| assertThat(uri.isAbsolute()).isTrue(); | ||
| assertThat(uri.isPathAbsolute()).isFalse(); | ||
| assertThat(uri.isPathRootless()).isTrue(); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -151,6 +157,8 @@ public void parse_emptyPath() throws URISyntaxException { | |
| assertThat(uri.getFragment()).isNull(); | ||
| assertThat(uri.toString()).isEqualTo("scheme:"); | ||
| assertThat(uri.isAbsolute()).isTrue(); | ||
| assertThat(uri.isPathAbsolute()).isFalse(); | ||
| assertThat(uri.isPathRootless()).isFalse(); | ||
| } | ||
|
|
||
| @Test | ||
|
|
@@ -348,12 +356,34 @@ public void parse_onePathSegment_trailingSlash() throws URISyntaxException { | |
| assertThat(uri.getPathSegments()).containsExactly("foo", ""); | ||
| } | ||
|
|
||
| @Test | ||
| public void parse_onePathSegment_rootless() throws URISyntaxException { | ||
| Uri uri = Uri.create("dns:www.example.com"); | ||
| assertThat(uri.getPathSegments()).containsExactly("www.example.com"); | ||
| assertThat(uri.isPathAbsolute()).isFalse(); | ||
| assertThat(uri.isPathRootless()).isTrue(); | ||
| } | ||
|
|
||
| @Test | ||
| public void parse_twoPathSegments() throws URISyntaxException { | ||
| Uri uri = Uri.create("file:/foo/bar"); | ||
| assertThat(uri.getPathSegments()).containsExactly("foo", "bar"); | ||
| } | ||
|
|
||
| @Test | ||
| public void parse_twoPathSegments_rootless() throws URISyntaxException { | ||
| Uri uri = Uri.create("file:foo/bar"); | ||
| assertThat(uri.getPathSegments()).containsExactly("foo", "bar"); | ||
| } | ||
|
|
||
| @Test | ||
| public void parse_percentEncodedPathSegment_rootless() throws URISyntaxException { | ||
| Uri uri = Uri.create("mailto:%2Fdev%[email protected]"); | ||
| assertThat(uri.getPathSegments()).containsExactly("/dev/[email protected]"); | ||
| assertThat(uri.isPathAbsolute()).isFalse(); | ||
| assertThat(uri.isPathRootless()).isTrue(); | ||
| } | ||
|
|
||
| @Test | ||
| public void toString_percentEncoding() throws URISyntaxException { | ||
| Uri uri = | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to say the "first path segment" by itself. I would be fine with that if we also say "there must only be one segment." I don't think we want arbitrary stuff added to the end to be ignored dns:/example.com/foo. Allowing dns:example.com is good and fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. My commits all got squashed but this first one was actually just documenting the current behavior, as verified by my new
newNameResolver_toleratesTrailingPathSegments()test. It's a surprising (to me) side effect ofhostportparsing later, inDnsNameResolver.java, where a new tempnameUriis parsed from the concatenation of "//" withgetPath()from the original input target URI. The constructor proceeds withnameUri.getHost()andnameUri.getPort()but ignoresnameUri.getPath(), where any additional path segments ended up.I can certainly change
DnsNameResolverProvider#newNameResolver(io.grpc.Uri...)to forbid this but this new restriction will only take effect when the RFC 3984 flag is flipped. I do worry that it could break existing clients and make actually flipping the flag more likely to get rolled back. You could also argue that it's inconsistent with how DnsNameResolverProvider ignores other parts of the target URI it doesn't care about, like user info, query string and fragment . LMK what you think.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To the extent that RFC 4501 matters, its grammar does not seem to permit additional path segments, user info, fragment, or query params other than CLASS or TYPE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Then that is a pre-existing bug. Let's not document it as if it was the intention; we could add a TODO. I agree we'd want to preserve the existing behavior for the new URI parser change.
We don't care about RFC 4501. Our dns is completely separate from it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM. Sent you #12607