From a2a0ac4731d58d45d62daca2211547d550e4a86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Marti=C5=A1ka?= Date: Thu, 3 Jun 2021 14:49:04 +0200 Subject: [PATCH] Accept GraphQL queries with line breaks and tabs --- .../graphql/deployment/GraphQLTest.java | 22 ++++++++++++++++ .../SmallRyeGraphQLExecutionHandler.java | 25 ++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java index eeb0b5b1c11ef..cb5b0ae464c3a 100644 --- a/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java +++ b/extensions/smallrye-graphql/deployment/src/test/java/io/quarkus/smallrye/graphql/deployment/GraphQLTest.java @@ -152,6 +152,28 @@ public void testGenerics() { "{\"data\":{\"generics\":{\"message\":\"I know it\"}}}")); } + /** + * Send a query in JSON that contains raw unescaped line breaks and tabs inside the "query" string, + * which technically is forbidden by the JSON spec, but we want to seamlessly support + * queries from Java text blocks, for example, which preserve line breaks and tab characters. + */ + @Test + public void testQueryWithNewlinesAndTabs() { + String foosRequest = "{\"query\": \"query myquery { \n generics { \n \t message } } \"}"; + + RestAssured.given().when() + .accept(MEDIATYPE_JSON) + .contentType(MEDIATYPE_JSON) + .body(foosRequest) + .post("/graphql") + .then() + .assertThat() + .statusCode(200) + .and() + .body(CoreMatchers.containsString( + "{\"data\":{\"generics\":{\"message\":\"I know it\"}}}")); + } + @Test public void testContext() { String query = getPayload("{context}"); diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java index 215dec4994bdb..99af8227e9528 100644 --- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java +++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLExecutionHandler.java @@ -118,7 +118,7 @@ private void handleGet(HttpServerResponse response, RoutingContext ctx) { private JsonObject getJsonObjectFromQueryParameters(RoutingContext ctx) throws UnsupportedEncodingException { JsonObjectBuilder input = Json.createObjectBuilder(); // Query - String query = readQueryParameter(ctx, QUERY); + String query = stripNewlinesAndTabs(readQueryParameter(ctx, QUERY)); if (query != null && !query.isEmpty()) { input.add(QUERY, URLDecoder.decode(query, "UTF8")); } @@ -129,14 +129,14 @@ private JsonObject getJsonObjectFromQueryParameters(RoutingContext ctx) throws U } // Variables - String variables = readQueryParameter(ctx, VARIABLES); + String variables = stripNewlinesAndTabs(readQueryParameter(ctx, VARIABLES)); if (variables != null && !variables.isEmpty()) { JsonObject jsonObject = toJsonObject(URLDecoder.decode(variables, "UTF8")); input.add(VARIABLES, jsonObject); } // Extensions - String extensions = readQueryParameter(ctx, EXTENSIONS); + String extensions = stripNewlinesAndTabs(readQueryParameter(ctx, EXTENSIONS)); if (extensions != null && !extensions.isEmpty()) { JsonObject jsonObject = toJsonObject(URLDecoder.decode(extensions, "UTF8")); input.add(EXTENSIONS, jsonObject); @@ -148,7 +148,8 @@ private JsonObject getJsonObjectFromQueryParameters(RoutingContext ctx) throws U private JsonObject getJsonObjectFromBody(RoutingContext ctx) throws IOException { String contentType = readContentType(ctx); - String body = readBody(ctx); + String body = stripNewlinesAndTabs(readBody(ctx)); + // If the content type is application/graphql, the query is in the body if (contentType != null && contentType.startsWith(APPLICATION_GRAPHQL)) { JsonObjectBuilder input = Json.createObjectBuilder(); @@ -188,6 +189,22 @@ private String readQueryParameter(RoutingContext ctx, String parameterName) { return null; } + /** + * Strip away unescaped tabs and line breaks from the incoming JSON document so that it can be + * successfully parsed by a JSON parser. + * This does NOT remove properly escaped \n and \t inside the document, just the raw characters (ASCII + * values 9 and 10). Technically, this is not compliant with the JSON spec, + * but we want to seamlessly support queries from Java text blocks, for example, + * which preserve line breaks and tab characters. + */ + private String stripNewlinesAndTabs(String input) { + if (input == null || input.isEmpty()) { + return input; + } + return input.replaceAll("\\n", " ") + .replaceAll("\\t", " "); + } + private boolean hasQueryParameters(RoutingContext ctx) { return hasQueryParameter(ctx, QUERY) || hasQueryParameter(ctx, OPERATION_NAME) || hasQueryParameter(ctx, VARIABLES) || hasQueryParameter(ctx, EXTENSIONS);