Skip to content

Commit

Permalink
Fix for: Exception occurring when encoding the path containing unwise…
Browse files Browse the repository at this point in the history
… characters (i.e space) (#364)
  • Loading branch information
lwitkowski authored Mar 28, 2024
1 parent 2e958b7 commit dc05920
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.when;
import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.nullValue;

Expand Down Expand Up @@ -54,4 +55,12 @@ void shouldRegisterProblemPostProcessorCustomImplementationsFromCDI() {
.then()
.body("injected_from_custom_post_processor", equalTo("you called /throw/generic/runtime-exception"));
}

@Test
void instanceShouldHandleUnwiseCharactersProperly() {
given()
.get("/non|existing path /with unwisecharacters")
.then()
.body("instance", equalTo("/non|existing path /with unwisecharacters"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.tietoevry.quarkus.resteasy.problem;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

public final class InstanceUtils {

public static URI pathToInstance(String path) {
if (path == null) {
return null;
}
try {
return new URI(encodeUnwiseCharacters(path));
} catch (URISyntaxException e) {
return null;
}
}

/**
* @see <a href="https://www.ietf.org/rfc/rfc2396.txt">About unwise characters in RFC-2396</a>
*/
private static String encodeUnwiseCharacters(String path) {
return URLEncoder.encode(path, StandardCharsets.UTF_8);
}

public static String instanceToPath(URI instance) {
return URLDecoder.decode(instance.toString(), StandardCharsets.UTF_8);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.tietoevry.quarkus.resteasy.problem.HttpProblem;
import com.tietoevry.quarkus.resteasy.problem.InstanceUtils;
import java.io.IOException;
import java.util.Map;

Expand Down Expand Up @@ -36,7 +37,7 @@ public void serialize(final HttpProblem problem, final JsonGenerator json, final
json.writeStringField("detail", problem.getDetail());
}
if (problem.getInstance() != null) {
json.writeStringField("instance", problem.getInstance().toASCIIString());
json.writeStringField("instance", InstanceUtils.instanceToPath(problem.getInstance()));
}

for (Map.Entry<String, Object> entry : problem.getParameters().entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.tietoevry.quarkus.resteasy.problem.jsonb;

import com.tietoevry.quarkus.resteasy.problem.HttpProblem;
import com.tietoevry.quarkus.resteasy.problem.InstanceUtils;
import jakarta.json.bind.serializer.JsonbSerializer;
import jakarta.json.bind.serializer.SerializationContext;
import jakarta.json.stream.JsonGenerator;
Expand All @@ -25,7 +26,7 @@ public void serialize(HttpProblem problem, JsonGenerator generator, Serializatio
generator.write("detail", problem.getDetail());
}
if (problem.getInstance() != null) {
generator.write("instance", problem.getInstance().toASCIIString());
generator.write("instance", InstanceUtils.instanceToPath(problem.getInstance()));
}

problem.getParameters().forEach((key, value) -> ctx.serialize(key, value, generator));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.tietoevry.quarkus.resteasy.problem.postprocessing;

import com.tietoevry.quarkus.resteasy.problem.HttpProblem;
import com.tietoevry.quarkus.resteasy.problem.InstanceUtils;
import java.net.URI;

/**
Expand All @@ -21,12 +22,8 @@ public HttpProblem apply(HttpProblem problem, ProblemContext context) {
}

return HttpProblem.builder(problem)
.withInstance(defaultInstance(context))
.withInstance(InstanceUtils.pathToInstance(context.path))
.build();
}

private URI defaultInstance(ProblemContext context) {
return context.path == null ? null : URI.create(context.path);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tietoevry.quarkus.resteasy.problem.jackson;

import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.core.JsonEncoding;
Expand All @@ -9,6 +10,7 @@
import com.tietoevry.quarkus.resteasy.problem.HttpProblemMother;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -34,8 +36,7 @@ void shouldSerializeAllFields() throws IOException {

serializer.serialize(problem, jsonGenerator, null);

jsonGenerator.close();
assertThat(outputStream.toString(StandardCharsets.UTF_8.name()))
assertThat(serializedProblem())
.isEqualTo(HttpProblemMother.SERIALIZED_COMPLEX_PROBLEM);
}

Expand All @@ -46,9 +47,27 @@ void shouldSerializeOnlyNotNullFields() throws IOException {

serializer.serialize(problem, jsonGenerator, null);

jsonGenerator.close();
assertThat(outputStream.toString(StandardCharsets.UTF_8.name()))
assertThat(serializedProblem())
.isEqualTo(HttpProblemMother.SERIALIZED_BAD_REQUEST_PROBLEM);
}

@Test
@DisplayName("Should decode uri for instance field")
void shouldDecodeUriForInstanceField() throws IOException {
HttpProblem problem = HttpProblem.builder()
.withStatus(NOT_FOUND)
.withInstance(URI.create("%2Fnon%7Cexisting%7Bpath+%2Fwith%7Bunwise%5Ccharacters%3E%23"))
.build();

serializer.serialize(problem, jsonGenerator, null);

assertThat(serializedProblem()).contains("""
"instance":"/non|existing{path /with{unwise\\\\characters>#"}""");
}

private String serializedProblem() throws IOException {
jsonGenerator.close();
return outputStream.toString(StandardCharsets.UTF_8);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tietoevry.quarkus.resteasy.problem.jsonb;

import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
import static org.assertj.core.api.Assertions.assertThat;

import com.tietoevry.quarkus.resteasy.problem.HttpProblem;
Expand All @@ -9,6 +10,7 @@
import jakarta.json.bind.serializer.SerializationContext;
import jakarta.json.stream.JsonGenerator;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import org.eclipse.parsson.JsonProviderImpl;
import org.eclipse.yasson.internal.JsonbContext;
Expand All @@ -31,8 +33,7 @@ void shouldSerializeAllFields() {

serializer.serialize(problem, jsonGenerator, context);

jsonGenerator.close();
assertThat(outputStream.toString(StandardCharsets.UTF_8))
assertThat(serializedProblem())
.isEqualTo(HttpProblemMother.SERIALIZED_COMPLEX_PROBLEM);
}

Expand All @@ -43,9 +44,27 @@ void shouldSerializeOnlyNotNullFields() {

serializer.serialize(problem, jsonGenerator, context);

jsonGenerator.close();
assertThat(outputStream.toString(StandardCharsets.UTF_8))
assertThat(serializedProblem())
.isEqualTo(HttpProblemMother.SERIALIZED_BAD_REQUEST_PROBLEM);
}

@Test
@DisplayName("Should decode uri for instance field")
void shouldDecodeUriForInstanceField() {
HttpProblem problem = HttpProblem.builder()
.withStatus(NOT_FOUND)
.withInstance(URI.create("%2Fnon%7Cexisting%7Bpath+%2Fwith%7Bunwise%5Ccharacters%3E%23"))
.build();

serializer.serialize(problem, jsonGenerator, null);

assertThat(serializedProblem()).contains("""
"instance":"/non|existing{path /with{unwise\\\\characters>#"}""");
}

private String serializedProblem() {
jsonGenerator.close();
return outputStream.toString(StandardCharsets.UTF_8);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,14 @@ void shouldNotOverrideExistingValues() {
assertThat(enhancedProblem.getInstance()).hasPath("/non-default-endpoint");
}

@Test
void shouldHandleUnwiseCharactersInPath() {
HttpProblem problemWithUnwiseCharactersInPath = processor.apply(
badRequestProblem(),
ProblemContext.of(new RuntimeException(), "/non|existing{path /with{unwise\\characters>#"));

assertThat(problemWithUnwiseCharactersInPath.getInstance())
.hasPath("/non|existing{path+/with{unwise\\characters>#");
}

}

0 comments on commit dc05920

Please sign in to comment.