Skip to content

Commit 6036b71

Browse files
committed
Refactor multipart handling to reduce duplication
1 parent 2b175b7 commit 6036b71

File tree

1 file changed

+72
-92
lines changed

1 file changed

+72
-92
lines changed

src/main/java/graphql/servlet/AbstractGraphQLHttpServlet.java

Lines changed: 72 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.io.InputStream;
2424
import java.io.Writer;
2525
import java.util.ArrayList;
26+
import java.util.Arrays;
2627
import java.util.Collections;
2728
import java.util.HashMap;
2829
import java.util.List;
@@ -33,7 +34,6 @@
3334
import java.util.function.Consumer;
3435
import java.util.function.Function;
3536
import java.util.stream.Collectors;
36-
import java.util.stream.Stream;
3737

3838
/**
3939
* @author Andrew Potter
@@ -48,6 +48,7 @@ public abstract class AbstractGraphQLHttpServlet extends HttpServlet implements
4848
public static final int STATUS_BAD_REQUEST = 400;
4949

5050
private static final GraphQLRequest INTROSPECTION_REQUEST = new GraphQLRequest(IntrospectionQuery.INTROSPECTION_QUERY, new HashMap<>(), null);
51+
private static final String[] MULTIPART_KEYS = new String[]{"operations", "graphql", "query"};
5152

5253
protected abstract GraphQLQueryInvoker getQueryInvoker();
5354

@@ -114,109 +115,58 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
114115
String query = CharStreams.toString(request.getReader());
115116
query(queryInvoker, graphQLObjectMapper, invocationInputFactory.create(new GraphQLRequest(query, null, null)), response);
116117
} else if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data") && !request.getParts().isEmpty()) {
117-
final Map<String, List<Part>> fileItems = request.getParts().stream()
118-
.collect(Collectors.toMap(
119-
Part::getName,
120-
Collections::singletonList,
121-
(l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(Collectors.toList())));
122-
123-
if(fileItems.containsKey("operations")) {
124-
final Optional<Part> queryItem = getFileItem(fileItems, "operations");
125-
if (queryItem.isPresent()) {
126-
InputStream inputStream = queryItem.get().getInputStream();
127-
128-
if (!inputStream.markSupported()) {
129-
inputStream = new BufferedInputStream(inputStream);
130-
}
131-
132-
final Optional<Map<String, List<String>>> variablesMap =
133-
getFileItem(fileItems, "map")
134-
.map(graphQLObjectMapper::deserializeMultipartMap);
135-
136-
if (isBatchedQuery(inputStream)) {
137-
List<GraphQLRequest> graphQLRequests = graphQLObjectMapper.readBatchedGraphQLRequest(inputStream);
138-
variablesMap.ifPresent(map -> graphQLRequests.forEach(r -> mapMultipartVariables(r, map, fileItems)));
139-
GraphQLBatchedInvocationInput invocationInput = invocationInputFactory.create(graphQLRequests, request);
140-
invocationInput.getContext().setFiles(fileItems);
141-
queryBatched(queryInvoker, graphQLObjectMapper, invocationInput, response);
142-
return;
143-
} else {
144-
GraphQLRequest graphQLRequest = graphQLObjectMapper.readGraphQLRequest(inputStream);
145-
variablesMap.ifPresent(m -> mapMultipartVariables(graphQLRequest, m, fileItems));
146-
GraphQLSingleInvocationInput invocationInput =
147-
invocationInputFactory.create(graphQLRequest, request);
148-
invocationInput.getContext().setFiles(fileItems);
149-
query(queryInvoker, graphQLObjectMapper, invocationInput, response);
150-
return;
151-
}
118+
final Map<String, List<Part>> fileItems = request.getParts()
119+
.stream()
120+
.collect(Collectors.groupingBy(Part::getName));
121+
122+
for (String key : MULTIPART_KEYS) {
123+
// Check to see if there is a part under the key we seek
124+
if(!fileItems.containsKey(key)) {
125+
continue;
152126
}
153-
} else if (fileItems.containsKey("graphql")) {
154-
final Optional<Part> graphqlItem = getFileItem(fileItems, "graphql");
155-
if (graphqlItem.isPresent()) {
156-
InputStream inputStream = graphqlItem.get().getInputStream();
157-
158-
if (!inputStream.markSupported()) {
159-
inputStream = new BufferedInputStream(inputStream);
160-
}
161127

162-
if (isBatchedQuery(inputStream)) {
163-
GraphQLBatchedInvocationInput invocationInput = invocationInputFactory.create(graphQLObjectMapper.readBatchedGraphQLRequest(inputStream), request);
164-
invocationInput.getContext().setFiles(fileItems);
165-
queryBatched(queryInvoker, graphQLObjectMapper, invocationInput, response);
166-
return;
167-
} else {
168-
GraphQLSingleInvocationInput invocationInput = invocationInputFactory.create(graphQLObjectMapper.readGraphQLRequest(inputStream), request);
169-
invocationInput.getContext().setFiles(fileItems);
170-
query(queryInvoker, graphQLObjectMapper, invocationInput, response);
171-
return;
172-
}
128+
final Optional<Part> queryItem = getFileItem(fileItems, key);
129+
if (!queryItem.isPresent()) {
130+
// If there is a part, but we don't see an item, then break and return BAD_REQUEST
131+
break;
173132
}
174-
} else if (fileItems.containsKey("query")) {
175-
final Optional<Part> queryItem = getFileItem(fileItems, "query");
176-
if (queryItem.isPresent()) {
177-
InputStream inputStream = queryItem.get().getInputStream();
178-
179-
if (!inputStream.markSupported()) {
180-
inputStream = new BufferedInputStream(inputStream);
181-
}
182133

183-
if (isBatchedQuery(inputStream)) {
184-
GraphQLBatchedInvocationInput invocationInput = invocationInputFactory.create(graphQLObjectMapper.readBatchedGraphQLRequest(inputStream), request);
185-
invocationInput.getContext().setFiles(fileItems);
186-
queryBatched(queryInvoker, graphQLObjectMapper, invocationInput, response);
187-
return;
134+
InputStream inputStream = asMarkableInputStream(queryItem.get().getInputStream());
135+
136+
final Optional<Map<String, List<String>>> variablesMap =
137+
getFileItem(fileItems, "map").map(graphQLObjectMapper::deserializeMultipartMap);
138+
139+
if (isBatchedQuery(inputStream)) {
140+
List<GraphQLRequest> graphQLRequests =
141+
graphQLObjectMapper.readBatchedGraphQLRequest(inputStream);
142+
variablesMap.ifPresent(map -> graphQLRequests.forEach(r -> mapMultipartVariables(r, map, fileItems)));
143+
GraphQLBatchedInvocationInput invocationInput =
144+
invocationInputFactory.create(graphQLRequests, request);
145+
invocationInput.getContext().setFiles(fileItems);
146+
queryBatched(queryInvoker, graphQLObjectMapper, invocationInput, response);
147+
return;
148+
} else {
149+
GraphQLRequest graphQLRequest;
150+
if("query".equals(key)) {
151+
graphQLRequest = buildRequestFromQuery(inputStream, graphQLObjectMapper, fileItems);
188152
} else {
189-
String query = new String(ByteStreams.toByteArray(inputStream));
190-
191-
Map<String, Object> variables = null;
192-
final Optional<Part> variablesItem = getFileItem(fileItems, "variables");
193-
if (variablesItem.isPresent()) {
194-
variables = graphQLObjectMapper.deserializeVariables(new String(ByteStreams.toByteArray(variablesItem.get().getInputStream())));
195-
}
196-
197-
String operationName = null;
198-
final Optional<Part> operationNameItem = getFileItem(fileItems, "operationName");
199-
if (operationNameItem.isPresent()) {
200-
operationName = new String(ByteStreams.toByteArray(operationNameItem.get().getInputStream())).trim();
201-
}
202-
203-
GraphQLSingleInvocationInput invocationInput = invocationInputFactory.create(new GraphQLRequest(query, variables, operationName), request);
204-
invocationInput.getContext().setFiles(fileItems);
205-
query(queryInvoker, graphQLObjectMapper, invocationInput, response);
206-
return;
153+
graphQLRequest = graphQLObjectMapper.readGraphQLRequest(inputStream);
207154
}
155+
156+
variablesMap.ifPresent(m -> mapMultipartVariables(graphQLRequest, m, fileItems));
157+
GraphQLSingleInvocationInput invocationInput =
158+
invocationInputFactory.create(graphQLRequest, request);
159+
invocationInput.getContext().setFiles(fileItems);
160+
query(queryInvoker, graphQLObjectMapper, invocationInput, response);
161+
return;
208162
}
209163
}
210164

211165
response.setStatus(STATUS_BAD_REQUEST);
212-
log.info("Bad POST multipart request: no part named \"graphql\" or \"query\"");
166+
log.info("Bad POST multipart request: no part named " + Arrays.toString(MULTIPART_KEYS));
213167
} else {
214168
// this is not a multipart request
215-
InputStream inputStream = request.getInputStream();
216-
217-
if (!inputStream.markSupported()) {
218-
inputStream = new BufferedInputStream(inputStream);
219-
}
169+
InputStream inputStream = asMarkableInputStream(request.getInputStream());
220170

221171
if (isBatchedQuery(inputStream)) {
222172
queryBatched(queryInvoker, graphQLObjectMapper, invocationInputFactory.create(graphQLObjectMapper.readBatchedGraphQLRequest(inputStream), request), response);
@@ -231,6 +181,36 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
231181
};
232182
}
233183

184+
private static InputStream asMarkableInputStream(InputStream inputStream) {
185+
if (!inputStream.markSupported()) {
186+
inputStream = new BufferedInputStream(inputStream);
187+
}
188+
return inputStream;
189+
}
190+
191+
private GraphQLRequest buildRequestFromQuery(InputStream inputStream,
192+
GraphQLObjectMapper graphQLObjectMapper,
193+
Map<String, List<Part>> fileItems) throws IOException
194+
{
195+
GraphQLRequest graphQLRequest;
196+
String query = new String(ByteStreams.toByteArray(inputStream));
197+
198+
Map<String, Object> variables = null;
199+
final Optional<Part> variablesItem = getFileItem(fileItems, "variables");
200+
if (variablesItem.isPresent()) {
201+
variables = graphQLObjectMapper.deserializeVariables(new String(ByteStreams.toByteArray(variablesItem.get().getInputStream())));
202+
}
203+
204+
String operationName = null;
205+
final Optional<Part> operationNameItem = getFileItem(fileItems, "operationName");
206+
if (operationNameItem.isPresent()) {
207+
operationName = new String(ByteStreams.toByteArray(operationNameItem.get().getInputStream())).trim();
208+
}
209+
210+
graphQLRequest = new GraphQLRequest(query, variables, operationName);
211+
return graphQLRequest;
212+
}
213+
234214
private void mapMultipartVariables(GraphQLRequest request,
235215
Map<String, List<String>> variablesMap,
236216
Map<String, List<Part>> fileItems)

0 commit comments

Comments
 (0)