@@ -105,7 +105,7 @@ private void doTestChunkingCRLF(boolean chunkHeaderUsesCRLF,
105105 Context ctx =
106106 tomcat .addContext ("" , System .getProperty ("java.io.tmpdir" ));
107107
108- EchoHeaderServlet servlet = new EchoHeaderServlet ();
108+ EchoHeaderServlet servlet = new EchoHeaderServlet (expectPass );
109109 Tomcat .addServlet (ctx , "servlet" , servlet );
110110 ctx .addServletMapping ("/" , "servlet" );
111111
@@ -169,7 +169,7 @@ public void testTrailingHeadersSizeLimit() throws Exception {
169169 Context ctx =
170170 tomcat .addContext ("" , System .getProperty ("java.io.tmpdir" ));
171171
172- Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet ());
172+ Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet (false ));
173173 ctx .addServletMapping ("/" , "servlet" );
174174
175175 // Limit the size of the trailing header
@@ -233,7 +233,7 @@ private void doTestExtensionSizeLimit(int len, boolean ok) throws Exception {
233233 Context ctx =
234234 tomcat .addContext ("" , System .getProperty ("java.io.tmpdir" ));
235235
236- Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet ());
236+ Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet (ok ));
237237 ctx .addServletMapping ("/" , "servlet" );
238238
239239 tomcat .start ();
@@ -282,7 +282,7 @@ public void testNoTrailingHeaders() throws Exception {
282282 Context ctx =
283283 tomcat .addContext ("" , System .getProperty ("java.io.tmpdir" ));
284284
285- Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet ());
285+ Tomcat .addServlet (ctx , "servlet" , new EchoHeaderServlet (true ));
286286 ctx .addServletMapping ("/" , "servlet" );
287287
288288 tomcat .start ();
@@ -311,11 +311,136 @@ public void testNoTrailingHeaders() throws Exception {
311311 assertEquals ("nullnull7nullnull" , client .getResponseBody ());
312312 }
313313
314+ @ Test
315+ public void testChunkSizeZero () throws Exception {
316+ doTestChunkSize (true , true , "" , 10 , 0 );
317+ }
318+
319+ @ Test
320+ public void testChunkSizeAbsent () throws Exception {
321+ doTestChunkSize (false , false , SimpleHttpClient .CRLF , 10 , 0 );
322+ }
323+
324+ @ Test
325+ public void testChunkSizeTwentyFive () throws Exception {
326+ doTestChunkSize (true , true , "19" + SimpleHttpClient .CRLF
327+ + "Hello World!Hello World!!" + SimpleHttpClient .CRLF , 40 , 25 );
328+ }
329+
330+ @ Test
331+ public void testChunkSizeEightDigit () throws Exception {
332+ doTestChunkSize (true , true , "0000000C" + SimpleHttpClient .CRLF
333+ + "Hello World!" + SimpleHttpClient .CRLF , 20 , 12 );
334+ }
335+
336+ @ Test
337+ public void testChunkSizeNineDigit () throws Exception {
338+ doTestChunkSize (false , false , "00000000C" + SimpleHttpClient .CRLF
339+ + "Hello World!" + SimpleHttpClient .CRLF , 20 , 12 );
340+ }
341+
342+ @ Test
343+ public void testChunkSizeLong () throws Exception {
344+ doTestChunkSize (true , false , "7fFFffFF" + SimpleHttpClient .CRLF
345+ + "Hello World!" + SimpleHttpClient .CRLF , 10 , 10 );
346+ }
347+
348+ @ Test
349+ public void testChunkSizeIntegerMinValue () throws Exception {
350+ doTestChunkSize (false , false , "80000000" + SimpleHttpClient .CRLF
351+ + "Hello World!" + SimpleHttpClient .CRLF , 10 , 10 );
352+ }
353+
354+ @ Test
355+ public void testChunkSizeMinusOne () throws Exception {
356+ doTestChunkSize (false , false , "ffffffff" + SimpleHttpClient .CRLF
357+ + "Hello World!" + SimpleHttpClient .CRLF , 10 , 10 );
358+ }
359+
360+ /**
361+ * @param expectPass
362+ * If the servlet is expected to process the request
363+ * @param expectReadWholeBody
364+ * If the servlet is expected to fully read the body and reliably
365+ * deliver a response
366+ * @param chunks
367+ * Text of chunks
368+ * @param readLimit
369+ * Do not read more than this many bytes
370+ * @param expectReadCount
371+ * Expected count of read bytes
372+ * @throws Exception
373+ * Unexpected
374+ */
375+ private void doTestChunkSize (boolean expectPass ,
376+ boolean expectReadWholeBody , String chunks , int readLimit ,
377+ int expectReadCount ) throws Exception {
378+ // Setup Tomcat instance
379+ Tomcat tomcat = getTomcatInstance ();
380+
381+ // Must have a real docBase - just use temp
382+ Context ctx = tomcat .addContext ("" ,
383+ System .getProperty ("java.io.tmpdir" ));
384+
385+ BodyReadServlet servlet = new BodyReadServlet (expectPass , readLimit );
386+ Tomcat .addServlet (ctx , "servlet" , servlet );
387+ ctx .addServletMapping ("/" , "servlet" );
388+
389+ tomcat .start ();
390+
391+ String request = "POST /echo-params.jsp HTTP/1.1"
392+ + SimpleHttpClient .CRLF + "Host: any" + SimpleHttpClient .CRLF
393+ + "Transfer-encoding: chunked" + SimpleHttpClient .CRLF
394+ + "Content-Type: text/plain" + SimpleHttpClient .CRLF ;
395+ if (expectPass ) {
396+ request += "Connection: close" + SimpleHttpClient .CRLF ;
397+ }
398+ request += SimpleHttpClient .CRLF + chunks + "0" + SimpleHttpClient .CRLF
399+ + SimpleHttpClient .CRLF ;
400+
401+ TrailerClient client = new TrailerClient (tomcat .getConnector ()
402+ .getLocalPort ());
403+ client .setRequest (new String [] { request });
404+
405+ Exception processException = null ;
406+ client .connect ();
407+ try {
408+ client .processRequest ();
409+ } catch (Exception e ) {
410+ // Socket was probably closed before client had a chance to read
411+ // response
412+ processException = e ;
413+ }
414+ if (expectPass ) {
415+ if (expectReadWholeBody ) {
416+ assertNull (processException );
417+ }
418+ if (processException == null ) {
419+ assertTrue (client .getResponseLine (), client .isResponse200 ());
420+ assertEquals (String .valueOf (expectReadCount ),
421+ client .getResponseBody ());
422+ }
423+ assertEquals (expectReadCount , servlet .getCountRead ());
424+ } else {
425+ if (processException == null ) {
426+ assertTrue (client .getResponseLine (), client .isResponse500 ());
427+ }
428+ assertEquals (0 , servlet .getCountRead ());
429+ assertTrue (servlet .getExceptionDuringRead ());
430+ }
431+ }
432+
314433 private static class EchoHeaderServlet extends HttpServlet {
315434 private static final long serialVersionUID = 1L ;
316435
317436 private boolean exceptionDuringRead = false ;
318437
438+ private final boolean expectPass ;
439+
440+ public EchoHeaderServlet (boolean expectPass ) {
441+ this .expectPass = expectPass ;
442+ }
443+
319444 @ Override
320445 protected void doPost (HttpServletRequest req , HttpServletResponse resp )
321446 throws ServletException , IOException {
@@ -334,6 +459,11 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
334459 }
335460 } catch (IOException ioe ) {
336461 exceptionDuringRead = true ;
462+ if (!expectPass ) { // as expected
463+ log (ioe .toString ());
464+ resp .sendError (HttpServletResponse .SC_INTERNAL_SERVER_ERROR );
465+ return ;
466+ }
337467 throw ioe ;
338468 }
339469
@@ -358,6 +488,53 @@ private void dumpHeader(String headerName, HttpServletRequest req,
358488 }
359489 }
360490
491+ private static class BodyReadServlet extends HttpServlet {
492+ private static final long serialVersionUID = 1L ;
493+
494+ private boolean exceptionDuringRead = false ;
495+ private int countRead = 0 ;
496+ private final boolean expectPass ;
497+ private final int readLimit ;
498+
499+ public BodyReadServlet (boolean expectPass , int readLimit ) {
500+ this .expectPass = expectPass ;
501+ this .readLimit = readLimit ;
502+ }
503+
504+ @ Override
505+ protected void doPost (HttpServletRequest req , HttpServletResponse resp )
506+ throws ServletException , IOException {
507+ resp .setContentType ("text/plain" );
508+ PrintWriter pw = resp .getWriter ();
509+
510+ // Read the body - quick and dirty
511+ InputStream is = req .getInputStream ();
512+ try {
513+ while (is .read () > -1 && countRead < readLimit ) {
514+ countRead ++;
515+ }
516+ } catch (IOException ioe ) {
517+ exceptionDuringRead = true ;
518+ if (!expectPass ) { // as expected
519+ log (ioe .toString ());
520+ resp .sendError (HttpServletResponse .SC_INTERNAL_SERVER_ERROR );
521+ return ;
522+ }
523+ throw ioe ;
524+ }
525+
526+ pw .write (Integer .valueOf (countRead ).toString ());
527+ }
528+
529+ public boolean getExceptionDuringRead () {
530+ return exceptionDuringRead ;
531+ }
532+
533+ public int getCountRead () {
534+ return countRead ;
535+ }
536+ }
537+
361538 private static class TrailerClient extends SimpleHttpClient {
362539
363540 public TrailerClient (int port ) {
0 commit comments