4141import org .eclipse .jetty .client .util .BytesContentProvider ;
4242import org .eclipse .jetty .client .util .InputStreamResponseListener ;
4343import org .eclipse .jetty .client .util .MultiPartContentProvider ;
44+ import org .eclipse .jetty .client .util .OutputStreamContentProvider ;
4445import org .eclipse .jetty .client .util .StringContentProvider ;
4546import org .eclipse .jetty .http .HttpFields ;
4647import org .eclipse .jetty .http .HttpHeader ;
4748import org .eclipse .jetty .http .HttpMethod ;
4849import org .eclipse .jetty .http .HttpScheme ;
50+ import org .eclipse .jetty .http .HttpStatus ;
4951import org .eclipse .jetty .http .MimeTypes ;
5052import org .eclipse .jetty .http .MultiPartFormInputStream ;
53+ import org .eclipse .jetty .io .EofException ;
5154import org .eclipse .jetty .server .HttpChannel ;
5255import org .eclipse .jetty .server .HttpConnectionFactory ;
5356import org .eclipse .jetty .server .MultiPartFormDataCompliance ;
6770
6871import static org .hamcrest .MatcherAssert .assertThat ;
6972import static org .hamcrest .Matchers .containsString ;
73+ import static org .hamcrest .Matchers .equalTo ;
74+ import static org .hamcrest .Matchers .instanceOf ;
7075import static org .hamcrest .Matchers .is ;
7176import static org .hamcrest .Matchers .startsWith ;
7277import static org .junit .jupiter .api .Assertions .assertEquals ;
@@ -82,13 +87,27 @@ public class MultiPartServletTest
8287 private Path tmpDir ;
8388
8489 private static final int MAX_FILE_SIZE = 512 * 1024 ;
90+ private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 8 ;
8591 private static final int LARGE_MESSAGE_SIZE = 1024 * 1024 ;
8692
8793 public static Stream <Arguments > data ()
8894 {
8995 return Arrays .asList (MultiPartFormDataCompliance .values ()).stream ().map (Arguments ::of );
9096 }
9197
98+ public static class RequestParameterServlet extends HttpServlet
99+ {
100+ @ Override
101+ protected void doPost (HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException
102+ {
103+ req .getParameterMap ();
104+ req .getParts ();
105+ resp .setStatus (200 );
106+ resp .getWriter ().print ("success" );
107+ resp .getWriter ().close ();
108+ }
109+ }
110+
92111 public static class MultiPartServlet extends HttpServlet
93112 {
94113 @ Override
@@ -130,6 +149,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
130149 public void start () throws Exception
131150 {
132151 tmpDir = Files .createTempDirectory (MultiPartServletTest .class .getSimpleName ());
152+ Files .deleteIfExists (tmpDir );
133153 assertNotNull (tmpDir );
134154
135155 server = new Server ();
@@ -138,11 +158,19 @@ public void start() throws Exception
138158
139159 MultipartConfigElement config = new MultipartConfigElement (tmpDir .toAbsolutePath ().toString (),
140160 MAX_FILE_SIZE , -1 , 1 );
161+ MultipartConfigElement requestSizedConfig = new MultipartConfigElement (tmpDir .toAbsolutePath ().toString (),
162+ -1 , MAX_REQUEST_SIZE , 1 );
163+ MultipartConfigElement defaultConfig = new MultipartConfigElement (tmpDir .toAbsolutePath ().toString (),
164+ -1 , -1 , 1 );
141165
142166 ServletContextHandler contextHandler = new ServletContextHandler (ServletContextHandler .SESSIONS );
143167 contextHandler .setContextPath ("/" );
144168 ServletHolder servletHolder = contextHandler .addServlet (MultiPartServlet .class , "/" );
145169 servletHolder .getRegistration ().setMultipartConfig (config );
170+ servletHolder = contextHandler .addServlet (RequestParameterServlet .class , "/defaultConfig" );
171+ servletHolder .getRegistration ().setMultipartConfig (defaultConfig );
172+ servletHolder = contextHandler .addServlet (RequestParameterServlet .class , "/requestSizeLimit" );
173+ servletHolder .getRegistration ().setMultipartConfig (requestSizedConfig );
146174 servletHolder = contextHandler .addServlet (MultiPartEchoServlet .class , "/echo" );
147175 servletHolder .getRegistration ().setMultipartConfig (config );
148176
@@ -169,6 +197,119 @@ public void stop() throws Exception
169197 IO .delete (tmpDir .toFile ());
170198 }
171199
200+ @ ParameterizedTest
201+ @ MethodSource ("data" )
202+ public void testLargePart (MultiPartFormDataCompliance compliance ) throws Exception
203+ {
204+ connector .getConnectionFactory (HttpConnectionFactory .class ).getHttpConfiguration ()
205+ .setMultiPartFormDataCompliance (compliance );
206+
207+ OutputStreamContentProvider content = new OutputStreamContentProvider ();
208+ MultiPartContentProvider multiPart = new MultiPartContentProvider ();
209+ multiPart .addFieldPart ("param" , content , null );
210+ multiPart .close ();
211+
212+ InputStreamResponseListener listener = new InputStreamResponseListener ();
213+ client .newRequest ("localhost" , connector .getLocalPort ())
214+ .path ("/defaultConfig" )
215+ .scheme (HttpScheme .HTTP .asString ())
216+ .method (HttpMethod .POST )
217+ .content (multiPart )
218+ .send (listener );
219+
220+ // Write large amount of content to the part.
221+ byte [] byteArray = new byte [1024 * 1024 ];
222+ Arrays .fill (byteArray , (byte )1 );
223+ for (int i = 0 ; i < 128 * 2 ; i ++)
224+ {
225+ content .getOutputStream ().write (byteArray );
226+ }
227+ content .close ();
228+
229+ Response response = listener .get (2 , TimeUnit .MINUTES );
230+ assertThat (response .getStatus (), equalTo (HttpStatus .BAD_REQUEST_400 ));
231+ String responseContent = IO .toString (listener .getInputStream ());
232+ assertThat (responseContent , containsString ("Unable to parse form content" ));
233+ assertThat (responseContent , containsString ("Form is larger than max length" ));
234+ }
235+
236+ @ ParameterizedTest
237+ @ MethodSource ("data" )
238+ public void testManyParts (MultiPartFormDataCompliance compliance ) throws Exception
239+ {
240+ connector .getConnectionFactory (HttpConnectionFactory .class ).getHttpConfiguration ()
241+ .setMultiPartFormDataCompliance (compliance );
242+
243+ byte [] byteArray = new byte [1024 ];
244+ Arrays .fill (byteArray , (byte )1 );
245+
246+ MultiPartContentProvider multiPart = new MultiPartContentProvider ();
247+ for (int i = 0 ; i < 1024 * 1024 ; i ++)
248+ {
249+ BytesContentProvider content = new BytesContentProvider (byteArray );
250+ multiPart .addFieldPart ("part" + i , content , null );
251+ }
252+ multiPart .close ();
253+
254+ InputStreamResponseListener listener = new InputStreamResponseListener ();
255+ client .newRequest ("localhost" , connector .getLocalPort ())
256+ .path ("/defaultConfig" )
257+ .scheme (HttpScheme .HTTP .asString ())
258+ .method (HttpMethod .POST )
259+ .content (multiPart )
260+ .send (listener );
261+
262+ Response response = listener .get (30 , TimeUnit .SECONDS );
263+ assertThat (response .getStatus (), equalTo (HttpStatus .BAD_REQUEST_400 ));
264+ String responseContent = IO .toString (listener .getInputStream ());
265+ assertThat (responseContent , containsString ("Unable to parse form content" ));
266+ assertThat (responseContent , containsString ("Form with too many parts" ));
267+ }
268+
269+ @ ParameterizedTest
270+ @ MethodSource ("data" )
271+ public void testMaxRequestSize (MultiPartFormDataCompliance compliance ) throws Exception
272+ {
273+ connector .getConnectionFactory (HttpConnectionFactory .class ).getHttpConfiguration ()
274+ .setMultiPartFormDataCompliance (compliance );
275+
276+ OutputStreamContentProvider content = new OutputStreamContentProvider ();
277+ MultiPartContentProvider multiPart = new MultiPartContentProvider ();
278+ multiPart .addFieldPart ("param" , content , null );
279+ multiPart .close ();
280+
281+ InputStreamResponseListener listener = new InputStreamResponseListener ();
282+ client .newRequest ("localhost" , connector .getLocalPort ())
283+ .path ("/requestSizeLimit" )
284+ .scheme (HttpScheme .HTTP .asString ())
285+ .method (HttpMethod .POST )
286+ .content (multiPart )
287+ .send (listener );
288+
289+ Throwable writeError = null ;
290+ try
291+ {
292+ // Write large amount of content to the part.
293+ byte [] byteArray = new byte [1024 * 1024 ];
294+ Arrays .fill (byteArray , (byte )1 );
295+ for (int i = 0 ; i < 512 ; i ++)
296+ {
297+ content .getOutputStream ().write (byteArray );
298+ }
299+ }
300+ catch (Exception e )
301+ {
302+ writeError = e ;
303+ }
304+
305+ if (writeError != null )
306+ assertThat (writeError , instanceOf (EofException .class ));
307+
308+ // We should get 400 response.
309+ Response response = listener .get (30 , TimeUnit .SECONDS );
310+ assertThat (response .getStatus (), equalTo (HttpStatus .BAD_REQUEST_400 ));
311+ }
312+
172313 @ ParameterizedTest
173314 @ MethodSource ("data" )
174315 public void testTempFilesDeletedOnError (MultiPartFormDataCompliance compliance ) throws Exception
0 commit comments