libmicrohttpd

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

test_postprocessor.c (28320B)


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