Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,16 @@ private ParsedBody validateAndParseBody(HttpExchange exchange, Operation op, byt
new ValidationError(
BODY_POINTER, "content-type", "unsupported content type: " + mediaType, null));
}
Object parsed = mapper.readFrom(body, header);
Object parsed;
try {
parsed = mapper.readFrom(body, header);
} catch (RuntimeException e) {
// Body could not be parsed (e.g. malformed JSON). Untrusted input -> 400, not 500.
LOG.debug("Failed to parse request body", e);
throw new ValidationException(
new ValidationError(
BODY_POINTER, "malformed", "request body is not valid " + mediaType, null));
}
if (mediaType.equals("application/x-www-form-urlencoded") && parsed instanceof Map<?, ?> map) {
@SuppressWarnings("unchecked")
Map<String, Object> typed = (Map<String, Object>) map;
Expand Down
37 changes: 24 additions & 13 deletions src/test/java/com/retailsvc/http/OpenApiServerIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,28 @@ void postDataShouldReturnJsonBody() {
}
}

@Test
void postDataShouldReturnBadRequestOnMalformedJson() throws Exception {
try (var server = newServer(Map.of("post-data", new EchoHandler()));
var client = httpClient()) {

// Double comma after a property is invalid JSON; must be 400, not 500.
var body = "{\"id\":\"some-id\",,\"age\":42}";
var headers = Map.of("correlation-id", UUID.randomUUID().toString());
var request = newRequest(server, path, "POST", ofString(body), headers);

var response = client.send(request, BodyHandlers.ofString());
var statusCode = response.statusCode();
var contentType = response.headers().firstValue("Content-Type").orElse("");
var responseBody = response.body();

assertThat(statusCode).isEqualTo(400);
assertThat(contentType).contains("application/problem+json");
assertThat(responseBody).contains("keyword");
assertThat(responseBody).contains("pointer");
}
}

@Test
void postDataShouldReturnBadRequestOnMissingRequiredProperties() {
Map<String, RequestHandler> handlers = Map.of("post-data", new EchoHandler());
Expand Down Expand Up @@ -217,7 +239,7 @@ void listObjectsShouldReturnJsonBody() {
}

@Test
void listObjectsShouldReturnBadRequestOnPassingObjectInsteadOfArray() {
void listObjectsShouldReturnBadRequestOnPassingObjectInsteadOfArray() throws Exception {
try (var server = newServer(Map.of("post-list-objects", new EchoHandler()));
var client = httpClient()) {

Expand All @@ -235,12 +257,6 @@ void listObjectsShouldReturnBadRequestOnPassingObjectInsteadOfArray() {
assertThat(contentType).contains("application/problem+json");
assertThat(responseBody).contains("keyword");
assertThat(responseBody).contains("pointer");

} catch (IOException e) {
fail(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
fail(e);
}
}
}
Expand Down Expand Up @@ -448,7 +464,7 @@ void postShapeUnknownKindReturns400() {
}

@Test
void postShapeMissingDiscriminatorReturns400() {
void postShapeMissingDiscriminatorReturns400() throws Exception {
// omitting "kind" makes both branches fail "required".
try (var server = newServer(Map.of("post-shape", new EchoHandler()));
var client = httpClient()) {
Expand All @@ -464,11 +480,6 @@ void postShapeMissingDiscriminatorReturns400() {
// Both branches fail identically at /kind required -> de-duplicated to one entry.
assertThat(response.body()).contains("\"errors\"").contains("#/kind");
assertThat(response.body().split("#/kind", -1)).hasSize(2); // exactly one occurrence
} catch (IOException e) {
fail(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
fail(e);
}
}
}
Expand Down
Loading