2222import java .util .concurrent .CountDownLatch ;
2323import java .util .concurrent .Exchanger ;
2424import java .util .concurrent .TimeUnit ;
25+ import java .util .stream .Stream ;
2526
2627import org .awaitility .Awaitility ;
2728import org .eclipse .jetty .http .HttpException ;
4445import org .junit .jupiter .api .AfterEach ;
4546import org .junit .jupiter .api .BeforeEach ;
4647import org .junit .jupiter .api .Test ;
48+ import org .junit .jupiter .params .ParameterizedTest ;
49+ import org .junit .jupiter .params .provider .Arguments ;
50+ import org .junit .jupiter .params .provider .MethodSource ;
4751
4852import static org .hamcrest .MatcherAssert .assertThat ;
4953import static org .hamcrest .Matchers .containsString ;
@@ -536,12 +540,10 @@ public boolean handle(Request request, Response response, Callback callback) thr
536540 @ Test
537541 public void testEagerFormFieldsLimits () throws Exception
538542 {
539- // Set the global form limits for maxFormContentSize.
540- System .setProperty ("org.eclipse.jetty.server.Request.maxFormKeys" , "-1" );
541- System .setProperty ("org.eclipse.jetty.server.Request.maxFormContentSize" , "15" );
543+ // Set the server context limit for maxFormContentSize.
544+ _server .getContext ().setAttribute ("org.eclipse.jetty.server.Request.maxFormContentSize" , "15" );
542545
543- CountDownLatch processing = new CountDownLatch (1 );
544- CompletableFuture <Throwable > contentLoaderErrorFuture = new CompletableFuture <>();
546+ CountDownLatch processing = new CountDownLatch (2 );
545547 CompletableFuture <Throwable > handlerErrorFuture = new CompletableFuture <>();
546548 EagerContentHandler eagerContentHandler = new EagerContentHandler (new EagerContentHandler .FormContentLoaderFactory ()
547549 {
@@ -554,16 +556,8 @@ public EagerContentHandler.ContentLoader newContentLoader(String contentType, St
554556 @ Override
555557 protected void load () throws Exception
556558 {
557- try
558- {
559- contentLoader .load ();
560- processing .countDown ();
561- }
562- catch (Throwable t )
563- {
564- contentLoaderErrorFuture .complete (t );
565- throw t ;
566- }
559+ contentLoader .load ();
560+ processing .countDown ();
567561 }
568562 };
569563 }
@@ -605,8 +599,8 @@ public boolean handle(Request request, Response response, Callback callback) thr
605599 output .write (request .getBytes (StandardCharsets .UTF_8 ));
606600 output .flush ();
607601
608- // The EagerContentHandler's ContentLoader should throw so we never reach the handler.
609- Throwable throwable = contentLoaderErrorFuture .get (5 , TimeUnit .SECONDS );
602+ // The failure should not be thrown from the content loader but the handler.
603+ Throwable throwable = handlerErrorFuture .get (5 , TimeUnit .SECONDS );
610604 assertThat (throwable , instanceOf (HttpException .IllegalStateException .class ));
611605
612606 // The response code should be 413 PAYLOAD_TOO_LARGE.
@@ -658,6 +652,76 @@ public boolean handle(Request request, Response response, Callback callback) thr
658652 }
659653 }
660654
655+ public static Stream <Arguments > formLimitsProvider ()
656+ {
657+ // The form content has a size of 27 bytes with 2 fields.
658+ return Stream .of (
659+ Arguments .of (-1 , -1 , HttpStatus .OK_200 ),
660+ Arguments .of (100 , 100 , HttpStatus .OK_200 ),
661+ Arguments .of (2 , -1 , HttpStatus .OK_200 ),
662+ Arguments .of (1 , -1 , HttpStatus .PAYLOAD_TOO_LARGE_413 ),
663+ Arguments .of (-1 , 27 , HttpStatus .OK_200 ),
664+ Arguments .of (-1 , 26 , HttpStatus .PAYLOAD_TOO_LARGE_413 )
665+ );
666+ }
667+
668+ @ ParameterizedTest
669+ @ MethodSource ("formLimitsProvider" )
670+ public void perRequestFormLimitsTest (int maxFormFields , int maxFormLength , int expectedStatusCode ) throws Exception
671+ {
672+ _server .setHandler (new Handler .Abstract ()
673+ {
674+ @ Override
675+ public boolean handle (Request request , Response response , Callback callback ) throws Exception
676+ {
677+ String maxFieldsAttribute = request .getHeaders ().get (FormFields .MAX_FIELDS_ATTRIBUTE );
678+ if (maxFieldsAttribute != null )
679+ request .setAttribute (FormFields .MAX_FIELDS_ATTRIBUTE , maxFieldsAttribute );
680+
681+ String maxLengthAttribute = request .getHeaders ().get (FormFields .MAX_LENGTH_ATTRIBUTE );
682+ if (maxLengthAttribute != null )
683+ request .setAttribute (FormFields .MAX_LENGTH_ATTRIBUTE , maxLengthAttribute );
684+
685+ Fields fields = FormFields .getFields (request );
686+ Content .Sink .write (response , true , String .valueOf (fields ), callback );
687+ return true ;
688+ }
689+ });
690+ _server .start ();
691+
692+ HttpTester .Response response = getResponse (maxFormFields , maxFormLength );
693+ assertThat (response .getStatus (), is (expectedStatusCode ));
694+ }
695+
696+ private HttpTester .Response getResponse (int maxFormFields , int maxFormLength ) throws Exception
697+ {
698+ try (Socket socket = new Socket ("localhost" , _connector .getLocalPort ()))
699+ {
700+ // Write the first request content which does not exceed the form limits.
701+ StringBuilder request = new StringBuilder ();
702+ request .append ("""
703+ POST /foo HTTP/1.1\r
704+ Host: localhost\r
705+ """ );
706+ if (maxFormFields != -1 )
707+ request .append (FormFields .MAX_FIELDS_ATTRIBUTE ).append (": " ).append (maxFormFields ).append ("\r \n " );
708+ if (maxFormLength != -1 )
709+ request .append (FormFields .MAX_LENGTH_ATTRIBUTE ).append (": " ).append (maxFormLength ).append ("\r \n " );
710+ request .append ("""
711+ Content-Type: application/x-www-form-urlencoded\r
712+ Content-Length: 27\r
713+ \r
714+ param1=value1¶m2=value2\
715+ """ );
716+ OutputStream output = socket .getOutputStream ();
717+ output .write (request .toString ().getBytes (StandardCharsets .UTF_8 ));
718+ output .flush ();
719+
720+ HttpTester .Input input = HttpTester .from (socket .getInputStream ());
721+ return HttpTester .parseResponse (input );
722+ }
723+ }
724+
661725 @ Test
662726 public void testDirectCallFormFields () throws Exception
663727 {
0 commit comments