3434#define MAX_HEADERS 10
3535#define MAX_ELEMENT_SIZE 500
3636
37+ #define MIN (a ,b ) ((a) < (b) ? (a) : (b))
38+
3739static http_parser * parser ;
3840
3941struct message {
@@ -47,6 +49,7 @@ struct message {
4749 char fragment [MAX_ELEMENT_SIZE ];
4850 char query_string [MAX_ELEMENT_SIZE ];
4951 char body [MAX_ELEMENT_SIZE ];
52+ size_t body_size ;
5053 int num_headers ;
5154 enum { NONE = 0 , FIELD , VALUE } last_header_element ;
5255 char headers [MAX_HEADERS ][2 ][MAX_ELEMENT_SIZE ];
@@ -597,6 +600,7 @@ const struct message responses[] =
597600 ,.status_code = 404
598601 ,.num_headers = 0
599602 ,.headers = {}
603+ ,.body_size = 0
600604 ,.body = ""
601605 }
602606
@@ -639,6 +643,7 @@ const struct message responses[] =
639643 { {"Content-Type" , "text/plain" }
640644 , {"Transfer-Encoding" , "chunked" }
641645 }
646+ ,.body_size = 37 + 28
642647 ,.body =
643648 "This is the data in the first chunk\r\n"
644649 "and this is the second one\r\n"
@@ -785,10 +790,20 @@ body_cb (http_parser *p, const char *buf, size_t len)
785790{
786791 assert (p == parser );
787792 strncat (messages [num_messages ].body , buf , len );
793+ messages [num_messages ].body_size += len ;
788794 // printf("body_cb: '%s'\n", requests[num_messages].body);
789795 return 0 ;
790796}
791797
798+ int
799+ count_body_cb (http_parser * p , const char * buf , size_t len )
800+ {
801+ assert (p == parser );
802+ assert (buf );
803+ messages [num_messages ].body_size += len ;
804+ return 0 ;
805+ }
806+
792807int
793808message_begin_cb (http_parser * p )
794809{
@@ -830,7 +845,7 @@ message_complete_cb (http_parser *p)
830845 return 0 ;
831846}
832847
833- static http_parser_settings settings =
848+ static http_parser_settings settings =
834849 {.on_message_begin = message_begin_cb
835850 ,.on_header_field = header_field_cb
836851 ,.on_header_value = header_value_cb
@@ -843,6 +858,19 @@ static http_parser_settings settings =
843858 ,.on_message_complete = message_complete_cb
844859 };
845860
861+ static http_parser_settings settings_count_body =
862+ {.on_message_begin = message_begin_cb
863+ ,.on_header_field = header_field_cb
864+ ,.on_header_value = header_value_cb
865+ ,.on_path = request_path_cb
866+ ,.on_url = request_url_cb
867+ ,.on_fragment = fragment_cb
868+ ,.on_query_string = query_string_cb
869+ ,.on_body = count_body_cb
870+ ,.on_headers_complete = headers_complete_cb
871+ ,.on_message_complete = message_complete_cb
872+ };
873+
846874void
847875parser_init (enum http_parser_type type )
848876{
@@ -874,6 +902,14 @@ inline size_t parse (const char *buf, size_t len)
874902 return nparsed ;
875903}
876904
905+ inline size_t parse_count_body (const char * buf , size_t len )
906+ {
907+ size_t nparsed ;
908+ currently_parsing_eof = (len == 0 );
909+ nparsed = http_parser_execute (parser , settings_count_body , buf , len );
910+ return nparsed ;
911+ }
912+
877913static inline int
878914check_str_eq (const struct message * m ,
879915 const char * prop ,
@@ -936,7 +972,11 @@ message_eq (int index, const struct message *expected)
936972 MESSAGE_CHECK_STR_EQ (expected , m , query_string );
937973 MESSAGE_CHECK_STR_EQ (expected , m , fragment );
938974 MESSAGE_CHECK_STR_EQ (expected , m , request_url );
939- MESSAGE_CHECK_STR_EQ (expected , m , body );
975+ if (expected -> body_size ) {
976+ MESSAGE_CHECK_NUM_EQ (expected , m , body_size );
977+ } else {
978+ MESSAGE_CHECK_STR_EQ (expected , m , body );
979+ }
940980
941981 MESSAGE_CHECK_NUM_EQ (expected , m , num_headers );
942982
@@ -1030,6 +1070,42 @@ test_message (const struct message *message)
10301070 parser_free ();
10311071}
10321072
1073+ void
1074+ test_message_count_body (const struct message * message )
1075+ {
1076+ parser_init (message -> type );
1077+
1078+ size_t read ;
1079+ size_t l = strlen (message -> raw );
1080+ size_t i , toread ;
1081+ size_t chunk = 4024 ;
1082+
1083+ for (i = 0 ; i < l ; i += chunk ) {
1084+ toread = MIN (l - i , chunk );
1085+ read = parse_count_body (message -> raw + i , toread );
1086+ if (read != toread ) {
1087+ print_error (message -> raw , read );
1088+ exit (1 );
1089+ }
1090+ }
1091+
1092+
1093+ read = parse_count_body (NULL , 0 );
1094+ if (read != 0 ) {
1095+ print_error (message -> raw , read );
1096+ exit (1 );
1097+ }
1098+
1099+ if (num_messages != 1 ) {
1100+ printf ("\n*** num_messages != 1 after testing '%s' ***\n\n" , message -> name );
1101+ exit (1 );
1102+ }
1103+
1104+ if (!message_eq (0 , message )) exit (1 );
1105+
1106+ parser_free ();
1107+ }
1108+
10331109void
10341110test_error (const char * buf )
10351111{
@@ -1214,6 +1290,50 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
12141290 exit (1 );
12151291}
12161292
1293+ // user required to free the result
1294+ // string terminated by \0
1295+ char *
1296+ create_large_chunked_message (int body_size_in_kb , const char * headers )
1297+ {
1298+ int i ;
1299+ size_t needed , wrote = 0 ;
1300+ size_t headers_len = strlen (headers );
1301+ size_t bufsize = headers_len + 10 ;
1302+ char * buf = malloc (bufsize );
1303+
1304+ strncpy (buf , headers , headers_len );
1305+ wrote += headers_len ;
1306+
1307+ for (i = 0 ; i < body_size_in_kb ; i ++ ) {
1308+ // write 1kb chunk into the body.
1309+ needed = 5 + 1024 + 2 ; // "400\r\nCCCC...CCCC\r\n"
1310+ if (bufsize - wrote < needed ) {
1311+ buf = realloc (buf , bufsize + needed );
1312+ bufsize += needed ;
1313+ }
1314+
1315+ strcpy (buf + wrote , "400\r\n" );
1316+ wrote += 5 ;
1317+ memset (buf + wrote , 'C' , 1024 );
1318+ wrote += 1024 ;
1319+ strcpy (buf + wrote , "\r\n" );
1320+ wrote += 2 ;
1321+ }
1322+
1323+ needed = 5 ; // "0\r\n\r\n"
1324+ if (bufsize - wrote < needed ) {
1325+ buf = realloc (buf , bufsize + needed );
1326+ bufsize += needed ;
1327+ }
1328+ strcpy (buf + wrote , "0\r\n\r\n" );
1329+ wrote += 5 ;
1330+
1331+ assert (buf [wrote ] == 0 );
1332+
1333+ return buf ;
1334+ }
1335+
1336+
12171337int
12181338main (void )
12191339{
@@ -1243,6 +1363,38 @@ main (void)
12431363 }
12441364 }
12451365
1366+ test_message_count_body (& responses [NO_HEADERS_NO_BODY_404 ]);
1367+ test_message_count_body (& responses [TRAILING_SPACE_ON_CHUNKED_BODY ]);
1368+
1369+ // test very large chunked response
1370+ {
1371+ char * msg = create_large_chunked_message (31337 ,
1372+ "HTTP/1.0 200 OK\r\n"
1373+ "Transfer-Encoding: chunked\r\n"
1374+ "Content-Type: text/plain\r\n"
1375+ "\r\n" );
1376+ struct message large_chunked =
1377+ {.name = "large chunked"
1378+ ,.type = HTTP_RESPONSE
1379+ ,.raw = msg
1380+ ,.should_keep_alive = FALSE
1381+ ,.message_complete_on_eof = FALSE
1382+ ,.http_major = 1
1383+ ,.http_minor = 0
1384+ ,.status_code = 200
1385+ ,.num_headers = 2
1386+ ,.headers =
1387+ { { "Transfer-Encoding" , "chunked" }
1388+ , { "Content-Type" , "text/plain" }
1389+ }
1390+ ,.body_size = 31337 * 1024
1391+ };
1392+ test_message_count_body (& large_chunked );
1393+ free (msg );
1394+ }
1395+
1396+
1397+
12461398 printf ("response scan 1/1 " );
12471399 test_scan ( & responses [TRAILING_SPACE_ON_CHUNKED_BODY ]
12481400 , & responses [NO_HEADERS_NO_BODY_404 ]
0 commit comments