Skip to content

Commit dd4ec34

Browse files
Tom Buntingodrotbohm
authored andcommitted
#337 - Fixed potential double-encoding in Traverson.
We now avoid calling URI.toString() to early to prevent the need to re-parse it and thus accidentally cause a double-encoding. Original pull request: #382.
1 parent a019eda commit dd4ec34

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

src/main/java/org/springframework/hateoas/client/Traverson.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
* @author Oliver Gierke
6060
* @author Dietrich Schulten
6161
* @author Greg Turnquist
62+
* @author Tom Bunting
6263
* @since 0.11
6364
*/
6465
public class Traverson {
@@ -305,7 +306,7 @@ public TraversalBuilder withHeaders(HttpHeaders headers) {
305306
public <T> T toObject(Class<T> type) {
306307

307308
Assert.notNull(type, "Target type must not be null!");
308-
return operations.exchange(traverseToFinalUrl(true), GET, prepareRequest(headers), type).getBody();
309+
return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody();
309310
}
310311

311312
/**
@@ -318,7 +319,7 @@ public <T> T toObject(Class<T> type) {
318319
public <T> T toObject(ParameterizedTypeReference<T> type) {
319320

320321
Assert.notNull(type, "Target type must not be null!");
321-
return operations.exchange(traverseToFinalUrl(true), GET, prepareRequest(headers), type).getBody();
322+
return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody();
322323
}
323324

324325
/**
@@ -332,7 +333,7 @@ public <T> T toObject(String jsonPath) {
332333

333334
Assert.hasText(jsonPath, "JSON path must not be null or empty!");
334335

335-
String forObject = operations.exchange(traverseToFinalUrl(true), GET, prepareRequest(headers), String.class)
336+
String forObject = operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), String.class)
336337
.getBody();
337338
return JsonPath.read(forObject, jsonPath);
338339
}
@@ -346,7 +347,7 @@ public <T> T toObject(String jsonPath) {
346347
public <T> ResponseEntity<T> toEntity(Class<T> type) {
347348

348349
Assert.notNull(type, "Target type must not be null!");
349-
return operations.exchange(traverseToFinalUrl(true), GET, prepareRequest(headers), type);
350+
return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type);
350351
}
351352

352353
/**
@@ -374,14 +375,20 @@ public Link asTemplatedLink() {
374375
private Link traverseToLink(boolean expandFinalUrl) {
375376

376377
Assert.isTrue(rels.size() > 0, "At least one rel needs to be provided!");
377-
return new Link(traverseToFinalUrl(expandFinalUrl), rels.get(rels.size() - 1).getRel());
378+
return new Link(expandFinalUrl ? traverseToExpandedFinalUrl().toString() : traverseToFinalUrl(),
379+
rels.get(rels.size() - 1).getRel());
378380
}
379381

380-
private String traverseToFinalUrl(boolean expandFinalUrl) {
382+
private String traverseToFinalUrl() {
381383

382384
String uri = getAndFindLinkWithRel(baseUri.toString(), rels.iterator());
383-
UriTemplate uriTemplate = new UriTemplate(uri);
384-
return expandFinalUrl ? uriTemplate.expand(templateParameters).toString() : uriTemplate.toString();
385+
return new UriTemplate(uri).toString();
386+
}
387+
388+
private URI traverseToExpandedFinalUrl() {
389+
390+
String uri = getAndFindLinkWithRel(baseUri.toString(), rels.iterator());
391+
return new UriTemplate(uri).expand(templateParameters);
385392
}
386393

387394
private String getAndFindLinkWithRel(String uri, Iterator<Hop> rels) {

src/test/java/org/springframework/hateoas/client/Server.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ public Server() {
147147
respond(). //
148148
withBody(springagramItemWithoutImageHalDocument). //
149149
withContentType(MediaTypes.HAL_JSON.toString());
150+
151+
// For Traverson URI double encoding test
152+
153+
onRequest(). //
154+
havingPathEqualTo("/springagram/items"). //
155+
havingQueryString(equalTo("projection=no%20images")). //
156+
respond(). //
157+
withBody(springagramItemsHalDocument). //
158+
withContentType(MediaTypes.HAL_JSON.toString());
159+
150160
}
151161

152162
public String rootResource() {

src/test/java/org/springframework/hateoas/client/TraversonTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,23 @@ public void allowGlobalsToImpactSingleHops() {
355355
assertThat(item.image, equalTo(server.rootResource() + "/springagram/file/cat"));
356356
assertThat(item.description, equalTo("cat"));
357357
}
358+
359+
/**
360+
* @see #337
361+
*/
362+
@Test
363+
public void doesNotDoubleEncodeURI() {
364+
365+
this.traverson = new Traverson(URI.create(server.rootResource() + "/springagram"), MediaTypes.HAL_JSON);
366+
367+
Resource<?> itemResource = traverson.//
368+
follow(rel("items").withParameters(Collections.singletonMap("projection", "no images"))).//
369+
toObject(Resource.class);
370+
371+
assertThat(itemResource.hasLink("self"), is(true));
372+
assertThat(itemResource.getLink("self").expand().getHref(),
373+
equalTo(server.rootResource() + "/springagram/items"));
374+
}
358375

359376
private void setUpActors() {
360377

0 commit comments

Comments
 (0)