gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (22796735 -> 2949c070)


From: gnunet
Subject: [libmicrohttpd] branch master updated (22796735 -> 2949c070)
Date: Thu, 28 Jul 2022 06:26:04 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 22796735 Added test for Digest Auth with username in extended notation
     new 7ace1e11 digestauth: fixed copy-paste error in request algo parsing
     new 8714ba41 gen_auth: fixed detection of userhash in Digest Auth requests
     new 658e94e1 gen_auth: cosmetics
     new c024f7c8 digest_auth_check(): reduced scope of one-time variable
     new 7e5206cc digest_auth_check(): added support for userhash
     new 961635da digest_auth_check(): added check for too large realm value
     new 5866b68f Updated doxy for old Digest Auth API function
     new 50828ba7 digestauth: term correction in comment
     new 5da848a1 test_digestauth{,_sha256,_with_aguments}: moved back to 
Digest Auth APIv2
     new aa49d605 test_digestauth_emu_ext: cosmetics, additional check
     new 99c44002 tests_digestauth*: added workarounds for libcurl bug
     new 0194db85 test_digestauth_concurrent: fixed compiler warnings
     new 012a2d3b Updated Digest Auth enums in the header
     new 13db035f MHD_add_response_entry(): refactoring + added internal 
function
     new 4baec145 Digest Auth public structs: removed redundant member
     new c03c57c9 Added MHD_queue_auth_required_response3(); Refactored public 
Digest Auth API v3
     new 2fd1a01f test_digestauth2: added new group of tests for Digest Auth 
checking
     new a64c96c4 MHD_FEATURE_*: added some values related to Digest Auth
     new 2949c070 Makefile: cosmetics

The 19 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/examples/digest_auth_example.c            |    4 +-
 src/include/microhttpd.h                      |  452 ++++++++--
 src/microhttpd/daemon.c                       |   38 +
 src/microhttpd/digestauth.c                   |  941 ++++++++++++++------
 src/microhttpd/gen_auth.c                     |   11 +-
 src/microhttpd/response.c                     |  136 ++-
 src/microhttpd/response.h                     |   22 +
 src/testcurl/.gitignore                       |    5 +
 src/testcurl/Makefile.am                      |   23 +-
 src/testcurl/test_digestauth.c                |   24 +-
 src/testcurl/test_digestauth2.c               | 1159 +++++++++++++++++++++++++
 src/testcurl/test_digestauth_concurrent.c     |   30 +-
 src/testcurl/test_digestauth_emu_ext.c        |   44 +-
 src/testcurl/test_digestauth_sha256.c         |   29 +-
 src/testcurl/test_digestauth_with_arguments.c |   37 +-
 15 files changed, 2503 insertions(+), 452 deletions(-)
 create mode 100644 src/testcurl/test_digestauth2.c

diff --git a/src/examples/digest_auth_example.c 
b/src/examples/digest_auth_example.c
index 4f576bf0..db8a1f28 100644
--- a/src/examples/digest_auth_example.c
+++ b/src/examples/digest_auth_example.c
@@ -84,8 +84,8 @@ ahc_echo (void *cls,
   res_e = MHD_digest_auth_check3 (connection, realm,
                                   username,
                                   password,
-                                  300,
-                                  MHD_DIGEST_ALG_MD5);
+                                  300, 60, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                  MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
   MHD_free (username);
   if (res_e != MHD_DAUTH_OK)
   {
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 670a990d..c0e911c5 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097521
+#define MHD_VERSION 0x00097527
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -329,12 +329,6 @@ _MHD_DEPR_MACRO ( \
 #endif
 
 
-/**
- * Length of the binary output of the MD5 hash function.
- */
-#define  MHD_MD5_DIGEST_SIZE 16
-
-
 /**
  * @defgroup httpcode HTTP response codes.
  * These are the status codes defined for HTTP responses.
@@ -4327,6 +4321,22 @@ MHD_destroy_post_processor (struct MHD_PostProcessor 
*pp);
 /* ********************* Digest Authentication functions *************** */
 
 
+/**
+ * Length of the binary output of the MD5 hash function.
+ * @sa #MHD_digest_get_hash_size()
+ * @ingroup authentication
+ */
+#define MHD_MD5_DIGEST_SIZE 16
+
+
+/**
+ * Length of the binary output of the SHA-256 hash function.
+ * @sa #MHD_digest_get_hash_size()
+ * @ingroup authentication
+ */
+#define MHD_SHA256_DIGEST_SIZE 32
+
+
 /**
  * Constant to indicate that the nonce of the provided
  * authentication code was wrong.
@@ -4347,23 +4357,26 @@ enum MHD_DigestBaseAlgo
    * Invalid hash algorithm value
    */
   MHD_DIGEST_BASE_ALGO_INVALID = 0,
+
   /**
    * MD5 hash algorithm.
    * As specified by RFC1321
    */
   MHD_DIGEST_BASE_ALGO_MD5 = (1 << 0),
+
   /**
-   * SHA-256 has algorithm.
+   * SHA-256 hash algorithm.
    * As specified by FIPS PUB 180-4
    */
   MHD_DIGEST_BASE_ALGO_SHA256 = (1 << 1),
+
   /**
-   * SHA-512/256 has algorithm.
+   * SHA-512/256 hash algorithm.
    * Not supported for calculations, only supported for parsing of
    * client's authorisation headers.
    */
   MHD_DIGEST_BASE_ALGO_SHA512_256 = (1 << 2)
-};
+} _MHD_FIXED_FLAGS_ENUM;
 
 /**
  * The flag indicating digest calculation types,
@@ -4372,13 +4385,6 @@ enum MHD_DigestBaseAlgo
  */
 #define MHD_DIGEST_AUTH_ALGO3_NON_SESSION    (1 << 6)
 
-/**
- * The flag indicating non-session algorithm types,
- * like 'MD5' or 'SHA-256'.
- * @note Available since #MHD_VERSION 0x00097519
- */
-#define MHD_DIGEST_AUTH_ALGO3_NON_SESSION    (1 << 6)
-
 /**
  * The flag indicating session algorithm types,
  * like 'MD5-sess' or 'SHA-256-sess'.
@@ -4390,7 +4396,7 @@ enum MHD_DigestBaseAlgo
  * Digest algorithm identification
  * @warning Do not be confused with #MHD_DigestAuthAlgorithm,
  *          which uses other values!
- * @note Available since #MHD_VERSION 0x00097519
+ * @note Available since #MHD_VERSION 0x00097523
  */
 enum MHD_DigestAuthAlgo3
 {
@@ -4400,57 +4406,140 @@ enum MHD_DigestAuthAlgo3
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_ALGO3_INVALID = 0,
+
   /**
-   * The 'MD5' algorithm.
+   * The 'MD5' algorithm, non-session version.
    */
   MHD_DIGEST_AUTH_ALGO3_MD5 =
     MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION,
+
   /**
    * The 'MD5-sess' algorithm.
-   * Not supported by MHD.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_ALGO3_MD5_SESSION =
     MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_AUTH_ALGO3_SESSION,
+
   /**
-   * The 'SHA-256' algorithm.
+   * The 'SHA-256' algorithm, non-session version.
    */
   MHD_DIGEST_AUTH_ALGO3_SHA256 =
     MHD_DIGEST_BASE_ALGO_SHA256 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION,
+
   /**
    * The 'SHA-256-sess' algorithm.
-   * Not supported by MHD.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION =
     MHD_DIGEST_BASE_ALGO_SHA256 | MHD_DIGEST_AUTH_ALGO3_SESSION,
+
   /**
    * The 'SHA-512-256' (SHA-512/256) algorithm.
-   * Not supported by MHD.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_ALGO3_SHA512_256 =
     MHD_DIGEST_BASE_ALGO_SHA512_256 | MHD_DIGEST_AUTH_ALGO3_NON_SESSION,
+
   /**
    * The 'SHA-512-256-sess' (SHA-512/256 session) algorithm.
-   * Not supported by MHD.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION =
     MHD_DIGEST_BASE_ALGO_SHA512_256 | MHD_DIGEST_AUTH_ALGO3_SESSION,
+};
+
+/**
+ * Digest algorithm identification, allow multiple selection.
+ *
+ * #MHD_DigestAuthAlgo3 always can be casted to #MHD_DigestAuthMultiAlgo3, but
+ * not vice versa.
+ *
+ * @note Available since #MHD_VERSION 0x00097523
+ */
+enum MHD_DigestAuthMultiAlgo3
+{
+  /**
+   * Unknown or wrong algorithm type.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_INVALID = MHD_DIGEST_AUTH_ALGO3_INVALID,
+
+  /**
+   * The 'MD5' algorithm, non-session version.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_MD5 = MHD_DIGEST_AUTH_ALGO3_MD5,
+
+  /**
+   * The 'MD5-sess' algorithm.
+   * Not supported by MHD for authentication.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_MD5_SESSION = MHD_DIGEST_AUTH_ALGO3_MD5_SESSION,
+
+  /**
+   * The 'SHA-256' algorithm, non-session version.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_SHA256 = MHD_DIGEST_AUTH_ALGO3_SHA256,
+
+  /**
+   * The 'SHA-256-sess' algorithm.
+   * Not supported by MHD for authentication.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_SHA256_SESSION =
+    MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION,
+
+  /**
+   * The 'SHA-512-256' (SHA-512/256) algorithm.
+   * Not supported by MHD for authentication.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_SHA512_256 = MHD_DIGEST_AUTH_ALGO3_SHA512_256,
+
+  /**
+   * The 'SHA-512-256-sess' (SHA-512/256 session) algorithm.
+   * Not supported by MHD for authentication.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_SHA512_256_SESSION =
+    MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION,
+
   /**
    * Any non-session algorithm, MHD will choose.
    */
-  MHD_DIGEST_AUTH_ALGO3_ANY_NON_SESSION =
+  MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION =
     (0x3F) | MHD_DIGEST_AUTH_ALGO3_NON_SESSION,
+
   /**
    * Any session algorithm, MHD will choose.
    * Not supported by MHD.
+   * Reserved value.
    */
-  MHD_DIGEST_AUTH_ALGO3_ANY_SESSION =
+  MHD_DIGEST_AUTH_MULT_ALGO3_ANY_SESSION =
     (0x3F) | MHD_DIGEST_AUTH_ALGO3_SESSION,
+
+  /**
+   * The 'MD5' algorithm, session or non-session.
+   * Not supported by MHD.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_MD5_ANY =
+    MHD_DIGEST_AUTH_MULT_ALGO3_MD5 | MHD_DIGEST_AUTH_MULT_ALGO3_MD5_SESSION,
+
+  /**
+   * The 'SHA-256' algorithm, session or non-session.
+   * Not supported by MHD.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_ALGO3_SHA256_ANY =
+    MHD_DIGEST_AUTH_MULT_ALGO3_SHA256
+    | MHD_DIGEST_AUTH_MULT_ALGO3_SHA256_SESSION,
+
   /**
    * Any algorithm, MHD will choose.
    */
-  MHD_DIGEST_AUTH_ALGO3_ANY =
+  MHD_DIGEST_AUTH_MULT_ALGO3_ANY =
     (0x3F) | MHD_DIGEST_AUTH_ALGO3_NON_SESSION | MHD_DIGEST_AUTH_ALGO3_SESSION
-} _MHD_FLAGS_ENUM;
+};
 
 /**
  * The type of username used by client in Digest Authorization header
@@ -4464,10 +4553,12 @@ enum MHD_DigestAuthUsernameType
    * This should be treated as an error.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_MISSING = 0,
+
   /**
    * The 'username' parameter is used to specify the username.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD = 1,
+
   /**
    * The username is specified by 'username*' parameter with
    * the extended notation (see RFC 5987 #section-3.2.1).
@@ -4475,11 +4566,13 @@ enum MHD_DigestAuthUsernameType
    * the way how username value is encoded in the header.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED = 2,
+
   /**
    * The username provided in form of 'userhash' as
    * specified by RFC 7616 #section-3.4.4.
    */
   MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = 3,
+
   /**
    * The invalid combination of username parameters are used by client.
    * Either:
@@ -4503,20 +4596,69 @@ enum MHD_DigestAuthQOP
    * cannot by identified.
    */
   MHD_DIGEST_AUTH_QOP_INVALID = 0,
+
   /**
-   * No QOP value.
+   * No QOP parameter.
+   * Match old RFC 2069 specification.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_QOP_NONE = 1 << 0,
+
   /**
    * The 'auth' QOP type.
    */
   MHD_DIGEST_AUTH_QOP_AUTH = 1 << 1,
+
   /**
    * The 'auth-int' QOP type.
-   * Not supported by MHD.
+   * Not supported by MHD for authentication.
    */
   MHD_DIGEST_AUTH_QOP_AUTH_INT = 1 << 2
-} _MHD_FLAGS_ENUM;
+} _MHD_FIXED_FLAGS_ENUM;
+
+/**
+ * The QOP ('quality of protection') types, multiple selection.
+ *
+ * #MHD_DigestAuthQOP always can be casted to #MHD_DigestAuthMultiQOP, but
+ * not vice versa.
+ *
+ * @note Available since #MHD_VERSION 0x00097523
+ */
+enum MHD_DigestAuthMultiQOP
+{
+  /**
+   * Invalid/unknown QOP.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_INVALID = MHD_DIGEST_AUTH_QOP_INVALID,
+
+  /**
+   * No QOP parameter.
+   * Match old RFC 2069 specification.
+   * Not supported by MHD.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_NONE = MHD_DIGEST_AUTH_QOP_NONE,
+
+  /**
+   * The 'auth' QOP type.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_AUTH = MHD_DIGEST_AUTH_QOP_AUTH,
+
+  /**
+   * The 'auth-int' QOP type.
+   * Not supported by MHD.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT = MHD_DIGEST_AUTH_QOP_AUTH_INT,
+
+  /**
+   * Any 'auth' QOP type ('auth' or 'auth-int').
+   * Not supported by MHD.
+   * Reserved value.
+   */
+  MHD_DIGEST_AUTH_MULT_QOP_AUTH_ANY =
+    MHD_DIGEST_AUTH_QOP_AUTH | MHD_DIGEST_AUTH_QOP_AUTH_INT
+} _MHD_FIXED_ENUM;
 
 /**
  * The invalid value of 'nc' parameter in client Digest Authorization header.
@@ -4532,7 +4674,7 @@ enum MHD_DigestAuthQOP
  *
  * Application may modify buffers as needed until #MHD_free() is called for
  * pointer to this structure
- * @note Available since #MHD_VERSION 0x00097519
+ * @note Available since #MHD_VERSION 0x00097525
  */
 struct MHD_DigestAuthInfo
 {
@@ -4544,10 +4686,12 @@ struct MHD_DigestAuthInfo
    *          which uses other values!
    */
   enum MHD_DigestAuthAlgo3 algo;
+
   /**
    * The type of username used by client.
    */
   enum MHD_DigestAuthUsernameType uname_type;
+
   /**
    * The username string.
    * Valid only if username is standard, extended, or userhash.
@@ -4559,46 +4703,54 @@ struct MHD_DigestAuthInfo
    * This can be NULL is username is missing or invalid.
    */
   char *username;
+
   /**
    * The length of the @a username.
    * When the @a username is NULL, this member is always zero.
    */
   size_t username_len;
+
   /**
    * The userhash decoded to binary form.
    * Used only if username type is userhash, always NULL otherwise.
-   * @warning this is a binary data, no zero termination
+   * When not NULL, this points to binary sequence @a username_len /2 bytes
+   * long.
+   * @warning This is binary data, no zero termination.
+   * @warning To avoid buffer overruns, always check the size of the data 
before
+   *          use, because @a userhash_bin can point even to zero-sized
+   *          data.
    */
   uint8_t *userhash_bin;
-  /**
-   * The number of bytes pointed by the @a userhash_bin.
-   * When the @a userhash_bin is NULL, this member is always zero.
-   */
-  size_t userhash_bin_size;
+
   /**
    * The 'opaque' parameter value, as specified by client.
    * NULL if not specified by client.
    */
   char *opaque;
+
   /**
    * The length of the @a opaque.
    * When the @a opaque is NULL, this member is always zero.
    */
   size_t opaque_len;
+
   /**
    * The 'realm' parameter value, as specified by client.
    * NULL if not specified by client.
    */
   char *realm;
+
   /**
    * The length of the @a realm.
    * When the @a realm is NULL, this member is always zero.
    */
   size_t realm_len;
+
   /**
    * The 'qop' parameter value.
    */
   enum MHD_DigestAuthQOP qop;
+
   /**
    * The length of the 'cnonce' parameter value, including possible
    * backslash-escape characters.
@@ -4608,11 +4760,12 @@ struct MHD_DigestAuthInfo
    * characters long.
    */
   size_t cnonce_len;
+
   /**
    * The nc parameter value.
-   * Can be used by application to limit the number of nonce re-uses. If @ nc
-   * is higher than application wants to allow, then fail response with
-   * 'stale=true' could be used to ask force client to get the fresh 'nonce'.
+   * Can be used by application to limit the number of nonce re-uses. If @a nc
+   * is higher than application wants to allow, then auth required response 
with
+   * 'stale=true' could be used to force client to get the fresh 'nonce'.
    * If not specified by client or does not have hexadecimal digits only, the
    * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE.
    */
@@ -4641,7 +4794,7 @@ MHD_digest_auth_get_request_info3 (struct MHD_Connection 
*connection);
  *
  * Application may modify buffers as needed until #MHD_free() is called for
  * pointer to this structure
- * @note Available since #MHD_VERSION 0x00097519
+ * @note Available since #MHD_VERSION 0x00097525
  */
 struct MHD_DigestAuthUsernameInfo
 {
@@ -4651,6 +4804,7 @@ struct MHD_DigestAuthUsernameInfo
    * instead NULL is returned by #MHD_digest_auth_get_username3().
    */
   enum MHD_DigestAuthUsernameType uname_type;
+
   /**
    * The username string.
    * Valid only if username is standard, extended, or userhash.
@@ -4662,24 +4816,42 @@ struct MHD_DigestAuthUsernameInfo
    * This can be NULL is username is missing or invalid.
    */
   char *username;
+
   /**
    * The length of the @a username.
    * When the @a username is NULL, this member is always zero.
    */
   size_t username_len;
+
   /**
    * The userhash decoded to binary form.
-   * Used only if username type is userhash, always NULL if not used.
-   * @warning this is a binary data, no zero termination
+   * When not NULL, this points to binary sequence @a username_len /2 bytes
+   * long.
+   * @warning This is binary data, no zero termination.
+   * @warning To avoid buffer overruns, always check the size of the data 
before
+   *          use, because @a userhash_bin can point even to zero-sized
+   *          data.
    */
   uint8_t *userhash_bin;
-  /**
-   * The number of bytes pointed by the @a userhash_bin.
-   * When the @a userhash_bin is NULL, this member is always zero.
-   */
-  size_t userhash_bin_size;
 };
 
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
+ *         recognised/valid
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3);
+
+
 /**
  * Get the username from Digest Authorization client's header.
  *
@@ -4701,9 +4873,14 @@ MHD_digest_auth_get_username3 (struct MHD_Connection 
*connection);
 /**
  * Get the username from the authorization header sent by the client
  *
+ * This function supports username in standard and extended notations.
+ * "userhash" is not supported by this function.
+ *
  * @param connection The MHD connection structure
- * @return NULL if no username could be found, a pointer
- *      to the username if found, free using #MHD_free().
+ * @return NULL if no username could be found, username provided as
+ *         "userhash" or memory allocation error occurred;
+ *         a pointer to the username if found, free using #MHD_free().
+ * @warning Returned value must be freed by #MHD_free().
  * @deprecated use MHD_digest_auth_get_username3()
  * @ingroup authentication
  */
