41
41
import org .eclipse .jetty .client .util .BytesContentProvider ;
42
42
import org .eclipse .jetty .client .util .InputStreamResponseListener ;
43
43
import org .eclipse .jetty .client .util .MultiPartContentProvider ;
44
+ import org .eclipse .jetty .client .util .OutputStreamContentProvider ;
44
45
import org .eclipse .jetty .client .util .StringContentProvider ;
45
46
import org .eclipse .jetty .http .HttpFields ;
46
47
import org .eclipse .jetty .http .HttpHeader ;
47
48
import org .eclipse .jetty .http .HttpMethod ;
48
49
import org .eclipse .jetty .http .HttpScheme ;
50
+ import org .eclipse .jetty .http .HttpStatus ;
49
51
import org .eclipse .jetty .http .MimeTypes ;
50
52
import org .eclipse .jetty .http .MultiPartFormInputStream ;
53
+ import org .eclipse .jetty .io .EofException ;
51
54
import org .eclipse .jetty .server .HttpChannel ;
52
55
import org .eclipse .jetty .server .HttpConnectionFactory ;
53
56
import org .eclipse .jetty .server .MultiPartFormDataCompliance ;
67
70
68
71
import static org .hamcrest .MatcherAssert .assertThat ;
69
72
import static org .hamcrest .Matchers .containsString ;
73
+ import static org .hamcrest .Matchers .equalTo ;
74
+ import static org .hamcrest .Matchers .instanceOf ;
70
75
import static org .hamcrest .Matchers .is ;
71
76
import static org .hamcrest .Matchers .startsWith ;
72
77
import static org .junit .jupiter .api .Assertions .assertEquals ;
@@ -82,13 +87,27 @@ public class MultiPartServletTest
82
87
private Path tmpDir ;
83
88
84
89
private static final int MAX_FILE_SIZE = 512 * 1024 ;
90
+ private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 8 ;
85
91
private static final int LARGE_MESSAGE_SIZE = 1024 * 1024 ;
86
92
87
93
public static Stream <Arguments > data ()
88
94
{
89
95
return Arrays .asList (MultiPartFormDataCompliance .values ()).stream ().map (Arguments ::of );
90
96
}
91
97
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
+
92
111
public static class MultiPartServlet extends HttpServlet
93
112
{
94
113
@ Override
@@ -130,6 +149,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
130
149
public void start () throws Exception
131
150
{
132
151
tmpDir = Files .createTempDirectory (MultiPartServletTest .class .getSimpleName ());
152
+ Files .deleteIfExists (tmpDir );
133
153
assertNotNull (tmpDir );
134
154
135
155
server = new Server ();
@@ -138,11 +158,19 @@ public void start() throws Exception
138
158
139
159
MultipartConfigElement config = new MultipartConfigElement (tmpDir .toAbsolutePath ().toString (),
140
160
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 );
141
165
142
166
ServletContextHandler contextHandler = new ServletContextHandler (ServletContextHandler .SESSIONS );
143
167
contextHandler .setContextPath ("/" );
144
168
ServletHolder servletHolder = contextHandler .addServlet (MultiPartServlet .class , "/" );
145
169
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 );
146
174
servletHolder = contextHandler .addServlet (MultiPartEchoServlet .class , "/echo" );
147
175
servletHolder .getRegistration ().setMultipartConfig (config );
148
176
@@ -169,6 +197,119 @@ public void stop() throws Exception
169
197
IO .delete (tmpDir .toFile ());
170
198
}
171
199
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
+
172
313
@ ParameterizedTest
173
314
@ MethodSource ("data" )
174
315
public void testTempFilesDeletedOnError (MultiPartFormDataCompliance compliance ) throws Exception
0 commit comments