Core, REST: Fix path segment encoding to use RFC 3986 percent-encoding - #15989
Core, REST: Fix path segment encoding to use RFC 3986 percent-encoding#15989adutra wants to merge 5 commits into
Conversation
|
A few data points: In the range of 0 to 32767, there are only 3 characters that
In particular the
And there is only one character that fails a crossed round trip, the space char (32):
This shows evidence that:
That's why it's safe to upgrade clients right away, but servers should only upgrade when they feel it's safe to do so. |
|
This pull request has been marked as stale due to 30 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@iceberg.apache.org list. Thank you for your contributions. |
|
cc @c-thiel – I think this PR should not be closed. |
| String expected = "+%25%26%2B%C2%A3%E2%82%AC"; | ||
|
|
||
| assertThat(RESTUtil.encodeString(utf8)).isEqualTo(expected); | ||
| } |
There was a problem hiding this comment.
can we add some tests here to make it more explicit how old/new client/server behavior is defined? For example, an old client (encodeString) talking to a new server (decodePathSegment). The same would apply for encodeNamespace/decodeNamespace / encodeNamespaceAsPathSegment/decodeNamespaceAsPathSegment. This makes it easier to reason about what the actual behavior is in these scenarios and can be named similar to encodeAsOldClientAndDecodeAsNewServer()
There was a problem hiding this comment.
Added two more tests:
encodePathAsOldAndNewClientDecodeAsOldServerencodePathAsOldAndNewClientDecodeAsNewServer
I tried to highlight the fact that there is no one-size-fits-all solution for the decoding side.
@adutra couldn't we fix this by keeping |
| * Decodes a URL path segment per RFC 3986. Unlike {@link #decodeString(String)}, this method does | ||
| * <b>not</b> treat {@code +} as a space — it is left as a literal {@code +} character. | ||
| * | ||
| * <p>Note: this method is introduced in this release but is not yet wired into server-side |
There was a problem hiding this comment.
I think we should aim for a solution that we can wire everywhere and that doesn't introduce a behavioral regression
Yes, keeping This is also why Are you implying that you would rather have |
|
@nastra also the problem is broader ecosystem compatibility. Using But RFC 3986 does not require this sign to be percent-encoded. Other clients could send For a general RFC 3986-compliant server that may receive requests from any client written in any language, |
|
I did a small survey over PyIceberg, Iceberg Rust and Iceberg Go:
|
bb92658 to
595a7ea
Compare
|
This pull request has been marked as stale due to 30 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@iceberg.apache.org list. Thank you for your contributions. |
|
Not stale. |
|
hi @nastra, Do we need anything else for this PR? Can you please take another look? |
This PR introduces new methods in `RESTUtil`: - `encodePathSegment`/`decodePathSegment` - `encodeNamespaceAsPathSegment`/`decodeNamespaceAsPathSegment` They use RFC 3986 percent-encoding (spaces as `%20`) instead of `application/x-www-form-urlencoded` (spaces as `+`). This PR also switches `ResourcePaths` to use the new path-segment methods, thus fixing the encoding issue on the client (encoding) side. The decoding methods are free to be used on the server side; servers should switch to them when they can/want to support the new, correct encoding. The REST TCK harness has been updated already.
595a7ea to
9e596d4
Compare
|
FYI I rebased to fix conflicts with #17697. |
uros-b
left a comment
There was a problem hiding this comment.
cc @aboueleyes, regarding #17769 (review)
|
Thanks @uros-b Cross-linking from #17769, which we are closing in favor of this PR as part of consolidation. Three small things from that PR that this one does not seem to cover, offered in case they are useful:
Happy to provide any of these as a patch on top of your branch if easier than hand-porting |
|
Thanks @aboueleyes I incorporated your extra tests. I also linked your issue #17759 - although the issue was first reported on the dev ML: https://lists.apache.org/thread/c498svln0x18vvm42998b9nm9j6ck5yh. |
| * Decodes a URL path segment per RFC 3986. Unlike {@link #decodeString(String)}, this method does | ||
| * <b>not</b> treat {@code +} as a space — it is left as a literal {@code +} character. | ||
| * | ||
| * <p>Note: this method is introduced in this release but is not yet wired into server-side |
There was a problem hiding this comment.
Different note, This doc note doesn't belong here. It just describes PR planning
There was a problem hiding this comment.
Agreed, removed the whole note.
|
So if I get this correctly, we are doing this fix to make sure that path's are encoded with %20 and not +. (There are 2 other characters I see we are covering but they are also ... very unlikely to really be used) The worry is that we may have clients sending "+" that mean "+" and will get decoded differently on server side if the server changes it's decoder? Is that right @nastra ? Honestly the surface area is so small I'm not that worried about broken folks. From the table @adutra posted we are already broken if you are using multiple implementations with your catalog correct? The Python version and the Java client are currently in agreement as are Go and Rust. The fact that this is coming up so rarely does make me think we should just go compliant with the RFC in Java and convince the Python impl to do the same. Does this fix come from a real user who broke on this? I'm just wondering what kind of names they were using |
| assertThat(withoutPrefix.namespaceProperties(ns)).isEqualTo("v1/namespaces/n%1Fs/properties"); | ||
| } | ||
|
|
||
| @Test |
There was a problem hiding this comment.
This PR is already pretty large, do we need a separate test for both " " and "+" for every endpoint? Couldn't we just do "foo +bar" and cover everything at once? We have coverage for the individual correctness in the utility, here we just want to make sure the right encoder was used.
There was a problem hiding this comment.
I just imported these tests from @aboueleyes PR. I think the whole test class could be reduced by 60% at least if we adopt parameterized tests instead of the tests we have today. However I would do that as a follow-up task. Wdyt?
It came initially from a report by @c-thiel on the dev ML. I think that @aboueleyes also posted another real-life report in #17759. |
17759 just looks like an AI issue :) But I trust @c-thiel if he actually saw this in the wild. I do want to make sure @nastra gets a chance to close on this too before we merge |
nastra
left a comment
There was a problem hiding this comment.
LGTM but I think we need to highlight it explicitly in the PR/release notes that we're breaking old java clients that would send a + to mean a space
|
Thanks @nastra. I'll keep in mind for the release notes |
This PR introduces new methods in
RESTUtil:encodePathSegment/decodePathSegmentencodeNamespaceAsPathSegment/decodeNamespaceAsPathSegmentThey use RFC 3986 percent-encoding (spaces as
%20) instead ofapplication/x-www-form-urlencoded(spaces as+).This PR also switches
ResourcePathsto use the new path-segment methods, thus fixing the encoding issue on the client (encoding) side.The decoding methods are free to be used on the server side; servers should switch to them when they can/want to support the new, correct encoding. The REST TCK harness has been updated already.
ML discussion: https://lists.apache.org/thread/c498svln0x18vvm42998b9nm9j6ck5yh
Fixes #17759.