libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_postprocessor.c (28153B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2013, 2019, 2020 Christian Grothoff
      4      Copyright (C) 2021 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 3, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 /**
     22  * @file test_postprocessor.c
     23  * @brief  Testcase for postprocessor
     24  * @author Christian Grothoff
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 #include "platform.h"
     28 #include "microhttpd.h"
     29 #include "internal.h"
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <stdio.h>
     33 #include "mhd_compat.h"
     34 
     35 #ifndef WINDOWS
     36 #include <unistd.h>
     37 #endif
     38 
     39 #ifndef MHD_DEBUG_PP
     40 #define MHD_DEBUG_PP 0
     41 #endif /* MHD_DEBUG_PP */
     42 
     43 struct expResult
     44 {
     45   const char *key;
     46   const char *fname;
     47   const char *cnt_type;
     48   const char *tr_enc;
     49   const char *data;
     50 };
     51 
     52 /**
     53  * Array of values that the value checker "wants".
     54  * Each series of checks should be terminated by
     55  * five NULL-entries.
     56  */
     57 static struct expResult exp_results[] = {
     58 #define URL_NOVALUE1_DATA "abc&x=5"
     59 #define URL_NOVALUE1_START 0
     60   {"abc", NULL, NULL, NULL, /* NULL */ ""}, /* change after API update */
     61   {"x", NULL, NULL, NULL, "5"},
     62 #define URL_NOVALUE1_END (URL_NOVALUE1_START + 2)
     63 #define URL_NOVALUE2_DATA "abc=&x=5"
     64 #define URL_NOVALUE2_START URL_NOVALUE1_END
     65   {"abc", NULL, NULL, NULL, ""},
     66   {"x", NULL, NULL, NULL, "5"},
     67 #define URL_NOVALUE2_END (URL_NOVALUE2_START + 2)
     68 #define URL_NOVALUE3_DATA "xyz="
     69 #define URL_NOVALUE3_START URL_NOVALUE2_END
     70   {"xyz", NULL, NULL, NULL, ""},
     71 #define URL_NOVALUE3_END (URL_NOVALUE3_START + 1)
     72 #define URL_NOVALUE4_DATA "xyz"
     73 #define URL_NOVALUE4_START URL_NOVALUE3_END
     74   {"xyz", NULL, NULL, NULL, /* NULL */ ""}, /* change after API update */
     75 #define URL_NOVALUE4_END (URL_NOVALUE4_START + 1)
     76 #define URL_DATA "abc=def&x=5"
     77 #define URL_START URL_NOVALUE4_END
     78   {"abc", NULL, NULL, NULL, "def"},
     79   {"x", NULL, NULL, NULL, "5"},
     80 #define URL_END (URL_START + 2)
     81 #define URL_ENC_DATA "space=%20&key%201=&crlf=%0D%0a&mix%09ed=%2001%0d%0A"
     82 #define URL_ENC_START URL_END
     83   {"space", NULL, NULL, NULL, " "},
     84   {"key 1", NULL, NULL, NULL, ""},
     85   {"crlf", NULL, NULL, NULL, "\r\n"},
     86   {"mix\ted", NULL, NULL, NULL, " 01\r\n"},
     87 #define URL_ENC_END (URL_ENC_START + 4)
     88   {NULL, NULL, NULL, NULL, NULL},
     89 #define FORM_DATA \
     90   "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\n" \
     91   "Joe Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\";" \
     92   " filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n" \
     93   "Content-Transfer-Encoding: binary\r\n\r\nfiledata\r\n--AaB03x--\r\n"
     94 #define FORM_START (URL_ENC_END + 1)
     95   {"field1", NULL, NULL, NULL, "Joe Blow"},
     96   {"pics", "file1.txt", "text/plain", "binary", "filedata"},
     97 #define FORM_END (FORM_START + 2)
     98   {NULL, NULL, NULL, NULL, NULL},
     99 #define FORM_NESTED_DATA \
    100   "--AaB03x\r\ncontent-disposition: form-data; name=\"field1\"\r\n\r\n" \
    101   "Jane Blow\r\n--AaB03x\r\ncontent-disposition: form-data; name=\"pics\"\r\n" \
    102   "Content-type: multipart/mixed, boundary=BbC04y\r\n\r\n--BbC04y\r\n" \
    103   "Content-disposition: attachment; filename=\"file1.txt\"\r\n" \
    104   "Content-Type: text/plain\r\n\r\nfiledata1\r\n--BbC04y\r\n" \
    105   "Content-disposition: attachment; filename=\"file2.gif\"\r\n" \
    106   "Content-type: image/gif\r\nContent-Transfer-Encoding: binary\r\n\r\n" \
    107   "filedata2\r\n--BbC04y--\r\n--AaB03x--"
    108 #define FORM_NESTED_START (FORM_END + 1)
    109   {"field1", NULL, NULL, NULL, "Jane Blow"},
    110   {"pics", "file1.txt", "text/plain", NULL, "filedata1"},
    111   {"pics", "file2.gif", "image/gif", "binary", "filedata2"},
    112 #define FORM_NESTED_END (FORM_NESTED_START + 3)
    113   {NULL, NULL, NULL, NULL, NULL},
    114 #define URL_EMPTY_VALUE_DATA "key1=value1&key2=&key3="
    115 #define URL_EMPTY_VALUE_START (FORM_NESTED_END + 1)
    116   {"key1", NULL, NULL, NULL, "value1"},
    117   {"key2", NULL, NULL, NULL, ""},
    118   {"key3", NULL, NULL, NULL, ""},
    119 #define URL_EMPTY_VALUE_END (URL_EMPTY_VALUE_START + 3)
    120   {NULL, NULL, NULL, NULL, NULL}
    121 };
    122 
    123 
    124 static int
    125 mismatch (const char *a, const char *b)
    126 {
    127   if (a == b)
    128     return 0;
    129   if ((a == NULL) || (b == NULL))
    130     return 1;
    131   return 0 != strcmp (a, b);
    132 }
    133 
    134 
    135 static int
    136 mismatch2 (const char *data, const char *expected, size_t offset, size_t size)
    137 {
    138   if (data == expected)
    139     return 0;
    140   if ((data == NULL) || (expected == NULL))
    141     return 1;
    142   return 0 != memcmp (data, expected + offset, size);
    143 }
    144 
    145 
    146 static enum MHD_Result
    147 value_checker (void *cls,
    148                enum MHD_ValueKind kind,
    149                const char *key,
    150                const char *filename,
    151                const char *content_type,
    152                const char *transfer_encoding,
    153                const char *data,
    154                uint64_t off,
    155                size_t size)
    156 {
    157   unsigned int *idxp = cls;
    158   struct expResult *expect = exp_results + *idxp;
    159   (void) kind;  /* Unused. Silent compiler warning. */
    160 
    161 #if MHD_DEBUG_PP
    162   fprintf (stderr,
    163            "VC: `%s' `%s' `%s' `%s' (+%u)`%.*s' (%d)\n",
    164            key ? key : "(NULL)",
    165            filename ? filename : "(NULL)",
    166            content_type ? content_type : "(NULL)",
    167            transfer_encoding ? transfer_encoding : "(NULL)",
    168            (unsigned int) off,
    169            (int) (data ? size : 6),
    170            data ? data : "(NULL)",
    171            (int) size);
    172 #endif
    173   if (*idxp == (unsigned int) -1)
    174     exit (99);
    175   if ( (0 != off) && (0 == size) )
    176   {
    177     if (NULL == expect->data)
    178       *idxp += 1;
    179     return MHD_YES;
    180   }
    181   if ((expect->key == NULL) ||
    182       (0 != strcmp (key, expect->key)) ||
    183       (mismatch (filename, expect->fname)) ||
    184       (mismatch (content_type, expect->cnt_type)) ||
    185       (mismatch (transfer_encoding, expect->tr_enc)) ||
    186       (strlen (expect->data) < off) ||
    187       (mismatch2 (data, expect->data, (size_t) off, size)))
    188   {
    189     *idxp = (unsigned int) -1;
    190     fprintf (stderr,
    191              "Failed with: `%s' `%s' `%s' `%s' `%.*s'\n",
    192              key ? key : "(NULL)",
    193              filename ? filename : "(NULL)",
    194              content_type ? content_type : "(NULL)",
    195              transfer_encoding ? transfer_encoding : "(NULL)",
    196              (int) (data ? size : 6),
    197              data ? data : "(NULL)");
    198     fprintf (stderr,
    199              "Wanted: `%s' `%s' `%s' `%s' `%s'\n",
    200              expect->key ? expect->key : "(NULL)",
    201              expect->fname ? expect->fname : "(NULL)",
    202              expect->cnt_type ? expect->cnt_type : "(NULL)",
    203              expect->tr_enc ? expect->tr_enc : "(NULL)",
    204              expect->data ? expect->data : "(NULL)");
    205     fprintf (stderr,
    206              "Unexpected result: %d/%d/%d/%d/%d/%d\n",
    207              (expect->key == NULL),
    208              (NULL != expect->key) && (0 != strcmp (key, expect->key)),
    209              (mismatch (filename, expect->fname)),
    210              (mismatch (content_type, expect->cnt_type)),
    211              (mismatch (transfer_encoding, expect->tr_enc)),
    212              (strlen (expect->data) < off)
    213              || (mismatch2 (data, expect->data, (size_t) off, size)));
    214     return MHD_NO;
    215   }
    216   if ( ( (NULL == expect->data) &&
    217          (0 == off + size) ) ||
    218        ( (NULL != expect->data) &&
    219          (off + size == strlen (expect->data)) ) )
    220     *idxp += 1;
    221   return MHD_YES;
    222 }
    223 
    224 
    225 static unsigned int
    226 test_urlencoding_case (unsigned int want_start,
    227                        unsigned int want_end,
    228                        const char *url_data)
    229 {
    230   size_t step;
    231   unsigned int errors = 0;
    232   const size_t size = strlen (url_data);
    233 
    234   for (step = 1; size >= step; ++step)
    235   {
    236     struct MHD_Connection connection;
    237     struct MHD_HTTP_Req_Header header;
    238     struct MHD_PostProcessor *pp;
    239     unsigned int want_off = want_start;
    240     size_t i;
    241 
    242     memset (&connection, 0, sizeof (struct MHD_Connection));
    243     memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    244     connection.rq.headers_received = &header;
    245     header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    246     header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    247     header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
    248     header.value_size =
    249       MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
    250     header.kind = MHD_HEADER_KIND;
    251     pp = MHD_create_post_processor (&connection,
    252                                     1024,
    253                                     &value_checker,
    254                                     &want_off);
    255     if (NULL == pp)
    256     {
    257       fprintf (stderr, "Failed to create post processor.\n"
    258                "Line: %u\n", (unsigned int) __LINE__);
    259       exit (50);
    260     }
    261     for (i = 0; size > i; i += step)
    262     {
    263       size_t left = size - i;
    264       if (MHD_YES != MHD_post_process (pp,
    265                                        &url_data[i],
    266                                        (left > step) ? step : left))
    267       {
    268         fprintf (stderr, "Failed to process the data.\n"
    269                  "i: %u. step: %u.\n"
    270                  "Line: %u\n", (unsigned) i, (unsigned) step,
    271                  (unsigned int) __LINE__);
    272         exit (49);
    273       }
    274     }
    275     MHD_destroy_post_processor (pp);
    276     if (want_off != want_end)
    277     {
    278       fprintf (stderr,
    279                "Test failed in line %u.\tStep: %u.\tData: \"%s\"\n" \
    280                " Got: %u\tExpected: %u\n",
    281                (unsigned int) __LINE__,
    282                (unsigned int) step,
    283                url_data,
    284                want_off,
    285                want_end);
    286       errors++;
    287     }
    288   }
    289   return errors;
    290 }
    291 
    292 
    293 static unsigned int
    294 test_urlencoding (void)
    295 {
    296   unsigned int errorCount = 0;
    297 
    298   errorCount += test_urlencoding_case (URL_START,
    299                                        URL_END,
    300                                        URL_DATA);
    301   errorCount += test_urlencoding_case (URL_ENC_START,
    302                                        URL_ENC_END,
    303                                        URL_ENC_DATA);
    304   errorCount += test_urlencoding_case (URL_NOVALUE1_START,
    305                                        URL_NOVALUE1_END,
    306                                        URL_NOVALUE1_DATA);
    307   errorCount += test_urlencoding_case (URL_NOVALUE2_START,
    308                                        URL_NOVALUE2_END,
    309                                        URL_NOVALUE2_DATA);
    310   errorCount += test_urlencoding_case (URL_NOVALUE3_START,
    311                                        URL_NOVALUE3_END,
    312                                        URL_NOVALUE3_DATA);
    313   errorCount += test_urlencoding_case (URL_NOVALUE4_START,
    314                                        URL_NOVALUE4_START, /* No advance */
    315                                        URL_NOVALUE4_DATA);
    316   errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
    317                                        URL_EMPTY_VALUE_END,
    318                                        URL_EMPTY_VALUE_DATA);
    319 
    320   errorCount += test_urlencoding_case (URL_START,
    321                                        URL_END,
    322                                        URL_DATA "\n");
    323   errorCount += test_urlencoding_case (URL_ENC_START,
    324                                        URL_ENC_END,
    325                                        URL_ENC_DATA "\n");
    326   errorCount += test_urlencoding_case (URL_NOVALUE1_START,
    327                                        URL_NOVALUE1_END,
    328                                        URL_NOVALUE1_DATA "\n");
    329   errorCount += test_urlencoding_case (URL_NOVALUE2_START,
    330                                        URL_NOVALUE2_END,
    331                                        URL_NOVALUE2_DATA "\n");
    332   errorCount += test_urlencoding_case (URL_NOVALUE3_START,
    333                                        URL_NOVALUE3_END,
    334                                        URL_NOVALUE3_DATA "\n");
    335   errorCount += test_urlencoding_case (URL_NOVALUE4_START,
    336                                        URL_NOVALUE4_END, /* With advance */
    337                                        URL_NOVALUE4_DATA "\n");
    338   errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
    339                                        URL_EMPTY_VALUE_END,
    340                                        URL_EMPTY_VALUE_DATA "\n");
    341 
    342   errorCount += test_urlencoding_case (URL_START,
    343                                        URL_END,
    344                                        "&&" URL_DATA);
    345   errorCount += test_urlencoding_case (URL_ENC_START,
    346                                        URL_ENC_END,
    347                                        "&&" URL_ENC_DATA);
    348   errorCount += test_urlencoding_case (URL_NOVALUE1_START,
    349                                        URL_NOVALUE1_END,
    350                                        "&&" URL_NOVALUE1_DATA);
    351   errorCount += test_urlencoding_case (URL_NOVALUE2_START,
    352                                        URL_NOVALUE2_END,
    353                                        "&&" URL_NOVALUE2_DATA);
    354   errorCount += test_urlencoding_case (URL_NOVALUE3_START,
    355                                        URL_NOVALUE3_END,
    356                                        "&&" URL_NOVALUE3_DATA);
    357   errorCount += test_urlencoding_case (URL_NOVALUE4_START,
    358                                        URL_NOVALUE4_START, /* No advance */
    359                                        "&&" URL_NOVALUE4_DATA);
    360   errorCount += test_urlencoding_case (URL_EMPTY_VALUE_START,
    361                                        URL_EMPTY_VALUE_END,
    362                                        "&&" URL_EMPTY_VALUE_DATA);
    363   if (0 != errorCount)
    364     fprintf (stderr,
    365              "Test failed in line %u with %u errors\n",
    366              (unsigned int) __LINE__,
    367              errorCount);
    368   return errorCount;
    369 }
    370 
    371 
    372 static unsigned int
    373 test_multipart_garbage (void)
    374 {
    375   struct MHD_Connection connection;
    376   struct MHD_HTTP_Req_Header header;
    377   struct MHD_PostProcessor *pp;
    378   unsigned int want_off;
    379   size_t size = MHD_STATICSTR_LEN_ (FORM_DATA);
    380   size_t splitpoint;
    381   char xdata[MHD_STATICSTR_LEN_ (FORM_DATA) + 3];
    382 
    383   /* fill in evil garbage at the beginning */
    384   xdata[0] = '-';
    385   xdata[1] = 'x';
    386   xdata[2] = '\r';
    387   memcpy (&xdata[3], FORM_DATA, size);
    388   size += 3;
    389   for (splitpoint = 1; splitpoint < size; splitpoint++)
    390   {
    391     want_off = FORM_START;
    392     memset (&connection, 0, sizeof (struct MHD_Connection));
    393     memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    394     connection.rq.headers_received = &header;
    395     header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    396     header.value =
    397       MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
    398     header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
    399     header.value_size =
    400       MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA \
    401                           ", boundary=AaB03x");
    402     header.kind = MHD_HEADER_KIND;
    403     pp = MHD_create_post_processor (&connection,
    404                                     1024, &value_checker, &want_off);
    405     if (NULL == pp)
    406     {
    407       fprintf (stderr, "Failed to create post processor.\n"
    408                "Line: %u\n", (unsigned int) __LINE__);
    409       exit (50);
    410     }
    411     if (MHD_YES != MHD_post_process (pp, xdata, splitpoint))
    412     {
    413       fprintf (stderr,
    414                "Test failed in line %u at point %d\n",
    415                (unsigned int) __LINE__,
    416                (int) splitpoint);
    417       exit (49);
    418     }
    419     if (MHD_YES != MHD_post_process (pp, &xdata[splitpoint], size - splitpoint))
    420     {
    421       fprintf (stderr,
    422                "Test failed in line %u at point %u\n",
    423                (unsigned int) __LINE__,
    424                (unsigned int) splitpoint);
    425       exit (49);
    426     }
    427     MHD_destroy_post_processor (pp);
    428     if (want_off != FORM_END)
    429     {
    430       fprintf (stderr,
    431                "Test failed in line %u at point %u\n",
    432                (unsigned int) __LINE__,
    433                (unsigned int) splitpoint);
    434       return (unsigned int) splitpoint;
    435     }
    436   }
    437   return 0;
    438 }
    439 
    440 
    441 static unsigned int
    442 test_multipart_splits (void)
    443 {
    444   struct MHD_Connection connection;
    445   struct MHD_HTTP_Req_Header header;
    446   struct MHD_PostProcessor *pp;
    447   unsigned int want_off;
    448   size_t size;
    449   size_t splitpoint;
    450 
    451   size = strlen (FORM_DATA);
    452   for (splitpoint = 1; splitpoint < size; splitpoint++)
    453   {
    454     want_off = FORM_START;
    455     memset (&connection, 0, sizeof (struct MHD_Connection));
    456     memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    457     connection.rq.headers_received = &header;
    458     header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    459     header.value =
    460       MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
    461     header.header_size = strlen (header.header);
    462     header.value_size = strlen (header.value);
    463     header.kind = MHD_HEADER_KIND;
    464     pp = MHD_create_post_processor (&connection,
    465                                     1024, &value_checker, &want_off);
    466     if (NULL == pp)
    467     {
    468       fprintf (stderr, "Failed to create post processor.\n"
    469                "Line: %u\n", (unsigned int) __LINE__);
    470       exit (50);
    471     }
    472     if (MHD_YES != MHD_post_process (pp, FORM_DATA, splitpoint))
    473     {
    474       fprintf (stderr,
    475                "Test failed in line %u at point %d\n",
    476                (unsigned int) __LINE__,
    477                (int) splitpoint);
    478       exit (49);
    479     }
    480     if (MHD_YES != MHD_post_process (pp, &FORM_DATA[splitpoint],
    481                                      size - splitpoint))
    482     {
    483       fprintf (stderr,
    484                "Test failed in line %u at point %u\n",
    485                (unsigned int) __LINE__,
    486                (unsigned int) splitpoint);
    487       exit (49);
    488     }
    489     MHD_destroy_post_processor (pp);
    490     if (want_off != FORM_END)
    491     {
    492       fprintf (stderr,
    493                "Test failed in line %u at point %u\n",
    494                (unsigned int) __LINE__,
    495                (unsigned int) splitpoint);
    496       return (unsigned int) splitpoint;
    497     }
    498   }
    499   return 0;
    500 }
    501 
    502 
    503 static unsigned int
    504 test_multipart (void)
    505 {
    506   struct MHD_Connection connection;
    507   struct MHD_HTTP_Req_Header header;
    508   struct MHD_PostProcessor *pp;
    509   unsigned int want_off = FORM_START;
    510   size_t i;
    511   size_t delta;
    512   size_t size;
    513 
    514   memset (&connection, 0, sizeof (struct MHD_Connection));
    515   memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    516   connection.rq.headers_received = &header;
    517   header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    518   header.value =
    519     MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
    520   header.kind = MHD_HEADER_KIND;
    521   header.header_size = strlen (header.header);
    522   header.value_size = strlen (header.value);
    523   pp = MHD_create_post_processor (&connection,
    524                                   1024, &value_checker, &want_off);
    525   if (NULL == pp)
    526   {
    527     fprintf (stderr, "Failed to create post processor.\n"
    528              "Line: %u\n", (unsigned int) __LINE__);
    529     exit (50);
    530   }
    531   i = 0;
    532   size = strlen (FORM_DATA);
    533   while (i < size)
    534   {
    535     delta = 1 + ((size_t) MHD_random_ ()) % (size - i);
    536     if (MHD_YES != MHD_post_process (pp,
    537                                      &FORM_DATA[i],
    538                                      delta))
    539     {
    540       fprintf (stderr, "Failed to process the data.\n"
    541                "i: %u. delta: %u.\n"
    542                "Line: %u\n", (unsigned) i, (unsigned) delta,
    543                (unsigned int) __LINE__);
    544       exit (49);
    545     }
    546     i += delta;
    547   }
    548   MHD_destroy_post_processor (pp);
    549   if (want_off != FORM_END)
    550   {
    551     fprintf (stderr,
    552              "Test failed in line %u\n",
    553              (unsigned int) __LINE__);
    554     return 2;
    555   }
    556   return 0;
    557 }
    558 
    559 
    560 static unsigned int
    561 test_nested_multipart (void)
    562 {
    563   struct MHD_Connection connection;
    564   struct MHD_HTTP_Req_Header header;
    565   struct MHD_PostProcessor *pp;
    566   unsigned int want_off = FORM_NESTED_START;
    567   size_t i;
    568   size_t delta;
    569   size_t size;
    570 
    571   memset (&connection, 0, sizeof (struct MHD_Connection));
    572   memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    573   connection.rq.headers_received = &header;
    574   header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    575   header.value =
    576     MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ", boundary=AaB03x";
    577   header.kind = MHD_HEADER_KIND;
    578   header.header_size = strlen (header.header);
    579   header.value_size = strlen (header.value);
    580   pp = MHD_create_post_processor (&connection,
    581                                   1024, &value_checker, &want_off);
    582   if (NULL == pp)
    583   {
    584     fprintf (stderr, "Failed to create post processor.\n"
    585              "Line: %u\n", (unsigned int) __LINE__);
    586     exit (50);
    587   }
    588   i = 0;
    589   size = strlen (FORM_NESTED_DATA);
    590   while (i < size)
    591   {
    592     delta = 1 + ((size_t) MHD_random_ ()) % (size - i);
    593     if (MHD_YES != MHD_post_process (pp,
    594                                      &FORM_NESTED_DATA[i],
    595                                      delta))
    596     {
    597       fprintf (stderr, "Failed to process the data.\n"
    598                "i: %u. delta: %u.\n"
    599                "Line: %u\n", (unsigned) i, (unsigned) delta,
    600                (unsigned int) __LINE__);
    601       exit (49);
    602     }
    603     i += delta;
    604   }
    605   MHD_destroy_post_processor (pp);
    606   if (want_off != FORM_NESTED_END)
    607   {
    608     fprintf (stderr,
    609              "Test failed in line %u\n",
    610              (unsigned int) __LINE__);
    611     return 4;
    612   }
    613   return 0;
    614 }
    615 
    616 
    617 static enum MHD_Result
    618 value_checker2 (void *cls,
    619                 enum MHD_ValueKind kind,
    620                 const char *key,
    621                 const char *filename,
    622                 const char *content_type,
    623                 const char *transfer_encoding,
    624                 const char *data,
    625                 uint64_t off,
    626                 size_t size)
    627 {
    628   (void) cls; (void) kind; (void) key; /* Mute compiler warnings */
    629   (void) filename; (void) content_type; (void) transfer_encoding;
    630   (void) data; (void) off; (void) size;
    631   return MHD_YES;
    632 }
    633 
    634 
    635 static unsigned int
    636 test_overflow (void)
    637 {
    638   struct MHD_Connection connection;
    639   struct MHD_HTTP_Req_Header header;
    640   struct MHD_PostProcessor *pp;
    641   size_t i;
    642   size_t j;
    643   size_t delta;
    644   char *buf;
    645 
    646   memset (&connection, 0, sizeof (struct MHD_Connection));
    647   memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    648   connection.rq.headers_received = &header;
    649   header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    650   header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    651   header.header_size = strlen (header.header);
    652   header.value_size = strlen (header.value);
    653   header.kind = MHD_HEADER_KIND;
    654   for (i = 128; i < 1024 * 1024; i += 1024)
    655   {
    656     pp = MHD_create_post_processor (&connection,
    657                                     1024,
    658                                     &value_checker2,
    659                                     NULL);
    660     if (NULL == pp)
    661     {
    662       fprintf (stderr, "Failed to create post processor.\n"
    663                "Line: %u\n", (unsigned int) __LINE__);
    664       exit (50);
    665     }
    666     buf = malloc (i);
    667     if (NULL == buf)
    668       return 1;
    669     memset (buf, 'A', i);
    670     buf[i / 2] = '=';
    671     delta = 1 + (((size_t) MHD_random_ ()) % (i - 1));
    672     j = 0;
    673     while (j < i)
    674     {
    675       if (j + delta > i)
    676         delta = i - j;
    677       if (MHD_NO ==
    678           MHD_post_process (pp,
    679                             &buf[j],
    680                             delta))
    681         break;
    682       j += delta;
    683     }
    684     free (buf);
    685     MHD_destroy_post_processor (pp);
    686   }
    687   return 0;
    688 }
    689 
    690 
    691 static unsigned int
    692 test_empty_key (void)
    693 {
    694   const char form_data[] = "=abcdef";
    695   size_t step;
    696   const size_t size = MHD_STATICSTR_LEN_ (form_data);
    697 
    698   for (step = 1; size >= step; ++step)
    699   {
    700     size_t i;
    701     struct MHD_Connection connection;
    702     struct MHD_HTTP_Req_Header header;
    703     struct MHD_PostProcessor *pp;
    704     memset (&connection, 0, sizeof (struct MHD_Connection));
    705     memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    706 
    707     connection.rq.headers_received = &header;
    708     connection.rq.headers_received_tail = &header;
    709     header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    710     header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
    711     header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    712     header.value_size =
    713       MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
    714     header.kind = MHD_HEADER_KIND;
    715     pp = MHD_create_post_processor (&connection,
    716                                     1024, &value_checker2, NULL);
    717     if (NULL == pp)
    718     {
    719       fprintf (stderr, "Failed to create post processor.\n"
    720                "Line: %u\n", (unsigned int) __LINE__);
    721       exit (50);
    722     }
    723     for (i = 0; size > i; i += step)
    724     {
    725       if (MHD_NO != MHD_post_process (pp,
    726                                       form_data + i,
    727                                       (step > size - i) ? (size - i) : step))
    728       {
    729         fprintf (stderr, "Succeed to process the broken data.\n"
    730                  "i: %u. step: %u.\n"
    731                  "Line: %u\n", (unsigned) i, (unsigned) step,
    732                  (unsigned int) __LINE__);
    733         exit (49);
    734       }
    735     }
    736     MHD_destroy_post_processor (pp);
    737   }
    738   return 0;
    739 }
    740 
    741 
    742 static unsigned int
    743 test_double_value (void)
    744 {
    745   const char form_data[] = URL_DATA "=abcdef";
    746   size_t step;
    747   const size_t size = MHD_STATICSTR_LEN_ (form_data);
    748   const size_t safe_size = MHD_STATICSTR_LEN_ (URL_DATA);
    749 
    750   for (step = 1; size >= step; ++step)
    751   {
    752     size_t i;
    753     struct MHD_Connection connection;
    754     struct MHD_HTTP_Req_Header header;
    755     struct MHD_PostProcessor *pp;
    756     unsigned int results_off = URL_START;
    757     unsigned int results_final = results_off + 1; /* First value is correct */
    758     memset (&connection, 0, sizeof (struct MHD_Connection));
    759     memset (&header, 0, sizeof (struct MHD_HTTP_Res_Header));
    760 
    761     connection.rq.headers_received = &header;
    762     connection.rq.headers_received_tail = &header;
    763     header.header = MHD_HTTP_HEADER_CONTENT_TYPE;
    764     header.header_size = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_TYPE);
    765     header.value = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    766     header.value_size =
    767       MHD_STATICSTR_LEN_ (MHD_HTTP_POST_ENCODING_FORM_URLENCODED);
    768     header.kind = MHD_HEADER_KIND;
    769     pp = MHD_create_post_processor (&connection,
    770                                     1024, &value_checker, &results_off);
    771     if (NULL == pp)
    772     {
    773       fprintf (stderr, "Failed to create post processor.\n"
    774                "Line: %u\n", (unsigned int) __LINE__);
    775       exit (50);
    776     }
    777     for (i = 0; size > i; i += step)
    778     {
    779       if (MHD_NO != MHD_post_process (pp,
    780                                       form_data + i,
    781                                       (step > size - i) ? (size - i) : step))
    782       {
    783         if (safe_size == i + step)
    784           results_final = URL_END;
    785         if (safe_size < i + step)
    786         {
    787           fprintf (stderr, "Succeed to process the broken data.\n"
    788                    "i: %u. step: %u.\n"
    789                    "Line: %u\n", (unsigned) i, (unsigned) step,
    790                    (unsigned int) __LINE__);
    791           exit (49);
    792         }
    793       }
    794       else
    795       {
    796         if (safe_size >= i + step)
    797         {
    798           fprintf (stderr, "Failed to process the data.\n"
    799                    "i: %u. step: %u.\n"
    800                    "Line: %u\n", (unsigned) i, (unsigned) step,
    801                    (unsigned int) __LINE__);
    802           exit (49);
    803         }
    804       }
    805     }
    806     MHD_destroy_post_processor (pp);
    807     if (results_final != results_off)
    808     {
    809       fprintf (stderr,
    810                "Test failed in line %u.\tStep:%u\n Got: %u\tExpected: %u\n",
    811                (unsigned int) __LINE__,
    812                (unsigned int) step,
    813                results_off,
    814                results_final);
    815       return 1;
    816     }
    817   }
    818   return 0;
    819 }
    820 
    821 
    822 int
    823 main (int argc, char *const *argv)
    824 {
    825   unsigned int errorCount = 0;
    826   (void) argc; (void) argv;  /* Unused. Silent compiler warning. */
    827 
    828   errorCount += test_multipart_splits ();
    829   errorCount += test_multipart_garbage ();
    830   errorCount += test_urlencoding ();
    831   errorCount += test_multipart ();
    832   errorCount += test_nested_multipart ();
    833   errorCount += test_empty_key ();
    834   errorCount += test_double_value ();
    835   errorCount += test_overflow ();
    836   if (errorCount != 0)
    837     fprintf (stderr, "Error (code: %u)\n", errorCount);
    838   return (errorCount == 0) ? 0 : 1;       /* 0 == pass */
    839 }