3434import lombok .Setter ;
3535import lombok .SneakyThrows ;
3636import lombok .extern .slf4j .Slf4j ;
37+ import org .apache .commons .fileupload .FileItem ;
38+ import org .apache .commons .fileupload .FileItemFactory ;
39+ import org .apache .commons .fileupload .disk .DiskFileItemFactory ;
3740import org .apache .commons .fileupload .servlet .ServletFileUpload ;
3841
3942import javax .security .auth .Subject ;
4245import javax .servlet .http .HttpServlet ;
4346import javax .servlet .http .HttpServletRequest ;
4447import javax .servlet .http .HttpServletResponse ;
45- import javax .servlet .http .Part ;
4648import java .io .IOException ;
4749import java .io .InputStream ;
4850import java .io .InputStreamReader ;
@@ -75,14 +77,113 @@ public abstract class GraphQLServlet extends HttpServlet implements Servlet, Gra
7577
7678 private final List <GraphQLOperationListener > operationListeners ;
7779 private final List <GraphQLServletListener > servletListeners ;
80+ private final ServletFileUpload fileUpload ;
81+
82+ private final RequestHandler getHandler ;
83+ private final RequestHandler postHandler ;
7884
7985 public GraphQLServlet () {
80- this (null , null );
86+ this (null , null , null );
8187 }
8288
83- public GraphQLServlet (List <GraphQLOperationListener > operationListeners , List <GraphQLServletListener > servletListeners ) {
89+ public GraphQLServlet (List <GraphQLOperationListener > operationListeners , List <GraphQLServletListener > servletListeners , FileItemFactory fileItemFactory ) {
8490 this .operationListeners = operationListeners != null ? new ArrayList <>(operationListeners ) : new ArrayList <>();
8591 this .servletListeners = servletListeners != null ? new ArrayList <>(servletListeners ) : new ArrayList <>();
92+ this .fileUpload = new ServletFileUpload (fileItemFactory != null ? fileItemFactory : new DiskFileItemFactory ());
93+
94+ this .getHandler = (request , response ) -> {
95+ GraphQLContext context = createContext (Optional .of (request ), Optional .of (response ));
96+ String path = request .getPathInfo ();
97+ if (path == null ) {
98+ path = request .getServletPath ();
99+ }
100+ if (path .contentEquals ("/schema.json" )) {
101+ query (CharStreams .toString (new InputStreamReader (GraphQLServlet .class .getResourceAsStream ("introspectionQuery" ))), null , new HashMap <>(), getSchema (), request , response , context );
102+ } else {
103+ if (request .getParameter ("query" ) != null ) {
104+ Map <String , Object > variables = new HashMap <>();
105+ if (request .getParameter ("variables" ) != null ) {
106+ variables .putAll (mapper .readValue (request .getParameter ("variables" ), new TypeReference <Map <String , Object >>() { }));
107+ }
108+ String operationName = null ;
109+ if (request .getParameter ("operationName" ) != null ) {
110+ operationName = request .getParameter ("operationName" );
111+ }
112+ query (request .getParameter ("query" ), operationName , variables , getReadOnlySchema (), request , response , context );
113+ } else {
114+ response .setStatus (STATUS_BAD_REQUEST );
115+ log .info ("Bad GET request: path was not \" /schema.json\" or no query variable named \" query\" given" );
116+ }
117+ }
118+ };
119+
120+ this .postHandler = (request , response ) -> {
121+ GraphQLContext context = createContext (Optional .of (request ), Optional .of (response ));
122+ GraphQLRequest graphQLRequest = null ;
123+
124+ try {
125+ InputStream inputStream = null ;
126+
127+ if (ServletFileUpload .isMultipartContent (request )) {
128+ Map <String , List <FileItem >> fileItems = fileUpload .parseParameterMap (request );
129+
130+ if (fileItems .containsKey ("graphql" )) {
131+ Optional <FileItem > graphqlItem = getFileItem (fileItems , "graphql" );
132+ if (graphqlItem .isPresent ()) {
133+ inputStream = graphqlItem .get ().getInputStream ();
134+ }
135+
136+ } else if (fileItems .containsKey ("query" )) {
137+ Optional <FileItem > queryItem = getFileItem (fileItems , "query" );
138+ if (queryItem .isPresent ()) {
139+ graphQLRequest = new GraphQLRequest ();
140+ graphQLRequest .setQuery (new String (queryItem .get ().get ()));
141+
142+ Optional <FileItem > operationNameItem = getFileItem (fileItems , "operationName" );
143+ if (operationNameItem .isPresent ()) {
144+ graphQLRequest .setOperationName (new String (operationNameItem .get ().get ()).trim ());
145+ }
146+
147+ Optional <FileItem > variablesItem = getFileItem (fileItems , "variables" );
148+ if (variablesItem .isPresent ()) {
149+ String variables = new String (variablesItem .get ().get ());
150+ if (!variables .isEmpty ()) {
151+ graphQLRequest .setVariables ((Map <String , Object >) mapper .readValue (variables , Map .class ));
152+ }
153+ }
154+ }
155+ }
156+
157+ if (inputStream == null && graphQLRequest == null ) {
158+ response .setStatus (STATUS_BAD_REQUEST );
159+ log .info ("Bad POST multipart request: no part named \" graphql\" or \" query\" " );
160+ return ;
161+ }
162+
163+ context .setFiles (Optional .of (fileItems ));
164+
165+ } else {
166+ // this is not a multipart request
167+ inputStream = request .getInputStream ();
168+ }
169+
170+ if (graphQLRequest == null ) {
171+ graphQLRequest = mapper .readValue (inputStream , GraphQLRequest .class );
172+ }
173+
174+ } catch (Exception e ) {
175+ log .info ("Bad POST request: parsing failed" , e );
176+ response .setStatus (STATUS_BAD_REQUEST );
177+ return ;
178+ }
179+
180+ Map <String ,Object > variables = graphQLRequest .getVariables ();
181+ if (variables == null ) {
182+ variables = new HashMap <>();
183+ }
184+
185+ query (graphQLRequest .getQuery (), graphQLRequest .getOperationName (), variables , getSchema (), request , response , context );
186+ };
86187 }
87188
88189 public void addOperationListener (GraphQLOperationListener operationListener ) {
@@ -121,71 +222,6 @@ public String executeQuery(String query) {
121222 }
122223 }
123224
124- private final RequestHandler getHandler = (request , response ) -> {
125- GraphQLContext context = createContext (Optional .of (request ), Optional .of (response ));
126- String path = request .getPathInfo ();
127- if (path == null ) {
128- path = request .getServletPath ();
129- }
130- if (path .contentEquals ("/schema.json" )) {
131- query (CharStreams .toString (new InputStreamReader (GraphQLServlet .class .getResourceAsStream ("introspectionQuery" ))), null , new HashMap <>(), getSchema (), request , response , context );
132- } else {
133- if (request .getParameter ("query" ) != null ) {
134- Map <String , Object > variables = new HashMap <>();
135- if (request .getParameter ("variables" ) != null ) {
136- variables .putAll (mapper .readValue (request .getParameter ("variables" ), new TypeReference <Map <String , Object >>() { }));
137- }
138- String operationName = null ;
139- if (request .getParameter ("operationName" ) != null ) {
140- operationName = request .getParameter ("operationName" );
141- }
142- query (request .getParameter ("query" ), operationName , variables , getReadOnlySchema (), request , response , context );
143- } else {
144- response .setStatus (STATUS_BAD_REQUEST );
145- log .info ("Bad GET request: path was not \" /schema.json\" or no query variable named \" query\" given" );
146- }
147- }
148- };
149-
150- private final RequestHandler postHandler = (request , response ) -> {
151- GraphQLContext context = createContext (Optional .of (request ), Optional .of (response ));
152- InputStream inputStream = null ;
153-
154- if (ServletFileUpload .isMultipartContent (request )) {
155- Part part = request .getPart ("graphql" );
156- if (part != null ) {
157- inputStream = part .getInputStream ();
158- }
159-
160- if (inputStream == null ) {
161- response .setStatus (STATUS_BAD_REQUEST );
162- log .info ("Bad POST multipart request: no part named \" graphql\" " );
163- return ;
164- }
165-
166- context .setParts (Optional .of (request .getParts ()));
167-
168- } else {
169- // this is not a multipart request
170- inputStream = request .getInputStream ();
171- }
172-
173- GraphQLRequest graphQLRequest ;
174- try {
175- graphQLRequest = mapper .readValue (inputStream , GraphQLRequest .class );
176- } catch (Exception e ) {
177- log .info ("Bad POST request: deserialization failed" , e );
178- response .setStatus (STATUS_BAD_REQUEST );
179- return ;
180- }
181-
182- Map <String ,Object > variables = graphQLRequest .variables ;
183- if (variables == null ) {
184- variables = new HashMap <>();
185- }
186- query (graphQLRequest .query , graphQLRequest .operationName , variables , getSchema (), request , response , context );
187- };
188-
189225 private void doRequest (HttpServletRequest request , HttpServletResponse response , RequestHandler handler ) {
190226 try {
191227 runListeners (servletListeners , l -> l .onStart (request , response ));
@@ -210,6 +246,16 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
210246 doRequest (req , resp , postHandler );
211247 }
212248
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 ();
256+ }
257+
258+
213259 private void query (String query , String operationName , Map <String , Object > variables , GraphQLSchema schema , HttpServletRequest req , HttpServletResponse resp , GraphQLContext context ) throws IOException {
214260 if (Subject .getSubject (AccessController .getContext ()) == null && context .getSubject ().isPresent ()) {
215261 Subject .doAs (context .getSubject ().get (), new PrivilegedAction <Void >() {
0 commit comments