I think this patch was wrong.
The post-processor fails to parse url-encoded post-data.
A testcase is attached: The value of yyyy should be yyyy.

Best wishes
Markus


-------- Weitergeleitete Nachricht --------
Von: Christian Grothoff <groth...@gnunet.org>
Antwort an: libmicrohttpd development and user mailinglist <
libmicrohttpd@gnu.org>
An: libmicrohttpd@gnu.org
Betreff: Re: [libmicrohttpd] www-form-urlencoded: dealing with keys
with no value
Datum: Sat, 28 Dec 2019 00:52:16 +0100

Hi Ethan,
I've implemented this now in Git master. Feedback welcome...
Happy hacking!
Christian
On 12/25/19 9:45 AM, Ethan Tuttle wrote:
> Thanks for the response, Chrstian.  I'll take a look at the
> parsingcode, but I don't have high confidence I can fix it to your
> liking(and in a secure / performant way).
> Otherwise, I'll stick with my uriparser patch until you get around to
> it.
> Happy hacking and happy holidays!
> Ethan
> On Tue, Dec 24, 2019 at 6:30 AM Christian Grothoff <
> groth...@gnunet.org> wrote:
> > Hi Ethan,
> > I agree that this is a bug, and yes, we should fix it. Thanks for
> > thetest case, that is very helpful.
> > I don't know when I can work on it, but worst-case expect a patch
> > bythe end of January, if you're lucky and my other bugs go fast
> > (orsomeone else sends a fix), might of course happen earlier.
> > Happy hacking!
> > Christian
> > On Tue, 2019-12-24 at 04:11 -0800, Ethan Tuttle wrote:
> > > Given post body "a&b=1", how should MHD interpret the data?
> > > I'm at the end of a long investigation and it's come down to
> > > thatquestionfor post_process_urlencoded() in MHD.
> > > MHD currently calls back with ("a&b", "1").  The app I'm
> > > workingbreakswhen it doesn't receive a callback for "b".  The
> > > http client in thiscase(that did the urlencoding) is google-http-
> > > java-client 1.23.0.
> > > The client behavior may be questionable, but MHD's behavior
> > > doesn'tseemright either.  Isn't there some principle, "clients
> > > should strive forconformance, servers should strive for
> > > forgiveness".
> > > I've attached a patch to MHD to add a failing test, but without
> > > afix.  IsMHD open to changing this behavior, given its maturity?
> > > Should I post a bug to the bug tracker?
> > > As for relevant standards, the W3C[1] is not detailed enough to
> > > coverit.The WhatWG[2] is more specific and allows for empty
> > > values and evenemptykeys.  I'd like to callout uriparser[3],
> > > another C library I'vepatched inas a work-around.  Uriparser
> > > documents their handling of these caseswell:
> > > * NULL in the value member means there was no '=' in the item
> > > text aswith"?abc&def".* An empty string in the value member means
> > > there was '=' in the itemaswith "?abc=&def".
> > > [1] https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
> > > [2] https://url.spec.whatwg.org/#urlencoded-parsing
> > > [3] https://uriparser.github.io/doc/api/latest/#querystrings
> > >
> > > commit aa0534af56d135e1b261d127af09c22015c1ff87Author: Ethan
> > > Tuttle <et...@ethantuttle.com>Date:   Tue Dec 24 03:50:59 2019
> > > -0800
> > >     urlencoding post-processor: add failing tests for keys
> > > withoutvalues
> > > diff --git
> > > a/src/microhttpd/test_postprocessor.cb/src/microhttpd/test_postpr
> > > ocessor.cindex 75b5ba33..6d5be040 100644---
> > > a/src/microhttpd/test_postprocessor.c+++
> > > b/src/microhttpd/test_postprocessor.c@@ -42,8 +42,18 @@  * five
> > > NULL-entries.  */ const char *want[] = {+#define
> > > URL_NOVALUE1_DATA "abc&x=5"+#define URL_NOVALUE1_START
> > > 0+        "abc", NULL, NULL, NULL, NULL,+        "x", NULL, NULL,
> > > NULL, "5",+#define URL_NOVALUE1_END (URL_NOVALUE1_START +
> > > 10)+#define URL_NOVALUE2_DATA "abc=&x=5"+#define
> > > URL_NOVALUE2_START URL_NOVALUE1_END+        "abc", NULL, NULL,
> > > NULL, "",+        "x", NULL, NULL, NULL, "5",+#define
> > > URL_NOVALUE2_END (URL_NOVALUE2_START + 10) #define URL_DATA
> > > "abc=def&x=5"-#define URL_START 0+#define URL_START
> > > URL_NOVALUE2_END   "abc", NULL, NULL, NULL, "def",   "x", NULL,
> > > NULL, NULL, "5", #define URL_END (URL_START + 10)@@ -125,12
> > > +135,14 @@ value_checker (void *cls,
> > >
> > >  static int-test_urlencoding (void)+test_urlencoding_case
> > > (unsigned int want_start,+                       unsigned int
> > > want_end,+                       const char *url_data) {   struct
> > > MHD_Connection connection;   struct MHD_HTTP_Header
> > > header;   struct MHD_PostProcessor *pp;-  unsigned int want_off =
> > > URL_START;+  unsigned int want_off = want_start;   size_t
> > > i;   size_t delta;   size_t size;@@ -147,19 +159,27 @@
> > > test_urlencoding (void)   pp = MHD_create_post_processor
> > > (&connection,                                   1024,
> > > &value_checker, &want_off);   i = 0;-  size = strlen
> > > (URL_DATA);+  size = strlen (url_data);   while (i <
> > > size)   {     delta = 1 + MHD_random_ () % (size -
> > > i);-    MHD_post_process (pp, &URL_DATA[i],
> > > delta);+    MHD_post_process (pp, &url_data[i], delta);     i +=
> > > delta;   }   MHD_destroy_post_processor (pp);-  if (want_off !=
> > > URL_END)+  if (want_off != want_end)     return 1;   return 0; }
> > > +static int+test_urlencoding (void) {+  unsigned int errorCount =
> > > 0;+  errorCount += test_urlencoding_case(URL_START, URL_END,
> > > URL_DATA);+  errorCount +=
> > > test_urlencoding_case(URL_NOVALUE1_START,URL_NOVALUE1_END,
> > > URL_NOVALUE1_DATA);+  errorCount +=
> > > test_urlencoding_case(URL_NOVALUE2_START,URL_NOVALUE2_END,
> > > URL_NOVALUE2_DATA);+  return errorCount;+}
> > >  static int test_multipart_garbage (void)


#include <stdio.h>
#include <stdlib.h>
#include <microhttpd.h>

enum PP_State
{
  PP_Error,
  PP_Done,
  PP_Init,
  PP_NextBoundary,
  PP_ProcessValue,
  PP_Callback,
  PP_ExpectNewLine,
  PP_ProcessEntryHeaders,
  PP_PerformCheckMultipart,
  PP_ProcessValueToBoundary,
  PP_PerformCleanup,
  PP_Nested_Init,
  PP_Nested_PerformMarking,
  PP_Nested_ProcessEntryHeaders,
  PP_Nested_ProcessValueToBoundary,
  PP_Nested_PerformCleanup
};
enum RN_State
{
  RN_Inactive = 0,
  RN_OptN = 1,
  RN_Full = 2,
  RN_Dash = 3,
  RN_Dash2 = 4
};
enum NE_State
{
  NE_none = 0,
  NE_content_name = 1,
  NE_content_type = 2,
  NE_content_filename = 4,
  NE_content_transfer_encoding = 8
};
struct MHD_PostProcessor
{
    struct MHD_Connection *connection;
    MHD_PostDataIterator ikvi;
    void *cls;
    const char *encoding;
    const char *boundary;
    char *nested_boundary;
    char *content_name;
    char *content_type;
    char *content_filename;
    char *content_transfer_encoding;
    char xbuf[2];
    size_t buffer_size;
    size_t buffer_pos;
    size_t xbuf_pos;
    uint64_t value_offset;
    size_t blen;
    size_t nlen;
    bool must_ikvi;
    bool must_unescape_key;
    enum PP_State state;
    enum RN_State skip_rn;
    enum PP_State dash_state;
    enum NE_State have;
};
static int post_data_iterator( void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type,
                               const char *transfer_encoding, const char *data, uint64_t off, size_t size )
{
    printf("%s\t%s\n", key, data );
    return MHD_YES;
}
int main( int argc, char *argv[] )
{
    struct MHD_PostProcessor *postprocessor = (struct MHD_PostProcessor *)calloc(1, sizeof(struct MHD_PostProcessor) + 0x1000+1);
    postprocessor->connection = nullptr;
    postprocessor->ikvi = post_data_iterator;
    postprocessor->cls = nullptr;
    postprocessor->encoding = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
    postprocessor->buffer_size = 0x1000;
    postprocessor->state = PP_Init;
    postprocessor->blen = 0;
    postprocessor->boundary = nullptr;
    postprocessor->skip_rn = RN_Inactive;
    MHD_post_process( postprocessor, "xxxx=xxxx", 9 );
    MHD_post_process( postprocessor, "&yyyy=yyyy&zzzz=&aaaa=", 22 );
    MHD_post_process( postprocessor, "", 0 );

    return EXIT_SUCCESS;
}

Reply via email to