@@ -4718,7 +4895,7 @@ enum MHD_DigestAuthAlgorithm
 {
 
   /**
-   * MHD should pick (currently defaults to SHA-256).
+   * MHD should pick (currently defaults to MD5).
    */
   MHD_DIGEST_ALG_AUTO = 0,
 
@@ -4819,10 +4996,17 @@ enum MHD_DigestAuthResult
  * @param username the username needs to be authenticated
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
- * @param algo the digest algorithms allowed for verification
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097521
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -4831,34 +5015,49 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
                         const char *username,
                         const char *password,
                         unsigned int nonce_timeout,
-                        enum MHD_DigestAuthAlgorithm algo);
+                        uint32_t max_nc,
+                        enum MHD_DigestAuthMultiQOP mqop,
+                        enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
 /**
- * Authenticates the authorization header sent by the client.
+ * Authenticates the authorization header sent by the client by using
+ * hash of "username:realm:password".
  *
  * @param connection the MHD connection structure
- * @param realm the realm to be used for authorization of the client
+ * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
- * @param digest the pointer to the binary digest for the precalculated hash
- *        value "username:realm:password" with specified @a algo
- * @param digest_size the number of bytes in @a digest (the size must match
- *        @a algo!)
- * @param nonce_timeout the nonce validity duration in seconds
- * @param algo digest algorithms allowed for verification
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
+ *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
+ *                        #MHD_SHA256_DIGEST_SIZE)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based
+ *               algorithms cannot be used at the same time for this function
+ *               as @a userdigest_size must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097521
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection,
                                const char *realm,
                                const char *username,
-                               const uint8_t *digest,
-                               size_t digest_size,
+                               const void *userdigest,
+                               size_t userdigest_size,
                                unsigned int nonce_timeout,
-                               enum MHD_DigestAuthAlgorithm algo);
+                               uint32_t max_nc,
+                               enum MHD_DigestAuthMultiQOP mqop,
+                               enum MHD_DigestAuthMultiAlgo3 malgo3);
 
 
 /**
@@ -4971,6 +5170,66 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
 /**
  * Queues a response to request authentication from the client
  *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed (by #MHD_destroy_response()) after call of
+ * this function.
+ *
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque the string for opaque value, can be NULL, but NULL is
+ *               not recommended for better compatibility with clients
+ * @param domain the optional space-separated list of URIs for which the
+ *               same authorisation could be used, URIs can be in form
+ *               "path-absolute" (the path for the same host with initial 
slash)
+ *               or in form "absolute-URI" (the full path with protocol), in
+ *               any case client may assume that any URI which starts with
+ *               any of specified URI is in the same "protection space";
+ *               could be NULL (clients typically assume that the same
+ *               credentials could be used for any URI on the same host)
+ * @param response the reply to send; should contain the "access denied"
+ *                 body; note that this function sets the "WWW Authenticate"
+ *                 header and that the caller should not do this;
+ *                 the NULL is tolerated
+ * @param signal_stale set to #MHD_YES if the nonce is stale to add 
'stale=true'
+ *                     to the authentication header, this instructs the client
+ *                     to retry immediately with the new nonce and the same
+ *                     credentials, without asking user for the new password
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
+ * @param userhash_support if set to non-zero value (#MHD_YES) then support of
+ *                         userhash is indicated, the client may provide
+ *                         hash("username:realm") instead of username in
+ *                         clear text; note that client is allowed to provide
+ *                         the username in cleartext even if this parameter set
+ *                         to non-zero
+ * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
+ *                    added, indicating for the client that UTF-8 encoding
+ *                    is preferred
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_queue_auth_required_response3 (struct MHD_Connection *connection,
+                                   const char *realm,
+                                   const char *opaque,
+                                   const char *domain,
+                                   struct MHD_Response *response,
+                                   int signal_stale,
+                                   enum MHD_DigestAuthMultiQOP qop,
+                                   enum MHD_DigestAuthMultiAlgo3 algo,
+                                   int userhash_support,
+                                   int prefer_utf8);
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed after call of this function.
+ *
  * @param connection The MHD connection structure
  * @param realm the realm presented to the client
  * @param opaque string to user for opaque value
@@ -4982,6 +5241,7 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
  * @param algo digest algorithm to use
  * @return #MHD_YES on success, #MHD_NO otherwise
  * @note Available since #MHD_VERSION 0x00096200
+ * @deprecated use MHD_queue_auth_required_response3()
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_Result
@@ -4998,6 +5258,9 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
  * For now uses MD5 (for backwards-compatibility). Still, if you
  * need to be sure, use #MHD_queue_auth_fail_response2().
  *
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed after call of this function.
+ *
  * @param connection The MHD connection structure
  * @param realm the realm presented to the client
  * @param opaque string to user for opaque value
@@ -5007,8 +5270,8 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
  * @param signal_stale #MHD_YES if the nonce is stale to add
  *        'stale=true' to the authentication header
  * @return #MHD_YES on success, #MHD_NO otherwise
+ * @deprecated use MHD_queue_auth_required_response3()
  * @ingroup authentication
- * @deprecated use MHD_queue_auth_fail_response2()
  */
 _MHD_EXTERN enum MHD_Result
 MHD_queue_auth_fail_response (struct MHD_Connection *connection,
@@ -5445,8 +5708,57 @@ enum MHD_FEATURE
    * MHD versions before 0x00097514 always support cookie parsing.
    * @note Available since #MHD_VERSION 0x00097514
    */
-  MHD_FEATURE_HTTPS_COOKIE_PARSING = 24
-} _MHD_FIXED_ENUM;
+  MHD_FEATURE_HTTPS_COOKIE_PARSING = 24,
+
+  /**
+   * Get whether the early version the Digest Authorization (RFC 2069) is
+   * supported.
+   * Currently it is always not supported if Digest Auth module is built.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_RFC2069 = 25,
+
+  /**
+   * Get whether the MD5-based hashing algorithms are supported for Digest
+   * Authorization.
+   * Currently it is always not supported if Digest Auth module is built.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_MD5 = 26,
+
+  /**
+   * Get whether the SHA-256-based hashing algorithms are supported for Digest
+   * Authorization.
+   * It it always supported since #MHD_VERSION 0x00096200 if Digest Auth
+   * module is built.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_SHA256 = 27,
+
+  /**
+   * Get whether QOP with value 'auth-int' (authentication with integrity
+   * protection) is supported for Digest Authorization.
+   * Currently it is always not supported if Digest Auth module is built.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_AUTH_INT = 28,
+
+  /**
+   * Get whether 'session' algorithms (like 'MD5-sess') are supported for 
Digest
+   * Authorization.
+   * Currently it is always not supported.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_ALGO_SESSION = 29,
+
+  /**
+   * Get whether 'userhash' is supported for Digest Authorization.
+   * It it always supported since #MHD_VERSION 0x00097526 if Digest Auth
+   * module is built.
+   * @note Available since #MHD_VERSION 0x00097527
+   */
+  MHD_FEATURE_DIGEST_AUTH_USERHASH = 30
+};
 
 
 /**
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index 251e563e..2f868bfb 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -8379,7 +8379,45 @@ MHD_is_feature_supported (enum MHD_FEATURE feature)
 #else
     return MHD_NO;
 #endif
+  case MHD_FEATURE_DIGEST_AUTH_RFC2069:
+#ifdef DAUTH_SUPPORT
+    return MHD_NO;
+#else
+    return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_MD5:
+#ifdef DAUTH_SUPPORT
+    return MHD_YES;
+#else
+    return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_SHA256:
+#ifdef DAUTH_SUPPORT
+    return MHD_YES;
+#else
+    return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_AUTH_INT:
+#ifdef DAUTH_SUPPORT
+    return MHD_NO;
+#else
+    return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_ALGO_SESSION:
+#ifdef DAUTH_SUPPORT
+    return MHD_NO;
+#else
+    return MHD_NO;
+#endif
+  case MHD_FEATURE_DIGEST_AUTH_USERHASH:
+#ifdef DAUTH_SUPPORT
+    return MHD_YES;
+#else
+    return MHD_NO;
+#endif
 
+  default:
+    break;
   }
   return MHD_NO;
 }
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index fe80bf0d..f8f06d6d 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -30,6 +30,7 @@
 #include "platform.h"
 #include "mhd_limits.h"
 #include "internal.h"
+#include "response.h"
 #include "md5.h"
 #include "sha256.h"
 #include "mhd_mono_clock.h"
@@ -153,10 +154,20 @@
 #define _MHD_SHA512_256_TOKEN "SHA-512-256"
 
 /**
- * The postfix token for "session" algorithms.
+ * The suffix token for "session" algorithms.
  */
 #define _MHD_SESS_TOKEN "-sess"
 
+/**
+ * The "auth" token for QOP
+ */
+#define MHD_TOKEN_AUTH_ "auth"
+
+/**
+ * The "auth-int" token for QOP
+ */
+#define MHD_TOKEN_AUTH_INT_ "auth-int"
+
 /**
  * The required prefix of parameter with the extended notation
  */
@@ -192,6 +203,73 @@ enum MHD_CheckNonceNC_
 };
 
 
