Dear all,

We're happy to announce reaching the first milestone for the
STF-funded MHD 2.0 project, which is completing the MHD HTTP header
and thus the design for the next generation API.  We have now
spent several months iterating between discussing, designing,
optimising, editing, and testing details and are finally
happy with the result.

Key objectives for us were:

- simplify application code that uses MHD
- keep the API and the MHD code size small
- ensure the API is extensible
- enable use of different TLS backends, avoid backend-specific
  settings to use backends in the same way.
- make API work with HTTP 1.x, HTTP/2 and HTTP/3
- preserve or improve portability across platforms
- stay compatible to a wide range of C and C++ compilers

The main changes these objectives inspired are to:

- Split the MHD_AccessHandlerCallback functionality into
  various separate callbacks to keep the API simple for
  simple requests while allowing complex logic to be
  incrementally introduced via the new "struct MHD_Action".
  Main effects:
  * improved type safety
  * client code most likely to always need all arguments
    passed to callbacks, while keeping rarely used
    arguments available via the introspection API
  * harder or impossible to make calls at the wrong time
    or to forget to do key processing steps
  * clients more likely to avoid repeated URL dispatching
  * Easier to add commonly used features like a generic
    URL dispatcher
  * Improved modularity, easier to not compile in some
    features (actions) to minimize code size
- We changed how MHD is configured, providing a new
  strongly-typed but still extensible mechanism
  to set options, avoiding the use of 'varargs' for
  options while also not introducing new symbols for
  any kind of option. The new construction uses
  either inline functions or macros depending on the
  compiler to initialize a struct with a variant union;
  as a result, application developers get the experience
  of using well-typed functions, while no such functions
  actually exist in the library, keeping the code size
  minimal, especially if features are not even used :-).
- Unified the way how settings are used for daemon,
  connection and response objects.
- Removed the separation of options and flags and
  made it harder to pass inconsistent options
- improved terminology across the API, in particular by
  eliminating confusion between 'request' and 'connection',
  but also by introducing new 'nouns' such as 'session',
  'stream' and 'action', but also 'String' which returns a
  'String' that is both 0-terminated but ALSO has a
  known length (eliminating need for applications
  to call strlen() on all strings returned by MHD).
  Also changed the API to use more consistent
  prefixes for related functions by using
  "MHD_subject_verb_object" naming convention
- significantly simplified for application processing of
  client's upload. Removed the need for troublesome (for
  application) incremental processing (especially problematic
  for forms processing), while keeping automatic limits
  for memory allocations, preventing by design a wide range
  of remote attacks.
- Added unified and detailed introspection API for library,
  daemon, connection, stream and request objects.
  The API is designed in the same way for all object, simplifying
  use for the application. The new API is detailed and allow
  application to extract any required information in a simple
  way. Also separated "fixed" and "dynamic" properties of objects
  for letting compiler optimise application code better.
- Integrated HTTP status into the response object, as
  this is way more logical and we are aware of various
  implementations being forced to basically pass them
  around as a tuple.
- simplified API for common-case of one-shot responses by
  eliminating need for destroy response in most cases
- Improved portability by avoiding fixed types, like uint32_t,
  as they may not exist on some platforms. Instead use
  types like uint_fast32_t.  Avoided use of enums with very
  large bitmasks as 'int' could be just 16 bits on some platforms
  resulting in enum values higher than 65535 being silently dropped.
- Improved possibility of use of zero-copy style for parsing
  uploaded data, including of the PostProcessor parser while
  still allowing applications to do stream processing if data
  does not fit into main memory. This both simplifies usage
  in the common case where uploaded data is small, while also
  nicely supporting use-cases with large data streams.
- Made responses unmodifiable after first use. Modifiable responses
  cannot be thread-safe. However, MHD-generated headers (Date,
  Connection/Keep-Alive) are part of the *request* and do not count
  as part of the immutable "response" here.  Removed "footers" from
  responses.  With unmodifiable responses everything should be "headers".
  However, footers are supported as part of a *request* instead.
- Move response codes from MHD_HTTP_xxx namespace to MHD_HTTP_CODE_xxx
  namespace. This avoids potential clashes with other MHD constant names.
- Introduced various new "enums" especially for constants
  introduced in HTTP/2 where use of these constants can
  then avoid having to map between protocol numbers and
  strings (but applications may still also use the strings)
  This also includes better status codes returned from
  API calls to diagnose issues (no more just "YES/NO")
- Let application to use request methods as a enum, avoid repeated
  string comparison by sharing result of the already performed
  internal detection of the request method.  Keep ability to
  use non-standard HTTP requests methods if needed via use
  of introspection API.
- Introduced "MHD_APP_SOCKET_CNTX_TYPE" hack to allow
  applications to improve type-safety by overriding
  the type of "closure" arguments instead of using
  "void *" pointers.
- Significant re-design of the event loop APIs to simplify integrating
  MHD with external event loops while preserving O(1) processing cost.
- Added many annotations to help compiler determine invariants (if
  supported by the compiler), such as arguments not being NULL, etc.
- Removal of various legacy symbols only exported for API compatibility.


While these are lots of changes, we want to give you a first brief preview of
how this will impact client code using the library.  Here is an example of how
clients used to initialize the MHD daemon (from demo.c):

OLD>>
d = MHD_start_daemon (
    MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD
    | MHD_USE_ERROR_LOG,
    (uint16_t) port,
    NULL, NULL,
    &generate_page,
    NULL,
    MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    (size_t) (256 * 1024),
#ifdef PRODUCTION
    MHD_OPTION_PER_IP_CONNECTION_LIMIT,
    (unsigned int) (64),
#endif
    MHD_OPTION_CONNECTION_TIMEOUT,
    (unsigned int) (120 /* seconds */),
    MHD_OPTION_THREAD_POOL_SIZE,
    (unsigned int) NUMBER_OF_THREADS,
    MHD_OPTION_NOTIFY_COMPLETED,
    &response_completed_callback,
    NULL,
    MHD_OPTION_END);
if (NULL == d)
  error();
<<

This was one big variadic mess, and getting any of the
types wrong for any of the options could result in
trouble, sometimes depending on the target platform.

With MHD 2.0, the same code will look like this:

NEW>>
d = MHD_daemon_create (&generate_page,
                       NULL);
if (MHD_SC_OK !=
    MHD_daemon_options_set (
      d,
      MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL, port),
      MHD_D_OPTION_WM_WORKER_THREADS (NUMBER_OF_THREADS),
      MHD_D_OPTION_CONN_MEMORY_LIMIT (256*1024),
      MHD_D_OPTION_PER_IP_LIMIT (64),
      MHD_D_OPTION_DEFAULT_TIMEOUT (120),
      MHD_D_OPTION_NOTIFY_CONNECTION (&response_completed_callback,
                                      NULL)))
  error();
if (MHD_SC_OK !=
    MHD_daemon_start (d))
  error();
<<

Note that you can can call MHD_daemon_options_set() multiple times if you need
to handle errors for individual options.  Thanks to extensive trickery on our
part, the resulting type-safe code should *also* be almost as compact and fast
as the previous version (mostly, this adds two additional library API calls,
but in return you can get more precise status codes back).


While we thought hard about the new API and poured our experience into the
re-design, we still might have overlooked something and thus value community
feedback.

We thank Sovereign Tech Fund for funding this work.

Happy hacking!

Christian & Evgeny
