Skip to content
This repository was archived by the owner on Jul 22, 2026. It is now read-only.

Commit 7e0a454

Browse files
author
Jason Leezer
committed
Remove logging and add tests
1 parent e79f5ca commit 7e0a454

2 files changed

Lines changed: 136 additions & 8 deletions

File tree

d2/src/main/java/com/linkedin/d2/balancer/util/D2URIRewriter.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import com.linkedin.jersey.api.uri.UriBuilder;
2020
import com.linkedin.util.ArgumentUtil;
2121
import java.net.URI;
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
2422

2523

2624
/**
@@ -29,7 +27,6 @@
2927

3028
public class D2URIRewriter implements URIRewriter
3129
{
32-
final private static Logger LOGGER = LoggerFactory.getLogger(D2URIRewriter.class);
3330
final private URI _httpURI;
3431
final private boolean _skipReEncoding;
3532

@@ -55,11 +52,7 @@ public D2URIRewriter(URI httpURI, boolean skipReEncoding)
5552
@Override
5653
public URI rewriteURI(URI d2Uri)
5754
{
58-
URI rewrittenUri = _skipReEncoding ? rewriteURIFromRaw(d2Uri) : rewriteURIWithBuilder(d2Uri);
59-
60-
LOGGER.debug("rewrite uri {} -> {}", d2Uri, rewrittenUri);
61-
62-
return rewrittenUri;
55+
return _skipReEncoding ? rewriteURIFromRaw(d2Uri) : rewriteURIWithBuilder(d2Uri);
6356
}
6457

6558
private URI rewriteURIWithBuilder(URI d2Uri)

d2/src/test/java/com/linkedin/d2/balancer/util/TestD2URIRewriter.java

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
import java.net.URI;
2020
import java.net.URISyntaxException;
21+
import java.util.ArrayList;
22+
import java.util.List;
2123
import org.apache.http.client.utils.URIBuilder;
2224
import org.testng.Assert;
25+
import org.testng.annotations.DataProvider;
2326
import org.testng.annotations.Test;
2427

2528

@@ -92,4 +95,136 @@ public void testSkipReEncodingWithFragment() throws URISyntaxException
9295
Assert.assertEquals(resultDefault.toString(), expectURL);
9396
Assert.assertEquals(resultFast.toString(), expectURL);
9497
}
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+
}
95230
}

0 commit comments

Comments
 (0)