11package graphql .servlet ;
22
3+ import com .google .common .io .ByteStreams ;
4+ import com .google .common .io .CharStreams ;
35import graphql .ExecutionResult ;
46import graphql .introspection .IntrospectionQuery ;
57import graphql .schema .GraphQLFieldDefinition ;
68import graphql .servlet .internal .GraphQLRequest ;
7- import org .apache .commons .fileupload .FileItem ;
8- import org .apache .commons .fileupload .FileItemFactory ;
9- import org .apache .commons .fileupload .disk .DiskFileItemFactory ;
10- import org .apache .commons .fileupload .servlet .ServletFileUpload ;
119import org .slf4j .Logger ;
1210import org .slf4j .LoggerFactory ;
1311
12+ import javax .servlet .AsyncContext ;
1413import javax .servlet .Servlet ;
1514import javax .servlet .ServletException ;
1615import javax .servlet .http .HttpServlet ;
1716import javax .servlet .http .HttpServletRequest ;
1817import javax .servlet .http .HttpServletResponse ;
19- import java .io .BufferedInputStream ;
20- import java .io .ByteArrayOutputStream ;
21- import java .io .IOException ;
22- import java .io .InputStream ;
23- import java .io .Writer ;
24- import java .util .ArrayList ;
25- import java .util .Collections ;
26- import java .util .HashMap ;
27- import java .util .List ;
28- import java .util .Map ;
29- import java .util .Objects ;
30- import java .util .Optional ;
18+ import javax .servlet .http .Part ;
19+ import java .io .*;
20+ import java .util .*;
3121import java .util .function .BiConsumer ;
3222import java .util .function .Consumer ;
3323import java .util .function .Function ;
3424import java .util .stream .Collectors ;
25+ import java .util .stream .Stream ;
3526
3627/**
3728 * @author Andrew Potter
@@ -48,7 +39,9 @@ public abstract class AbstractGraphQLHttpServlet extends HttpServlet implements
4839 private static final GraphQLRequest INTROSPECTION_REQUEST = new GraphQLRequest (IntrospectionQuery .INTROSPECTION_QUERY , new HashMap <>(), null );
4940
5041 protected abstract GraphQLQueryInvoker getQueryInvoker ();
42+
5143 protected abstract GraphQLInvocationInputFactory getInvocationInputFactory ();
44+
5245 protected abstract GraphQLObjectMapper getGraphQLObjectMapper ();
5346
5447 private final List <GraphQLServletListener > listeners ;
@@ -89,10 +82,7 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
8982 variables .putAll (graphQLObjectMapper .deserializeVariables (request .getParameter ("variables" )));
9083 }
9184
92- String operationName = null ;
93- if (request .getParameter ("operationName" ) != null ) {
94- operationName = request .getParameter ("operationName" );
95- }
85+ String operationName = request .getParameter ("operationName" );
9686
9787 query (queryInvoker , graphQLObjectMapper , invocationInputFactory .createReadOnly (new GraphQLRequest (query , variables , operationName ), request ), response );
9888 }
@@ -109,11 +99,18 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
10999 GraphQLQueryInvoker queryInvoker = getQueryInvoker ();
110100
111101 try {
112- if (ServletFileUpload .isMultipartContent (request )) {
113- final Map <String , List <FileItem >> fileItems = fileUpload .parseParameterMap (request );
102+ if (APPLICATION_GRAPHQL .equals (request .getContentType ())) {
103+ String query = CharStreams .toString (request .getReader ());
104+ query (queryInvoker , graphQLObjectMapper , invocationInputFactory .create (graphQLObjectMapper .readGraphQLRequest (query ), request ), response );
105+ } else if (request .getContentType () != null && request .getContentType ().startsWith ("multipart/form-data" ) && !request .getParts ().isEmpty ()) {
106+ final Map <String , List <Part >> fileItems = request .getParts ().stream ()
107+ .collect (Collectors .toMap (
108+ Part ::getName ,
109+ Collections ::singletonList ,
110+ (l1 , l2 ) -> Stream .concat (l1 .stream (), l2 .stream ()).collect (Collectors .toList ())));
114111
115112 if (fileItems .containsKey ("graphql" )) {
116- final Optional <FileItem > graphqlItem = getFileItem (fileItems , "graphql" );
113+ final Optional <Part > graphqlItem = getFileItem (fileItems , "graphql" );
117114 if (graphqlItem .isPresent ()) {
118115 InputStream inputStream = graphqlItem .get ().getInputStream ();
119116
@@ -134,7 +131,7 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
134131 }
135132 }
136133 } else if (fileItems .containsKey ("query" )) {
137- final Optional <FileItem > queryItem = getFileItem (fileItems , "query" );
134+ final Optional <Part > queryItem = getFileItem (fileItems , "query" );
138135 if (queryItem .isPresent ()) {
139136 InputStream inputStream = queryItem .get ().getInputStream ();
140137
@@ -148,18 +145,18 @@ public AbstractGraphQLHttpServlet(List<GraphQLServletListener> listeners, boolea
148145 queryBatched (queryInvoker , graphQLObjectMapper , invocationInput , response );
149146 return ;
150147 } else {
151- String query = new String (queryItem . get (). get ( ));
148+ String query = new String (ByteStreams . toByteArray ( inputStream ));
152149
153150 Map <String , Object > variables = null ;
154- final Optional <FileItem > variablesItem = getFileItem (fileItems , "variables" );
151+ final Optional <Part > variablesItem = getFileItem (fileItems , "variables" );
155152 if (variablesItem .isPresent ()) {
156- variables = graphQLObjectMapper .deserializeVariables (new String (variablesItem .get ().get ( )));
153+ variables = graphQLObjectMapper .deserializeVariables (new String (ByteStreams . toByteArray ( variablesItem .get ().getInputStream () )));
157154 }
158155
159156 String operationName = null ;
160- final Optional <FileItem > operationNameItem = getFileItem (fileItems , "operationName" );
157+ final Optional <Part > operationNameItem = getFileItem (fileItems , "operationName" );
161158 if (operationNameItem .isPresent ()) {
162- operationName = new String (operationNameItem .get ().get ( )).trim ();
159+ operationName = new String (ByteStreams . toByteArray ( operationNameItem .get ().getInputStream () )).trim ();
163160 }
164161
165162 GraphQLSingleInvocationInput invocationInput = invocationInputFactory .create (new GraphQLRequest (query , variables , operationName ), request );
@@ -220,7 +217,18 @@ public String executeQuery(String query) {
220217 }
221218 }
222219
223- private void doRequest (HttpServletRequest request , HttpServletResponse response , HttpRequestHandler handler ) {
220+ private void doRequestAsync (HttpServletRequest request , HttpServletResponse response , HttpRequestHandler handler ) {
221+ if (asyncServletMode ) {
222+ AsyncContext asyncContext = request .startAsync ();
223+ HttpServletRequest asyncRequest = (HttpServletRequest ) asyncContext .getRequest ();
224+ HttpServletResponse asyncResponse = (HttpServletResponse ) asyncContext .getResponse ();
225+ new Thread (() -> doRequest (asyncRequest , asyncResponse , handler , asyncContext )).start ();
226+ } else {
227+ doRequest (request , response , handler , null );
228+ }
229+ }
230+
231+ private void doRequest (HttpServletRequest request , HttpServletResponse response , HttpRequestHandler handler , AsyncContext asyncContext ) {
224232
225233 List <GraphQLServletListener .RequestCallback > requestCallbacks = runListeners (l -> l .onRequest (request , response ));
226234
@@ -233,26 +241,24 @@ private void doRequest(HttpServletRequest request, HttpServletResponse response,
233241 runCallbacks (requestCallbacks , c -> c .onError (request , response , t ));
234242 } finally {
235243 runCallbacks (requestCallbacks , c -> c .onFinally (request , response ));
244+ if (asyncContext != null ) {
245+ asyncContext .complete ();
246+ }
236247 }
237248 }
238249
239250 @ Override
240251 protected void doGet (HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException {
241- doRequest (req , resp , getHandler );
252+ doRequestAsync (req , resp , getHandler );
242253 }
243254
244255 @ Override
245256 protected void doPost (HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException {
246- doRequest (req , resp , postHandler );
257+ doRequestAsync (req , resp , postHandler );
247258 }
248259
249- private Optional <FileItem > getFileItem (Map <String , List <FileItem >> fileItems , String name ) {
250- List <FileItem > items = fileItems .get (name );
251- if (items == null || items .isEmpty ()) {
252- return Optional .empty ();
253- }
254-
255- return items .stream ().findFirst ();
260+ private Optional <Part > getFileItem (Map <String , List <Part >> fileItems , String name ) {
261+ return Optional .ofNullable (fileItems .get (name )).filter (list -> !list .isEmpty ()).map (list -> list .get (0 ));
256262 }
257263
258264 private void query (GraphQLQueryInvoker queryInvoker , GraphQLObjectMapper graphQLObjectMapper , GraphQLSingleInvocationInput invocationInput , HttpServletResponse resp ) throws IOException {
@@ -272,7 +278,7 @@ private void queryBatched(GraphQLQueryInvoker queryInvoker, GraphQLObjectMapper
272278
273279 queryInvoker .query (invocationInput , (result , hasNext ) -> {
274280 respWriter .write (graphQLObjectMapper .serializeResultAsJson (result ));
275- if (hasNext ) {
281+ if (hasNext ) {
276282 respWriter .write (',' );
277283 }
278284 });
@@ -286,16 +292,16 @@ private <R> List<R> runListeners(Function<? super GraphQLServletListener, R> act
286292 }
287293
288294 return listeners .stream ()
289- .map (listener -> {
290- try {
291- return action .apply (listener );
292- } catch (Throwable t ) {
293- log .error ("Error running listener: {}" , listener , t );
294- return null ;
295- }
296- })
297- .filter (Objects ::nonNull )
298- .collect (Collectors .toList ());
295+ .map (listener -> {
296+ try {
297+ return action .apply (listener );
298+ } catch (Throwable t ) {
299+ log .error ("Error running listener: {}" , listener , t );
300+ return null ;
301+ }
302+ })
303+ .filter (Objects ::nonNull )
304+ .collect (Collectors .toList ());
299305 }
300306
301307 private <T > void runCallbacks (List <T > callbacks , Consumer <T > action ) {
0 commit comments