+/**
+ * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value.
+ * @param algo3 the MHD_DigestAuthAlgo3 value
+ * @return the base hash calculation algorithm
+ */
+_MHD_static_inline enum MHD_DigestBaseAlgo
+get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3)
+{
+  unsigned int base_algo;
+
+  base_algo =
+    ((unsigned int) algo3)
+    & ~((unsigned int)
+        (MHD_DIGEST_AUTH_ALGO3_NON_SESSION
+         | MHD_DIGEST_AUTH_ALGO3_NON_SESSION));
+  return (enum MHD_DigestBaseAlgo) base_algo;
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * Internal inline version.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest or zero if the input value is not
+ *         recognised/valid
+ */
+_MHD_static_inline size_t
+digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
+{
+  mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE);
+  mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE);
+  /* Both MD5 and SHA-256 must not be specified at the same time */
+  mhd_assert ( (0 == (((unsigned int) algo3)   \
+                      & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) || \
+               (0 == (((unsigned int) algo3)   \
+                      & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) );
+  if (0 != (((unsigned int) algo3)
+            & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
+    return MHD_MD5_DIGEST_SIZE;
+  else if (0 != (((unsigned int) algo3)
+                 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
+    return MHD_SHA256_DIGEST_SIZE;
+
+  return 0; /* Wrong input */
+}
+
+
+/**
+ * Get digest size for specified algorithm.
+ *
+ * The size of the digest specifies the size of the userhash, userdigest
+ * and other parameters which size depends on used hash algorithm.
+ * @param algo3 the algorithm to check
+ * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or
+ *         #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not
+ *         recognised/valid
+ * @note Available since #MHD_VERSION 0x00097526
+ * @ingroup authentication
+ */
+_MHD_EXTERN size_t
+MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3)
+{
+  return digest_get_hash_size (algo3);
+}
+
+
 /**
  * Digest context data
  */
@@ -743,26 +821,26 @@ get_rq_algo (const struct MHD_RqDAuth *params)
   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN, \
                                        algo_param->value.str, \
                                        algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
+    return MHD_DIGEST_AUTH_ALGO3_SHA256;
   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_MD5_TOKEN _MHD_SESS_TOKEN, \
                                        algo_param->value.str, \
                                        algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
+    return MHD_DIGEST_AUTH_ALGO3_MD5_SESSION;
   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA256_TOKEN _MHD_SESS_TOKEN, \
                                        algo_param->value.str, \
                                        algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
+    return MHD_DIGEST_AUTH_ALGO3_SHA256_SESSION;
 
   /* Algorithms below are not supported by MHD for authentication */
 
   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN, \
                                        algo_param->value.str, \
                                        algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
+    return MHD_DIGEST_AUTH_ALGO3_SHA512_256;
   if (MHD_str_equal_caseless_s_bin_n_ (_MHD_SHA512_256_TOKEN _MHD_SESS_TOKEN, \
                                        algo_param->value.str, \
                                        algo_param->value.len))
-    return MHD_DIGEST_AUTH_ALGO3_MD5;
+    return MHD_DIGEST_AUTH_ALGO3_SHA512_256_SESSION;
 
   /* No known algorithm has been detected */
   return MHD_DIGEST_AUTH_ALGO3_INVALID;
@@ -881,20 +959,23 @@ get_rq_uname (const struct MHD_RqDAuth *params,
     buf_used += uname_info->username_len + 1;
     if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
     {
-      uname_info->userhash_bin_size = MHD_hex_to_bin (uname_info->username,
-                                                      uname_info->username_len,
-                                                      buf + buf_used);
-      if ( (0 == uname_info->userhash_bin_size) &&
-           (0 != uname_info->username_len) )
+      size_t res;
+      uint8_t *const bin_data = (uint8_t *) (buf + buf_used);
+      res = MHD_hex_to_bin (uname_info->username,
+                            uname_info->username_len,
+                            bin_data);
+      if (res != uname_info->username_len / 2)
       {
         uname_info->userhash_bin = NULL;
         uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID;
       }
       else
       {
-        uname_info->userhash_bin = (uint8_t *) (buf + buf_used);
+        /* Avoid pointers outside allocated region when the size is zero */
+        uname_info->userhash_bin = (0 != res) ?
+                                   bin_data : (uint8_t *) uname_info->username;
         uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH;
-        buf_used += uname_info->userhash_bin_size;
+        buf_used += res;
       }
     }
     else
@@ -1093,7 +1174,6 @@ MHD_digest_auth_get_request_info3 (struct MHD_Connection 
*connection)
     info->username = uname_strct.username;
     info->username_len = uname_strct.username_len;
     info->userhash_bin = uname_strct.userhash_bin;
-    info->userhash_bin_size = uname_strct.userhash_bin_size;
   }
   else
     info->uname_type = uname_type;
@@ -1189,10 +1269,15 @@ MHD_digest_auth_get_username3 (struct MHD_Connection 
*connection)
 /**
  * Get the username from the authorization header sent by the client
  *
+ * This function support username in standard and extended notations.
+ * "userhash" is not supported by this function.
+ *
  * @param connection The MHD connection structure
- * @return NULL if no username could be found, a pointer
- *      to the username if found
+ * @return NULL if no username could be found, username provided as
+ *         "userhash" or memory allocation error occurs;
+ *         a pointer to the username if found, free using #MHD_free().
  * @warning Returned value must be freed by #MHD_free().
+ * @deprecated use MHD_digest_auth_get_username3()
  * @ingroup authentication
  */
 _MHD_EXTERN char *
@@ -1897,18 +1982,22 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
 /**
  * Authenticates the authorization header sent by the client
  *
- * @param connection The MHD connection structure
- * @param[in,out] da digest algorithm to use for checking (written to as
- *         part of the calculations, but the values left in the struct
- *         are not actually expected to be useful for the caller)
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param digest An optional binary hash
- *     of the precalculated hash value "username:realm:password"
- *     (must contain "digest_get_size(da)" bytes or be NULL)
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param username the username needs to be authenticated
+ * @param password the password used in the authentication
+ * @param userdigest the optional precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *            #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithms to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer,
  *                  to be free if not NULL upon return
  * @return #MHD_DAUTH_OK if authenticated,
@@ -1917,23 +2006,26 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam 
*param,
  */
 static enum MHD_DigestAuthResult
 digest_auth_check_all_inner (struct MHD_Connection *connection,
-                             struct DigestAlgorithm *da,
                              const char *realm,
                              const char *username,
                              const char *password,
-                             const uint8_t *digest,
+                             const uint8_t *userdigest,
                              unsigned int nonce_timeout,
+                             uint32_t max_nc,
+                             enum MHD_DigestAuthMultiQOP mqop,
+                             enum MHD_DigestAuthMultiAlgo3 malgo3,
                              char **pbuf)
 {
   struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
-  const unsigned int digest_size = digest_get_size (da);
+  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
+  struct DigestAlgorithm da;
+  unsigned int digest_size;
   uint8_t hash1_bin[MAX_DIGEST];
   uint8_t hash2_bin[MAX_DIGEST];
 #if 0
   const char *hentity = NULL; /* "auth-int" is not supported */
 #endif
   uint64_t nonce_time;
-  uint64_t t;
   uint64_t nci;
   const struct MHD_RqDAuth *params;
   /**
@@ -1954,6 +2046,28 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (NULL == params)
     return MHD_DAUTH_WRONG_HEADER;
 
+  /* ** Initial parameters checks and setup ** */
+  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop)
+    MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
+
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'session' algorithms are not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_DAUTH_WRONG_ALGO;
+  }
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;
+  else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
+  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
+  digest_size = digest_get_size (&da);
+
   /* ** A quick check for presence of all required parameters ** */
 
   if ((NULL == params->username.value.str) &&
@@ -1964,10 +2078,19 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_WRONG_HEADER; /* Parameters cannot be used together */
   else if ((NULL != params->username_ext.value.str) &&
            (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
-    return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */
+    return MHD_DAUTH_WRONG_HEADER;  /* Broken extended notation */
+  else if (params->userhash && (NULL == params->username.value.str))
+    return MHD_DAUTH_WRONG_HEADER;  /* Userhash cannot be used with extended 
notation */
+  else if (params->userhash && (digest_size * 2 > params->username.value.len))
+    return MHD_DAUTH_WRONG_HEADER;  /* Too few chars for correct userhash */
+  else if (params->userhash && (digest_size * 4 < params->username.value.len))
+    return MHD_DAUTH_WRONG_HEADER;  /* Too many chars for correct userhash */
 
   if (NULL == params->realm.value.str)
     return MHD_DAUTH_WRONG_HEADER;
+  else if (((NULL == userdigest) || params->userhash) &&
+           (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
+    return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in 
hash calculations */
 
   if (NULL == params->nc.value.str)
     return MHD_DAUTH_WRONG_HEADER;
@@ -2014,16 +2137,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Check simple parameters match ** */
 
   /* Check 'algorithm' */
-  if (1)
-  {
-    const enum MHD_DigestAuthAlgo3 r_algo = get_rq_algo (params);
-    const enum MHD_DigestBaseAlgo p_algo = da->algo;
-    if ( (! ((MHD_DIGEST_AUTH_ALGO3_MD5 == r_algo) &&
-             (MHD_DIGEST_BASE_ALGO_MD5 == p_algo))) &&
-         (! ((MHD_DIGEST_AUTH_ALGO3_SHA256 == r_algo) &&
-             (MHD_DIGEST_BASE_ALGO_SHA256 == p_algo))) )
-      return MHD_DAUTH_WRONG_ALGO;
-  }
+  /* The 'algorithm' was checked at the start of the function */
   /* 'algorithm' valid */
 
   /* Check 'qop' */
@@ -2040,32 +2154,48 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 
   /* Check 'username' */
   username_len = strlen (username);
-  if (NULL != params->username.value.str)
-  { /* Username in standard notation */
-    if (! is_param_equal (&params->username, username, username_len))
-      return MHD_DAUTH_WRONG_USERNAME;
+  if (! params->userhash)
+  {
+    if (NULL != params->username.value.str)
+    { /* Username in standard notation */
+      if (! is_param_equal (&params->username, username, username_len))
+        return MHD_DAUTH_WRONG_USERNAME;
+    }
+    else
+    { /* Username in extended notation */
+      char *r_uname;
+      size_t buf_size = params->username_ext.value.len;
+      ssize_t res;
+
+      mhd_assert (NULL != params->username_ext.value.str);
+      mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked 
already */
+      buf_size += 1; /* For zero-termination */
+      buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN;
+      r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
+      if (NULL == r_uname)
+        return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
+               MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
+      res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
+                                          params->username_ext.value.len,
+                                          r_uname, buf_size);
+      if (0 > res)
+        return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */
+      if ((username_len != (size_t) res) ||
+          (0 != memcmp (username, r_uname, username_len)))
+        return MHD_DAUTH_WRONG_USERNAME;
+    }
   }
   else
-  { /* Username in extended notation */
-    char *r_uname;
-    size_t buf_size = params->username_ext.value.len;
-    ssize_t res;
-
-    mhd_assert (NULL != params->username_ext.value.str);
-    mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked 
already */
-    buf_size += 1; /* For zero-termination */
-    buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN;
-    r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
-    if (NULL == r_uname)
-      return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
-             MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR;
-    res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
-                                        params->username_ext.value.len,
-                                        r_uname, buf_size);
-    if (0 > res)
-      return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */
-    if ((username_len != (size_t) res) ||
-        (0 != memcmp (username, r_uname, username_len)))
+  { /* Userhash */
+    mhd_assert (NULL != params->username.value.str);
+    digest_init (&da);
+    digest_update (&da, username, username_len);
+    digest_update_with_colon (&da);
+    digest_update (&da, realm, realm_len);
+    digest_calc_hash (&da, hash1_bin);
+    mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1));
+    MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
+    if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size))
       return MHD_DAUTH_WRONG_USERNAME;
   }
   /* 'username' valid */
@@ -2096,7 +2226,10 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 #endif
     return MHD_DAUTH_WRONG_HEADER;   /* invalid nc value */
   }
+  if ((0 != max_nc) && (max_nc < nci))
+    return MHD_DAUTH_NONCE_STALE;    /* Too large 'nc' value */
   /* Got 'nc' digital value */
+
   /* Get 'nonce' with basic checks */
   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
@@ -2112,14 +2245,20 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 #endif
     return MHD_DAUTH_NONCE_WRONG;
   }
-  t = MHD_monotonic_msec_counter ();
-  /*
-   * First level vetting for the nonce validity: if the timestamp
-   * attached to the nonce exceeds `nonce_timeout', then the nonce is
-   * invalid.
-   */
-  if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
-    return MHD_DAUTH_NONCE_STALE; /* too old */
+
+  if (1)
+  {
+    uint64_t t;
+
+    t = MHD_monotonic_msec_counter ();
+    /*
+     * First level vetting for the nonce validity: if the timestamp
+     * attached to the nonce exceeds `nonce_timeout', then the nonce is
+     * invalid.
+     */
+    if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
+      return MHD_DAUTH_NONCE_STALE; /* too old */
+  }
   if (1)
   {
     enum MHD_CheckNonceNC_ nonce_nc_check;
@@ -2146,7 +2285,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     {
 #ifdef HAVE_MESSAGES
       MHD_DLOG (daemon,
-                _ ("Received nonce that technically valid, but was not "
+                _ ("Received nonce that was not "
                    "generated by MHD. This may indicate an attack 
attempt.\n"));
 #endif
       return MHD_DAUTH_NONCE_WRONG;
@@ -2159,9 +2298,9 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   /* ** Build H(A2) and check URI match in the header and in the request ** */
 
   /* Get 'uri' */
-  digest_init (da);
-  digest_update_str (da, connection->method);
-  digest_update_with_colon (da);
+  digest_init (&da);
+  digest_update_str (&da, connection->method);
+  digest_update_with_colon (&da);
 #if 0
   /* TODO: add support for "auth-int" */
   digest_update_str (da, hentity);
@@ -2172,37 +2311,37 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
 
-  digest_update (da, unq_copy.str, unq_copy.len);
+  digest_update (&da, unq_copy.str, unq_copy.len);
   /* The next check will modify copied URI string */
   if (! check_uri_match (connection, unq_copy.str, unq_copy.len))
     return MHD_DAUTH_WRONG_URI;
-  digest_calc_hash (da, hash2_bin);
+  digest_calc_hash (&da, hash2_bin);
   /* Got H(A2) */
 
   /* ** Build H(A1) ** */
-  if (NULL == digest)
+  if (NULL == userdigest)
   {
-    digest_init (da);
-    digest_update (da, (const uint8_t *) username, username_len);
-    digest_update_with_colon (da);
-    digest_update (da, (const uint8_t *) realm, realm_len);
-    digest_update_with_colon (da);
-    digest_update_str (da, password);
-    digest_calc_hash (da, hash1_bin);
+    digest_init (&da);
+    digest_update (&da, (const uint8_t *) username, username_len);
+    digest_update_with_colon (&da);
+    digest_update (&da, (const uint8_t *) realm, realm_len);
+    digest_update_with_colon (&da);
+    digest_update_str (&da, password);
+    digest_calc_hash (&da, hash1_bin);
   }
   /* TODO: support '-sess' versions */
   /* Got H(A1) */
 
   /* **  Check 'response' ** */
 
-  digest_init (da);
+  digest_init (&da);
   /* Update digest with H(A1) */
   mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1));
-  if (NULL == digest)
+  if (NULL == userdigest)
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
   else
-    MHD_bin_to_hex (digest, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
+    MHD_bin_to_hex (userdigest, digest_size, tmp1);
+  digest_update (&da, (const uint8_t *) tmp1, digest_size * 2);
 
   /* H(A1) is not needed anymore, reuse the buffer.
    * Use hash1_bin for the client's 'response' decoded to binary form. */
@@ -2214,46 +2353,46 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_RESPONSE_WRONG;
 
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'nonce' text value */
   unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'nc' text value */
   unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'cnonce' value */
   unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with 'qop' value */
   unq_res = get_unquoted_param (&params->qop, tmp1, ptmp2, &tmp2_size,
                                 &unquoted);
   if (_MHD_UNQ_OK != unq_res)
     return MHD_DAUTH_ERROR;
-  digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
+  digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len);
   /* Update digest with ':' */
-  digest_update_with_colon (da);
+  digest_update_with_colon (&da);
   /* Update digest with H(A2) */
   MHD_bin_to_hex (hash2_bin, digest_size, tmp1);
-  digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
+  digest_update (&da, (const uint8_t *) tmp1, digest_size * 2);
 
   /* H(A2) is not needed anymore, reuse the buffer.
    * Use hash2_bin for the calculated response in binary form */
-  digest_calc_hash (da, hash2_bin);
+  digest_calc_hash (&da, hash2_bin);
 
   if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
     return MHD_DAUTH_RESPONSE_WRONG;
@@ -2270,7 +2409,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                    connection->headers_received,
                    realm,
                    realm_len,
-                   da,
+                   &da,
                    tmp1);
 
   if (! is_param_equal (&params->nonce, tmp1,
@@ -2285,37 +2424,46 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 /**
  * Authenticates the authorization header sent by the client
  *
- * @param connection The MHD connection structure
- * @param[in,out] da digest algorithm to use for checking (written to as
- *         part of the calculations, but the values left in the struct
- *         are not actually expected to be useful for the caller)
- * @param realm The realm presented to the client
- * @param username The username needs to be authenticated
- * @param password The password used in the authentication
- * @param digest An optional binary hash
- *     of the precalculated hash value "username:realm:password"
- *     (must contain "digest_get_size(da)" bytes or be NULL)
- * @param nonce_timeout The amount of time for a nonce to be
- *      invalid in seconds
+ * @param connection the MHD connection structure
+ * @param realm the realm presented to the client
+ * @param username the username needs to be authenticated
+ * @param password the password used in the authentication
+ * @param userdigest the optional precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *            #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithms to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         error code otherwise.
  * @ingroup authentication
  */
 static enum MHD_DigestAuthResult
 digest_auth_check_all (struct MHD_Connection *connection,
-                       struct DigestAlgorithm *da,
                        const char *realm,
                        const char *username,
                        const char *password,
-                       const uint8_t *digest,
-                       unsigned int nonce_timeout)
+                       const uint8_t *userdigest,
+                       unsigned int nonce_timeout,
+                       uint32_t max_nc,
+                       enum MHD_DigestAuthMultiQOP mqop,
+                       enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
   enum MHD_DigestAuthResult res;
   char *buf;
 
   buf = NULL;
-  res = digest_auth_check_all_inner (connection, da, realm, username, password,
-                                     digest, nonce_timeout, &buf);
+  res = digest_auth_check_all_inner (connection, realm, username, password,
+                                     userdigest,
+                                     nonce_timeout,
+                                     max_nc, mqop, malgo3,
+                                     &buf);
   if (NULL != buf)
     free (buf);
 
@@ -2365,10 +2513,17 @@ MHD_digest_auth_check (struct MHD_Connection 
*connection,
  * @param username the username needs to be authenticated
  * @param password the password used in the authentication
  * @param nonce_timeout the nonce validity duration in seconds
- * @param algo the digest algorithms allowed for verification
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097513
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
@@ -2377,87 +2532,84 @@ MHD_digest_auth_check3 (struct MHD_Connection 
*connection,
                         const char *username,
                         const char *password,
                         unsigned int nonce_timeout,
-                        enum MHD_DigestAuthAlgorithm algo)
+                        uint32_t max_nc,
+                        enum MHD_DigestAuthMultiQOP mqop,
+                        enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
-  struct DigestAlgorithm da;
-
   mhd_assert (NULL != password);
 
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
-
   return digest_auth_check_all (connection,
-                                &da,
                                 realm,
                                 username,
                                 password,
                                 NULL,
-                                nonce_timeout);
+                                nonce_timeout,
+                                max_nc,
+                                mqop,
+                                malgo3);
 }
 
 
 /**
- * Authenticates the authorization header sent by the client.
+ * Authenticates the authorization header sent by the client by using
+ * hash of "username:realm:password".
  *
  * @param connection the MHD connection structure
- * @param realm the realm to be used for authorization of the client
+ * @param realm the realm presented to the client
  * @param username the username needs to be authenticated
- * @param digest the pointer to the binary digest for the precalculated hash
- *        value "username:realm:password" with specified @a algo
- * @param digest_size the number of bytes in @a digest (the size must match
- *        @a algo!)
- * @param nonce_timeout the nonce validity duration in seconds
- * @param algo digest algorithms allowed for verification
+ * @param userdigest the precalculated binary hash of the string
+ *                   "username:realm:password"
+ * @param userdigest_size the size of the @a userdigest in bytes, must match 
the
+ *                        hashing algorithm (see #MHD_MD5_DIGEST_SIZE,
+ *                        #MHD_SHA256_DIGEST_SIZE)
+ * @param nonce_timeout the period of seconds since nonce generation, when
+ *                      the nonce is recognised as valid and not stale.
+ * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc
+ *               exceeds the specified value then MHD_DAUTH_NONCE_STALE is
+ *               returned;
+ *               zero for no limit
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based
+ *               algorithms cannot be used at the same time for this function
+ *               as @a userdigest_size must match specified algorithm
  * @return #MHD_DAUTH_OK if authenticated,
  *         the error code otherwise
- * @note Available since #MHD_VERSION 0x00097513
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_DigestAuthResult
 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection,
                                const char *realm,
                                const char *username,
-                               const uint8_t *digest,
-                               size_t digest_size,
+                               const void *userdigest,
+                               size_t userdigest_size,
                                unsigned int nonce_timeout,
-                               enum MHD_DigestAuthAlgorithm algo)
+                               uint32_t max_nc,
+                               enum MHD_DigestAuthMultiQOP mqop,
+                               enum MHD_DigestAuthMultiAlgo3 malgo3)
 {
-  struct DigestAlgorithm da;
-
-  mhd_assert (NULL != digest);
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
-
-  if (digest_get_size (&da) != digest_size)
-    MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */
+  if (((unsigned int) (MHD_DIGEST_BASE_ALGO_MD5
+                       | MHD_DIGEST_BASE_ALGO_SHA256)) ==
+      (((unsigned int) malgo3) & (MHD_DIGEST_BASE_ALGO_MD5
+                                  | MHD_DIGEST_BASE_ALGO_SHA256)))
+    MHD_PANIC (_ ("Wrong 'malgo3' value, both MD5 and SHA-256 specified, "
+                  "API violation"));
+
+  if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) !=
+      userdigest_size)
+    MHD_PANIC (_ ("Wrong 'userdigest_size' value, not matching 'malgo3, "
+                  "API violation"));
 
   return digest_auth_check_all (connection,
-                                &da,
                                 realm,
                                 username,
                                 NULL,
-                                digest,
-                                nonce_timeout);
+                                (const uint8_t *) userdigest,
+                                nonce_timeout,
+                                max_nc,
+                                mqop,
+                                malgo3);
 }
 
 
@@ -2486,12 +2638,24 @@ MHD_digest_auth_check2 (struct MHD_Connection 
*connection,
                         enum MHD_DigestAuthAlgorithm algo)
 {
   enum MHD_DigestAuthResult res;
+  enum MHD_DigestAuthMultiAlgo3 malgo3;
+
+  if (MHD_DIGEST_ALG_AUTO == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else if (MHD_DIGEST_ALG_MD5 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
+
   res = MHD_digest_auth_check3 (connection,
                                 realm,
                                 username,
                                 password,
                                 nonce_timeout,
-                                algo);
+                                0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
   else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
@@ -2530,6 +2694,16 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection 
*connection,
                                enum MHD_DigestAuthAlgorithm algo)
 {
   enum MHD_DigestAuthResult res;
+  enum MHD_DigestAuthMultiAlgo3 malgo3;
+
+  if (MHD_DIGEST_ALG_AUTO == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else if (MHD_DIGEST_ALG_MD5 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
 
   res = MHD_digest_auth_check_digest3 (connection,
                                        realm,
@@ -2537,7 +2711,8 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection 
*connection,
                                        digest,
                                        digest_size,
                                        nonce_timeout,
-                                       algo);
+                                       0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                       malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
   else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
@@ -2585,47 +2760,94 @@ MHD_digest_auth_check_digest (struct MHD_Connection 
*connection,
 /**
  * Queues a response to request authentication from the client
  *
- * @param connection The MHD connection structure
+ * This function modifies provided @a response. The @a response must not be
+ * reused and should be destroyed (by #MHD_destroy_response()) after call of
+ * this function.
+ *
+ * @param connection the MHD connection structure
  * @param realm the realm presented to the client
- * @param opaque string to user for opaque value
- * @param response reply to send; should contain the "access denied"
- *        body; note that this function will set the "WWW Authenticate"
- *        header and that the caller should not do this; the NULL is tolerated
- * @param signal_stale #MHD_YES if the nonce is stale to add
- *        'stale=true' to the authentication header
- * @param algo digest algorithm to use
+ * @param opaque the string for opaque value, can be NULL, but NULL is
+ *               not recommended for better compatibility with clients
+ * @param domain the optional space-separated list of URIs for which the
+ *               same authorisation could be used, URIs can be in form
+ *               "path-absolute" (the path for the same host with initial 
slash)
+ *               or in form "absolute-URI" (the full path with protocol), in
+ *               any case client may assume that any URI which starts with
+ *               any of specified URI is in the same "protection space";
+ *               could be NULL (clients typically assume that the same
+ *               credentials could be used for any URI on the same host)
+ * @param response the reply to send; should contain the "access denied"
+ *                 body; note that this function sets the "WWW Authenticate"
+ *                 header and that the caller should not do this;
+ *                 the NULL is tolerated
+ * @param signal_stale set to #MHD_YES if the nonce is stale to add 
'stale=true'
+ *                     to the authentication header, this instructs the client
+ *                     to retry immediately with the new nonce and the same
+ *                     credentials, without asking user for the new password
+ * @param mqop the QOP to use, currently the only allowed value is
+ *             #MHD_DIGEST_AUTH_MULT_QOP_AUTH
+ * @param malgo3 digest algorithm to use, if several algorithms are specified
+ *               then MD5 is used (if allowed)
+ * @param userhash_support if set to non-zero value (#MHD_YES) then support of
+ *                         userhash is indicated, the client may provide
+ *                         hash("username:realm") instead of username in
+ *                         clear text; note that client is allowed to provide
+ *                         the username in cleartext even if this parameter set
+ *                         to non-zero
+ * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is
+ *                    added, indicating for the client that UTF-8 encoding
+ *                    is preferred
  * @return #MHD_YES on success, #MHD_NO otherwise
- * @note Available since #MHD_VERSION 0x00096200
+ * @note Available since #MHD_VERSION 0x00097526
  * @ingroup authentication
  */
 _MHD_EXTERN enum MHD_Result
-MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
-                               const char *realm,
-                               const char *opaque,
-                               struct MHD_Response *response,
-                               int signal_stale,
-                               enum MHD_DigestAuthAlgorithm algo)
+MHD_queue_auth_required_response3 (struct MHD_Connection *connection,
+                                   const char *realm,
+                                   const char *opaque,
+                                   const char *domain,
+                                   struct MHD_Response *response,
+                                   int signal_stale,
+                                   enum MHD_DigestAuthMultiQOP mqop,
+                                   enum MHD_DigestAuthMultiAlgo3 malgo3,
+                                   int userhash_support,
+                                   int prefer_utf8)
 {
-  enum MHD_Result ret;
-  int hlen;
-
+  static const char prefix_realm[] = "realm=\"";
+  static const char prefix_qop[] = "qop=\"";
+  static const char prefix_algo[] = "algorithm=";
+  static const char prefix_nonce[] = "nonce=\"";
+  static const char prefix_opaque[] = "opaque=\"";
+  static const char prefix_domain[] = "domain=\"";
+  static const char str_charset[] = "charset=UTF-8";
+  static const char str_userhash[] = "userhash=true";
+  static const char str_stale[] = "stale=true";
+  enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */
+  size_t realm_len;
+  size_t opaque_len;
+  size_t domain_len;
+  size_t buf_size;
+  char *buf;
+  size_t p; /* The position in the buffer */
   struct DigestAlgorithm da;
 
-  if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
-  {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
-  }
-  else if (MHD_DIGEST_ALG_SHA256 == algo)
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))
   {
-    if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256))
-      MHD_PANIC (_ ("Error initialising hash algorithm.\n"));
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'session' algorithms are not supported.\n"));
+#endif /* HAVE_MESSAGES */
+    return MHD_NO;
   }
+  if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_MD5;
+  else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
+    s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256;
   else
-    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
+    MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
 
-  if (NULL == response)
-    return MHD_NO;
+  if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop)
+    MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
 
   if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
   {
@@ -2636,89 +2858,256 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection 
*connection,
     return MHD_NO;
   }
 
-  if (1)
-  {
-    char nonce[NONCE_STD_LEN (MAX_DIGEST) + 1];
+  if (! digest_setup (&da, get_base_digest_algo (s_algo)))
+    MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
 
-    /* VLA_CHECK_LEN_DIGEST (digest_get_size (&da)); */
-    if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce))
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (connection->daemon,
-                _ ("Could not register nonce. Client's requests with this "
-                   "nonce will be always 'stale'. Probably clients' requests "
-                   "are too intensive.\n"));
-#else  /* ! HAVE_MESSAGES */
-      (void) 0;
-#endif /* ! HAVE_MESSAGES */
-    }
-    /* Building the authentication header */
-    hlen = MHD_snprintf_ (NULL,
-                          0,
-                          "Digest 
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
-                          realm,
-                          nonce,
-                          opaque,
-                          digest_get_algo_name (&da),
-                          signal_stale
-                          ? ",stale=\"true\""
-                          : "");
-    if (hlen > 0)
-    {
-      char *header;
+  /* Calculate required size */
+  buf_size = 0;
+  /* 'Digest ' */
+  buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */
+  /* 'realm="xxxx", ' */
+  realm_len = strlen (realm);
+  if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len)
+    return MHD_NO;
+  if ((NULL != memchr (realm, '\r', realm_len)) ||
+      (NULL != memchr (realm, '\n', realm_len)))
+    return MHD_NO;
+  buf_size += realm_len * 2; /* Quoting may double the size */
+  /* 'qop="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */
+  buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
+  /* 'algorithm="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
+  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+    buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+    buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+  else
+    mhd_assert (0);
+  /* 'nonce="xxxx", ' */
+  buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
+  buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */
+  /* 'opaque="xxxx", ' */
+  if (NULL != opaque)
+  {
+    buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */
+    opaque_len = strlen (opaque);
+    if ((NULL != memchr (opaque, '\r', opaque_len)) ||
+        (NULL != memchr (opaque, '\n', opaque_len)))
+      return MHD_NO;
+    buf_size += opaque_len * 2; /* Quoting may double the size */
+  }
+  else
+    opaque_len = 0;
+  /* 'domain="xxxx", ' */
+  if (NULL != domain)
+  {
+    buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */
+    domain_len = strlen (domain);
+    if ((NULL != memchr (domain, '\r', domain_len)) ||
+        (NULL != memchr (domain, '\n', domain_len)))
+      return MHD_NO;
+    buf_size += domain_len * 2; /* Quoting may double the size */
+  }
+  else
+    domain_len = 0;
+  /* 'charset=UTF-8' */
+  if (MHD_NO != prefer_utf8)
+    buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */
+  /* 'userhash=true' */
+  if (MHD_NO != userhash_support)
+    buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */
+  /* 'stale=true' */
+  if (MHD_NO != signal_stale)
+    buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */
+
+  /* The calculated length is for string ended with ", ". One character will
+   * be used for zero-termination, the last one will not be used. */
+
+  /* Allocate the buffer */
+  buf = malloc (buf_size);
+  if (NULL == buf)
+    return MHD_NO;
 
