2323import java .io .InputStream ;
2424import java .io .Writer ;
2525import java .util .ArrayList ;
26+ import java .util .Arrays ;
2627import java .util .Collections ;
2728import java .util .HashMap ;
2829import java .util .List ;
3334import java .util .function .Consumer ;
3435import java .util .function .Function ;
3536import 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