|
18 | 18 |
|
19 | 19 | import java.net.URI; |
20 | 20 | import java.net.URISyntaxException; |
| 21 | +import java.util.ArrayList; |
| 22 | +import java.util.List; |
21 | 23 | import org.apache.http.client.utils.URIBuilder; |
22 | 24 | import org.testng.Assert; |
| 25 | +import org.testng.annotations.DataProvider; |
23 | 26 | import org.testng.annotations.Test; |
24 | 27 |
|
25 | 28 |
|
@@ -92,4 +95,136 @@ public void testSkipReEncodingWithFragment() throws URISyntaxException |
92 | 95 | Assert.assertEquals(resultDefault.toString(), expectURL); |
93 | 96 | Assert.assertEquals(resultFast.toString(), expectURL); |
94 | 97 | } |
| 98 | + |
| 99 | + /** |
| 100 | + * Exhaustive proof that skipReEncoding produces identical results to the UriBuilder path |
| 101 | + * for every printable ASCII character in a query string when the character is already |
| 102 | + * percent-encoded (which is the form that RestRequest URIs always use). |
| 103 | + * |
| 104 | + * Also tests literal characters that java.net.URI accepts in raw queries. The only known |
| 105 | + * divergence is literal '[' and ']' — Jersey encodes them but the fast path preserves them |
| 106 | + * as-is. This is safe because these characters never appear un-encoded in practice: |
| 107 | + * the Servlet spec returns percent-encoded query strings, and Rest.li's UriBuilder encodes |
| 108 | + * all query parameters. The test documents this divergence explicitly rather than hiding it. |
| 109 | + */ |
| 110 | + @DataProvider(name = "asciiQueryCharsEncoded") |
| 111 | + public Object[][] asciiQueryCharsEncoded() |
| 112 | + { |
| 113 | + List<Object[]> cases = new ArrayList<>(); |
| 114 | + for (int c = 0x20; c <= 0x7E; c++) |
| 115 | + { |
| 116 | + // Every character in its percent-encoded form — this is how characters arrive |
| 117 | + // in real RestRequest URIs from the HTTP layer. |
| 118 | + String percentEncoded = String.format("%%%02X", c); |
| 119 | + cases.add(new Object[]{ |
| 120 | + "percent-encoded 0x" + String.format("%02X", c) + " '" + (char) c + "'", |
| 121 | + "/path?q=" + percentEncoded |
| 122 | + }); |
| 123 | + } |
| 124 | + |
| 125 | + // Realistic rest.li query strings |
| 126 | + cases.add(new Object[]{"restli query", "/myResource/1?q=findByName&name=hello%20world"}); |
| 127 | + cases.add(new Object[]{"restli complex query", |
| 128 | + "/myResource?q=search&keywords=java%20engineer&start=0&count=10&fields=id,firstName,lastName"}); |
| 129 | + cases.add(new Object[]{"restli batch get", |
| 130 | + "/myResource?ids=List(1,2,3)&fields=id,name"}); |
| 131 | + cases.add(new Object[]{"query with encoded special chars", |
| 132 | + "/myResource?filter=(key%3Avalue)&sort=name%26date"}); |
| 133 | + cases.add(new Object[]{"restli encoded brackets", |
| 134 | + "/myResource?criteria%5B0%5D=valueA&criteria%5B1%5D=valueB"}); |
| 135 | + |
| 136 | + return cases.toArray(new Object[0][]); |
| 137 | + } |
| 138 | + |
| 139 | + @Test(dataProvider = "asciiQueryCharsEncoded") |
| 140 | + public void testSkipReEncodingEquivalenceForEncodedInputs(String description, String uriString) |
| 141 | + { |
| 142 | + URI configuredURI = URI.create("d2://testService"); |
| 143 | + D2URIRewriter defaultRewriter = new D2URIRewriter(configuredURI); |
| 144 | + D2URIRewriter fastRewriter = new D2URIRewriter(configuredURI, true); |
| 145 | + |
| 146 | + URI input = URI.create(uriString); |
| 147 | + URI resultDefault = defaultRewriter.rewriteURI(input); |
| 148 | + URI resultFast = fastRewriter.rewriteURI(input); |
| 149 | + |
| 150 | + Assert.assertEquals(resultFast.toString(), resultDefault.toString(), |
| 151 | + "Divergence for [" + description + "] input=" + uriString); |
| 152 | + } |
| 153 | + |
| 154 | + /** |
| 155 | + * Documents the known divergence for literal '[' and ']'. Jersey's contextualEncode encodes |
| 156 | + * them to %5B/%5D, but the fast path preserves them. This is safe because these characters |
| 157 | + * never appear un-encoded in RestRequest URIs — the Servlet spec and Rest.li's UriBuilder |
| 158 | + * both guarantee percent-encoding. |
| 159 | + */ |
| 160 | + @Test |
| 161 | + public void testSkipReEncodingKnownDivergenceLiteralBrackets() |
| 162 | + { |
| 163 | + URI configuredURI = URI.create("d2://testService"); |
| 164 | + D2URIRewriter defaultRewriter = new D2URIRewriter(configuredURI); |
| 165 | + D2URIRewriter fastRewriter = new D2URIRewriter(configuredURI, true); |
| 166 | + |
| 167 | + // Literal brackets — can only happen with hand-crafted URIs, never in real RestRequest URIs |
| 168 | + URI inputBracket = URI.create("/path?q=[value]"); |
| 169 | + URI resultDefault = defaultRewriter.rewriteURI(inputBracket); |
| 170 | + URI resultFast = fastRewriter.rewriteURI(inputBracket); |
| 171 | + |
| 172 | + // Jersey encodes them, fast path preserves them |
| 173 | + Assert.assertEquals(resultDefault.toString(), "d2://testService/path?q=%5Bvalue%5D"); |
| 174 | + Assert.assertEquals(resultFast.toString(), "d2://testService/path?q=[value]"); |
| 175 | + |
| 176 | + // When properly percent-encoded (the real-world form), both paths agree |
| 177 | + URI inputEncoded = URI.create("/path?q=%5Bvalue%5D"); |
| 178 | + Assert.assertEquals( |
| 179 | + fastRewriter.rewriteURI(inputEncoded).toString(), |
| 180 | + defaultRewriter.rewriteURI(inputEncoded).toString()); |
| 181 | + } |
| 182 | + |
| 183 | + /** |
| 184 | + * Tests all literal ASCII characters that java.net.URI accepts in query strings, |
| 185 | + * excluding '[' and ']' (documented divergence above). |
| 186 | + */ |
| 187 | + @DataProvider(name = "asciiQueryCharsLiteral") |
| 188 | + public Object[][] asciiQueryCharsLiteral() |
| 189 | + { |
| 190 | + List<Object[]> cases = new ArrayList<>(); |
| 191 | + for (int c = 0x20; c <= 0x7E; c++) |
| 192 | + { |
| 193 | + // # terminates query, % needs hex pair, [ ] are the known divergence |
| 194 | + if (c == '#' || c == '%' || c == '[' || c == ']') |
| 195 | + { |
| 196 | + continue; |
| 197 | + } |
| 198 | + |
| 199 | + String literal = "/path?q=" + (char) c; |
| 200 | + try |
| 201 | + { |
| 202 | + URI.create(literal); |
| 203 | + cases.add(new Object[]{ |
| 204 | + "literal 0x" + String.format("%02X", c) + " '" + (char) c + "'", |
| 205 | + literal |
| 206 | + }); |
| 207 | + } |
| 208 | + catch (IllegalArgumentException e) |
| 209 | + { |
| 210 | + // Character not valid in URI.create — skip |
| 211 | + } |
| 212 | + } |
| 213 | + return cases.toArray(new Object[0][]); |
| 214 | + } |
| 215 | + |
| 216 | + @Test(dataProvider = "asciiQueryCharsLiteral") |
| 217 | + public void testSkipReEncodingEquivalenceForLiteralChars(String description, String uriString) |
| 218 | + { |
| 219 | + URI configuredURI = URI.create("d2://testService"); |
| 220 | + D2URIRewriter defaultRewriter = new D2URIRewriter(configuredURI); |
| 221 | + D2URIRewriter fastRewriter = new D2URIRewriter(configuredURI, true); |
| 222 | + |
| 223 | + URI input = URI.create(uriString); |
| 224 | + URI resultDefault = defaultRewriter.rewriteURI(input); |
| 225 | + URI resultFast = fastRewriter.rewriteURI(input); |
| 226 | + |
| 227 | + Assert.assertEquals(resultFast.toString(), resultDefault.toString(), |
| 228 | + "Divergence for [" + description + "] input=" + uriString); |
| 229 | + } |
95 | 230 | } |
0 commit comments