-      header = MHD_calloc_ (1,
-                            (size_t) hlen + 1);
-      if (NULL == header)
-      {
+  /* Build the challenge string */
+  p = 0;
+  /* 'Digest: ' */
+  memcpy (buf + p, _MHD_AUTH_DIGEST_BASE,
+          MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE));
+  p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
+  buf[p++] = ' ';
+  /* 'realm="xxxx", ' */
+  memcpy (buf + p, prefix_realm,
+          MHD_STATICSTR_LEN_ (prefix_realm));
+  p += MHD_STATICSTR_LEN_ (prefix_realm);
+  mhd_assert ((buf_size - p) >= (realm_len * 2));
+  p += MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'qop="xxxx", ' */
+  memcpy (buf + p, prefix_qop,
+          MHD_STATICSTR_LEN_ (prefix_qop));
+  p += MHD_STATICSTR_LEN_ (prefix_qop);
+  memcpy (buf + p, MHD_TOKEN_AUTH_,
+          MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_));
+  p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'algorithm="xxxx", ' */
+  memcpy (buf + p, prefix_algo,
+          MHD_STATICSTR_LEN_ (prefix_algo));
+  p += MHD_STATICSTR_LEN_ (prefix_algo);
+  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+  {
+    memcpy (buf + p, _MHD_MD5_TOKEN,
+            MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN));
+    p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+  }
+  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+  {
+    memcpy (buf + p, _MHD_SHA256_TOKEN,
+            MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN));
+    p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+  }
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'nonce="xxxx", ' */
+  memcpy (buf + p, prefix_nonce,
+          MHD_STATICSTR_LEN_ (prefix_nonce));
+  p += MHD_STATICSTR_LEN_ (prefix_nonce);
+  mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (&da))));
+  if (! calculate_add_nonce_with_retry (connection, realm, &da, buf + p))
+  {
 #ifdef HAVE_MESSAGES
-        MHD_DLOG (connection->daemon,
-                  _ ("Failed to allocate memory for auth response header.\n"));
+    MHD_DLOG (connection->daemon,
+              _ ("Could not register nonce. Client's requests with this "
+                 "nonce will be always 'stale'. Probably clients' requests "
+                 "are too intensive.\n"));
 #endif /* HAVE_MESSAGES */
-        return MHD_NO;
-      }
-
-      if (MHD_snprintf_ (header,
-                         (size_t) hlen + 1,
-                         "Digest 
realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
-                         realm,
-                         nonce,
-                         opaque,
-                         digest_get_algo_name (&da),
-                         signal_stale
-                         ? ",stale=\"true\""
-                         : "") == hlen)
-        ret = MHD_add_response_header (response,
-                                       MHD_HTTP_HEADER_WWW_AUTHENTICATE,
-                                       header);
-      else
-        ret = MHD_NO;
-#if 0
-      if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...))
-        ret = MHD_add_response_header (response,
-                                       MHD_HTTP_HEADER_CONNECTION,
-                                       "close");
-#endif
-      free (header);
-    }
-    else
-      ret = MHD_NO;
+    (void) 0; /* Mute compiler warning for builds without messages */
   }
-
-  if (MHD_NO != ret)
+  p += NONCE_STD_LEN (digest_get_size (&da));
+  buf[p++] = '\"';
+  buf[p++] = ',';
+  buf[p++] = ' ';
+  /* 'opaque="xxxx", ' */
+  if (NULL != opaque)
   {
-    ret = MHD_queue_response (connection,
-                              MHD_HTTP_UNAUTHORIZED,
-                              response);
+    memcpy (buf + p, prefix_opaque,
+            MHD_STATICSTR_LEN_ (prefix_opaque));
+    p += MHD_STATICSTR_LEN_ (prefix_opaque);
+    mhd_assert ((buf_size - p) >= (opaque_len * 2));
+    p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p);
+    buf[p++] = '\"';
+    buf[p++] = ',';
+    buf[p++] = ' ';
   }
-  else
+  /* 'domain="xxxx", ' */
+  if (NULL != domain)
+  {
+    memcpy (buf + p, prefix_domain,
+            MHD_STATICSTR_LEN_ (prefix_domain));
+    p += MHD_STATICSTR_LEN_ (prefix_domain);
+    mhd_assert ((buf_size - p) >= (domain_len * 2));
+    p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p);
+    buf[p++] = '\"';
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'charset=UTF-8' */
+  if (MHD_NO != prefer_utf8)
+  {
+    memcpy (buf + p, str_charset,
+            MHD_STATICSTR_LEN_ (str_charset));
+    p += MHD_STATICSTR_LEN_ (str_charset);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'userhash=true' */
+  if (MHD_NO != userhash_support)
+  {
+    memcpy (buf + p, str_userhash,
+            MHD_STATICSTR_LEN_ (str_userhash));
+    p += MHD_STATICSTR_LEN_ (str_userhash);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  /* 'stale=true' */
+  if (MHD_NO != signal_stale)
+  {
+    memcpy (buf + p, str_stale,
+            MHD_STATICSTR_LEN_ (str_stale));
+    p += MHD_STATICSTR_LEN_ (str_stale);
+    buf[p++] = ',';
+    buf[p++] = ' ';
+  }
+  mhd_assert (buf_size >= p);
+  /* The build string ends with ", ". Replace comma with zero-termination. */
+  --p;
+  buf[--p] = 0;
+
+  if (! MHD_add_response_entry_no_check_ (response, MHD_HEADER_KIND,
+                                          MHD_HTTP_HEADER_WWW_AUTHENTICATE,
+                                          MHD_STATICSTR_LEN_ ( \
+                                            MHD_HTTP_HEADER_WWW_AUTHENTICATE),
+                                          buf, p))
   {
 #ifdef HAVE_MESSAGES
     MHD_DLOG (connection->daemon,
               _ ("Failed to add Digest auth header.\n"));
 #endif /* HAVE_MESSAGES */
+    free (buf);
+    return MHD_NO;
   }
-  return ret;
+  free (buf);
+
+  return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
+}
+
+
+/**
+ * Queues a response to request authentication from the client
+ *
+ * @param connection The MHD connection structure
+ * @param realm the realm presented to the client
+ * @param opaque string to user for opaque value
+ * @param response reply to send; should contain the "access denied"
+ *        body; note that this function will set the "WWW Authenticate"
+ *        header and that the caller should not do this; the NULL is tolerated
+ * @param signal_stale #MHD_YES if the nonce is stale to add
+ *        'stale=true' to the authentication header
+ * @param algo digest algorithm to use
+ * @return #MHD_YES on success, #MHD_NO otherwise
+ * @note Available since #MHD_VERSION 0x00096200
+ * @ingroup authentication
+ */
+_MHD_EXTERN enum MHD_Result
+MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
+                               const char *realm,
+                               const char *opaque,
+                               struct MHD_Response *response,
+                               int signal_stale,
+                               enum MHD_DigestAuthAlgorithm algo)
+{
+  enum MHD_DigestAuthMultiAlgo3 algo3;
+
+  if (MHD_DIGEST_ALG_MD5 == algo)
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5;
+  else if (MHD_DIGEST_ALG_SHA256 == algo)
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256;
+  else if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo))
+    algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION;
+  else
+    MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
+
+  return MHD_queue_auth_required_response3 (connection, realm, opaque,
+                                            NULL, response, signal_stale,
+                                            MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                            algo3,
+                                            0, 0);
 }
 
 
diff --git a/src/microhttpd/gen_auth.c b/src/microhttpd/gen_auth.c
index 67bd9dc6..71a1a742 100644
--- a/src/microhttpd/gen_auth.c
+++ b/src/microhttpd/gen_auth.c
@@ -296,14 +296,13 @@ parse_dauth_params (const char *str,
   {
     if (userhash.quoted)
       pdauth->userhash =
-        MHD_str_equal_caseless_quoted_bin_n (userhash.value.str,
-                                             userhash.value.len,
-                                             "true",
-                                             MHD_STATICSTR_LEN_ ("true"));
+        MHD_str_equal_caseless_quoted_s_bin_n (userhash.value.str, \
+                                               userhash.value.len, \
+                                               "true");
     else
       pdauth->userhash =
-        (MHD_STATICSTR_LEN_ ("true") == userhash.value.len) &&
-        (0 == memcmp (str, userhash.value.str, userhash.value.len));
+        MHD_str_equal_caseless_s_bin_n_ ("true", userhash.value.str, \
+                                         userhash.value.len);
 
   }
   else
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c
index 3bdd2a59..cffb859a 100644
--- a/src/microhttpd/response.c
+++ b/src/microhttpd/response.c
@@ -144,6 +144,103 @@
   } \
 } while (0)
 
+/**
+ * Add a header or footer line to the response without checking.
+ *
+ * It is assumed that parameters are correct.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header to add, does not need to be zero-terminated
+ * @param header_len the length of the @a header
+ * @param content value to add, does not need to be zero-terminated
+ * @param content_len the length of the @a content
+ * @return false on error (like out-of-memory),
+ *         true if succeed
+ */
+bool
+MHD_add_response_entry_no_check_ (struct MHD_Response *response,
+                                  enum MHD_ValueKind kind,
+                                  const char *header,
+                                  size_t header_len,
+                                  const char *content,
+                                  size_t content_len)
+{
+  struct MHD_HTTP_Res_Header *hdr;
+
+  mhd_assert (0 != header_len);
+  mhd_assert (0 != content_len);
+  if (NULL == (hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Res_Header))))
+    return false;
+  hdr->header = malloc (header_len + 1);
+  if (NULL != hdr->header)
+  {
+    memcpy (hdr->header, header, header_len);
+    hdr->header[header_len] = 0;
+    hdr->header_size = header_len;
+
+    hdr->value = malloc (content_len + 1);
+    if (NULL != hdr->value)
+    {
+      memcpy (hdr->value, content, content_len);
+      hdr->value[content_len] = 0;
+      hdr->value_size = content_len;
+
+      hdr->kind = kind;
+      _MHD_insert_header_last (response, hdr);
+
+      return true; /* Success exit point */
+    }
+    free (hdr->header);
+  }
+  free (hdr);
+
+  return false; /* Failure exit point */
+}
+
+
+/**
+ * Add a header or footer line to the response.
+ *
+ * @param header the header to add, does not need to be zero-terminated
+ * @param header_len the length of the @a header
+ * @param content value to add, does not need to be zero-terminated
+ * @param content_len the length of the @a content
+ * @return false on error (out-of-memory, invalid header or content),
+ *         true if succeed
+ */
+static bool
+add_response_entry_n (struct MHD_Response *response,
+                      enum MHD_ValueKind kind,
+                      const char *header,
+                      size_t header_len,
+                      const char *content,
+                      size_t content_len)
+{
+  if (NULL == response)
+    return false;
+  if (0 == header_len)
+    return false;
+  if (0 == content_len)
+    return false;
+  if (NULL != memchr (header, '\t', header_len))
+    return false;
+  if (NULL != memchr (header, ' ', header_len))
+    return false;
+  if (NULL != memchr (header, '\r', header_len))
+    return false;
+  if (NULL != memchr (header, '\n', header_len))
+    return false;
+  if (NULL != memchr (content, '\r', content_len))
+    return false;
+  if (NULL != memchr (content, '\n', content_len))
+    return false;
+
+  return MHD_add_response_entry_no_check_ (response, kind, header, header_len,
+                                           content, content_len);
+}
+
+
 /**
  * Add a header or footer line to the response.
  *
@@ -159,38 +256,17 @@ add_response_entry (struct MHD_Response *response,
                     const char *header,
                     const char *content)
 {
-  struct MHD_HTTP_Res_Header *hdr;
+  size_t header_len;
+  size_t content_len;
 
-  if ( (NULL == response) ||
-       (NULL == header) ||
-       (NULL == content) ||
-       (0 == header[0]) ||
-       (0 == content[0]) ||
-       (NULL != strchr (header, '\t')) ||
-       (NULL != strchr (header, ' ')) ||
-       (NULL != strchr (header, '\r')) ||
-       (NULL != strchr (header, '\n')) ||
-       (NULL != strchr (content, '\r')) ||
-       (NULL != strchr (content, '\n')) )
+  if (NULL == content)
     return MHD_NO;
-  if (NULL == (hdr = MHD_calloc_ (1, sizeof (struct MHD_HTTP_Res_Header))))
-    return MHD_NO;
-  if (NULL == (hdr->header = strdup (header)))
-  {
-    free (hdr);
-    return MHD_NO;
-  }
-  hdr->header_size = strlen (header);
-  if (NULL == (hdr->value = strdup (content)))
-  {
-    free (hdr->header);
-    free (hdr);
-    return MHD_NO;
-  }
-  hdr->value_size = strlen (content);
-  hdr->kind = kind;
-  _MHD_insert_header_last (response, hdr);
-  return MHD_YES;
+
+  header_len = strlen (header);
+  content_len = strlen (content);
+  return add_response_entry_n (response, kind, header,
+                               header_len, content,
+                               content_len) ? MHD_YES : MHD_NO;
 }
 
 
diff --git a/src/microhttpd/response.h b/src/microhttpd/response.h
index ba6c4ab8..4f9c32c9 100644
--- a/src/microhttpd/response.h
+++ b/src/microhttpd/response.h
@@ -73,4 +73,26 @@ MHD_get_response_element_n_ (struct MHD_Response *response,
                              const char *key,
                              size_t key_len);
 
+/**
+ * Add a header or footer line to the response without checking.
+ *
+ * It is assumed that parameters are correct.
+ *
+ * @param response response to add a header to
+ * @param kind header or footer
+ * @param header the header to add, does not need to be zero-terminated
+ * @param header_len the length of the @a header
+ * @param content value to add, does not need to be zero-terminated
+ * @param content_len the length of the @a content
+ * @return false on error (like out-of-memory),
+ *         true if succeed
+ */
+bool
+MHD_add_response_entry_no_check_ (struct MHD_Response *response,
+                                  enum MHD_ValueKind kind,
+                                  const char *header,
+                                  size_t header_len,
+                                  const char *content,
+                                  size_t content_len);
+
 #endif
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index e23f52c9..26e59bfc 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -157,3 +157,8 @@ core
 /test_basicauth_preauth_oldapi
 /test_digestauth_emu_ext
 /test_digestauth_emu_ext_oldapi
+/test_digestauth2
+/test_digestauth2_oldapi
+/test_digestauth2_userhash
+/test_digestauth2_sha256
+/test_digestauth2_sha256_userhash
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index e46bbb2d..e8297b05 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -169,7 +169,12 @@ THREAD_ONLY_TESTS += \
 
 check_PROGRAMS += \
   test_digestauth_emu_ext \
-  test_digestauth_emu_ext_oldapi
+  test_digestauth_emu_ext_oldapi \
+  test_digestauth2 \
+  test_digestauth2_oldapi \
+  test_digestauth2_userhash \
+  test_digestauth2_sha256 \
+  test_digestauth2_sha256_userhash
 endif
 
 if HEAVY_TESTS
@@ -279,6 +284,21 @@ test_digestauth_emu_ext_SOURCES = \
 test_digestauth_emu_ext_oldapi_SOURCES = \
   test_digestauth_emu_ext.c
 
+test_digestauth2_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_userhash_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_sha256_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_sha256_userhash_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
 test_get_iovec_SOURCES = \
   test_get_iovec.c mhd_has_in_name.h
 
@@ -509,4 +529,3 @@ test_tricky_url_SOURCES = \
 
 test_tricky_header2_SOURCES = \
   test_tricky.c mhd_has_in_name.h mhd_has_param.h
-  
\ No newline at end of file
diff --git a/src/testcurl/test_digestauth.c b/src/testcurl/test_digestauth.c
index 872eebc4..61b5b647 100644
--- a/src/testcurl/test_digestauth.c
+++ b/src/testcurl/test_digestauth.c
@@ -216,7 +216,7 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 /* Could be increased to facilitate debugging */
 #define TIMEOUTS_VAL 5
 
-#define MHD_URI_BASE_PATH "/bar%20foo?key=value"
+#define MHD_URI_BASE_PATH "/bar%20foo%20without%20args"
 
 #define PAGE \
   "<html><head><title>libmicrohttpd demo</title></head><body>Access 
granted</body></html>"
@@ -265,7 +265,7 @@ ahc_echo (void *cls,
   const char *password = "testpass";
   const char *realm = "test@example.com";
   enum MHD_Result ret;
-  enum MHD_DigestAuthResult ret_e;
+  int ret_i;
   static int already_called_marker;
   (void) cls; (void) url;                         /* Unused. Silent compiler 
warning. */
   (void) method; (void) version; (void) upload_data; /* Unused. Silent 
compiler warning. */
@@ -297,14 +297,14 @@ ahc_echo (void *cls,
     MHD_destroy_response (response);
     return ret;
   }
-  ret_e = MHD_digest_auth_check3 (connection,
+  ret_i = MHD_digest_auth_check2 (connection,
                                   realm,
                                   username,
                                   password,
                                   300,
                                   MHD_DIGEST_ALG_MD5);
   MHD_free (username);
-  if (ret_e != MHD_DAUTH_OK)
+  if (ret_i != MHD_YES)
   {
     response = MHD_create_response_from_buffer_static (strlen (DENIED),
                                                        DENIED);
@@ -314,7 +314,7 @@ ahc_echo (void *cls,
                                          realm,
                                          MY_OPAQUE,
                                          response,
-                                         (MHD_DAUTH_NONCE_STALE == ret_e) ?
+                                         (MHD_INVALID_NONCE == ret_i) ?
                                          MHD_YES : MHD_NO,
                                          MHD_DIGEST_ALG_MD5);
     if (MHD_YES != ret)
@@ -337,7 +337,7 @@ ahc_echo (void *cls,
 
 
 static CURL *
-setupCURL (void *cbc, int port)
+setupCURL (void *cbc, uint16_t port)
 {
   CURL *c;
   char url[512];
@@ -348,7 +348,7 @@ setupCURL (void *cbc, int port)
     /* A workaround for some old libcurl versions, which ignore the specified
      * port by CURLOPT_PORT when digest authorisation is used. */
     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
-                    "http://127.0.0.1:%d%s";, port, MHD_URI_BASE_PATH);
+                    "http://127.0.0.1:%d%s";, (int) port, MHD_URI_BASE_PATH);
     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
       externalErrorExitDesc ("Cannot form request URL");
   }
@@ -387,15 +387,15 @@ setupCURL (void *cbc, int port)
 }
 
 
-static int
-testDigestAuth ()
+static unsigned int
+testDigestAuth (void)
 {
   CURL *c;
   struct MHD_Daemon *d;
   struct CBC cbc;
   char buf[2048];
   char rnd[8];
-  int port;
+  uint16_t port;
 #ifndef WINDOWS
   int fd;
   size_t len;
@@ -446,7 +446,7 @@ testDigestAuth ()
 #endif
   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
                         port, NULL, NULL,
-                        &ahc_echo, PAGE,
+                        &ahc_echo, NULL,
                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
                         MHD_OPTION_NONCE_NC_SIZE, 300,
                         MHD_OPTION_END);
@@ -461,7 +461,7 @@ testDigestAuth ()
     if ( (NULL == dinfo) ||
          (0 == dinfo->port) )
       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
-    port = (int) dinfo->port;
+    port = dinfo->port;
   }
   c = setupCURL (&cbc, port);
 
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c
new file mode 100644
index 00000000..0d6b844a
--- /dev/null
+++ b/src/testcurl/test_digestauth2.c
@@ -0,0 +1,1159 @@
+/*
+     This file is part of libmicrohttpd
+     Copyright (C) 2010 Christian Grothoff
+     Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
+
+     libmicrohttpd is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     libmicrohttpd is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with libmicrohttpd; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @file test_digest2.c
+ * @brief  Testcase for MHD Digest Authorisation
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "MHD_config.h"
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <unistd.h>
+#else
+#include <wincrypt.h>
+#endif
+
+#include "mhd_has_param.h"
+#include "mhd_has_in_name.h"
+
+#ifndef MHD_STATICSTR_LEN_
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! MHD_STATICSTR_LEN_ */
+
+#ifndef CURL_VERSION_BITS
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
+#endif /* ! CURL_VERSION_BITS */
+#ifndef CURL_AT_LEAST_VERSION
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+  (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+#endif /* ! CURL_AT_LEAST_VERSION */
+
+#ifndef _MHD_INSTRMACRO
+/* Quoted macro parameter */
+#define _MHD_INSTRMACRO(a) #a
+#endif /* ! _MHD_INSTRMACRO */
+#ifndef _MHD_STRMACRO
+/* Quoted expanded macro parameter */
+#define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
+#endif /* ! _MHD_STRMACRO */
+
+#if defined(HAVE___FUNC__)
+#define externalErrorExit(ignore) \
+    _externalErrorExit_func(NULL, __func__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+    _externalErrorExit_func(errDesc, __func__, __LINE__)
+#define libcurlErrorExit(ignore) \
+    _libcurlErrorExit_func(NULL, __func__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+    _libcurlErrorExit_func(errDesc, __func__, __LINE__)
+#define mhdErrorExit(ignore) \
+    _mhdErrorExit_func(NULL, __func__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+    _mhdErrorExit_func(errDesc, __func__, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+    _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
+                        __func__, __LINE__)
+#elif defined(HAVE___FUNCTION__)
+#define externalErrorExit(ignore) \
+    _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+    _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define libcurlErrorExit(ignore) \
+    _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+    _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define mhdErrorExit(ignore) \
+    _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+    _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+    _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
+                        __FUNCTION__, __LINE__)
+#else
+#define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+  _externalErrorExit_func(errDesc, NULL, __LINE__)
+#define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
+#define libcurlErrorExitDesc(errDesc) \
+  _libcurlErrorExit_func(errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
+#define checkCURLE_OK(libcurlcall) \
+  _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, 
__LINE__)
+#endif
+
+
+_MHD_NORETURN static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int 
lineNum)
+{
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "System or external library call failed");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
+
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+  fflush (stderr);
+  exit (99);
+}
+
+
+/* Not actually used in this test */
+static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
+
+_MHD_NORETURN static void
+_libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "CURL library call failed");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
+
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+  if (0 != libcurl_errbuf[0])
+    fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
+
+  fflush (stderr);
+  exit (99);
+}
+
+
+_MHD_NORETURN static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+  fflush (stdout);
+  if ((NULL != errDesc) && (0 != errDesc[0]))
+    fprintf (stderr, "%s", errDesc);
+  else
+    fprintf (stderr, "MHD unexpected error");
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
+
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+  fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+
+  fflush (stderr);
+  exit (8);
+}
+
+
+#if 0
+/* Function unused in this test */
+static void
+_checkCURLE_OK_func (CURLcode code, const char *curlFunc,
+                     const char *funcName, int lineNum)
+{
+  if (CURLE_OK == code)
+    return;
+
+  fflush (stdout);
+  if ((NULL != curlFunc) && (0 != curlFunc[0]))
+    fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
+             curl_easy_strerror (code));
+  else
+    fprintf (stderr, "libcurl function call resulted in '%s'",
+             curl_easy_strerror (code));
+  if ((NULL != funcName) && (0 != funcName[0]))
+    fprintf (stderr, " in %s", funcName);
+  if (0 < lineNum)
+    fprintf (stderr, " at line %d", lineNum);
+
+  fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+           strerror (errno));
+  if (0 != libcurl_errbuf[0])
+    fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
+
+  fflush (stderr);
+  exit (9);
+}
+
+
+#endif
+
+
+/* Could be increased to facilitate debugging */
+#define TIMEOUTS_VAL 10
+
+#define MHD_URI_BASE_PATH "/bar%20foo?key=value"
+
+#define REALM "TestRealm"
+#define USERNAME1 "test_user"
+/* The hex form of MD5("test_user:TestRealm") */
+#define USERHASH1_MD5_HEX "c53c601503ff176f18f623725fba4281"
+#define USERHASH1_MD5_BIN 0xc5, 0x3c, 0x60, 0x15, 0x03, 0xff, 0x17, 0x6f, \
+  0x18, 0xf6, 0x23, 0x72, 0x5f, 0xba, 0x42, 0x81
+/* The hex form of SHA-256("test_user:TestRealm") */
+#define USERHASH1_SHA256_HEX \
+  "090c7e06b77d6614cf5fe6cafa004d2e5f8fb36ba45a0e35eacb2eb7728f34de"
+/* The binary form of SHA-256("test_user:TestRealm") */
+#define USERHASH1_SHA256_BIN 0x09, 0x0c, 0x7e, 0x06, 0xb7, 0x7d, 0x66, 0x14, \
+  0xcf, 0x5f, 0xe6, 0xca, 0xfa, 0x00, 0x4d, 0x2e, 0x5f, 0x8f, 0xb3, 0x6b, \
+  0xa4, 0x5a, 0x0e, 0x35, 0xea, 0xcb, 0x2e, 0xb7, 0x72, 0x8f, 0x34, 0xde
+/* "titkos szuperügynök" in UTF-8 */
+#define USERNAME2 "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
+/* percent-encoded username */
+#define USERNAME2_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k"
+#define PASSWORD_VALUE "test pass"
+#define OPAQUE_VALUE "opaque+content" /* Base64 character set */
+
+
+#define PAGE \
+  "<html><head><title>libmicrohttpd demo page</title>" \
+  "</head><body>Access granted</body></html>"
+
+#define DENIED \
+  "<html><head><title>libmicrohttpd - Access denied</title>" \
+  "</head><body>Access denied</body></html>"
+
+/* Global parameters */
+static int verbose;
+static int test_oldapi;
+static int test_userhash;
+static int test_sha256;
+static int curl_uses_usehash;
+
+/* Static helper variables */
+static const char userhash1_md5_hex[] = USERHASH1_MD5_HEX;
+static const uint8_t userhash1_md5_bin[] = { USERHASH1_MD5_BIN };
+static const char userhash1_sha256_hex[] = USERHASH1_SHA256_HEX;
+static const uint8_t userhash1_sha256_bin[] = { USERHASH1_SHA256_BIN };
+static const char *userhash1_hex;
+static size_t userhash1_hex_len;
+static const uint8_t *userhash1_bin;
+static const char *username_ptr;
+
+static void
+test_global_init (void)
+{
+  libcurl_errbuf[0] = 0;
+
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+    externalErrorExit ();
+
+  username_ptr = USERNAME1;
+  if (! test_sha256)
+  {
+    userhash1_hex = userhash1_md5_hex;
+    userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_md5_hex);
+    userhash1_bin = userhash1_md5_bin;
+    if ((userhash1_hex_len / 2) != \
+        (sizeof(userhash1_md5_bin) / sizeof(userhash1_md5_bin[0])))
+      externalErrorExitDesc ("Wrong size of the 'userhash1_md5_bin' array");
+  }
+  else
+  {
+    userhash1_hex = userhash1_sha256_hex;
+    userhash1_hex_len = MHD_STATICSTR_LEN_ (userhash1_sha256_hex);
+    userhash1_bin = userhash1_sha256_bin;
+    if ((userhash1_hex_len / 2) != \
+        (sizeof(userhash1_sha256_bin)   \
+         / sizeof(userhash1_sha256_bin[0])))
+      externalErrorExitDesc ("Wrong size of the 'userhash1_sha256_bin' array");
+  }
+}
+
+
+static void
+test_global_cleanup (void)
+{
+  curl_global_cleanup ();
+}
+
+
+static int
+gen_good_rnd (void *rnd_buf, size_t rnd_buf_size)
+{
+  if (1024 < rnd_buf_size)
+    externalErrorExitDesc ("Too large amount of random data " \
+                           "is requested");
+#ifndef _WIN32
+  if (1)
+  {
+    const int urand_fd = open ("/dev/urandom", O_RDONLY);
+    if (0 < urand_fd)
+    {
+      size_t pos = 0;
+      do
+      {
+        ssize_t res = read (urand_fd,
+                            ((uint8_t *) rnd_buf) + pos, rnd_buf_size - pos);
+        if (0 > res)
+          break;
+        pos += (size_t) res;
+      } while (rnd_buf_size > pos);
+      (void) close (urand_fd);
+
+      if (rnd_buf_size == pos)
+        return ! 0; /* Success */
+    }
+  }
+#else  /* _WIN32 */
+  if (1)
+  {
+    HCRYPTPROV cpr_hndl;
+    if (CryptAcquireContextW (&cpr_hndl, NULL, NULL, PROV_RSA_FULL,
+                              CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+    {
+      if (CryptGenRandom (cpr_hndl, (DWORD) rnd_buf_size, (BYTE *) rnd_buf))
+      {
+        (void) CryptReleaseContext (cpr_hndl, 0);
+        return ! 0; /* Success */
+      }
+      (void) CryptReleaseContext (cpr_hndl, 0);
+    }
+  }
+#endif /* _WIN32 */
+  return 0; /* Failure */
+}
+
+
+struct CBC
+{
+  char *buf;
+  size_t pos;
+  size_t size;
+};
+
+
+static size_t
+copyBuffer (void *ptr,
+            size_t size,
+            size_t nmemb,
+            void *ctx)
+{
+  struct CBC *cbc = ctx;
+
+  if (cbc->pos + size * nmemb > cbc->size)
+    mhdErrorExitDesc ("Wrong too large data");       /* overflow */
+  memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+  cbc->pos += size * nmemb;
+  return size * nmemb;
+}
+
+
+static enum MHD_Result
+ahc_echo (void *cls,
+          struct MHD_Connection *connection,
+          const char *url,
+          const char *method,
+          const char *version,
+          const char *upload_data,
+          size_t *upload_data_size,
+          void **req_cls)
+{
+  struct MHD_Response *response;
+  enum MHD_Result res;
+  static int already_called_marker;
+  (void) cls; (void) url;         /* Unused. Silent compiler warning. */
+  (void) method; (void) version; (void) upload_data; /* Unused. Silent 
compiler warning. */
+  (void) upload_data_size;        /* Unused. Silent compiler warning. */
+
+  if (&already_called_marker != *req_cls)
+  { /* Called for the first time, request not fully read yet */
+    *req_cls = &already_called_marker;
+    /* Wait for complete request */
+    return MHD_YES;
+  }
+
+  if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+    mhdErrorExitDesc ("Unexpected HTTP method");
+
+  response = NULL;
+  if (! test_oldapi)
+  {
+    struct MHD_DigestAuthInfo *dinfo;
+    const enum MHD_DigestAuthAlgo3 algo3 =
+      test_sha256 ? MHD_DIGEST_AUTH_ALGO3_SHA256 : MHD_DIGEST_AUTH_ALGO3_MD5;
+
+    dinfo = MHD_digest_auth_get_request_info3 (connection);
+    if (NULL != dinfo)
+    {
+      /* Got any kind of Digest response. Check it, it must be valid */
+      struct MHD_DigestAuthUsernameInfo *uname;
+      enum MHD_DigestAuthResult check_res;
+
+      if (NULL == dinfo->username)
+        mhdErrorExitDesc ("'username' is NULL");
+      if (curl_uses_usehash)
+      {
+        if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != dinfo->uname_type)
+        {
+          fprintf (stderr, "Unexpected 'uname_type'.\n"
+                   "Expected: %d\tRecieved: %d. ",
+                   (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH,
+                   (int) dinfo->uname_type);
+          mhdErrorExitDesc ("Wrong 'uname_type'");
+        }
+        else if (dinfo->username_len != userhash1_hex_len)
+        {
+          fprintf (stderr, "'username_len' does not match.\n"
+                   "Expected: %u\tRecieved: %u. ",
+                   (unsigned) userhash1_hex_len,
+                   (unsigned) dinfo->username_len);
+          mhdErrorExitDesc ("Wrong 'username_len'");
+        }
+        else if (0 != memcmp (dinfo->username, userhash1_hex,
+                              dinfo->username_len))
+        {
+          fprintf (stderr, "'username' does not match.\n"
+                   "Expected: '%s'\tRecieved: '%.*s'. ",
+                   userhash1_hex,
+                   (int) dinfo->username_len,
+                   dinfo->username);
+          mhdErrorExitDesc ("Wrong 'username'");
+        }
+        else if (NULL == dinfo->userhash_bin)
+          mhdErrorExitDesc ("'userhash_bin' is NULL");
+        else if (0 != memcmp (dinfo->userhash_bin, userhash1_bin,
+                              dinfo->username_len / 2))
+          mhdErrorExitDesc ("Wrong 'userhash_bin'");
+      }
+      else
+      {
+        if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != dinfo->uname_type)
+        {
+          fprintf (stderr, "Unexpected 'uname_type'.\n"
+                   "Expected: %d\tRecieved: %d. ",
+                   (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD,
+                   (int) dinfo->uname_type);
+          mhdErrorExitDesc ("Wrong 'uname_type'");
+        }
+        else if (dinfo->username_len != strlen (username_ptr))
+        {
+          fprintf (stderr, "'username_len' does not match.\n"
+                   "Expected: %u\tRecieved: %u. ",
+                   (unsigned) strlen (username_ptr),
+                   (unsigned) dinfo->username_len);
+          mhdErrorExitDesc ("Wrong 'username_len'");
+        }
+        else if (0 != memcmp (dinfo->username, username_ptr,
+                              dinfo->username_len))
+        {
+          fprintf (stderr, "'username' does not match.\n"
+                   "Expected: '%s'\tRecieved: '%.*s'. ",
+                   username_ptr,
+                   (int) dinfo->username_len,
+                   dinfo->username);
+          mhdErrorExitDesc ("Wrong 'username'");
+        }
+        else if (NULL != dinfo->userhash_bin)
+          mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
+      }
+      if (algo3 != dinfo->algo)
+      {
+        fprintf (stderr, "Unexpected 'algo'.\n"
+                 "Expected: %d\tRecieved: %d. ",
+                 (int) algo3,
+                 (int) dinfo->algo);
+        mhdErrorExitDesc ("Wrong 'algo'");
+      }
+      else if (10 >= dinfo->cnonce_len)
+      {
+        fprintf (stderr, "Unexpected small 'cnonce_len': %ld. ",
+                 (long) dinfo->cnonce_len);
+        mhdErrorExitDesc ("Wrong 'cnonce_len'");
+      }
+      else if (NULL == dinfo->opaque)
+        mhdErrorExitDesc ("'opaque' is NULL");
+      else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE))
+      {
+        fprintf (stderr, "'opaque_len' does not match.\n"
+                 "Expected: %u\tRecieved: %u. ",
+                 (unsigned) MHD_STATICSTR_LEN_ (OPAQUE_VALUE),
+                 (unsigned) dinfo->opaque_len);
+        mhdErrorExitDesc ("Wrong 'opaque_len'");
+      }
+      else if (0 != memcmp (dinfo->opaque, OPAQUE_VALUE, dinfo->opaque_len))
+      {
+        fprintf (stderr, "'opaque' does not match.\n"
+                 "Expected: '%s'\tRecieved: '%.*s'. ",
+                 OPAQUE_VALUE,
+                 (int) dinfo->opaque_len,
+                 dinfo->opaque);
+        mhdErrorExitDesc ("Wrong 'opaque'");
+      }
+      else if (MHD_DIGEST_AUTH_QOP_AUTH != dinfo->qop)
+      {
+        fprintf (stderr, "Unexpected 'qop'.\n"
+                 "Expected: %d\tRecieved: %d. ",
+                 (int) MHD_DIGEST_AUTH_QOP_AUTH,
+                 (int) dinfo->qop);
+        mhdErrorExitDesc ("Wrong 'qop'");
+      }
+      else if (NULL == dinfo->realm)
+        mhdErrorExitDesc ("'realm' is NULL");
+      else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM))
+      {
+        fprintf (stderr, "'realm_len' does not match.\n"
+                 "Expected: %u\tRecieved: %u. ",
+                 (unsigned) MHD_STATICSTR_LEN_ (REALM),
+                 (unsigned) dinfo->realm_len);
+        mhdErrorExitDesc ("Wrong 'realm_len'");
+      }
+      else if (0 != memcmp (dinfo->realm, REALM, dinfo->realm_len))
+      {
+        fprintf (stderr, "'realm' does not match.\n"
+                 "Expected: '%s'\tRecieved: '%.*s'. ",
+                 OPAQUE_VALUE,
+                 (int) dinfo->realm_len,
+                 dinfo->realm);
+        mhdErrorExitDesc ("Wrong 'realm'");
+      }
+      MHD_free (dinfo);
+
+      uname = MHD_digest_auth_get_username3 (connection);
+      if (NULL == uname)
+        mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
+      else if (NULL == uname->username)
+        mhdErrorExitDesc ("'username' is NULL");
+      if (curl_uses_usehash)
+      {
+        if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH != uname->uname_type)
+        {
+          fprintf (stderr, "Unexpected 'uname_type'.\n"
+                   "Expected: %d\tRecieved: %d. ",
+                   (int) MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH,
+                   (int) uname->uname_type);
+          mhdErrorExitDesc ("Wrong 'uname_type'");
+        }
+        else if (uname->username_len != userhash1_hex_len)
+        {
+          fprintf (stderr, "'username_len' does not match.\n"
+                   "Expected: %u\tRecieved: %u. ",
+                   (unsigned) userhash1_hex_len,
+                   (unsigned) uname->username_len);
+          mhdErrorExitDesc ("Wrong 'username_len'");
+        }
+        else if (0 != memcmp (uname->username, userhash1_hex,
+                              uname->username_len))
+        {
+          fprintf (stderr, "'username' does not match.\n"
+                   "Expected: '%s'\tRecieved: '%.*s'. ",
+                   userhash1_hex,
+                   (int) uname->username_len,
+                   uname->username);
+          mhdErrorExitDesc ("Wrong 'username'");
+        }
+        else if (NULL == uname->userhash_bin)
+          mhdErrorExitDesc ("'userhash_bin' is NULL");
+        else if (0 != memcmp (uname->userhash_bin, userhash1_bin,
+                              uname->username_len / 2))
+          mhdErrorExitDesc ("Wrong 'userhash_bin'");
+      }
+      else
+      {
+        if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname->uname_type)
+        {
+          fprintf (stderr, "Unexpected 'uname_type'.\n"
+                   "Expected: %d\tRecieved: %d. ",
+                   (int) MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD,
+                   (int) uname->uname_type);
+          mhdErrorExitDesc ("Wrong 'uname_type'");
+        }
+        else if (uname->username_len != strlen (username_ptr))
+        {
+          fprintf (stderr, "'username_len' does not match.\n"
+                   "Expected: %u\tRecieved: %u. ",
+                   (unsigned) strlen (username_ptr),
+                   (unsigned) uname->username_len);
+          mhdErrorExitDesc ("Wrong 'username_len'");
+        }
+        else if (0 != memcmp (uname->username, username_ptr,
+                              uname->username_len))
+        {
+          fprintf (stderr, "'username' does not match.\n"
+                   "Expected: '%s'\tRecieved: '%.*s'. ",
+                   username_ptr,
+                   (int) uname->username_len,
+                   uname->username);
+          mhdErrorExitDesc ("Wrong 'username'");
+        }
+        else if (NULL != uname->userhash_bin)
+          mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
+      }
+      MHD_free (uname);
+
+      check_res =
+        MHD_digest_auth_check3 (connection, REALM, username_ptr,
+                                PASSWORD_VALUE, 50 * TIMEOUTS_VAL,
+                                0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                (enum MHD_DigestAuthMultiAlgo3) algo3);
+
+      switch (check_res)
+      {
+      /* Valid result */
+      case MHD_DAUTH_OK:
+        if (verbose)
+          printf ("Got valid auth check result: MHD_DAUTH_OK.\n");
+        break;
+      /* Invalid results */
+      case MHD_DAUTH_NONCE_STALE:
+        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+                          "MHD_DAUTH_NONCE_STALE");
+        break;
+      case MHD_DAUTH_NONCE_WRONG:
+        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+                          "MHD_DAUTH_NONCE_WRONG");
+        break;
+      case MHD_DAUTH_ERROR:
+        externalErrorExitDesc ("General error returned " \
+                               "by 'MHD_digest_auth_check3()'");
+        break;
+      case MHD_DAUTH_WRONG_USERNAME:
+        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+                          "MHD_DAUTH_WRONG_USERNAME");
+        break;
+      case MHD_DAUTH_RESPONSE_WRONG:
+        mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
+                          "MHD_DAUTH_RESPONSE_WRONG");
+        break;
+      case MHD_DAUTH_WRONG_HEADER:
+      case MHD_DAUTH_WRONG_REALM:
+      case MHD_DAUTH_WRONG_URI:
+      case MHD_DAUTH_WRONG_QOP:
+      case MHD_DAUTH_WRONG_ALGO:
+      case MHD_DAUTH_TOO_LARGE:
+        fprintf (stderr, "'MHD_digest_auth_check3()' returned "
+                 "unexpected result: %d. ",
+                 check_res);
+        mhdErrorExitDesc ("Wrong returned code");
+        break;
+      default:
+        fprintf (stderr, "'MHD_digest_auth_check3()' returned "
+                 "impossible result code: %d. ",
+                 check_res);
+        mhdErrorExitDesc ("Impossible returned code");
+      }
+
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
+                                                (const void *) PAGE);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+
+      if (MHD_YES !=
+          MHD_queue_response (connection, MHD_HTTP_OK, response))
+        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+    }
+    else
+    {
+      /* No Digest auth header */
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
+                                                (const void *) DENIED);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+      res =
+        MHD_queue_auth_required_response3 (connection, REALM, OPAQUE_VALUE,
+                                           "/", response, 0,
+                                           MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                           (enum MHD_DigestAuthMultiAlgo3) 
algo3,
+                                           test_userhash, 0);
+      if (MHD_YES != res)
+        mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed");
+    }
+  }
+  else
+  {
+    /* Use old API */
+    char *username;
+    int check_res;
+
+    username = MHD_digest_auth_get_username (connection);
+    if (NULL != username)
+    { /* Has a valid username in header */
+      if (0 != strcmp (username, username_ptr))
+      {
+        fprintf (stderr, "'username' does not match.\n"
+                 "Expected: '%s'\tRecieved: '%s'. ",
+                 username_ptr,
+                 username);
+        mhdErrorExitDesc ("Wrong 'username'");
+      }
+      MHD_free (username);
+
+      check_res = MHD_digest_auth_check (connection, REALM, username_ptr,
+                                         PASSWORD_VALUE, 50 * TIMEOUTS_VAL);
+
+      if (MHD_YES != check_res)
+      {
+        fprintf (stderr, "'MHD_digest_auth_check()' returned unexpected"
+                 " result: %d. ", check_res);
+        mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check()' result");
+      }
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
+                                                (const void *) PAGE);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+
+      if (MHD_YES !=
+          MHD_queue_response (connection, MHD_HTTP_OK, response))
+        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+    }
+    else
+    {
+      /* Has no valid username in header */
+      response =
+        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
+                                                (const void *) DENIED);
+      if (NULL == response)
+        mhdErrorExitDesc ("Response creation failed");
+
+      res = MHD_queue_auth_fail_response (connection, REALM, OPAQUE_VALUE,
+                                          response, 0);
+      if (MHD_YES != res)
+        mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
+    }
+  }
+  MHD_destroy_response (response);
+  return MHD_YES;
+}
+
+
+static CURL *
+setupCURL (void *cbc, int port)
+{
+  CURL *c;
+  char url[512];
+
+  if (1)
+  {
+    int res;
+    /* A workaround for some old libcurl versions, which ignore the specified
+     * port by CURLOPT_PORT when authorisation is used. */
+    res = snprintf (url, (sizeof(url) / sizeof(url[0])),
+                    "http://127.0.0.1:%d%s";, port, MHD_URI_BASE_PATH);
+    if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
+      externalErrorExitDesc ("Cannot form request URL");
+  }
+
+  c = curl_easy_init ();
+  if (NULL == c)
+    libcurlErrorExitDesc ("curl_easy_init() failed");
+
+  if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
+                                     &copyBuffer)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
+                                     ((long) TIMEOUTS_VAL))) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
+                                     CURL_HTTP_VERSION_1_1)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
+                                     ((long) TIMEOUTS_VAL))) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
+                                     libcurl_errbuf)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH,
+                                     (long) CURLAUTH_DIGEST)) ||
+#if CURL_AT_LEAST_VERSION (7,19,1)
+      /* Need version 7.19.1 for separate username and password */
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERNAME, username_ptr)) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PASSWORD, PASSWORD_VALUE)) ||
+#endif /* CURL_AT_LEAST_VERSION(7,19,1) */
+#ifdef _DEBUG
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
+#endif /* _DEBUG */
+#if CURL_AT_LEAST_VERSION (7, 19, 4)
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
+#endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
+#if CURL_AT_LEAST_VERSION (7, 45, 0)
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
+#endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
+    libcurlErrorExitDesc ("curl_easy_setopt() failed");
+  return c;
+}
+
+
+static CURLcode
+performQueryExternal (struct MHD_Daemon *d, CURL *c)
+{
+  CURLM *multi;
+  time_t start;
+  struct timeval tv;
+  CURLcode ret;
+
+  ret = CURLE_FAILED_INIT; /* will be replaced with real result */
+  multi = NULL;
+  multi = curl_multi_init ();
+  if (multi == NULL)
+    libcurlErrorExitDesc ("curl_multi_init() failed");
+  if (CURLM_OK != curl_multi_add_handle (multi, c))
+    libcurlErrorExitDesc ("curl_multi_add_handle() failed");
+
+  start = time (NULL);
+  while (time (NULL) - start <= TIMEOUTS_VAL)
+  {
+    fd_set rs;
+    fd_set ws;
+    fd_set es;
+    MHD_socket maxMhdSk;
+    int maxCurlSk;
+    int running;
+
+    maxMhdSk = MHD_INVALID_SOCKET;
+    maxCurlSk = -1;
+    FD_ZERO (&rs);
+    FD_ZERO (&ws);
+    FD_ZERO (&es);
+    if (NULL != multi)
+    {
+      curl_multi_perform (multi, &running);
+      if (0 == running)
+      {
+        struct CURLMsg *msg;
+        int msgLeft;
+        int totalMsgs = 0;
+        do
+        {
+          msg = curl_multi_info_read (multi, &msgLeft);
+          if (NULL == msg)
+            libcurlErrorExitDesc ("curl_multi_info_read() failed");
+          totalMsgs++;
+          if (CURLMSG_DONE == msg->msg)
+            ret = msg->data.result;
+        } while (msgLeft > 0);
+        if (1 != totalMsgs)
+        {
+          fprintf (stderr,
+                   "curl_multi_info_read returned wrong "
+                   "number of results (%d).\n",
+                   totalMsgs);
+          externalErrorExit ();
+        }
+        curl_multi_remove_handle (multi, c);
+        curl_multi_cleanup (multi);
+        multi = NULL;
+      }
+      else
+      {
+        if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
+          libcurlErrorExitDesc ("curl_multi_fdset() failed");
+      }
+    }
+    if (NULL == multi)
+    { /* libcurl has finished, check whether MHD still needs to perform 
cleanup */
+      if (0 != MHD_get_timeout64s (d))
+        break; /* MHD finished as well */
+    }
+    if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
+      mhdErrorExitDesc ("MHD_get_fdset() failed");
+    tv.tv_sec = 0;
+    tv.tv_usec = 200000;
+#ifdef MHD_POSIX_SOCKETS
+    if (maxMhdSk > maxCurlSk)
+      maxCurlSk = maxMhdSk;
+#endif /* MHD_POSIX_SOCKETS */
+    if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
+    {
+#ifdef MHD_POSIX_SOCKETS
+      if (EINTR != errno)
+        externalErrorExitDesc ("Unexpected select() error");
+#else
+      if ((WSAEINVAL != WSAGetLastError ()) ||
+          (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
+        externalErrorExitDesc ("Unexpected select() error");
+      Sleep (200);
+#endif
+    }
+    if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
+      mhdErrorExitDesc ("MHD_run_from_select() failed");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Check request result
+ * @param curl_code the CURL easy return code
+ * @param pcbc the pointer struct CBC
+ * @return non-zero if success, zero if failed
+ */
+static unsigned int
+check_result (CURLcode curl_code, CURL *c, struct CBC *pcbc)
+{
+  long code;
+  if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
+    libcurlErrorExit ();
+
+  if (MHD_HTTP_OK != code)
+  {
+    fprintf (stderr, "Request returned wrong code: %ld.\n",
+             code);
+    return 0;
+  }
+
+  if (CURLE_OK != curl_code)
+  {
+    fflush (stdout);
+    if (0 != libcurl_errbuf[0])
+      fprintf (stderr, "Request failed. "
+               "libcurl error: '%s'.\n"
+               "libcurl error description: '%s'.\n",
+               curl_easy_strerror (curl_code),
+               libcurl_errbuf);
+    else
+      fprintf (stderr, "Request failed. "
+               "libcurl error: '%s'.\n",
+               curl_easy_strerror (curl_code));
+    fflush (stderr);
+    return 0;
+  }
+
+  if (pcbc->pos != MHD_STATICSTR_LEN_ (PAGE))
+  {
+    fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
+             (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
+             (unsigned) MHD_STATICSTR_LEN_ (PAGE));
+    mhdErrorExitDesc ("Wrong returned data length");
+  }
+  if (0 != memcmp (PAGE, pcbc->buf, pcbc->pos))
+  {
+    fprintf (stderr, "Got invalid response '%.*s'. ",
+             (int) pcbc->pos, pcbc->buf);
+    mhdErrorExitDesc ("Wrong returned data");
+  }
+  return 1;
+}
+
+
+static unsigned int
+testDigestAuth (void)
+{
+  struct MHD_Daemon *d;
+  uint16_t port;
+  uint8_t salt[8];
+  struct CBC cbc;
+  char buf[2048];
+  CURL *c;
+  int failed = 0;
+
+  if (! gen_good_rnd (salt, sizeof(salt)))
+  {
+    fprintf (stderr, "WARNING: the random buffer (used as salt value) is not "
+             "initialised completely, nonce generation may be "
+             "predictable in this test.\n");
+    fflush (stderr);
+  }
+
+  if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
+    port = 0;
+  else
+    port = 4210;
+
+  d = MHD_start_daemon (MHD_USE_ERROR_LOG,
+                        port, NULL, NULL,
+                        &ahc_echo, NULL,
+                        MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (salt), salt,
+                        MHD_OPTION_NONCE_NC_SIZE, 300,
+                        MHD_OPTION_END);
+  if (d == NULL)
+    return 1;
+  if (0 == port)
+  {
+    const union MHD_DaemonInfo *dinfo;
+
+    dinfo = MHD_get_daemon_info (d,
+                                 MHD_DAEMON_INFO_BIND_PORT);
+    if ( (NULL == dinfo) ||
+         (0 == dinfo->port) )
+      mhdErrorExitDesc ("MHD_get_daemon_info() failed");
+    port = dinfo->port;
+  }
+
+  /* First request */
+  cbc.buf = buf;
+  cbc.size = sizeof (buf);
+  cbc.pos = 0;
+  memset (cbc.buf, 0, cbc.size);
+  c = setupCURL (&cbc, port);
+  if (check_result (performQueryExternal (d, c), c, &cbc))
+  {
+    if (verbose)
+      printf ("Got expected response.\n");
+  }
+  else
+  {
+    fprintf (stderr, "Request FAILED.\n");
+    failed = 1;
+  }
+  curl_easy_cleanup (c);
+
+  MHD_stop_daemon (d);
+  return failed ? 1 : 0;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+#if ! CURL_AT_LEAST_VERSION (7,19,1)
+  (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+  /* Need version 7.19.1 for separate username and password */
+  fprintf (stderr, "Required libcurl version 7.19.1 at least"
+           " to run this test.\n");
+  return 77;
+#else  /* CURL_AT_LEAST_VERSION(7,19,1) */
+  unsigned int errorCount = 0;
+  const curl_version_info_data *const curl_info =
+    curl_version_info (CURLVERSION_NOW);
+  int curl_sspi;
+  (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+
+  /* Test type and test parameters */
+  verbose = ! (has_param (argc, argv, "-q") ||
+               has_param (argc, argv, "--quiet") ||
+               has_param (argc, argv, "-s") ||
+               has_param (argc, argv, "--silent"));
+  test_oldapi = has_in_name (argv[0], "_oldapi");
+  test_userhash = has_in_name (argv[0], "_userhash");
+  test_sha256 = has_in_name (argv[0], "_sha256");
+
+  if (test_oldapi)
+  { /* Wrong test types combination */
+    if (test_userhash || test_sha256)
+      return 99;
+  }
+
+  /* Curl version and known bugs checks */
+  curl_sspi = 0;
+#ifdef CURL_VERSION_SSPI
+  if (0 != (curl_info->features & CURL_VERSION_SSPI))
+    curl_sspi = 1;
+#endif /* CURL_VERSION_SSPI */
+
+  if ((CURL_VERSION_BITS (7,63,0) > curl_info->version_num) &&
+      (CURL_VERSION_BITS (7,62,0) <= curl_info->version_num) )
+  {
+    fprintf (stderr, "libcurl version 7.62.x has bug in processing"
+             "URI with GET argements for Digest Auth.\n");
+    fprintf (stderr, "This test with libcurl %u.%u.%u cannot be performed.\n",
+             0xFF & (curl_info->version_num >> 16),
+             0xFF & (curl_info->version_num >> 8),
+             0xFF & (curl_info->version_num >> 0));
+    return 77;
+  }
+  if (test_userhash)
+  {
+    if (curl_sspi)
+    {
+      printf ("WARNING: Windows SSPI API does not support 'userhash'.\n");
+      printf ("This test just checks Digest Auth compatibility with "
+              "the clients without 'userhash' support "
+              "when 'userhash=true' is specified by MHD.\n");
+      curl_uses_usehash = 0;
+    }
+    else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num)
+    {
+      printf ("WARNING: libcurl before version 7.57.0 does not "
+              "support 'userhash'.\n");
+      printf ("This test just checks Digest Auth compatibility with "
+              "libcurl version %u.%u.%u without 'userhash' support "
+              "when 'userhash=true' is specified by MHD.\n",
+              0xFF & (curl_info->version_num >> 16),
+              0xFF & (curl_info->version_num >> 8),
+              0xFF & (curl_info->version_num >> 0));
+      curl_uses_usehash = 0;
+    }
+    else if (CURL_VERSION_BITS (7,81,0) > curl_info->version_num)
+    {
+      fprintf (stderr, "Required libcurl version 7.81.0 at least"
+               " to run this test with userhash.\n");
+      fprintf (stderr, "This libcurl version %u.%u.%u has broken digest"
+               "calculation when userhash is used.\n",
+               0xFF & (curl_info->version_num >> 16),
+               0xFF & (curl_info->version_num >> 8),
+               0xFF & (curl_info->version_num >> 0));
+      return 77;
+    }
+    else
+      curl_uses_usehash = ! 0;
+  }
+  else
+    curl_uses_usehash = 0;
+
+  if (test_sha256)
+  {
+    if (curl_sspi)
+    {
+      fprintf (stderr, "Windows SSPI API does not support SHA-256 digests.\n");
+      return 77;
+    }
+    else if (CURL_VERSION_BITS (7,57,0) > curl_info->version_num)
+    {
+      fprintf (stderr, "Required libcurl version 7.57.0 at least"
+               " to run this test with SHA-256.\n");
+      fprintf (stderr, "This libcurl version %u.%u.%u "
+               "does not support SHA-256.\n",
+               0xFF & (curl_info->version_num >> 16),
+               0xFF & (curl_info->version_num >> 8),
+               0xFF & (curl_info->version_num >> 0));
+      return 77;
+    }
+  }
+
+  test_global_init ();
+
+  errorCount += testDigestAuth ();
+  if (errorCount != 0)
+    fprintf (stderr, "Error (code: %u)\n", errorCount);
+  test_global_cleanup ();
+  return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
+#endif /* CURL_AT_LEAST_VERSION(7,19,1) */
+}
diff --git a/src/testcurl/test_digestauth_concurrent.c 
b/src/testcurl/test_digestauth_concurrent.c
index 907f1eae..c80bf382 100644
--- a/src/testcurl/test_digestauth_concurrent.c
+++ b/src/testcurl/test_digestauth_concurrent.c
@@ -312,8 +312,9 @@ ahc_echo (void *cls,
                                   realm,
                                   username,
                                   password,
-                                  300,
-                                  MHD_DIGEST_ALG_MD5);
+                                  50 * TIMEOUTS_VAL,
+                                  0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                  MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
   MHD_free (username);
   if (ret_e != MHD_DAUTH_OK)
   {
@@ -348,7 +349,7 @@ ahc_echo (void *cls,
 
 
 static CURL *
-setupCURL (void *cbc, int port, char *errbuf)
+setupCURL (void *cbc, uint16_t port, char *errbuf)
 {
   CURL *c;
   char url[512];
@@ -359,7 +360,7 @@ setupCURL (void *cbc, int port, char *errbuf)
     /* A workaround for some old libcurl versions, which ignore the specified
      * port by CURLOPT_PORT when digest authorisation is used. */
     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
-                    "http://127.0.0.1:%d%s";, port, MHD_URI_BASE_PATH);
+                    "http://127.0.0.1:%d%s";, (int) port, MHD_URI_BASE_PATH);
     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
       externalErrorExitDesc ("Cannot form request URL");
   }
@@ -455,7 +456,7 @@ struct curlWokerInfo
   /**
    * The number of successful worker results
    */
-  volatile int success;
+  volatile unsigned int success;
 };
 
 
@@ -552,17 +553,17 @@ worker_func (void *param)
 
 #define CLIENT_BUF_SIZE 2048
 
-static int
+static unsigned int
 testDigestAuth (void)
 {
   struct MHD_Daemon *d;
   char rnd[8];
-  int port;
+  uint16_t port;
   size_t i;
   /* Run three workers in parallel so at least two workers would start within
    * the same monotonic clock second.*/
   struct curlWokerInfo workers[3];
-  int ret;
+  unsigned int ret;
 
   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
     port = 0;
@@ -573,7 +574,7 @@ testDigestAuth (void)
 
   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
                         port, NULL, NULL,
-                        &ahc_echo, PAGE,
+                        &ahc_echo, NULL,
                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
                         MHD_OPTION_NONCE_NC_SIZE, 300,
                         MHD_OPTION_THREAD_POOL_SIZE,
@@ -590,7 +591,7 @@ testDigestAuth (void)
     if ( (NULL == dinfo) ||
          (0 == dinfo->port) )
       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
-    port = (int) dinfo->port;
+    port = dinfo->port;
   }
 
   /* Initialise all workers */
@@ -645,6 +646,15 @@ main (int argc, char *const *argv)
 {
   unsigned int errorCount = 0;
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+#if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62)
+  if (1)
+  {
+    fprintf (stderr, "libcurl version 7.62.x has bug in processing"
+             "URI with GET argements for Digest Auth.\n");
+    fprintf (stderr, "This test cannot be performed.\n");
+    exit (77);
+  }
+#endif /* libcurl version 7.62.x */
 
   verbose = ! (has_param (argc, argv, "-q") ||
                has_param (argc, argv, "--quiet") ||
diff --git a/src/testcurl/test_digestauth_emu_ext.c 
b/src/testcurl/test_digestauth_emu_ext.c
index 740f43eb..468279ca 100644
--- a/src/testcurl/test_digestauth_emu_ext.c
+++ b/src/testcurl/test_digestauth_emu_ext.c
@@ -237,7 +237,7 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 #define USERNAME "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
 /* percent-encoded username */
 #define USERNAME_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k"
-#define PASSWORD "fake pass"
+#define PASSWORD_VALUE "fake pass"
 #define OPAQUE_VALUE "opaque-content"
 #define NONCE_EMU "badbadbadbadbadbadbadbadbadbadbadbadbadbadba"
 #define CNONCE_EMU "utututututututututututututututututututututs="
@@ -354,6 +354,14 @@ ahc_echo (void *cls,
     creds = MHD_digest_auth_get_username3 (connection);
     if (NULL == creds)
       mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
+    else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != creds->uname_type)
+    {
+      fprintf (stderr, "Unexpected 'uname_type'.\n"
+               "Expected: %d\tRecieved: %d. ",
+               (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
+               (int) creds->uname_type);
+      mhdErrorExitDesc ("Wrong 'uname_type'");
+    }
     else if (NULL == creds->username)
       mhdErrorExitDesc ("'username' is NULL");
     else if (creds->username_len != MHD_STATICSTR_LEN_ (USERNAME))
@@ -375,9 +383,12 @@ ahc_echo (void *cls,
     }
     else if (NULL != creds->userhash_bin)
       mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
-    else if (0 != creds->userhash_bin_size)
-      mhdErrorExitDesc ("'userhash_bin_size' is NOT zero");
-    else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != creds->uname_type)
+    MHD_free (creds);
+
+    dinfo = MHD_digest_auth_get_request_info3 (connection);
+    if (NULL == dinfo)
+      mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
+    else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != dinfo->uname_type)
     {
       fprintf (stderr, "Unexpected 'uname_type'.\n"
                "Expected: %d\tRecieved: %d. ",
@@ -385,11 +396,6 @@ ahc_echo (void *cls,
                (int) creds->uname_type);
       mhdErrorExitDesc ("Wrong 'uname_type'");
     }
-    MHD_free (creds);
-
-    dinfo = MHD_digest_auth_get_request_info3 (connection);
-    if (NULL == dinfo)
-      mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
     else if (NULL == dinfo->username)
       mhdErrorExitDesc ("'username' is NULL");
     else if (dinfo->username_len != MHD_STATICSTR_LEN_ (USERNAME))
@@ -411,16 +417,6 @@ ahc_echo (void *cls,
     }
     else if (NULL != dinfo->userhash_bin)
       mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
-    else if (0 != dinfo->userhash_bin_size)
-      mhdErrorExitDesc ("'userhash_bin_size' is NOT zero");
-    else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != dinfo->uname_type)
-    {
-      fprintf (stderr, "Unexpected 'uname_type'.\n"
-               "Expected: %d\tRecieved: %d. ",
-               (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
-               (int) dinfo->uname_type);
-      mhdErrorExitDesc ("Wrong 'uname_type'");
-    }
     else if (MHD_DIGEST_AUTH_ALGO3_MD5 != dinfo->algo)
     {
       fprintf (stderr, "Unexpected 'algo'.\n"
@@ -485,8 +481,11 @@ ahc_echo (void *cls,
     }
     MHD_free (dinfo);
 
-    check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME, PASSWORD,
-                                        300, MHD_DIGEST_ALG_MD5);
+    check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME,
+                                        PASSWORD_VALUE,
+                                        50 * TIMEOUTS_VAL,
+                                        0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
+                                        MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
 
     switch (check_res)
     {
@@ -563,7 +562,8 @@ ahc_echo (void *cls,
     }
     MHD_free (username);
 
-    check_res = MHD_digest_auth_check (connection, REALM, USERNAME, PASSWORD,
+    check_res = MHD_digest_auth_check (connection, REALM, USERNAME,
+                                       PASSWORD_VALUE,
                                        300);
 
     if (MHD_INVALID_NONCE != check_res)
diff --git a/src/testcurl/test_digestauth_sha256.c 
b/src/testcurl/test_digestauth_sha256.c
index b34dff9f..ffa1f4c7 100644
--- a/src/testcurl/test_digestauth_sha256.c
+++ b/src/testcurl/test_digestauth_sha256.c
@@ -94,7 +94,7 @@ ahc_echo (void *cls,
   const char *password = "testpass";
   const char *realm = "test@example.com";
   enum MHD_Result ret;
-  enum MHD_DigestAuthResult ret_e;
+  int ret_i;
   static int already_called_marker;
   (void) cls; (void) url;                         /* Unused. Silent compiler 
warning. */
   (void) method; (void) version; (void) upload_data; /* Unused. Silent 
compiler warning. */
@@ -122,14 +122,14 @@ ahc_echo (void *cls,
     MHD_destroy_response (response);
     return ret;
   }
-  ret_e = MHD_digest_auth_check3 (connection,
+  ret_i = MHD_digest_auth_check2 (connection,
                                   realm,
                                   username,
                                   password,
                                   300,
                                   MHD_DIGEST_ALG_SHA256);
   MHD_free (username);
-  if (ret_e != MHD_DAUTH_OK)
+  if (ret_i != MHD_YES)
   {
     response = MHD_create_response_from_buffer_static (strlen (DENIED),
                                                        DENIED);
@@ -139,7 +139,7 @@ ahc_echo (void *cls,
                                          realm,
                                          MY_OPAQUE,
                                          response,
-                                         (MHD_DAUTH_NONCE_STALE == ret_e) ?
+                                         (MHD_INVALID_NONCE == ret_i) ?
                                          MHD_YES : MHD_NO,
                                          MHD_DIGEST_ALG_SHA256);
     MHD_destroy_response (response);
@@ -155,8 +155,8 @@ ahc_echo (void *cls,
 }
 
 
-static int
-testDigestAuth ()
+static unsigned int
+testDigestAuth (void)
 {
   CURL *c;
   CURLcode errornum;
@@ -164,7 +164,7 @@ testDigestAuth ()
   struct CBC cbc;
   char buf[2048];
   char rnd[8];
-  int port;
+  uint16_t port;
   char url[128];
 #ifndef WINDOWS
   int fd;
@@ -239,7 +239,7 @@ testDigestAuth ()
 #endif
   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
                         port, NULL, NULL,
-                        &ahc_echo, PAGE,
+                        &ahc_echo, NULL,
                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
                         MHD_OPTION_NONCE_NC_SIZE, 300,
                         MHD_OPTION_END);
@@ -257,12 +257,12 @@ testDigestAuth ()
       MHD_stop_daemon (d);
       return 32;
     }
-    port = (int) dinfo->port;
+    port = dinfo->port;
   }
   snprintf (url,
             sizeof (url),
             "http://127.0.0.1:%d/bar%%20foo?key=value";,
-            port);
+            (int) port);
   c = curl_easy_init ();
   curl_easy_setopt (c, CURLOPT_URL, url);
   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
@@ -302,6 +302,15 @@ main (int argc, char *const *argv)
   unsigned int errorCount = 0;
   curl_version_info_data *d = curl_version_info (CURLVERSION_NOW);
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+#if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62)
+  if (1)
+  {
+    fprintf (stderr, "libcurl version 7.62.x has bug in processing"
+             "URI with GET argements for Digest Auth.\n");
+    fprintf (stderr, "This test cannot be performed.\n");
+    exit (77);
+  }
+#endif /* libcurl version 7.62.x */
 
 #ifdef CURL_VERSION_SSPI
   if (0 != (d->features & CURL_VERSION_SSPI))
diff --git a/src/testcurl/test_digestauth_with_arguments.c 
b/src/testcurl/test_digestauth_with_arguments.c
index 631169e8..a5a8fb10 100644
--- a/src/testcurl/test_digestauth_with_arguments.c
+++ b/src/testcurl/test_digestauth_with_arguments.c
@@ -87,7 +87,7 @@ ahc_echo (void *cls,
   const char *password = "testpass";
   const char *realm = "test@example.com";
   enum MHD_Result ret;
-  enum MHD_DigestAuthResult ret_e;
+  int ret_i;
   static int already_called_marker;
   (void) cls; (void) url;                         /* Unused. Silent compiler 
warning. */
   (void) method; (void) version; (void) upload_data; /* Unused. Silent 
compiler warning. */
@@ -114,24 +114,28 @@ ahc_echo (void *cls,
     MHD_destroy_response (response);
     return ret;
   }
-  ret_e = MHD_digest_auth_check3 (connection, realm,
+  ret_i = MHD_digest_auth_check2 (connection,
+                                  realm,
                                   username,
                                   password,
                                   300,
                                   MHD_DIGEST_ALG_MD5);
   MHD_free (username);
-  if (ret_e != MHD_DAUTH_OK)
+  if (ret_i != MHD_YES)
   {
     response = MHD_create_response_from_buffer_static (strlen (DENIED),
                                                        DENIED);
     if (NULL == response)
-      return MHD_NO;
-    ret = MHD_queue_auth_fail_response2 (connection, realm,
+      fprintf (stderr, "MHD_create_response_from_buffer() failed.\n");
+    ret = MHD_queue_auth_fail_response2 (connection,
+                                         realm,
                                          MY_OPAQUE,
                                          response,
-                                         (ret_e == MHD_DAUTH_NONCE_STALE) ?
+                                         (MHD_INVALID_NONCE == ret_i) ?
                                          MHD_YES : MHD_NO,
                                          MHD_DIGEST_ALG_MD5);
+    if (MHD_YES != ret)
+      fprintf (stderr, "MHD_queue_auth_fail_response2() failed.\n");
     MHD_destroy_response (response);
     return ret;
   }
@@ -143,8 +147,8 @@ ahc_echo (void *cls,
 }
 
 
-static int
-testDigestAuth ()
+static unsigned int
+testDigestAuth (void)
 {
   CURL *c;
   CURLcode errornum;
@@ -152,7 +156,7 @@ testDigestAuth ()
   struct CBC cbc;
   char buf[2048];
   char rnd[8];
-  int port;
+  uint16_t port;
   char url[128];
 #ifndef WINDOWS
   int fd;
@@ -216,7 +220,7 @@ testDigestAuth ()
   }
 #endif
   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
-                        port, NULL, NULL, &ahc_echo, PAGE,
+                        port, NULL, NULL, &ahc_echo, NULL,
                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
                         MHD_OPTION_NONCE_NC_SIZE, 300,
                         MHD_OPTION_END);
@@ -230,13 +234,13 @@ testDigestAuth ()
     {
       MHD_stop_daemon (d); return 32;
     }
-    port = (int) dinfo->port;
+    port = dinfo->port;
   }
   snprintf (url,
             sizeof (url),
             "http://127.0.0.1:%d/bar%%20foo?";
             "key=value&more=even%%20more&empty&=no_key&&same=one&&same=two",
-            port);
+            (int) port);
   c = curl_easy_init ();
   curl_easy_setopt (c, CURLOPT_URL, url);
   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
@@ -275,6 +279,15 @@ main (int argc, char *const *argv)
 {
   unsigned int errorCount = 0;
   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
+#if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62)
+  if (1)
+  {
+    fprintf (stderr, "libcurl version 7.62.x has bug in processing"
+             "URI with GET argements for Digest Auth.\n");
+    fprintf (stderr, "This test cannot be performed.\n");
+    exit (77);
+  }
+#endif /* libcurl version 7.62.x */
 
 #ifdef MHD_HTTPS_REQUIRE_GRYPT
 #ifdef HAVE_GCRYPT_H

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]