Evgeny Grin (Karlson2k) vor 1 Jahr
Ursprung
Commit
b81de0bd22
63 geänderte Dateien mit 6438 neuen und 1727 gelöschten Zeilen
  1. 9 1
      configure.ac
  2. 202 75
      src/include/microhttpd2.h
  3. 15 11
      src/mhd2/action.c
  4. 180 0
      src/mhd2/conn_data_process.c
  5. 12 1
      src/mhd2/conn_data_process.h
  6. 157 0
      src/mhd2/conn_data_recv.c
  7. 50 0
      src/mhd2/conn_data_recv.h
  8. 279 0
      src/mhd2/conn_data_send.c
  9. 50 0
      src/mhd2/conn_data_send.h
  10. 4 1
      src/mhd2/daemon_add_conn.c
  11. 2 2
      src/mhd2/daemon_create.c
  12. 10 0
      src/mhd2/daemon_funcs.h
  13. 10 8
      src/mhd2/daemon_start.c
  14. 0 303
      src/mhd2/data_process.c
  15. 15 4
      src/mhd2/events_process.c
  16. 2 2
      src/mhd2/http_prot_ver.h
  17. 2 2
      src/mhd2/http_status_str.c
  18. 1 1
      src/mhd2/http_status_str.h
  19. 73 0
      src/mhd2/mhd_atomic_counter.c
  20. 203 0
      src/mhd2/mhd_atomic_counter.h
  21. 81 2
      src/mhd2/mhd_connection.h
  22. 30 8
      src/mhd2/mhd_daemon.h
  23. 15 6
      src/mhd2/mhd_iovec.h
  24. 1 1
      src/mhd2/mhd_itc.h
  25. 4 0
      src/mhd2/mhd_lib_init.c
  26. 177 0
      src/mhd2/mhd_limits.h
  27. 1 4
      src/mhd2/mhd_mempool.c
  28. 39 56
      src/mhd2/mhd_recv.c
  29. 2 1
      src/mhd2/mhd_recv.h
  30. 39 12
      src/mhd2/mhd_reply.h
  31. 11 30
      src/mhd2/mhd_request.h
  32. 17 18
      src/mhd2/mhd_response.h
  33. 373 435
      src/mhd2/mhd_send.c
  34. 75 64
      src/mhd2/mhd_send.h
  35. 78 0
      src/mhd2/mhd_socket_error.c
  36. 50 13
      src/mhd2/mhd_socket_error.h
  37. 53 7
      src/mhd2/mhd_sockets_macros.h
  38. 182 305
      src/mhd2/mhd_str.c
  39. 75 73
      src/mhd2/mhd_str.h
  40. 70 0
      src/mhd2/request_funcs.c
  41. 78 0
      src/mhd2/request_funcs.h
  42. 68 0
      src/mhd2/request_get_value.c
  43. 56 0
      src/mhd2/request_get_value.h
  44. 54 21
      src/mhd2/response_add_header.c
  45. 44 0
      src/mhd2/response_add_header.h
  46. 88 0
      src/mhd2/response_destroy.c
  47. 43 0
      src/mhd2/response_destroy.h
  48. 28 54
      src/mhd2/response_from.c
  49. 8 0
      src/mhd2/response_from.h
  50. 44 6
      src/mhd2/response_funcs.c
  51. 20 7
      src/mhd2/response_funcs.h
  52. 14 26
      src/mhd2/response_set_options.c
  53. 35 0
      src/mhd2/response_set_options.h
  54. 371 0
      src/mhd2/stream_funcs.c
  55. 86 0
      src/mhd2/stream_funcs.h
  56. 2149 0
      src/mhd2/stream_process_request.c
  57. 81 0
      src/mhd2/stream_process_request.h
  58. 359 0
      src/mhd2/stream_process_states.c
  59. 48 0
      src/mhd2/stream_process_states.h
  60. 11 0
      src/mhd2/sys_base_types.h
  61. 59 0
      src/mhd2/sys_ip_headers.h
  62. 45 11
      src/mhd2/sys_sockets_headers.h
  63. 0 156
      src/microhttpd/mhd_limits.h

+ 9 - 1
configure.ac

@@ -530,7 +530,7 @@ AC_CHECK_HEADERS([sys/socket.h sys/select.h netinet/in_systm.h netinet/in.h \
 # Check for other optional headers
 AC_CHECK_HEADERS([sys/msg.h sys/mman.h signal.h], [], [], [AC_INCLUDES_DEFAULT])
 
-AC_CHECK_TYPES([size_t,ssize_t,ptrdiff_t,intptr_t,uintptr_t],[],[],
+AC_CHECK_TYPES([size_t,ssize_t,ptrdiff_t,intptr_t,uintptr_t,uint8_t],[],[],
   [[
 /* Keep in sync with src/mhd2/sys_base_types.h */
 #include <stdint.h> /* uint_fast_XXt, int_fast_XXt */
@@ -554,6 +554,14 @@ AC_CHECK_TYPES([size_t,ssize_t,ptrdiff_t,intptr_t,uintptr_t],[],[],
 AS_IF([test "x$ac_cv_type_size_t" != "xyes"],
   [AC_MSG_FAILURE(['size_t' type is not provided by system headers])]
 )
+AS_IF([test "x$ac_cv_type_uint8_t" != "xyes"],
+  [AC_MSG_FAILURE(['uint8_t' type is not provided by system headers])]
+)
+AC_CHECK_SIZEOF([char])
+AS_IF([test "x$ac_cv_sizeof_char" != "x1"],
+  [AC_MSG_FAILURE(['char' type with size different from '1' is not supported])]
+)
+
 
 AC_CHECK_TYPE([socklen_t],
   [],

+ 202 - 75
src/include/microhttpd2.h

@@ -475,11 +475,11 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
   MHD_SC_UPGRADE_FORWARD_INCOMPLETE = 30100
   ,
   /**
-   * We failed to allocate memory for generating the response from our
-   * memory pool.  Likely the request header was too large to leave
+   * Failed to allocate memory from our memory pool for processing
+   * the request.  Likely the request fields are too large to leave
    * enough room.
    */
-  MHD_SC_CONNECTION_POOL_MALLOC_FAILURE = 30130
+  MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ = 30130
   ,
 
   /* 40000-level errors are caused by the HTTP client
@@ -528,6 +528,31 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_CHUNKED_ENCODING_MALFORMED = 40007
   ,
+  /**
+   * The first header line has whitespace at the start
+   */
+  MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED = 40100
+  ,
+  /**
+   * Wrong bare CR characters has been replaced with space.
+   */
+  MHD_SC_REQ_HEADER_CR_REPLACED = 40120
+  ,
+  /**
+   * Header line has not colon and skipped.
+   */
+  MHD_SC_REQ_HEADER_LINE_NO_COLON = 40121
+  ,
+  /**
+   * Wrong bare CR characters has been replaced with space.
+   */
+  MHD_SC_REQ_FOOTER_CR_REPLACED = 40140
+  ,
+  /**
+   * Footer line has not colon and skipped.
+   */
+  MHD_SC_REQ_FOOTER_LINE_NO_COLON = 40141
+  ,
 
   /* 50000-level errors are because of an error internal
      to the MHD logic, possibly including our interaction
@@ -1004,6 +1029,27 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
    */
   MHD_SC_RESPONSE_HEADER_MALLOC_FAILED = 50540
   ,
+  /**
+   * Failed to switch TCP_NODELAY option for the socket
+   */
+  MHD_SC_SOCKET_TCP_NODELAY_FAILED = 50600
+  ,
+  /**
+   * Failed to switch TCP_CORK or TCP_NOPUSH option for the socket
+   */
+  MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED = 50601
+  ,
+  /**
+   * Failed to force flush the last part of the response header or
+   * the response content
+   */
+  MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED = 50620
+  ,
+  /**
+   * Failed to push buffered data by zero-sized send()
+   */
+  MHD_SC_SOCKET_ZERO_SEND_FAILED = 50621
+  ,
   /**
    * Something wrong in the internal MHD logic.
    * This error should be never returned if MHD works as expected.
@@ -4672,7 +4718,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3);
  *
  * @param request request to get values from
  * @param kind what kind of value are we looking for
- * @param key the header to look for, NULL to lookup 'trailing' value without a key
+ * @param key the header to look for, empty to lookup 'trailing' value
+ *            without a key
  * @return NULL if no such item was found
  * @ingroup request
  */
@@ -4690,140 +4737,220 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3);
  * @defgroup httpcode HTTP response codes
  * @{
  */
+/* Registry export date: 2023-09-29 */
 /* See http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml */
 enum MHD_FIXED_ENUM_APP_SET_ MHD_HTTP_StatusCode
 {
-
-  MHD_HTTP_STATUS_CONTINUE = 100
+  /* 100 "Continue".            RFC9110, Section 15.2.1. */
+  MHD_HTTP_STATUS_CONTINUE =                    100
   ,
-  MHD_HTTP_STATUS_SWITCHING_PROTOCOLS = 101
+  /* 101 "Switching Protocols". RFC9110, Section 15.2.2. */
+  MHD_HTTP_STATUS_SWITCHING_PROTOCOLS =         101
   ,
-  MHD_HTTP_STATUS_PROCESSING = 102
+  /* 102 "Processing".          RFC2518. */
+  MHD_HTTP_STATUS_PROCESSING =                  102
   ,
-  MHD_HTTP_STATUS_OK = 200
+  /* 103 "Early Hints".         RFC8297. */
+  MHD_HTTP_STATUS_EARLY_HINTS =                 103
   ,
-  MHD_HTTP_STATUS_CREATED = 201
+
+  /* 200 "OK".                  RFC9110, Section 15.3.1. */
+  MHD_HTTP_STATUS_OK =                          200
   ,
-  MHD_HTTP_STATUS_ACCEPTED = 202
+  /* 201 "Created".             RFC9110, Section 15.3.2. */
+  MHD_HTTP_STATUS_CREATED =                     201
   ,
+  /* 202 "Accepted".            RFC9110, Section 15.3.3. */
+  MHD_HTTP_STATUS_ACCEPTED =                    202
+  ,
+  /* 203 "Non-Authoritative Information". RFC9110, Section 15.3.4. */
   MHD_HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203
   ,
-  MHD_HTTP_STATUS_NO_CONTENT = 204
+  /* 204 "No Content".          RFC9110, Section 15.3.5. */
+  MHD_HTTP_STATUS_NO_CONTENT =                  204
   ,
-  MHD_HTTP_STATUS_RESET_CONTENT = 205
+  /* 205 "Reset Content".       RFC9110, Section 15.3.6. */
+  MHD_HTTP_STATUS_RESET_CONTENT =               205
   ,
-  MHD_HTTP_STATUS_PARTIAL_CONTENT = 206
+  /* 206 "Partial Content".     RFC9110, Section 15.3.7. */
+  MHD_HTTP_STATUS_PARTIAL_CONTENT =             206
   ,
-  MHD_HTTP_STATUS_MULTI_STATUS = 207
+  /* 207 "Multi-Status".        RFC4918. */
+  MHD_HTTP_STATUS_MULTI_STATUS =                207
   ,
-  MHD_HTTP_STATUS_ALREADY_REPORTED = 208
+  /* 208 "Already Reported".    RFC5842. */
+  MHD_HTTP_STATUS_ALREADY_REPORTED =            208
   ,
-  MHD_HTTP_STATUS_IM_USED = 226
+
+  /* 226 "IM Used".             RFC3229. */
+  MHD_HTTP_STATUS_IM_USED =                     226
   ,
-  MHD_HTTP_STATUS_MULTIPLE_CHOICES = 300
+
+  /* 300 "Multiple Choices".    RFC9110, Section 15.4.1. */
+  MHD_HTTP_STATUS_MULTIPLE_CHOICES =            300
   ,
-  MHD_HTTP_STATUS_MOVED_PERMANENTLY = 301
+  /* 301 "Moved Permanently".   RFC9110, Section 15.4.2. */
+  MHD_HTTP_STATUS_MOVED_PERMANENTLY =           301
   ,
-  MHD_HTTP_STATUS_FOUND = 302
+  /* 302 "Found".               RFC9110, Section 15.4.3. */
+  MHD_HTTP_STATUS_FOUND =                       302
   ,
-  MHD_HTTP_STATUS_SEE_OTHER = 303
+  /* 303 "See Other".           RFC9110, Section 15.4.4. */
+  MHD_HTTP_STATUS_SEE_OTHER =                   303
   ,
-  MHD_HTTP_STATUS_NOT_MODIFIED = 304
+  /* 304 "Not Modified".        RFC9110, Section 15.4.5. */
+  MHD_HTTP_STATUS_NOT_MODIFIED =                304
   ,
-  MHD_HTTP_STATUS_USE_PROXY = 305
+  /* 305 "Use Proxy".           RFC9110, Section 15.4.6. */
+  MHD_HTTP_STATUS_USE_PROXY =                   305
   ,
-  MHD_HTTP_STATUS_SWITCH_PROXY = 306 /* IANA: unused */
+  /* 306 "Switch Proxy".        Not used! RFC9110, Section 15.4.7. */
+  MHD_HTTP_STATUS_SWITCH_PROXY =                306
   ,
-  MHD_HTTP_STATUS_TEMPORARY_REDIRECT = 307
+  /* 307 "Temporary Redirect".  RFC9110, Section 15.4.8. */
+  MHD_HTTP_STATUS_TEMPORARY_REDIRECT =          307
   ,
-  MHD_HTTP_STATUS_PERMANENT_REDIRECT = 308
+  /* 308 "Permanent Redirect".  RFC9110, Section 15.4.9. */
+  MHD_HTTP_STATUS_PERMANENT_REDIRECT =          308
   ,
-  MHD_HTTP_STATUS_BAD_REQUEST = 400
+
+  /* 400 "Bad Request".         RFC9110, Section 15.5.1. */
+  MHD_HTTP_STATUS_BAD_REQUEST =                 400
   ,
-  MHD_HTTP_STATUS_UNAUTHORIZED = 401
+  /* 401 "Unauthorized".        RFC9110, Section 15.5.2. */
+  MHD_HTTP_STATUS_UNAUTHORIZED =                401
   ,
-  MHD_HTTP_STATUS_PAYMENT_REQUIRED = 402
+  /* 402 "Payment Required".    RFC9110, Section 15.5.3. */
+  MHD_HTTP_STATUS_PAYMENT_REQUIRED =            402
   ,
-  MHD_HTTP_STATUS_FORBIDDEN = 403
+  /* 403 "Forbidden".           RFC9110, Section 15.5.4. */
+  MHD_HTTP_STATUS_FORBIDDEN =                   403
   ,
-  MHD_HTTP_STATUS_NOT_FOUND = 404
+  /* 404 "Not Found".           RFC9110, Section 15.5.5. */
+  MHD_HTTP_STATUS_NOT_FOUND =                   404
   ,
-  MHD_HTTP_STATUS_METHOD_NOT_ALLOWED = 405
+  /* 405 "Method Not Allowed".  RFC9110, Section 15.5.6. */
+  MHD_HTTP_STATUS_METHOD_NOT_ALLOWED =          405
   ,
-  MHD_HTTP_STATUS_NOT_ACCEPTABLE = 406
+  /* 406 "Not Acceptable".      RFC9110, Section 15.5.7. */
+  MHD_HTTP_STATUS_NOT_ACCEPTABLE =              406
   ,
+  /* 407 "Proxy Authentication Required". RFC9110, Section 15.5.8. */
   MHD_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407
   ,
-  MHD_HTTP_STATUS_REQUEST_TIMEOUT = 408
+  /* 408 "Request Timeout".     RFC9110, Section 15.5.9. */
+  MHD_HTTP_STATUS_REQUEST_TIMEOUT =             408
   ,
-  MHD_HTTP_STATUS_CONFLICT = 409
+  /* 409 "Conflict".            RFC9110, Section 15.5.10. */
+  MHD_HTTP_STATUS_CONFLICT =                    409
   ,
-  MHD_HTTP_STATUS_GONE = 410
+  /* 410 "Gone".                RFC9110, Section 15.5.11. */
+  MHD_HTTP_STATUS_GONE =                        410
   ,
-  MHD_HTTP_STATUS_LENGTH_REQUIRED = 411
+  /* 411 "Length Required".     RFC9110, Section 15.5.12. */
+  MHD_HTTP_STATUS_LENGTH_REQUIRED =             411
   ,
-  MHD_HTTP_STATUS_PRECONDITION_FAILED = 412
+  /* 412 "Precondition Failed". RFC9110, Section 15.5.13. */
+  MHD_HTTP_STATUS_PRECONDITION_FAILED =         412
   ,
-  MHD_HTTP_STATUS_PAYLOAD_TOO_LARGE = 413
+  /* 413 "Content Too Large".   RFC9110, Section 15.5.14. */
+  MHD_HTTP_STATUS_CONTENT_TOO_LARGE =           413
   ,
-  MHD_HTTP_STATUS_URI_TOO_LONG = 414
+  /* 414 "URI Too Long".        RFC9110, Section 15.5.15. */
+  MHD_HTTP_STATUS_URI_TOO_LONG =                414
   ,
-  MHD_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415
+  /* 415 "Unsupported Media Type". RFC9110, Section 15.5.16. */
+  MHD_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE =      415
   ,
-  MHD_HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416
+  /* 416 "Range Not Satisfiable". RFC9110, Section 15.5.17. */
+  MHD_HTTP_STATUS_RANGE_NOT_SATISFIABLE =       416
   ,
-  MHD_HTTP_STATUS_EXPECTATION_FAILED = 417
+  /* 417 "Expectation Failed".  RFC9110, Section 15.5.18. */
+  MHD_HTTP_STATUS_EXPECTATION_FAILED =          417
   ,
-  MHD_HTTP_STATUS_MISDIRECTED_REQUEST = 421
+
+
+  /* 421 "Misdirected Request". RFC9110, Section 15.5.20. */
+  MHD_HTTP_STATUS_MISDIRECTED_REQUEST =         421
   ,
-  MHD_HTTP_STATUS_UNPROCESSABLE_ENTITY = 422
+  /* 422 "Unprocessable Content". RFC9110, Section 15.5.21. */
+  MHD_HTTP_STATUS_UNPROCESSABLE_CONTENT =       422
   ,
-  MHD_HTTP_STATUS_LOCKED = 423
+  /* 423 "Locked".              RFC4918. */
+  MHD_HTTP_STATUS_LOCKED =                      423
   ,
-  MHD_HTTP_STATUS_FAILED_DEPENDENCY = 424
+  /* 424 "Failed Dependency".   RFC4918. */
+  MHD_HTTP_STATUS_FAILED_DEPENDENCY =           424
   ,
-  MHD_HTTP_STATUS_UNORDERED_COLLECTION = 425 /* IANA: unused */
+  /* 425 "Too Early".           RFC8470. */
+  MHD_HTTP_STATUS_TOO_EARLY =                   425
   ,
-  MHD_HTTP_STATUS_UPGRADE_REQUIRED = 426
+  /* 426 "Upgrade Required".    RFC9110, Section 15.5.22. */
+  MHD_HTTP_STATUS_UPGRADE_REQUIRED =            426
   ,
-  MHD_HTTP_STATUS_PRECONDITION_REQUIRED = 428
+
+  /* 428 "Precondition Required". RFC6585. */
+  MHD_HTTP_STATUS_PRECONDITION_REQUIRED =       428
   ,
-  MHD_HTTP_STATUS_TOO_MANY_REQUESTS = 429
+  /* 429 "Too Many Requests".   RFC6585. */
+  MHD_HTTP_STATUS_TOO_MANY_REQUESTS =           429
   ,
+
+  /* 431 "Request Header Fields Too Large". RFC6585. */
   MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431
   ,
-  MHD_HTTP_STATUS_NO_RESPONSE = 444 /* IANA: unused */
-  ,
-  MHD_HTTP_STATUS_RETRY_WITH = 449 /* IANA: unused */
+
+  /* 451 "Unavailable For Legal Reasons". RFC7725. */
+  MHD_HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451
   ,
-  MHD_HTTP_STATUS_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450  /* IANA: unused */
+
+  /* 500 "Internal Server Error". RFC9110, Section 15.6.1. */
+  MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR =       500
   ,
-  MHD_HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451
+  /* 501 "Not Implemented".     RFC9110, Section 15.6.2. */
+  MHD_HTTP_STATUS_NOT_IMPLEMENTED =             501
   ,
-  MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500
+  /* 502 "Bad Gateway".         RFC9110, Section 15.6.3. */
+  MHD_HTTP_STATUS_BAD_GATEWAY =                 502
   ,
-  MHD_HTTP_STATUS_NOT_IMPLEMENTED = 501
+  /* 503 "Service Unavailable". RFC9110, Section 15.6.4. */
+  MHD_HTTP_STATUS_SERVICE_UNAVAILABLE =         503
   ,
-  MHD_HTTP_STATUS_BAD_GATEWAY = 502
+  /* 504 "Gateway Timeout".     RFC9110, Section 15.6.5. */
+  MHD_HTTP_STATUS_GATEWAY_TIMEOUT =             504
   ,
-  MHD_HTTP_STATUS_SERVICE_UNAVAILABLE = 503
+  /* 505 "HTTP Version Not Supported". RFC9110, Section 15.6.6. */
+  MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED =  505
   ,
-  MHD_HTTP_STATUS_GATEWAY_TIMEOUT = 504
+  /* 506 "Variant Also Negotiates". RFC2295. */
+  MHD_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES =     506
   ,
-  MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
+  /* 507 "Insufficient Storage". RFC4918. */
+  MHD_HTTP_STATUS_INSUFFICIENT_STORAGE =        507
   ,
-  MHD_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506
+  /* 508 "Loop Detected".       RFC5842. */
+  MHD_HTTP_STATUS_LOOP_DETECTED =               508
   ,
-  MHD_HTTP_STATUS_INSUFFICIENT_STORAGE = 507
+
+  /* 510 "Not Extended".        (OBSOLETED) RFC2774; status-change-http-experiments-to-historic. */
+  MHD_HTTP_STATUS_NOT_EXTENDED =                510
   ,
-  MHD_HTTP_STATUS_LOOP_DETECTED = 508
+  /* 511 "Network Authentication Required". RFC6585. */
+  MHD_HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
   ,
-  MHD_HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509  /* IANA: unused */
+
+
+  /* Not registered non-standard codes */
+  /* 449 "Reply With".          MS IIS extension. */
+  MHD_HTTP_STATUS_RETRY_WITH =                  449
   ,
-  MHD_HTTP_STATUS_NOT_EXTENDED = 510
+
+  /* 450 "Blocked by Windows Parental Controls". MS extension. */
+  MHD_HTTP_STATUS_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450
   ,
-  MHD_HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
 
+  /* 509 "Bandwidth Limit Exceeded". Apache extension. */
+  MHD_HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED =    509
 };
 
 
@@ -4864,9 +4991,9 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion
   ,
   MHD_HTTP_VERSION_1_1 = 2
   ,
-  MHD_HTTP_VERSION_2_0 = 3
+  MHD_HTTP_VERSION_2 = 3
   ,
-  MHD_HTTP_VERSION_3_0 = 4
+  MHD_HTTP_VERSION_3 = 4
   ,
   MHD_HTTP_VERSION_FUTURE = 255
 };
@@ -4889,22 +5016,22 @@ MHD_FN_CONST_;
 /**
  * HTTP/1.0 identification string
  */
-#define MHD_HTTP_VERSION_1_0 "HTTP/1.0"
+#define MHD_HTTP_VERSION_1_0_STR "HTTP/1.0"
 /**
  * HTTP/1.1 identification string
  */
-#define MHD_HTTP_VERSION_1_1 "HTTP/1.1"
+#define MHD_HTTP_VERSION_1_1_STR "HTTP/1.1"
 /**
  * HTTP/2 identification string.
  * Not used by the HTTP protocol (except non-TLS handshake), useful for logs and
  * similar proposes.
  */
-#define MHD_HTTP_VERSION_2 "HTTP/2"
+#define MHD_HTTP_VERSION_2_STR "HTTP/2"
 /**
  * HTTP/3 identification string.
  * Not used by the HTTP protocol, useful for logs and similar proposes.
  */
-#define MHD_HTTP_VERSION_3 "HTTP/3"
+#define MHD_HTTP_VERSION_3_STR "HTTP/3"
 
 /** @} */ /* end of group versions */
 
@@ -4980,7 +5107,7 @@ struct MHD_Response;
  */
 MHD_EXTERN_ const struct MHD_Action *
 MHD_action_suspend (struct MHD_Request *request)
-MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_;
+MHD_FN_PAR_NONNULL_ALL_;
 
 
 /**

+ 15 - 11
src/mhd2/action.c

@@ -29,13 +29,17 @@
 #include "mhd_action.h"
 #include "mhd_request.h"
 #include "mhd_public_api.h"
+#include "daemon_logger.h"
 
 
-MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
 const struct MHD_Action *
 MHD_action_suspend (struct MHD_Request *request)
 {
   struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
+  if (mhd_UPLOAD_ACTION_NO_ACTION != head_act->act)
+    return (const struct MHD_Action *) NULL;
+
   head_act->act = mhd_ACTION_SUSPEND;
   return head_act;
 }
@@ -48,7 +52,7 @@ MHD_action_from_response (struct MHD_Request *request,
 {
   struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
   if (mhd_ACTION_NO_ACTION != head_act->act)
-    return NULL;
+    return (const struct MHD_Action *) NULL;
   if (NULL == response)
     return (const struct MHD_Action *) NULL;
 
@@ -70,18 +74,18 @@ MHD_action_process_upload (struct MHD_Request *request,
 {
   struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
   if (mhd_ACTION_NO_ACTION != head_act->act)
-    return NULL;
+    return (const struct MHD_Action *) NULL;
   if (0 == large_buffer_size)
   {
     if (NULL != uc_full)
-      return NULL;
+      return (const struct MHD_Action *) NULL;
     if (NULL == uc_inc)
-      return NULL;
+      return (const struct MHD_Action *) NULL;
   }
   else
   {
     if (NULL == uc_full)
-      return NULL;
+      return (const struct MHD_Action *) NULL;
   }
 
   head_act->act = mhd_ACTION_UPLOAD;
@@ -108,9 +112,9 @@ MHD_action_post_processor (struct MHD_Request *request,
 {
   struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
   if (mhd_ACTION_NO_ACTION != head_act->act)
-    return NULL;
+    return (const struct MHD_Action *) NULL;
   if (NULL == done_cb)
-    return NULL;
+    return (const struct MHD_Action *) NULL;
 
   head_act->act = mhd_ACTION_POST_PROCESS;
   head_act->data.post_process.pp_buffer_size = pp_buffer_size;
@@ -131,7 +135,7 @@ MHD_upload_action_suspend (struct MHD_Request *request)
 {
   struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
   if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
-    return NULL;
+    return (const struct MHD_UploadAction *) NULL;
 
   upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
 
@@ -146,7 +150,7 @@ MHD_upload_action_from_response (struct MHD_Request *request,
 {
   struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
   if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
-    return NULL;
+    return (const struct MHD_UploadAction *) NULL;
 
   upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
   upl_act->data.response = response;
@@ -160,7 +164,7 @@ MHD_upload_action_continue (struct MHD_Request *request)
 {
   struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
   if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
-    return NULL;
+    return (const struct MHD_UploadAction *) NULL;
 
   upl_act->act = mhd_UPLOAD_ACTION_CONTINUE;
 

+ 180 - 0
src/mhd2/conn_data_process.c

@@ -0,0 +1,180 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/data_process.c
+ * @brief  The implementation of data receiving, sending and processing
+ *         functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
+ * contributors.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "conn_data_process.h"
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_daemon.h"
+#include "mhd_connection.h"
+
+#include "daemon_logger.h"
+
+#include "conn_data_recv.h"
+#include "conn_data_send.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
+{
+  const bool send_ready_state_known =
+    ((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) ||
+     (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
+  const bool has_sock_err =
+    (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
+  bool data_processed;
+
+  data_processed = false;
+
+  if (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
+  {
+    bool use_recv;
+    use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready));
+    use_recv = use_recv ||
+      (has_sock_err && c->sk_nonblck);
+
+    if (use_recv)
+    {
+      mhd_conn_data_recv (c, has_sock_err);
+      if (! mhd_conn_process_data (c))
+        return false;
+      data_processed = true;
+    }
+  }
+
+  if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
+  {
+    bool use_send;
+    /* Perform sending if:
+     * + connection is ready for sending or
+     * + just formed send data, connection send ready status is not known and
+     *   connection socket is non-blocking
+     * + detected network error on the connection, to check to the error */
+    /* Assuming that after finishing receiving phase, connection send system
+       buffers should have some space as sending was performed before receiving
+       or has not been performed yet. */
+    use_send = (0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready));
+    use_send = use_send ||
+      (data_processed && (! send_ready_state_known) && c->sk_nonblck);
+    use_send = use_send ||
+      (has_sock_err && c->sk_nonblck);
+
+    if (use_send)
+    {
+      mhd_conn_data_send (c, has_sock_err);
+      if (! mhd_conn_process_data (c))
+        return false;
+      data_processed = true;
+    }
+  }
+  if (! data_processed)
+    return mhd_conn_process_data (c);
+  return true;
+
+#if 0 // TODO: re-implement fasttrack as a single unified buffer sending
+  if (! force_close)
+  {
+    /* No need to check value of 'ret' here as closed connection
+     * cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */
+    if ( (MHD_EVENT_LOOP_INFO_WRITE == c->event_loop_info) &&
+         write_ready)
+    {
+      MHD_connection_handle_write (c);
+      ret = MHD_connection_handle_idle (c);
+      states_info_processed = true;
+    }
+  }
+  else
+  {
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_WITH_ERROR);
+    return MHD_connection_handle_idle (c);
+  }
+
+  if (! states_info_processed)
+  {   /* Connection is not read or write ready, but external conditions
+       * may be changed and need to be processed. */
+    ret = MHD_connection_handle_idle (c);
+  }
+  /* Fast track for fast connections. */
+  /* If full request was read by single read_handler() invocation
+     and headers were completely prepared by single MHD_connection_handle_idle()
+     then try not to wait for next sockets polling and send response
+     immediately.
+     As writeability of socket was not checked and it may have
+     some data pending in system buffers, use this optimization
+     only for non-blocking sockets. */
+  /* No need to check 'ret' as connection is always in
+   * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */
+  else if (on_fasttrack && c->sk_nonblck)
+  {
+    if (MHD_CONNECTION_HEADERS_SENDING == c->state)
+    {
+      MHD_connection_handle_write (c);
+      /* Always call 'MHD_connection_handle_idle()' after each read/write. */
+      ret = MHD_connection_handle_idle (c);
+    }
+    /* If all headers were sent by single write_handler() and
+     * response body is prepared by single MHD_connection_handle_idle()
+     * call - continue. */
+    if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
+        (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
+    {
+      MHD_connection_handle_write (c);
+      ret = MHD_connection_handle_idle (c);
+    }
+  }
+
+  /* All connection's data and states are processed for this turn.
+   * If connection already has more data to be processed - use
+   * zero timeout for next select()/poll(). */
+  /* Thread-per-connection do not need global zero timeout as
+   * connections are processed individually. */
+  /* Note: no need to check for read buffer availability for
+   * TLS read-ready connection in 'read info' state as connection
+   * without space in read buffer will be marked as 'info block'. */
+  if ( (! c->daemon->data_already_pending) &&
+       (! MHD_D_IS_USING_THREAD_PER_CONN_ (c->daemon)) )
+  {
+    if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info))
+      c->daemon->data_already_pending = true;
+#ifdef HTTPS_SUPPORT
+    else if ( (c->tls_read_ready) &&
+              (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) )
+      c->daemon->data_already_pending = true;
+#endif /* HTTPS_SUPPORT */
+  }
+  return ret;
+#endif
+}

+ 12 - 1
src/mhd2/data_process.h → src/mhd2/conn_data_process.h

@@ -39,6 +39,17 @@ struct MHD_Connection; /* forward declaration */
  *         false if connection needs to be closed
  */
 MHD_INTERNAL bool
-mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c);
+mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_ ;
+
+/**
+ * Finally close and clean-up connection.
+ * Must be performed only when connection thread (for thread-per-connection)
+ * has stopped.
+ * @param c the connection to close
+ */
+MHD_INTERNAL void
+mhd_conn_close_final (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_ ;
 
 #endif /* ! MHD_DATA_PROCESS_H */

+ 157 - 0
src/mhd2/conn_data_recv.c

@@ -0,0 +1,157 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/conn_data_recv.c
+ * @brief  The implementation of data receiving functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
+ * and other contributors.
+ */
+
+#include "conn_data_recv.h"
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_recv.h"
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_data_recv (struct MHD_Connection restrict *c,
+                       bool has_err)
+{
+  ssize_t bytes_read;
+  uint_fast64_t dummy_buf;
+  void *buf;
+  size_t buf_size;
+  size_t received;
+  enum mhd_SocketError res;
+
+  mhd_assert (MHD_CONNECTION_CLOSED != c->state);
+  mhd_assert (NULL != c->read_buffer);
+  mhd_assert (c->read_buffer_size > c->read_buffer_offset);
+  mhd_assert (! has_err || \
+              (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)));
+  mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
+              has_err);
+
+  // TODO: TLS support: handshake/transport layer
+
+  buf = c->read_buffer + c->read_buffer_offset;
+  buf_size = c->read_buffer_size - c->read_buffer_offset;
+
+  res = mhd_recv (c,buf_size, buf, &received);
+
+  if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
+  {
+    /* Handle errors */
+    if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
+    {
+      /* Re-try last time to detect the error */
+      uint_fast64_t dummy_buf;
+      res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received);
+    }
+    if (mhd_SOCKET_ERR_IS_HARD (res))
+    {
+      c->sk_discnt_err = res;
+      c->sk_ready =
+        (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
+                                   | mhd_SOCKET_NET_STATE_ERROR_READY);
+    }
+    return;
+  }
+
+  if (0 == bytes_read)
+    c->sk_rmt_shut_wr = true;
+
+  c->read_buffer_offset += (size_t) bytes_read;
+  MHD_update_last_activity_ (c); // TODO: centralise activity update
+  return;
+}
+
+
+#if 0 // TODO: report disconnect
+if ((bytes_read < 0) || socket_error)
+{
+  if (MHD_ERR_CONNRESET_ == bytes_read)
+  {
+    if ( (MHD_CONNECTION_INIT < c->state) &&
+         (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (c->daemon,
+                _ ("Socket has been disconnected when reading request.\n"));
+#endif
+      c->discard_request = true;
+    }
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_READ_ERROR);
+    return;
+  }
+
+#ifdef HAVE_MESSAGES
+  if (MHD_CONNECTION_INIT != c->state)
+    MHD_DLOG (c->daemon,
+              _ ("Connection socket is closed when reading " \
+                 "request due to the error: %s\n"),
+              (bytes_read < 0) ? str_conn_error_ (bytes_read) :
+              "detected c closure");
+#endif
+  CONNECTION_CLOSE_ERROR (c,
+                          NULL);
+  return;
+}
+
+#if 0 // TODO: handle remote shut WR
+if (0 == bytes_read)
+{ /* Remote side closed c. */   // FIXME: Actually NOT!
+  c->sk_rmt_shut_wr = true;
+  if ( (MHD_CONNECTION_INIT < c->state) &&
+       (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (c->daemon,
+              _ ("Connection was closed by remote side with incomplete "
+                 "request.\n"));
+#endif
+    c->discard_request = true;
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_CLIENT_ABORT);
+  }
+  else if (MHD_CONNECTION_INIT == c->state)
+    /* This termination code cannot be reported to the application
+     * because application has not been informed yet about this request */
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
+  else
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_WITH_ERROR);
+  return;
+}
+#endif
+#endif

+ 50 - 0
src/mhd2/conn_data_recv.h

@@ -0,0 +1,50 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/conn_data_recv.h
+ * @brief  The definition of data receiving functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_DATA_RECV_H
+#define MHD_CONN_DATA_RECV_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declarations */
+
+/**
+ * Perform data receiving for the connection and try to detect the socket error
+ * type.
+ *
+ * @param c the connection to use
+ * @param has_err if 'true' then just check for the network error
+ *        type is performed
+ */
+MHD_INTERNAL void
+mhd_conn_data_recv (struct MHD_Connection restrict *c,
+                    bool has_err)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_CONN_DATA_RECV_H */

+ 279 - 0
src/mhd2/conn_data_send.c

@@ -0,0 +1,279 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
+  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/conn_data_send.c
+ * @brief  The implementation of data sending functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
+ * and other contributors.
+ */
+
+#include "mhd_sys_options.h"
+
+#include "conn_data_send.h"
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+#include "mhd_str_macros.h"
+#include "mhd_socket_error.h"
+
+#include "mhd_assert.h"
+
+#include "mhd_connection.h"
+
+#include "mhd_send.h"
+
+
+/**
+ * Check if we are done sending the write-buffer.
+ * If so, transition into "next_state".
+ *
+ * @param connection connection to check write status for
+ * @param next_state the next state to transition to
+ * @return #MHD_NO if we are not done, #MHD_YES if we are
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+check_write_done (struct MHD_Connection *restrict connection,
+                  enum MHD_CONNECTION_STATE next_state)
+{
+  // TODO: integrate into states processing
+  if ( (connection->write_buffer_append_offset !=
+        connection->write_buffer_send_offset)
+       /* || data_in_tls_buffers == true  */
+       )
+    return false;
+  connection->write_buffer_append_offset = 0;
+  connection->write_buffer_send_offset = 0;
+  connection->state = next_state;
+  return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_conn_data_send (struct MHD_Connection restrict *c,
+                    bool has_err)
+{
+  static const char http_100_continue_msg[] =
+    mdh_HTTP_1_1_100_CONTINUE_REPLY;
+  static const size_t http_100_continue_msg_len =
+      mhd_SSTR_LEN(mdh_HTTP_1_1_100_CONTINUE_REPLY);
+  enum mhd_SocketError res;
+  size_t sent;
+
+  // TODO: assert check suspended
+
+#ifdef HTTPS_SUPPORT
+  // TODO: TLS support, handshake
+#endif /* HTTPS_SUPPORT */
+
+  res = mhd_SOCKET_ERR_INTERNAL;
+
+  if (MHD_CONNECTION_CONTINUE_SENDING == c->state)
+  {
+    res = mhd_send_data (c,
+                         http_100_continue_msg
+                         [c->continue_message_write_offset],
+                         http_100_continue_msg_len
+                         - c->continue_message_write_offset,
+                         true,
+                         &sent);
+    if (mhd_SOCKET_ERR_NO_ERROR != res)
+      c->continue_message_write_offset += sent;
+  }
+  else if (MHD_CONNECTION_HEADERS_SENDING == c->state)
+  {
+    struct MHD_Response *const restrict resp = c->rp.response;
+    const size_t wb_ready = c->write_buffer_append_offset
+                            - c->write_buffer_send_offset;
+    mhd_assert (c->write_buffer_append_offset >= \
+                c->write_buffer_send_offset);
+    mhd_assert (NULL != resp);
+    mhd_assert ((MHD_CONN_MUST_UPGRADE != c->keepalive) || \
+                (! c->rp.props.send_reply_body));
+
+    // TODO: support body generating alongside with header sending
+
+    if ((c->rp.props.send_reply_body) &&
+        (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) &&
+        (! c->rp.props.chunked) )
+    {
+      /* Send response headers alongside the response body, if the body
+       * data is available. */
+      mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
+
+      res = mhd_send_hdr_and_body (c,
+                                   c->write_buffer +
+                                   c->write_buffer_send_offset,
+                                   wb_ready,
+                                   false,
+                                   (const char *) resp->cntn.buf,
+                                   resp->cntn_size,
+                                   true,
+                                   &sent);
+    }
+    else
+    {
+      /* This is response for HEAD request or reply body is not allowed
+       * for any other reason or reply body is dynamically generated. */
+      /* Do not send the body data even if it's available. */
+      res = mhd_send_hdr_and_body (c,
+                                   c->write_buffer +
+                                   c->write_buffer_send_offset,
+                                   wb_ready,
+                                   false,
+                                   NULL,
+                                   0,
+                                   ((0 == resp->cntn_size) ||
+                                    (! c->rp.props.send_reply_body)),
+                                   &sent);
+    }
+    if (mhd_SOCKET_ERR_NO_ERROR != res)
+    {
+      if (sent > wb_ready)
+      {
+        /* The complete header and some response data have been sent,
+         * update both offsets. */
+        mhd_assert (0 == c->rp.rsp_cntn_read_pos);
+        mhd_assert (! c->rp.props.chunked);
+        mhd_assert (c->rp.props.send_reply_body);
+        c->write_buffer_send_offset += wb_ready;
+        c->rp.rsp_cntn_read_pos = sent - wb_ready;
+      }
+      else
+        c->write_buffer_send_offset += sent;
+
+      mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state);
+
+      // TODO: move it to data processing
+      check_write_done (c,
+                        MHD_CONNECTION_HEADERS_SENT);
+
+    }
+
+  }
+  else if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
+           (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
+  {
+    struct MHD_Response *const restrict resp = c->rp.response;
+    mhd_assert (c->rp.props.send_reply_body);
+    mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size);
+    mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \
+                (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc));
+    if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
+    {
+      mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
+
+      res = mhd_send_data (c,
+                           (const char *) resp->cntn.buf +
+                           c->rp.rsp_cntn_read_pos,
+                           c->rp.rsp_cntn_read_pos - resp->cntn_size,
+                           true,
+                           &sent);
+    }
+    else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
+    {
+      mhd_assert (c->write_buffer_append_offset > \
+                  c->write_buffer_send_offset);
+
+      res = mhd_send_data (c,
+                           c->write_buffer + c->write_buffer_send_offset,
+                           c->write_buffer_append_offset
+                           - c->write_buffer_send_offset,
+                           true,
+                           &sent);
+    }
+    else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
+    {
+      mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype);
+
+      res = mhd_send_iovec (c,
+                            &c->rp.resp_iov,
+                            true,
+                            &sent);
+    }
+#if defined(MHD_USE_SENDFILE)
+    else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
+    {
+      mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype);
+
+      res = mhd_send_sendfile (c, &sent);
+    }
+#endif /* MHD_USE_SENDFILE */
+    else
+    {
+      mhd_assert (0 && "Should be unreachable");
+      res = mhd_SOCKET_ERR_INTERNAL;
+    }
+
+    if (mhd_SOCKET_ERR_NO_ERROR != res)
+    {
+      if (mhd_REPLY_CNTN_LOC_CONN_BUF != c->rp.cntn_loc)
+        c->rp.rsp_cntn_read_pos += sent;
+      else
+        c->write_buffer_send_offset += sent;
+    }
+
+    if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)
+    {
+      // TODO: move it to data processing
+      check_write_done (c,
+                        (c->rp.response->cntn_size ==
+                         c->rp.rsp_cntn_read_pos) ?
+                        MHD_CONNECTION_CHUNKED_BODY_SENT :
+                        MHD_CONNECTION_CHUNKED_BODY_UNREADY);
+    }
+  }
+  else if (MHD_CONNECTION_FOOTERS_SENDING == c->state)
+  {
+    res = mhd_send_data (c,
+                         c->write_buffer +
+                         c->write_buffer_send_offset,
+                         c->write_buffer_append_offset
+                         - c->write_buffer_send_offset,
+                         true,
+                         &sent);
+    if (mhd_SOCKET_ERR_NO_ERROR != res)
+    {
+      c->write_buffer_send_offset += sent;
+      // TODO: move it to data processing
+      check_write_done (c,
+                        MHD_CONNECTION_FULL_REPLY_SENT);
+    }
+  }
+  else
+  {
+    mhd_assert (0 && "Should be unreachable");
+    res = mhd_SOCKET_ERR_INTERNAL;
+  }
+
+  if (mhd_SOCKET_ERR_NO_ERROR != res)
+  {
+    MHD_update_last_activity_ (c);  // TODO: centralise activity mark updates
+  }
+  else if (mhd_SOCKET_ERR_IS_HARD(res))
+  {
+    c->sk_discnt_err = res;
+    c->sk_ready =
+      (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
+                                 | mhd_SOCKET_NET_STATE_ERROR_READY);
+  }
+}

+ 50 - 0
src/mhd2/conn_data_send.h

@@ -0,0 +1,50 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/conn_data_send.h
+ * @brief  The definition of data sending functions for connection
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_CONN_DATA_SEND_H
+#define MHD_CONN_DATA_SEND_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declarations */
+
+/**
+ * Perform data sending for the connection and try to detect the socket error
+ * type.
+ *
+ * @param c the connection to use
+ * @param has_err if 'true' then just check for the network error
+ *        type is performed
+ */
+MHD_INTERNAL void
+mhd_conn_data_send (struct MHD_Connection restrict *c,
+                    bool has_err)
+MHD_FN_PAR_NONNULL_ALL_;
+
+
+#endif /* ! MHD_CONN_DATA_SEND_H */

+ 4 - 1
src/mhd2/daemon_add_conn.c

@@ -42,6 +42,8 @@
 #include "sys_sockets_types.h"
 #include "sys_sockets_headers.h"
 
+#include <string.h>
+
 #include "compat_calloc.h"
 
 #include "mhd_panic.h"
@@ -59,6 +61,7 @@
 #include "events_process.h"
 
 #include "response_from.h"
+#include "response_destroy.h"
 
 
 /**
@@ -736,7 +739,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
 
     for (i = 0; i < daemon->threading.hier.pool.num; ++i)
     {
-      struct MHD_Daemon *const volatile worker =
+      struct MHD_Daemon *const restrict worker =
         daemon->threading.hier.pool.workers
         + (i + offset) % daemon->threading.hier.pool.num;
       if (worker->conns.block_new)

+ 2 - 2
src/mhd2/daemon_create.c

@@ -93,8 +93,8 @@ MHD_daemon_create (MHD_RequestCallback req_cb,
   s->fd_number_limit = MHD_INVALID_SOCKET;
 
   d->log_params.v_log_cb = mhd_logger_default;
-  d->req_cb = req_cb;
-  d->req_cb_cls = req_cb_cls;
+  d->req_cfg.cb = req_cb;
+  d->req_cfg.cb_cls = req_cb_cls;
   d->settings = s;
 
   return d;

+ 10 - 0
src/mhd2/daemon_funcs.h

@@ -46,6 +46,16 @@ struct MHD_Daemon; /* forward declaration */
 MHD_INTERNAL bool
 mhd_daemon_trigger_itc (struct MHD_Daemon *restrict d);
 
+
+
+/**
+ * Check whether any resuming connections are pending and resume them
+ * @param d the daemon to use
+ */
+MHD_INTERNAL void
+mhd_daemon_resume_conns (struct MHD_Daemon *restrict d)
+MHD_FN_PAR_NONNULL_ALL_ ;
+
 #endif /* MHD_USE_THREADS */
 
 #endif /* ! MHD_DAEMON_FUNCS_H */

+ 10 - 8
src/mhd2/daemon_start.c

@@ -74,6 +74,8 @@
 #  include "daemon_funcs.h"
 #endif
 
+#include "mhd_limits.h"
+
 
 /**
  * The default value for fastopen queue length (currently GNU/Linux only)
@@ -614,8 +616,8 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
     if (MHD_INVALID_SOCKET == sk)
 #endif /* MHD_WINSOCK_SOCKETS && WSA_FLAG_NO_HANDLE_INHERIT */
     sk = socket (p_use_sa->sa_family,
-                 SOCK_STREAM | MHD_SOCK_NONBLOCK
-                 | MHD_SOCK_CLOEXEC | MHD_SOCK_NOSIGPIPE, 0);
+                 SOCK_STREAM | mhd_SOCK_NONBLOCK
+                 | mhd_SOCK_CLOEXEC | mhd_SOCK_NOSIGPIPE, 0);
 
     if (MHD_INVALID_SOCKET == sk)
     {
@@ -639,8 +641,8 @@ create_bind_listen_stream_socket (struct MHD_Daemon *restrict d,
 
       return MHD_SC_FAILED_TO_OPEN_LISTEN_SOCKET;
     }
-    is_non_block = (0 != MHD_SOCK_NONBLOCK);
-    is_non_inhr = (0 == MHD_SOCK_CLOEXEC);
+    is_non_block = (0 != mhd_SOCK_NONBLOCK);
+    is_non_inhr = (0 == mhd_SOCK_CLOEXEC);
   }
   else
   {
@@ -1958,8 +1960,8 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
 #endif /* MHD_USE_THREADS */
 
   limit_by_conf = s->global_connection_limit;
-  limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
-  limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+  limit_by_num = UINT_MAX;
+  limit_by_select = UINT_MAX;
 
   error_by_fd_setsize = false;
 #ifdef MHD_POSIX_SOCKETS
@@ -1996,7 +1998,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
       }
     }
     else
-      limit_by_num = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+      limit_by_num = UINT_MAX;
   }
 #elif defined(MHD_WINSOCK_SOCKETS)
   if (1)
@@ -2020,7 +2022,7 @@ set_connections_total_limits (struct MHD_Daemon *restrict d,
       {
         limit_by_select = limit_per_worker * num_worker_daemons;
         if (limit_by_select / limit_per_worker != num_worker_daemons)
-          limit_by_select = (unsigned int) ~((unsigned int) 0); /* UINT_MAX */
+          limit_by_select = UINT_MAX;
       }
     }
 #endif /* MHD_USE_SELECT */

+ 0 - 303
src/mhd2/data_process.c

@@ -1,303 +0,0 @@
-/*
-  This file is part of GNU libmicrohttpd
-  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
-  Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
-
-  GNU libmicrohttpd is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  GNU 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
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-
-*/
-
-/**
- * @file src/mhd2/data_process.c
- * @brief  The implementation of data receiving, sending and processing
- *         functions for connection
- * @author Karlson2k (Evgeny Grin)
- *
- * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
- * contributors.
- */
-
-#include "mhd_sys_options.h"
-
-#include "data_process.h"
-
-#include "sys_bool_type.h"
-#include "sys_base_types.h"
-
-#include "mhd_assert.h"
-
-#include "mhd_daemon.h"
-#include "mhd_connection.h"
-
-#include "mhd_recv.h"
-
-#include "daemon_logger.h"
-
-/**
- * Perform data receiving for the connection and try to detect the socket error
- * type.
- *
- * @param c the connection to use
- * @param has_err if 'true' then just check for the network error
- *        type is performed
- */
-void
-conn_process_recv (struct MHD_Connection restrict *c,
-                   bool has_err)
-{
-  ssize_t bytes_read;
-  uint_fast64_t dummy_buf;
-  void *buf;
-  size_t buf_size;
-  size_t received;
-  enum mhd_SocketError res;
-
-  mhd_assert (MHD_CONNECTION_CLOSED != c->state);
-  mhd_assert ((NULL != c->read_buffer) || has_err);
-  mhd_assert ((c->read_buffer_size > c->read_buffer_offset) || has_err);
-  mhd_assert (! has_err || \
-              (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)));
-  mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
-              has_err);
-
-  // TODO: TLS support: handshake/transport layer
-
-  if (! has_err ||
-      (NULL != c->read_buffer) || (c->read_buffer_size > c->read_buffer_offset))
-  {
-    buf = c->read_buffer + c->read_buffer_offset;
-    buf_size = c->read_buffer_size - c->read_buffer_offset;
-  }
-  else
-  {
-    buf = &dummy_buf;
-    buf_size = sizeof(buf);
-  }
-
-  res = mhd_recv (c,buf_size, buf, &received);
-
-  if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
-  {
-    /* Handle errors */
-    if (! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
-    {
-      /* Re-try last time to detect the error */
-      res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received);
-    }
-    if (mhd_SOCKET_ERR_IS_HARD (res))
-    {
-      c->sk_discnt_err = res;
-      c->sk_ready =
-        (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
-                                   | mhd_SOCKET_NET_STATE_ERROR_READY);
-    }
-    return;
-  }
-
-  if (0 == bytes_read)
-    c->sk_rmt_shut_wr = true;
-
-  c->read_buffer_offset += (size_t) bytes_read;
-  MHD_update_last_activity_ (c); // TODO: centralise activity update
-  return;
-}
-
-
-#if 0 // TODO: report disconnect
-if ((bytes_read < 0) || socket_error)
-{
-  if (MHD_ERR_CONNRESET_ == bytes_read)
-  {
-    if ( (MHD_CONNECTION_INIT < c->state) &&
-         (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
-    {
-#ifdef HAVE_MESSAGES
-      MHD_DLOG (c->daemon,
-                _ ("Socket has been disconnected when reading request.\n"));
-#endif
-      c->discard_request = true;
-    }
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_READ_ERROR);
-    return;
-  }
-
-#ifdef HAVE_MESSAGES
-  if (MHD_CONNECTION_INIT != c->state)
-    MHD_DLOG (c->daemon,
-              _ ("Connection socket is closed when reading " \
-                 "request due to the error: %s\n"),
-              (bytes_read < 0) ? str_conn_error_ (bytes_read) :
-              "detected c closure");
-#endif
-  CONNECTION_CLOSE_ERROR (c,
-                          NULL);
-  return;
-}
-
-#if 0 // TODO: handle remote shut WR
-if (0 == bytes_read)
-{ /* Remote side closed c. */   // FIXME: Actually NOT!
-  c->sk_rmt_shut_wr = true;
-  if ( (MHD_CONNECTION_INIT < c->state) &&
-       (MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
-  {
-#ifdef HAVE_MESSAGES
-    MHD_DLOG (c->daemon,
-              _ ("Connection was closed by remote side with incomplete "
-                 "request.\n"));
-#endif
-    c->discard_request = true;
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_CLIENT_ABORT);
-  }
-  else if (MHD_CONNECTION_INIT == c->state)
-    /* This termination code cannot be reported to the application
-     * because application has not been informed yet about this request */
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_COMPLETED_OK);
-  else
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_WITH_ERROR);
-  return;
-}
-#endif
-#endif
-
-MHD_INTERNAL bool
-mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c)
-{
-  const bool send_ready_state_known =
-    ((0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)) ||
-     mhd_D_IS_USING_EDGE_TRIG (c->daemon));
-  const bool has_sock_err =
-    (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
-  bool data_processed;
-  bool need_send;
-
-  data_processed = false;
-
-  if ( ((0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready)) &&
-        ((0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
-         || has_sock_err))
-       || (has_sock_err && c->sk_nonblck))
-  {
-    conn_process_recv (c, has_sock_err);
-    if (! conn_process_data (c))
-      return false;
-    data_processed = true;
-  }
-  need_send = ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) &&
-               (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
-
-  if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
-  {
-    /* Perform sending if:
-     * + connection is ready for sending or
-     * + just formed send data, connection send ready status is not known and
-     *   connection socket is non-blocking */
-    /* Assuming that after finishing receiving phase, connection send buffers
-       should have some space as sending was performed before receiving or has
-       not been performed yet. */
-    if ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) ||
-        ((data_processed && ! send_ready_state_known && c->sk_nonblck) ||
-         (has_sock_err && c->sk_nonblck)))
-    {
-      conn_process_send (c, has_sock_err);
-      if (! conn_process_data (c))
-        return false;
-      data_processed = true;
-    }
-  }
-  if (! data_processed)
-    return conn_process_data (c);
-  return true;
-
-#if 0 // TODO: re-implement fasttrack as a single unified buffer sending
-  if (! force_close)
-  {
-    /* No need to check value of 'ret' here as closed connection
-     * cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */
-    if ( (MHD_EVENT_LOOP_INFO_WRITE == c->event_loop_info) &&
-         write_ready)
-    {
-      MHD_connection_handle_write (c);
-      ret = MHD_connection_handle_idle (c);
-      states_info_processed = true;
-    }
-  }
-  else
-  {
-    MHD_connection_close_ (c,
-                           MHD_REQUEST_TERMINATED_WITH_ERROR);
-    return MHD_connection_handle_idle (c);
-  }
-
-  if (! states_info_processed)
-  {   /* Connection is not read or write ready, but external conditions
-       * may be changed and need to be processed. */
-    ret = MHD_connection_handle_idle (c);
-  }
-  /* Fast track for fast connections. */
-  /* If full request was read by single read_handler() invocation
-     and headers were completely prepared by single MHD_connection_handle_idle()
-     then try not to wait for next sockets polling and send response
-     immediately.
-     As writeability of socket was not checked and it may have
-     some data pending in system buffers, use this optimization
-     only for non-blocking sockets. */
-  /* No need to check 'ret' as connection is always in
-   * MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */
-  else if (on_fasttrack && c->sk_nonblck)
-  {
-    if (MHD_CONNECTION_HEADERS_SENDING == c->state)
-    {
-      MHD_connection_handle_write (c);
-      /* Always call 'MHD_connection_handle_idle()' after each read/write. */
-      ret = MHD_connection_handle_idle (c);
-    }
-    /* If all headers were sent by single write_handler() and
-     * response body is prepared by single MHD_connection_handle_idle()
-     * call - continue. */
-    if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
-        (MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
-    {
-      MHD_connection_handle_write (c);
-      ret = MHD_connection_handle_idle (c);
-    }
-  }
-
-  /* All connection's data and states are processed for this turn.
-   * If connection already has more data to be processed - use
-   * zero timeout for next select()/poll(). */
-  /* Thread-per-connection do not need global zero timeout as
-   * connections are processed individually. */
-  /* Note: no need to check for read buffer availability for
-   * TLS read-ready connection in 'read info' state as connection
-   * without space in read buffer will be marked as 'info block'. */
-  if ( (! c->daemon->data_already_pending) &&
-       (! MHD_D_IS_USING_THREAD_PER_CONN_ (c->daemon)) )
-  {
-    if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info))
-      c->daemon->data_already_pending = true;
-#ifdef HTTPS_SUPPORT
-    else if ( (c->tls_read_ready) &&
-              (0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) )
-      c->daemon->data_already_pending = true;
-#endif /* HTTPS_SUPPORT */
-  }
-  return ret;
-#endif
-}

+ 15 - 4
src/mhd2/events_process.c

@@ -40,6 +40,9 @@
 #include "daemon_logger.h"
 #include "mhd_connection.h"
 #include "daemon_add_conn.h"
+#include "daemon_funcs.h"
+#include "conn_data_process.h"
+
 
 
 MHD_FN_PAR_NONNULL_ (1) static void
@@ -209,15 +212,20 @@ daemon_accept_new_conns (struct MHD_Daemon *restrict d)
 static bool
 daemon_process_all_act_coons (struct MHD_Daemon *restrict d)
 {
-  struct MHD_Connection *restrict c;
+  struct MHD_Connection *c;
   mhd_assert (! mhd_D_HAS_WORKERS (d));
 
-  for (c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready); c != NULL;
-       c = mhd_DLINKEDL_GET_NEXT (c, proc_ready))
+  c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready);
+  while (NULL != c)
   {
+    struct MHD_Connection *next;
+    next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
+    if (! mhd_conn_process_recv_send_data(c))
+      mhd_conn_close_final(c);
 
+    c = next;
   }
-  return false;
+  return true;
 }
 
 
@@ -524,6 +532,9 @@ mhd_worker_all_events (void *cls)
 
   while (! d->threading.stop_requested)
   {
+    if (d->threading.resume_requested)
+      mhd_daemon_resume_conns (d);
+
     if (! process_all_events_and_data (d))
       break;
   }

+ 2 - 2
src/mhd2/http_prot_ver.h

@@ -44,9 +44,9 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion
   ,
   MHD_HTTP_VERSION_1_1 = 2
   ,
-  MHD_HTTP_VERSION_2_0 = 3
+  MHD_HTTP_VERSION_2 = 3
   ,
-  MHD_HTTP_VERSION_3_0 = 4
+  MHD_HTTP_VERSION_3 = 4
   ,
   MHD_HTTP_VERSION_FUTURE = 255
 };

+ 2 - 2
src/mhd2/http_status_str.c

@@ -175,7 +175,7 @@ static const struct mhd_HttpStatusesBlock statuses[] = {
 };
 
 MHD_EXTERN_ MHD_FN_CONST_ const struct MHD_String *
-MHD_HTTP_status_code_to_string (enum MHD_StatusCode code)
+MHD_HTTP_status_code_to_string (enum MHD_HTTP_StatusCode code)
 {
   struct MHD_String *res;
   const unsigned int code_i = (unsigned int) code;
@@ -193,7 +193,7 @@ MHD_HTTP_status_code_to_string (enum MHD_StatusCode code)
 
 
 MHD_INTERNAL MHD_FN_CONST_ const struct MHD_String *
-mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
+mhd_HTTP_status_code_to_string_int (enum MHD_HTTP_StatusCode code)
 {
   static const struct MHD_String no_status =
     mhd_MSTR_INIT ("Nonstandard Status");

+ 1 - 1
src/mhd2/http_status_str.h

@@ -39,7 +39,7 @@
  * @return pointer to MHD_String, never NULL.
  */
 MHD_INTERNAL const struct MHD_String *
-mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
+mhd_HTTP_status_code_to_string_int (enum MHD_HTTP_StatusCode code)
 MHD_FN_CONST_;
 
 

+ 73 - 0
src/mhd2/mhd_atomic_counter.c

@@ -0,0 +1,73 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_atomic_counter.c
+ * @brief  The definition of the atomic counter functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "mhd_atomic_counter.h"
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt)
+{
+  mhd_ATOMIC_COUNTER_TYPE ret;
+
+  mhd_mutex_lock_chk(&(pcnt->lock));
+  ret = ++(pcnt->count);
+  mhd_mutex_unlock_chk(&(pcnt->lock));
+
+  mhd_assert (0 != ret); /* check for overflow */
+
+  return ret;
+}
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt)
+{
+  mhd_ATOMIC_COUNTER_TYPE ret;
+
+  mhd_mutex_lock_chk(&(pcnt->lock));
+  ret = --(pcnt->count);
+  mhd_mutex_unlock_chk(&(pcnt->lock));
+
+  mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); /* check for underflow */
+
+  return ret;
+}
+
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt)
+{
+  mhd_ATOMIC_COUNTER_TYPE ret;
+
+  mhd_mutex_lock_chk(&(pcnt->lock));
+  ret = pcnt->count;
+  mhd_mutex_unlock_chk(&(pcnt->lock));
+
+  return ret;
+}
+
+#endif /* mhd_ATOMIC_BY_LOCKS */

+ 203 - 0
src/mhd2/mhd_atomic_counter.h

@@ -0,0 +1,203 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_atomic_counter.h
+ * @brief  The definition of the atomic counter type and related functions
+ *         declarations
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_ATOMIC_COUNTER_H
+#define MHD_ATOMIC_COUNTER_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h" /* for size_t */
+
+/* Use 'size_t' to make sure it would never overflow when used for
+ * MHD needs. */
+
+/**
+ * The type used to contain the counter value.
+ * Always unsigned.
+ */
+#define mhd_ATOMIC_COUNTER_TYPE size_t
+/**
+ * The maximum counter value
+ */
+#define mhd_ATOMIC_COUNTER_MAX \
+  ((mhd_ATOMIC_COUNTER_TYPE)(~((mhd_ATOMIC_COUNTER_TYPE)0)))
+
+#ifdef MHD_USE_THREADS
+
+/**
+ * Atomic operations are based on locks
+ */
+#  define mhd_ATOMIC_BY_LOCKS 1
+
+#else  /* ! MHD_USE_THREADS */
+
+/**
+ * Atomic because single thread environment is used
+ */
+#  define mhd_ATOMIC_SINGLE_THREAD 1
+#endif /* ! MHD_USE_THREADS */
+
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+#  include "mhd_locks.h"
+#  include "sys_bool_type.h"
+
+/**
+ * The atomic counter
+ */
+struct mhd_AtomicCounter
+{
+  /**
+   * Counter value.
+   * Must be read or written only with @a lock held.
+   */
+  volatile mhd_ATOMIC_COUNTER_TYPE count;
+  /**
+   * The mutex.
+   */
+  mhd_mutex lock;
+};
+
+#elif defined(mhd_ATOMIC_SINGLE_THREAD)
+
+/**
+ * The atomic counter
+ */
+struct mhd_AtomicCounter
+{
+  /**
+   * Counter value.
+   */
+  volatile mhd_ATOMIC_COUNTER_TYPE count;
+};
+
+#endif /* mhd_ATOMIC_SINGLE_THREAD */
+
+
+#if defined(mhd_ATOMIC_BY_LOCKS)
+
+/**
+ * Initialise the counter to specified value.
+ * @param pcnt the pointer to the counter to initialise
+ * @param initial_value the initial value for the counter
+ * @return 'true' if succeed, "false' if failed
+ * @warning Must not be called for the counters that has been initialised
+ *          already.
+ */
+#  define mhd_atomic_counter_init(pcnt,initial_value) \
+  ((pcnt)->count=(initial_value), mhd_mutex_init_short(&(pcnt->lock)))
+
+/**
+ * Deinitialise the counter.
+ * @param pcnt the pointer to the counter to deinitialise
+ * @warning Must be called only for the counters that has been initialised.
+ */
+#  define mhd_atomic_counter_deinit(pcnt) mhd_mutex_destroy_chk(&((pcnt)->lock))
+
+/**
+ * Atomically increment the value of the counter
+ * @param pcnt the pointer to the counter to increment
+ */
+#  define mhd_atomic_counter_inc(pcnt)  do { \
+      mhd_mutex_lock_chk(&(pcnt->lock));     \
+      ++(pcnt->count);                       \
+      mhd_mutex_unlock_chk(&(pcnt->lock)); } while (0)
+
+/**
+ * Atomically increment the value of the counter and return the result
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt);
+
+/**
+ * Atomically decrement the value of the counter and return the result
+ * @param pcnt the pointer to the counter to decrement
+ * @return the final/resulting counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt);
+
+/**
+ * Atomically get the value of the counter
+ * @param pcnt the pointer to the counter to get
+ * @return the counter value
+ */
+MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
+mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt);
+
+#elif defined(mhd_ATOMIC_SINGLE_THREAD)
+
+/**
+ * Initialise the counter to specified value.
+ * @param pcnt the pointer to the counter to initialise
+ * @param initial_value the initial value for the counter
+ * @return 'true' if succeed, "false' if failed
+ * @warning Must not be called for the counters that has been initialised
+ *          already.
+ */
+#  define mhd_atomic_counter_init(pcnt,initial_value) \
+  ((pcnt)->count=(initial_value), (! 0))
+
+/**
+ * Deinitialise the counter.
+ * @param pcnt the pointer to the counter to deinitialise
+ * @warning Must be called only for the counters that has been initialised.
+ */
+#  define mhd_atomic_counter_deinit(pcnt) ((void)0)
+
+/**
+ * Atomically increment the value of the counter
+ * @param pcnt the pointer to the counter to increment
+ */
+#  define mhd_atomic_counter_inc(pcnt)  do { ++(pcnt->count); } while (0)
+
+/**
+ * Atomically increment the value of the counter and return the result
+ * @param pcnt the pointer to the counter to increment
+ * @return the final/resulting counter value
+ */
+#  define mhd_atomic_counter_inc_get(pcnt) (++((pcnt)->count))
+
+/**
+ * Atomically decrement the value of the counter and return the result
+ * @param pcnt the pointer to the counter to decrement
+ * @return the final/resulting counter value
+ */
+#  define mhd_atomic_counter_dec_get(pcnt) (--((pcnt)->count))
+
+/**
+ * Atomically get the value of the counter
+ * @param pcnt the pointer to the counter to get
+ * @return the counter value
+ */
+#  define mhd_atomic_counter_get(pcnt) ((pcnt)->count)
+
+#endif /* mhd_ATOMIC_SINGLE_THREAD */
+
+#endif /* ! MHD_ATOMIC_COUNTER_H */

+ 81 - 2
src/mhd2/mhd_connection.h

@@ -37,6 +37,7 @@
 
 #include "sys_bool_type.h"
 #include "sys_base_types.h"
+#include "mhd_socket_type.h"
 
 #include "mhd_threads.h"
 
@@ -50,6 +51,22 @@
 
 #include "mhd_public_api.h"
 
+/**
+ * Minimum reasonable size by which MHD tries to increment read/write buffers.
+ * We usually begin with half the available pool space for the
+ * IO-buffer, but if absolutely needed we additively grow by the
+ * number of bytes given here (up to -- theoretically -- the full pool
+ * space).
+ *
+ * Currently set to reasonable maximum MSS size.
+ */
+#define mhd_BUF_INC_SIZE 1500
+
+/**
+ * Message to transmit when http 1.1 request is received
+ */
+#define mdh_HTTP_1_1_100_CONTINUE_REPLY "HTTP/1.1 100 Continue\r\n\r\n"
+
 
 struct MHD_Connection; /* forward declaration */
 
@@ -113,6 +130,56 @@ enum MHD_FIXED_FLAGS_ENUM_ mhd_SocketNetState
 };
 
 
+/**
+ * The reason for the socket closure
+ */
+enum mhd_SocketClosureReason
+{
+  /**
+   * The socket is not closed / closing.
+   */
+  mhd_SCOKET_CLOSURE_REASON_NO_CLOSURE = 0
+  ,
+  /**
+   * Socket has to be closed because HTTP protocol successfully finished data
+   * exchange.
+   */
+  mhd_SCOKET_CLOSURE_REASON_PROTOCOL_SUCCESS
+  ,
+  /**
+   * Socket has to be closed because remote side violated some HTTP
+   * specification requirements or request processed with an error.
+   * The HTTP error response should be sent.
+   */
+  mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_SOFT
+  ,
+  /**
+   * Timeout expired
+   */
+  mhd_SCOKET_CLOSURE_REASON_TIMEOUT
+  ,
+  /**
+   * Socket has to be closed because received data cannot be interpreted as
+   * valid HTTP data.
+   */
+  mhd_SCOKET_CLOSURE_REASON_PROTOCOL_FAILURE_HARD
+  ,
+  /**
+   * Unrecoverable TLS error
+   */
+  mhd_SCOKET_CLOSURE_REASON_TLS_ERROR
+  ,
+  /**
+   * The remote side closed connection in abortive way
+   */
+  mhd_SCOKET_CLOSURE_REASON_REMOTE_HARD_DISCONN
+  ,
+  /**
+   * The connection has been broken for some reason
+   */
+  mhd_SCOKET_CLOSURE_REASON_CONN_BROKEN
+};
+
 /**
  * States in a state machine for a connection.
  *
@@ -317,7 +384,7 @@ struct MHD_Connection
 
   /**
    * Set to 'true' when the client shut down write/send and
-   * the last byte from remote has been read.
+   * __the last byte from the remote has been read__.
    */
   bool sk_rmt_shut_wr;
 
@@ -331,6 +398,16 @@ struct MHD_Connection
    */
   mhd_DLNKDL_LINKS (MHD_Connection,by_timeout);
 
+  /**
+   * True if connection is suspended
+   */
+  bool suspended;
+
+  /**
+   * True if connection is resuming
+   */
+  bool resuming;
+
   /**
    * Reference to the MHD_Daemon struct.
    */
@@ -465,7 +542,7 @@ struct MHD_Connection
   enum mhd_Tristate is_nonip;
 
   /**
-   * true if #socket_fd is non-blocking, false otherwise.
+   * true if @a socket_fd is non-blocking, false otherwise.
    */
   bool sk_nonblck;
 
@@ -474,10 +551,12 @@ struct MHD_Connection
    */
   bool sk_spipe_suppress;
 
+//#ifndef MHD_WINSOCK_SOCKETS // TODO: conditionally use in the code
   /**
    * Tracks TCP_CORK / TCP_NOPUSH of the connection socket.
    */
   enum mhd_Tristate sk_corked;
+//#endif
 
   /**
    * Tracks TCP_NODELAY state of the connection socket.

+ 30 - 8
src/mhd2/mhd_daemon.h

@@ -681,6 +681,11 @@ struct mhd_DaemonThreadingData
    */
   volatile bool stop_requested;
 
+  /**
+   * 'True' if resuming of any connection has been requested.
+   */
+  volatile bool resume_requested;
+
   /**
    * The handle of the daemon's thread (if managed by the daemon)
    */
@@ -765,6 +770,29 @@ struct mhd_DaemonConnections
 };
 
 
+/**
+ * Settings for requests processing
+ */
+struct mhd_DaemonRequestProcessingSettings
+{
+  /**
+   * Request callback.
+   * The main request processing callback.
+   */
+  MHD_RequestCallback cb;
+
+  /**
+   * The closure for @a req_cb
+   */
+  void *cb_cls;
+
+  /**
+   * Protocol strictness enforced by MHD on clients.
+   */
+  enum MHD_ProtocolStrictLevel strictnees;
+};
+
+
 #ifndef NDEBUG
 /**
  * Various debugging data
@@ -830,15 +858,9 @@ struct MHD_Daemon
   /* Request processing data */
 
   /**
-   * Request callback.
-   * The main request processing callback.
-   */
-  MHD_RequestCallback req_cb;
-
-  /**
-   * The closure for @a req_cb
+   * Settings for requests processing
    */
-  void *req_cb_cls;
+  struct mhd_DaemonRequestProcessingSettings req_cfg;
 
   /* Other data */
 

+ 15 - 6
src/mhd2/mhd_iovec.h

@@ -29,6 +29,7 @@
 
 #include "mhd_sys_options.h"
 #include "mhd_socket_type.h"
+#include "sys_base_types.h"
 
 #if defined(HAVE_WRITEV) || defined(HAVE_SENDMSG)
 #  ifdef HAVE_SYS_TYPES_H
@@ -47,6 +48,8 @@
 #  endif
 #endif
 
+#include "mhd_limits.h"
+
 #if defined(MHD_WINSOCK_SOCKETS)
 /**
  * Internally used I/O vector type for use with winsock.
@@ -58,24 +61,30 @@ struct mhd_w32_iovec
   char *iov_base;
 };
 typedef struct mhd_w32_iovec mhd_iovec;
-#define MHD_IOV_ELMN_MAX_SIZE    ULONG_MAX
-typedef unsigned long mhd_iov_size;
+#define mhd_IOV_ELMN_MAX_SIZE    ULONG_MAX
+typedef unsigned long mhd_iov_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE    LONG_MAX
+typedef long mhd_iov_ret_type;
 #elif defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
 /**
  * Internally used I/O vector type for use when writev or sendmsg
  * is available. Matches system "struct iovec".
  */
 typedef struct iovec mhd_iovec;
-#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
-typedef size_t mhd_iov_size;
+#define mhd_IOV_ELMN_MAX_SIZE    SIZE_MAX
+typedef size_t mhd_iov_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE     SSIZE_MAX
+typedef ssize_t mhd_iov_ret_type;
 #else
 /**
  * Internally used I/O vector type for use when writev or sendmsg
  * is not available.
  */
 typedef struct MHD_IoVec mhd_iovec;
-#define MHD_IOV_ELMN_MAX_SIZE    SIZE_MAX
-typedef size_t mhd_iov_size;
+#define mhd_IOV_ELMN_MAX_SIZE    SIZE_MAX
+typedef size_t mhd_iov_elmn_size;
+#define mhd_IOV_RET_MAX_SIZE     SSIZE_MAX
+typedef ssize_t mhd_iov_ret_type;
 #endif
 
 

+ 1 - 1
src/mhd2/mhd_itc.h

@@ -289,7 +289,7 @@ mhd_itc_nonblocking (struct mhd_itc *pitc);
  * @return non-zero if succeeded, zero otherwise
  */
 #  define mhd_itc_activate(itc) \
-        ((0 < mhd_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ())
+        ((0 < mhd_sys_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ())
 
 /**
  * Return read FD of @a itc which can be used for poll(), select() etc.

+ 4 - 0
src/mhd2/mhd_lib_init.c

@@ -28,6 +28,7 @@
 #include "mhd_panic.h"
 #include "mhd_mono_clock.h"
 #include "mhd_socket_type.h"
+#include "mhd_send.h"
 #ifdef MHD_WINSOCK_SOCKETS
 #  include <winsock2.h>
 #endif
@@ -46,12 +47,15 @@ mhd_lib_global_init (void)
       MHD_PANIC ("Failed to initialise WinSock.");
   }
 #endif /* MHD_WINSOCK_SOCKETS */
+  MHD_monotonic_msec_counter_init();
+  mhd_send_init_static_vars();
 }
 
 
 void
 mhd_lib_global_deinit (void)
 {
+  MHD_monotonic_msec_counter_finish();
 #if defined(MHD_WINSOCK_SOCKETS)
   (void) WSACleanup ();
 #endif /* MHD_WINSOCK_SOCKETS */

+ 177 - 0
src/mhd2/mhd_limits.h

@@ -0,0 +1,177 @@
+/*
+  This file is part of libmicrohttpd
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/**
+ * @file src/mhd2/mhd_limits.h
+ * @brief  limits values definitions
+ * @author Karlson2k (Evgeny Grin)
+ *
+ * This file provides maximum types values as macros. Macros may not work
+ * in preprocessor expressions, while macros always work in compiler
+ * expressions.
+ * This file does not include <stdint.h> and other type-definitions files.
+ * For best portability, make sure that this file is included after required
+ * type-definitions files.
+ */
+
+#ifndef MHD_LIMITS_H
+#define MHD_LIMITS_H
+
+#include "mhd_sys_options.h"
+
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#define mhd_UNSIGNED_TYPE_MAX(type) ((type)(~((type) 0)))
+
+/* Assume 8 bits per byte, no padding bits. */
+#define mhd_SIGNED_TYPE_MAX(type) \
+  ( (type) ((( ((type) 1) << (sizeof(type) * 8 - 2)) - 1) * 2 + 1) )
+
+/* The maximum value for signed type, based on knowledge of unsigned counterpart
+   type */
+#define mhd_SIGNED_TYPE_MAX2(type,utype) ((type)(((utype)(~((utype)0))) >> 1))
+
+#define mhd_IS_TYPE_SIGNED(type) (((type) 0) > ((type) - 1))
+
+#if defined(__GNUC__) || defined(__clang__)
+#  define mhd_USE_PREDEF_LIMITS
+#endif
+
+#ifndef INT_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT_MAX__)
+#    define INT_MAX __INT_MAX__
+#  else  /* ! __INT_MAX__ */
+#    define INT_MAX mhd_SIGNED_TYPE_MAX2 (int, unsigned int)
+#  endif /* ! __INT_MAX__ */
+#endif /* ! INT_MAX */
+
+#ifndef UINT_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT_MAX__)
+#    define UINT_MAX __UINT_MAX__
+#  else  /* ! __UINT_MAX__ */
+#    define UINT_MAX mhd_UNSIGNED_TYPE_MAX (unsigned int)
+#  endif /* ! __UINT_MAX__ */
+#endif /* !UINT_MAX */
+
+#ifndef LONG_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__LONG_MAX__)
+#    define LONG_MAX __LONG_MAX__
+#  else  /* ! __LONG_MAX__ */
+#    define LONG_MAX mhd_SIGNED_TYPE_MAX2 (long, unsigned long)
+#  endif /* ! __LONG_MAX__ */
+#endif /* !LONG_MAX */
+
+#ifndef ULONG_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__ULONG_MAX__)
+#    define ULONG_MAX ULONG_MAX
+#  else  /* ! __ULONG_MAX__ */
+#    define ULONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long)
+#  endif /* ! __ULONG_MAX__ */
+#endif /* !ULONG_MAX */
+
+#ifndef ULLONG_MAX
+#  ifdef ULONGLONG_MAX
+#    define ULLONG_MAX ULONGLONG_MAX
+#  else  /* ! ULONGLONG_MAX */
+#    define ULLONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long long)
+#  endif /* ! ULONGLONG_MAX */
+#endif /* !ULLONG_MAX */
+
+#ifndef INT32_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT32_MAX__)
+#    define INT32_MAX __INT32_MAX__
+#  else  /* ! __INT32_MAX__ */
+#    define INT32_MAX ((int32_t) 0x7FFFFFFF)
+#  endif /* ! __INT32_MAX__ */
+#endif /* !INT32_MAX */
+
+#ifndef UINT32_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT32_MAX__)
+#    define UINT32_MAX __UINT32_MAX__
+#  else  /* ! __UINT32_MAX__ */
+#    define UINT32_MAX ((uint32_t) 0xFFFFFFFFU)
+#  endif /* ! __UINT32_MAX__ */
+#endif /* !UINT32_MAX */
+
+#ifndef INT64_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT64_MAX__)
+#    define INT64_MAX __INT64_MAX__
+#  else  /* ! __INT64_MAX__ */
+#    define INT64_MAX ((int64_t) 0x7FFFFFFFFFFFFFFF)
+#  endif /* ! __UINT64_MAX__ */
+#endif /* !INT64_MAX */
+
+#ifndef UINT64_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT64_MAX__)
+#    define UINT64_MAX __UINT64_MAX__
+#  else  /* ! __UINT64_MAX__ */
+#    define UINT64_MAX ((uint64_t) 0xFFFFFFFFFFFFFFFFU)
+#  endif /* ! __UINT64_MAX__ */
+#endif /* !UINT64_MAX */
+
+#ifndef SIZE_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__SIZE_MAX__)
+#    define SIZE_MAX __SIZE_MAX__
+#  else  /* ! __SIZE_MAX__ */
+#    define SIZE_MAX mhd_UNSIGNED_TYPE_MAX (size_t)
+#  endif /* ! __SIZE_MAX__ */
+#endif /* !SIZE_MAX */
+
+#ifndef SSIZE_MAX
+#  if defined(mhd_USE_PREDEF_LIMITS) && defined(__SSIZE_MAX__)
+#    define SSIZE_MAX __SSIZE_MAX__
+#  else
+#    define SSIZE_MAX mhd_SIGNED_TYPE_MAX2 (ssize_t, size_t)
+#  endif
+#endif /* ! SSIZE_MAX */
+
+#ifndef OFF_T_MAX
+#  ifdef OFF_MAX
+#    define OFF_T_MAX OFF_MAX
+#  elif defined(OFFT_MAX)
+#    define OFF_T_MAX OFFT_MAX
+#  elif defined(__APPLE__) && defined(__MACH__)
+#    define OFF_T_MAX INT64_MAX
+#  else
+#    define OFF_T_MAX mhd_SIGNED_TYPE_MAX (off_t)
+#  endif
+#endif /* !OFF_T_MAX */
+
+#if defined(_LARGEFILE64_SOURCE) && ! defined(OFF64_T_MAX)
+#  define OFF64_T_MAX mhd_SIGNED_TYPE_MAX (off64_t)
+#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */
+
+#ifndef TIME_T_MAX
+#  define TIME_T_MAX ((time_t)                        \
+                      (mhd_IS_TYPE_SIGNED (time_t) ?  \
+                       mhd_SIGNED_TYPE_MAX (time_t) : \
+                       mhd_UNSIGNED_TYPE_MAX (time_t)))
+#endif /* !TIME_T_MAX */
+
+#ifndef TIMEVAL_TV_SEC_MAX
+#  ifndef _WIN32
+#    define TIMEVAL_TV_SEC_MAX TIME_T_MAX
+#  else  /* _WIN32 */
+#    define TIMEVAL_TV_SEC_MAX LONG_MAX
+#  endif /* _WIN32 */
+#endif /* !TIMEVAL_TV_SEC_MAX */
+
+#endif /* MHD_LIMITS_H */

+ 1 - 4
src/mhd2/mhd_mempool.c

@@ -60,10 +60,7 @@
 #  endif /* HAVE_SYS_PARAM_H */
 #endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
 
-#ifndef SIZE_MAX
-/* This file does not use SIZE_MAX macro with preprocessor */
-#  define SIZE_MAX ((size_t) ~((size_t) 0))
-#endif
+#include "mhd_limits.h"
 
 /**
  * Fallback value of page size

+ 39 - 56
src/mhd2/mhd_recv.c

@@ -34,73 +34,56 @@
 #include "sys_sockets_headers.h"
 #include "mhd_sockets_macros.h"
 
-#include <limits.h>
+#include "mhd_limits.h"
+#include "mhd_socket_error.h"
 
-#ifndef SSIZE_MAX
-#  define SSIZE_MAX ((ssize_t) ((~((size_t) 0)) >> 1))
-#endif
 
+static MHD_FN_PAR_NONNULL_ALL_
+MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+mhd_plain_recv (struct MHD_Connection *restrict c,
+                size_t buf_size,
+                char buf[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
+                size_t *restrict received)
+{
+  /* Plain TCP connection */
+  ssize_t res;
+  enum mhd_SocketError err;
+
+  if (MHD_SCKT_SEND_MAX_SIZE_ < buf_size)
+    buf_size = MHD_SCKT_SEND_MAX_SIZE_;
+
+  res = mhd_sys_recv (c->socket_fd, buf, buf_size);
+  if (0 <= res)
+  {
+    *received = (size_t) res;
+    if (buf_size > (size_t) res)
+      c->sk_ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+                    (((unsigned int) c->sk_ready)
+                     & (~mhd_SOCKET_NET_STATE_RECV_READY));
+    return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */
+  }
+
+  err = mhd_socket_error_get_from_sys_err(mhd_SCKT_GET_LERR ());
+
+  if (mhd_SOCKET_ERR_AGAIN == err)
+    c->sk_ready = (enum mhd_SocketNetState) /* Clear 'recv-ready' */
+                  (((unsigned int) c->sk_ready)
+                   & (~mhd_SOCKET_NET_STATE_RECV_READY));
+
+  return err; /* Failure exit point */
+}
 
 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
-MHD_FN_PAR_OUT_SIZE_ (3,2) \
-  MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
+MHD_FN_PAR_OUT_SIZE_ (3,2) MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
 mhd_recv (struct MHD_Connection *restrict c,
           size_t buf_size,
-          void *restrict buf,
+          char buf[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
           size_t *restrict received)
 {
   mhd_assert (MHD_INVALID_SOCKET != c->socket_fd);
   mhd_assert (MHD_CONNECTION_CLOSED != c->state);
 
   // TODO: implement TLS support
-  if (1) /* Plain TCP connection */
-  {
-    ssize_t res;
-    int err;
-
-    if (MHD_SCKT_SEND_MAX_SIZE_ < buf_size)
-      buf_size = MHD_SCKT_SEND_MAX_SIZE_;
-
-    res = mhd_sys_recv (c->socket_fd, buf, buf_size);
-    if (0 <= res)
-    {
-      *received = (size_t) res;
-      if (buf_size > (size_t) res)
-        c->sk_ready =
-          (enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
-                                     & (~mhd_SOCKET_NET_STATE_RECV_READY));
-      return mhd_SOCKET_ERR_NO_ERROR; /* Success exit point */
-    }
-
-    err = mhd_SCKT_GET_LERR ();
-
-    if (mhd_SCKT_ERR_IS_EAGAIN (err))
-    {
-      c->sk_ready = /* Clear 'recv-ready' */
-                    (enum mhd_SocketNetState)
-                    (((unsigned int) c->sk_ready)
-                     & (~mhd_SOCKET_NET_STATE_RECV_READY));
-      return mhd_SOCKET_ERR_AGAIN;
-    }
-    else if (mhd_SCKT_ERR_IS_CONNRESET (err))
-      return mhd_SOCKET_ERR_CONNRESET;
-    else if (mhd_SCKT_ERR_IS_EINTR (err))
-      return mhd_SOCKET_ERR_INTR;
-    else if (mhd_SCKT_ERR_IS_CONN_BROKEN (err))
-      return mhd_SOCKET_ERR_CONN_BROKEN;
-    else if (mhd_SCKT_ERR_IS_NOTCONN (err))
-      return mhd_SOCKET_ERR_NOTCONN;
-    else if (mhd_SCKT_ERR_IS_LOW_MEM (err))
-      return mhd_SOCKET_ERR_NOMEM;
-    else if (mhd_SCKT_ERR_IS_BADF (err))
-      return mhd_SOCKET_ERR_BADF;
-    else if (mhd_SCKT_ERR_IS_EINVAL (err))
-      return mhd_SOCKET_ERR_INVAL;
-    else if (mhd_SCKT_ERR_IS_OPNOTSUPP (err))
-      return mhd_SOCKET_ERR_OPNOTSUPP;
-    else if (mhd_SCKT_ERR_IS_NOTSOCK (err))
-      return mhd_SOCKET_ERR_NOTSOCK;
-  }
 
-  return mhd_SOCKET_ERR_OTHER;
+  return mhd_plain_recv(c, buf_size, buf, received);
 }

+ 2 - 1
src/mhd2/mhd_recv.h

@@ -49,8 +49,9 @@ struct MHD_Connection; /* forward declaration */
 MHD_INTERNAL enum mhd_SocketError
 mhd_recv (struct MHD_Connection *restrict c,
           size_t buf_size,
-          void *restrict buf,
+          char buffer[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
           size_t *restrict received)
 MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2) MHD_FN_PAR_OUT_ (4);
 
+
 #endif /* ! MHD_RECV_H */

+ 39 - 12
src/mhd2/mhd_reply.h

@@ -55,13 +55,37 @@ struct MHD_Reply_Properties
   bool chunked; /**< Use chunked encoding for reply */
 };
 
-#if defined(MHD_USE_SENDFILE)
-enum MHD_resp_sender_
+/**
+ * The location of the reply content
+ */
+enum MHD_FIXED_ENUM_ mhd_ReplyContentLocation
 {
-  MHD_resp_sender_std = 0,
-  MHD_resp_sender_sendfile
+  /**
+   * Reply content is absent
+   */
+  mhd_REPLY_CNTN_LOC_NOWHERE = 0
+  ,
+  /**
+   * Reply content is in the response buffer
+   */
+  mhd_REPLY_CNTN_LOC_RESP_BUF
+  ,
+  /**
+   * Reply content is in the connection buffer
+   */
+  mhd_REPLY_CNTN_LOC_CONN_BUF
+  ,
+  /**
+   * Reply content is in the vector data
+   */
+  mhd_REPLY_CNTN_LOC_IOV
+  ,
+  /**
+   * Reply content is in the file, to be used with sendfile() function
+   */
+  mhd_REPLY_CNTN_LOC_FILE
 };
-#endif /* MHD_USE_SENDFILE */
+
 
 /**
  * Reply-specific values.
@@ -88,11 +112,13 @@ struct MHD_Reply
   bool responseIcy;
 
   /**
-   * Current write position in the actual response
-   * (excluding headers, content only; should be 0
-   * while sending headers).
+   * Current rest position in the actual content (should be 0 while
+   * sending headers).
+   * When sending buffers located in the connection buffers, it is updated
+   * when the data copied to the buffers. In other cases it is updated when
+   * data is actually sent.
    */
-  uint_fast64_t rsp_write_position;
+  uint_fast64_t rsp_cntn_read_pos;
 
   /**
    * The copy of iov response.
@@ -102,9 +128,10 @@ struct MHD_Reply
    */
   struct mhd_iovec_track resp_iov;
 
-#if defined(MHD_USE_SENDFILE)
-  enum MHD_resp_sender_ resp_sender;
-#endif /* MHD_USE_SENDFILE */
+  /**
+   * The location of the reply content
+   */
+  enum mhd_ReplyContentLocation cntn_loc;
 
   /**
    * Reply-specific properties

+ 11 - 30
src/mhd2/mhd_request.h

@@ -32,6 +32,8 @@
 #include "mhd_sys_options.h"
 #include "sys_base_types.h"
 #include "sys_bool_type.h"
+#include "mhd_public_api.h"
+
 #include "mhd_dlinked_list.h"
 
 #include "http_prot_ver.h"
@@ -175,48 +177,27 @@ union MHD_StartOrSize
   size_t size;
 };
 
-struct MHD_HTTP_Req_Header; /* forward declarations */
+struct mhd_RequestField; /* forward declarations */
 
-mhd_DLINKEDL_LINKS_DEF (MHD_HTTP_Req_Header);
+mhd_DLINKEDL_LINKS_DEF (mhd_RequestField);
 
 /**
  * Header, footer, or cookie for HTTP request.
  */
-struct MHD_HTTP_Req_Header
+struct mhd_RequestField
 {
   /**
-   * Headers are kept in a double-linked list.
-   */
-  mhd_DLNKDL_LINKS (MHD_HTTP_Req_Header,list);
-
-  /**
-   * The name of the header (key), without the colon.
+   * The field data
    */
-  const char *header;
-
-  /**
-   * The length of the @a header, not including the final zero termination.
-   */
-  size_t header_size;
+  struct MHD_NameValueKind field;
 
   /**
-   * The value of the header.
-   */
-  const char *value;
-
-  /**
-   * The length of the @a value, not including the final zero termination.
-   */
-  size_t value_size;
-
-  /**
-   * Type of the value.
+   * Headers are kept in a double-linked list.
    */
-  enum MHD_ValueKind kind;
-
+  mhd_DLNKDL_LINKS (mhd_RequestField,fields);
 };
 
-mhd_DLINKEDL_LIST_DEF (MHD_HTTP_Req_Header);
+mhd_DLINKEDL_LIST_DEF (mhd_RequestField);
 
 /**
  * Request-specific values.
@@ -228,7 +209,7 @@ struct MHD_Request
   /**
    * Linked list of parsed headers.
    */
-  mhd_DLNKDL_LIST (MHD_HTTP_Req_Header,list);
+  mhd_DLNKDL_LIST (mhd_RequestField,fields);
 
   /**
    * The action set by the application

+ 17 - 18
src/mhd2/mhd_response.h

@@ -39,7 +39,11 @@
 
 #include "mhd_iovec.h"
 
-#include "mhd_locks.h"
+#ifdef MHD_USE_THREADS
+#  include "mhd_locks.h"
+#endif
+
+#include "mhd_atomic_counter.h"
 
 
 struct ResponseOptions; /* forward declaration */
@@ -72,17 +76,17 @@ struct mhd_ResponseHeader
 /**
  * The type of content
  */
-enum mhd_ResponseContentType
+enum mhd_ResponseContentDataType
 {
-  mhd_RESPONSE_CONTENT_INVALID = 0
+  mhd_RESPONSE_CONTENT_DATA_INVALID = 0
   ,
-  mhd_RESPONSE_CONTENT_BUFFER
+  mhd_RESPONSE_CONTENT_DATA_BUFFER
   ,
-  mhd_RESPONSE_CONTENT_IOVEC
+  mhd_RESPONSE_CONTENT_DATA_IOVEC
   ,
-  mhd_RESPONSE_CONTENT_FILE
+  mhd_RESPONSE_CONTENT_DATA_FILE
   ,
-  mhd_RESPONSE_CONTENT_CALLBACK
+  mhd_RESPONSE_CONTENT_DATA_CALLBACK
 };
 
 /**
@@ -159,7 +163,7 @@ union mhd_ResponseContent
   const unsigned char *restrict buf;
 
   /**
-   * The
+   * The I/O vector data
    */
   struct mhd_ResponseIoVec iovec;
 
@@ -202,17 +206,10 @@ struct mhd_ResponseReuseData
    * The number of active uses of the response.
    * Used only when @a reusable is 'true'.
    * When number reached zero, the response is destroyed.
-   * Must be checked only under the @a cnt_lock
    */
-  volatile uint_fast64_t counter;
+  struct mhd_AtomicCounter counter;
 
 #ifdef MHD_USE_THREADS
-  /**
-   * The mutex for @a counter access.
-   * Used only when @a reusable is 'true'.
-   */
-  mhd_mutex cnt_lock;
-
   /**
    * The mutex for @a settings access.
    * Used only when @a reusable is 'true'.
@@ -261,6 +258,8 @@ struct mhd_ResponseDebug
 
 mhd_DLINKEDL_LIST_DEF (mhd_ResponseHeader);
 
+// TODO: Group members in structs
+
 struct MHD_Response
 {
   /**
@@ -272,12 +271,12 @@ struct MHD_Response
    * The size of the response.
    * #MHD_SIZE_UNKNOWN if size is undefined
    */
-  uint_fast64_t size;
+  uint_fast64_t cntn_size;
 
   /**
    * The type of the content data
    */
-  enum mhd_ResponseContentType cntn_type;
+  enum mhd_ResponseContentDataType cntn_dtype;
 
   /**
    * The data of the content of the response

Datei-Diff unterdrückt, da er zu groß ist
+ 373 - 435
src/mhd2/mhd_send.c


+ 75 - 64
src/microhttpd/mhd_send.h → src/mhd2/mhd_send.h

@@ -1,14 +1,14 @@
 /*
   This file is part of libmicrohttpd
-  Copyright (C) 2017-2021 Karlson2k (Evgeny Grin)
+  Copyright (C) 2017-2024 Evgeny Grin (Karlson2k)
   Copyright (C) 2019 ng0
 
-  This library is free software; you can redistribute it and/or
+  GNU libmicrohttpd is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
 
-  This library is distributed in the hope that it will be useful,
+  GNU 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
   Lesser General Public License for more details.
@@ -20,37 +20,29 @@
 */
 
 /**
- * @file mhd_send.h
+ * @file src/mhd2/mhd_send.h
  * @brief Declarations of send() wrappers.
- * @author ng0
  * @author Karlson2k (Evgeny Grin)
+ * @author ng0 (N. Gillmann)
  */
 
 #ifndef MHD_SEND_H
 #define MHD_SEND_H
 
-#include "platform.h"
-#include "internal.h"
-#if defined(HAVE_STDBOOL_H)
-#include <stdbool.h>
-#endif /* HAVE_STDBOOL_H */
-#include <errno.h>
-#include "mhd_sockets.h"
-#include "connection.h"
-#ifdef HTTPS_SUPPORT
-#include "connection_https.h"
-#endif
+#include "mhd_sys_options.h"
+
+#include "sys_base_types.h"
+#include "sys_bool_type.h"
+#include "mhd_socket_error.h"
 
-#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
-  defined(MHD_WINSOCK_SOCKETS)
-#define MHD_VECT_SEND 1
-#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */
+struct MHD_Connection; /* forward declaration */
+struct mhd_iovec_track; /* forward declaration */
 
 /**
  * Initialises static variables
  */
 void
-MHD_send_init_static_vars_ (void);
+mhd_send_init_static_vars (void);
 
 
 /**
@@ -58,20 +50,23 @@ MHD_send_init_static_vars_ (void);
  * and full buffer is sent.
  *
  * @param connection the MHD_Connection structure
- * @param buffer content of the buffer to send
  * @param buffer_size the size of the @a buffer (in bytes)
+ * @param buffer content of the buffer to send
  * @param push_data set to true to force push the data to the network from
  *                  system buffers (usually set for the last piece of data),
  *                  set to false to prefer holding incomplete network packets
  *                  (more data will be send for the same reply).
- * @return sum of the number of bytes sent from both buffers or
- *         error code (negative)
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
  */
-ssize_t
-MHD_send_data_ (struct MHD_Connection *connection,
-                const char *buffer,
-                size_t buffer_size,
-                bool push_data);
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_data (struct MHD_Connection *restrict connection,
+               size_t buf_size,
+               const char buf[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
+               bool push_data,
+               size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_OUT_ (5);
 
 
 /**
@@ -89,31 +84,62 @@ MHD_send_data_ (struct MHD_Connection *connection,
  *                          is provided by @a header and @a body,
  *                          set to false if additional body data
  *                          will be sent later
- * @return sum of the number of bytes sent from both buffers or
- *         error code (negative)
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ *                  in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
  */
-ssize_t
-MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
-                        const char *header,
-                        size_t header_size,
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_hdr_and_body (struct MHD_Connection *restrict connection,
+                       size_t header_size,
+                        const char *restrict header,
                         bool never_push_hdr,
-                        const char *body,
                         size_t body_size,
-                        bool complete_response);
+                        const char *restrict body,
+                        bool complete_response,
+                        size_t *restrict sent)
+MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_NONNULL_(3)
+MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) MHD_FN_PAR_OUT_ (8);
 
-#if defined(_MHD_HAVE_SENDFILE)
+#if defined(MHD_USE_SENDFILE)
 /**
  * Function for sending responses backed by file FD.
  *
  * @param connection the MHD connection structure
- * @return actual number of bytes sent
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ *                  in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
  */
-ssize_t
-MHD_send_sendfile_ (struct MHD_Connection *connection);
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (2) enum mhd_SocketError
+mhd_send_sendfile (struct MHD_Connection *restrict connection,
+                   size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ ;
 
 #endif
 
 
+/**
+ * Function for sending responses backed by a an array of memory buffers.
+ *
+ * @param connection the MHD connection structure
+ * @param r_iov the pointer to iov response structure with tracking
+ * @param push_data set to true to force push the data to the network from
+ *                  system buffers (usually set for the last piece of data),
+ *                  set to false to prefer holding incomplete network packets
+ *                  (more data will be send for the same reply).
+ * @param[out] sent the pointer to get amount of actually sent bytes
+ *                  in total (from both buffers combined)
+ * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets
+ *         the sent size) or socket error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_send_iovec (struct MHD_Connection *restrict connection,
+                struct mhd_iovec_track *const restrict r_iov,
+                bool push_data,
+                size_t *restrict sent)
+MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4);
+
 /**
  * Set required TCP_NODELAY state for connection socket
  *
@@ -123,9 +149,10 @@ MHD_send_sendfile_ (struct MHD_Connection *connection);
  * @return true if succeed, false if failed or not supported
  *         by the current platform / kernel.
  */
-bool
-MHD_connection_set_nodelay_state_ (struct MHD_Connection *connection,
-                                   bool nodelay_state);
+MHD_INTERNAL bool
+mhd_connection_set_nodelay_state (struct MHD_Connection *connection,
+                                  bool nodelay_state)
+MHD_FN_PAR_NONNULL_ALL_;
 
 
 /**
@@ -138,26 +165,10 @@ MHD_connection_set_nodelay_state_ (struct MHD_Connection *connection,
  * @return true if succeed, false if failed or not supported
  *         by the current platform / kernel.
  */
-bool
-MHD_connection_set_cork_state_ (struct MHD_Connection *connection,
-                                bool cork_state);
-
-
-/**
- * Function for sending responses backed by a an array of memory buffers.
- *
- * @param connection the MHD connection structure
- * @param r_iov the pointer to iov response structure with tracking
- * @param push_data set to true to force push the data to the network from
- *                  system buffers (usually set for the last piece of data),
- *                  set to false to prefer holding incomplete network packets
- *                  (more data will be send for the same reply).
- * @return actual number of bytes sent
- */
-ssize_t
-MHD_send_iovec_ (struct MHD_Connection *connection,
-                 struct MHD_iovec_track_ *const r_iov,
-                 bool push_data);
+MHD_INTERNAL bool
+mhd_connection_set_cork_state (struct MHD_Connection *connection,
+                                bool cork_state)
+MHD_FN_PAR_NONNULL_ALL_;
 
 
 #endif /* MHD_SEND_H */

+ 78 - 0
src/mhd2/mhd_socket_error.c

@@ -0,0 +1,78 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/mhd_socket_error.c
+ * @brief  The definition of mhd_SocketError-related functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_socket_error.h"
+#include "sys_sockets_headers.h"
+#include "mhd_sockets_macros.h"
+
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_sys_err (int socket_err)
+{
+  if (mhd_SCKT_ERR_IS_EAGAIN (socket_err))
+    return mhd_SOCKET_ERR_AGAIN;
+  else if (mhd_SCKT_ERR_IS_CONNRESET (socket_err))
+    return mhd_SOCKET_ERR_CONNRESET;
+  else if (mhd_SCKT_ERR_IS_EINTR (socket_err))
+    return mhd_SOCKET_ERR_INTR;
+  else if (mhd_SCKT_ERR_IS_CONN_BROKEN (socket_err))
+    return mhd_SOCKET_ERR_CONN_BROKEN;
+  else if (mhd_SCKT_ERR_IS_PIPE (socket_err))
+    return mhd_SOCKET_ERR_PIPE;
+  else if (mhd_SCKT_ERR_IS_NOTCONN (socket_err))
+    return mhd_SOCKET_ERR_NOTCONN;
+  else if (mhd_SCKT_ERR_IS_LOW_MEM (socket_err))
+    return mhd_SOCKET_ERR_NOMEM;
+  else if (mhd_SCKT_ERR_IS_BADF (socket_err))
+    return mhd_SOCKET_ERR_BADF;
+  else if (mhd_SCKT_ERR_IS_EINVAL (socket_err))
+    return mhd_SOCKET_ERR_INVAL;
+  else if (mhd_SCKT_ERR_IS_OPNOTSUPP (socket_err))
+    return mhd_SOCKET_ERR_OPNOTSUPP;
+  else if (mhd_SCKT_ERR_IS_NOTSOCK (socket_err))
+    return mhd_SOCKET_ERR_NOTSOCK;
+
+  return mhd_SOCKET_ERR_OTHER;
+}
+
+
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_socket (MHD_Socket fd)
+{
+#if defined(SOL_SOCKET) && defined(SO_ERROR)
+  enum mhd_SocketError err;
+  int sock_err;
+  if (0 == setsockopt(fd, SOL_SOCKET, SOCKET_ERROR,
+                      (void *) &sock_err, sizeof(sock_err)))
+    return mhd_socket_error_get_from_sys_err (sock_err);
+
+  err = mhd_socket_error_get_from_sys_err(mhd_SCKT_GET_LERR());
+  if ((mhd_SOCKET_ERR_NOTSOCK == err) ||
+      (mhd_SOCKET_ERR_BADF == err))
+    return err;
+#endif /* SOL_SOCKET && SO_ERROR */
+  return mhd_SOCKET_ERR_NOT_CHECKED;
+}

+ 50 - 13
src/mhd2/mhd_socket_error.h

@@ -20,7 +20,8 @@
 
 /**
  * @file src/mhd2/mhd_socket_error.h
- * @brief  The definition of the mhd_SocketError enum
+ * @brief  The definition of the mhd_SocketError enum and related macros and
+ *         declarations of related functions
  * @author Karlson2k (Evgeny Grin)
  */
 
@@ -28,9 +29,11 @@
 #define MHD_SOCKET_ERROR_H 1
 
 #include "mhd_sys_options.h"
+#include "mhd_socket_type.h"
 
+// TODO: better classification, when clearer local closing / network aborts
 /**
- * Recognised socket errors
+ * Recognised socket errors for recv() and send()
  */
 enum MHD_FIXED_ENUM_ mhd_SocketError
 {
@@ -72,6 +75,23 @@ enum MHD_FIXED_ENUM_ mhd_SocketError
    */
   mhd_SOCKET_ERR_NOTCONN
   ,
+  /**
+   * General TLS encryption or decryption error
+   */
+  mhd_SOCKET_ERR_TLS
+  ,
+  /**
+   * The socket has been shut down for writing or no longer connected
+   * Only for 'send()'.
+   */
+  mhd_SOCKET_ERR_PIPE
+  ,
+  /**
+   * The error status reported, but concrete code error has not been
+   * checked by MHD
+   */
+  mhd_SOCKET_ERR_NOT_CHECKED
+  ,
   /**
    * The socket FD is invalid
    */
@@ -93,21 +113,15 @@ enum MHD_FIXED_ENUM_ mhd_SocketError
   mhd_SOCKET_ERR_NOTSOCK
   ,
   /**
-   * The remote side shut down reading, the socket has been shut down
-   * for writing or no longer connected
-   * Only for 'send()'.
-   */
-  mhd_SOCKET_ERR_PIPE
-  ,
-  /**
-   * General TLS encryption or decryption error
+   * Other socket error
    */
-  mhd_SOCKET_ERR_TLS
+  mhd_SOCKET_ERR_OTHER
   ,
   /**
-   * Other socket error
+   * Internal (MHD) error
+   * Not actually reported by the OS
    */
-  mhd_SOCKET_ERR_OTHER
+  mhd_SOCKET_ERR_INTERNAL
 
 };
 
@@ -116,4 +130,27 @@ enum MHD_FIXED_ENUM_ mhd_SocketError
  */
 #define mhd_SOCKET_ERR_IS_HARD(err) (mhd_SOCKET_ERR_CONNRESET <= (err))
 
+/**
+ * Check whether the socket error is unexpected
+ */
+#define mhd_SOCKET_ERR_IS_BAD(err) (mhd_SOCKET_ERR_BADF <= (err))
+
+/**
+ * Map recv() / send() system socket error to the enum value
+ * @param socket_err the system socket error
+ * @return the enum value for the @a socket_err
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_sys_err (int socket_err);
+
+/**
+ * Get the last socket error recoded for the given socket
+ * @param fd the socket to check for the error
+ * @return the recorded error @a fd,
+ *         #mhd_SOCKET_ERR_NOT_CHECKED if not possible to check @a fd for
+ *         the error
+ */
+MHD_INTERNAL enum mhd_SocketError
+mhd_socket_error_get_from_socket (MHD_Socket fd);
+
 #endif /* ! MHD_SOCKET_ERROR_H */

+ 53 - 7
src/mhd2/mhd_sockets_macros.h

@@ -55,7 +55,7 @@
 #endif
 
 /**
- * mhd_send4 is a wrapper for system's send()
+ * mhd_sys_send4 is a wrapper for system's send()
  * @param s the socket to use
  * @param b the buffer with data to send
  * @param l the length of data in @a b
@@ -64,11 +64,11 @@
  */
 #define mhd_sys_send4(s,b,l,f) \
         ((ssize_t) send ((s),(const void*) (b),(mhd_SCKT_SEND_SIZE) (l), \
-                         ((MHD_MSG_NOSIGNAL) | (f))))
+                         ((mhd_MSG_NOSIGNAL) | (f))))
 
 
 /**
- * mhd_send is a simple wrapper for system's send()
+ * mhd_sys_send is a simple wrapper for system's send()
  * @param s the socket to use
  * @param b the buffer with data to send
  * @param l the length of data in @a b
@@ -218,6 +218,16 @@
 #  define mhd_SCKT_ERR_IS_OPNOTSUPP(err) (WSAEOPNOTSUPP == (err))
 #endif
 
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef ENOPROTOOPT
+#    define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (ENOPROTOOPT == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_NOPROTOOPT(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_NOPROTOOPT(err) (WSAENOPROTOOPT == (err))
+#endif
+
 #if defined(MHD_POSIX_SOCKETS)
 #  ifdef EBADF
 #    define mhd_SCKT_ERR_IS_BADF(err) (EBADF == (err))
@@ -238,6 +248,16 @@
 #  define mhd_SCKT_ERR_IS_NOTSOCK(err) (WSAENOTSOCK == (err))
 #endif
 
+#if defined(MHD_POSIX_SOCKETS)
+#  ifdef EPIPE
+#    define mhd_SCKT_ERR_IS_PIPE(err) (EPIPE == (err))
+#  else
+#    define mhd_SCKT_ERR_IS_PIPE(err) ((void) (err), ! ! 0)
+#  endif
+#elif defined(MHD_WINSOCK_SOCKETS)
+#  define mhd_SCKT_ERR_IS_PIPE(err) (WSAESHUTDOWN == (err))
+#endif
+
 /**
  * Check whether is given socket error is type of "incoming connection
  * was disconnected before 'accept()' is called".
@@ -262,8 +282,8 @@
  */
 #if defined(MHD_POSIX_SOCKETS)
 /* + EHOSTUNREACH: probably reported by intermediate
-    + ETIMEDOUT: probably keep-alive ping failure
-    + ENETUNREACH: probably cable physically disconnected or similar */
+   + ETIMEDOUT: probably keep-alive ping failure
+   + ENETUNREACH: probably cable physically disconnected or similar */
 #    define mhd_SCKT_ERR_IS_CONN_BROKEN(err) \
         ((0 != (err)) && \
          ((MHD_EHOSTUNREACH_OR_ZERO == (err)) || \
@@ -317,8 +337,8 @@
 #    if defined(HAVE_SOCK_NONBLOCK)
 #      ifdef MHD_AF_UNIX
 #        define mhd_socket_pair_nblk(fdarr_ptr) \
-        (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, ( \
-                            fdarr_ptr)))
+        (0 != socketpair (MHD_AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, \
+                          (fdarr_ptr)))
 #      else
 #        define mhd_socket_pair_nblk(fdarr_ptr) \
         (0 != socketpair (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0, (fdarr_ptr))) /* Fallback, could be broken on many platforms */
@@ -331,4 +351,30 @@
 /* mhd_socket_pair() implemented in "mhd_sockets_funcs.h" based on local function */
 #endif
 
+#if defined(SOL_SOCKET) && defined(SO_NOSIGPIPE)
+/**
+ * Helper for mhd_socket_nosignal()
+ */
+#  ifdef HAVE_COMPOUND_LITERALS_LVALUES
+#    define mhd_socket_nosig_helper_int_one ((int){1})
+#  else
+/**
+ * Internal static const helper for mhd_socket_nosignal()
+ */
+static const int mhd_socket_nosig_helper_int_one = 1;
+#  endif
+
+
+/**
+ * Change socket options to no signal on remote disconnect / broken connection.
+ *
+ * @param sock socket to manipulate
+ * @return non-zero if succeeded, zero otherwise
+ */
+#  define mhd_socket_nosignal(sock) \
+        (! setsockopt ((sock),SOL_SOCKET,SO_NOSIGPIPE, \
+                       &mhd_socket_nosig_helper_int_one, sizeof(int)))
+#endif /* SOL_SOCKET && SO_NOSIGPIPE */
+
+
 #endif /* ! MHD_SOCKETS_MACROS_H */

+ 182 - 305
src/mhd2/mhd_str.c

@@ -1,13 +1,13 @@
 /*
   This file is part of libmicrohttpd
-  Copyright (C) 2015-2024 Karlson2k (Evgeny Grin)
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
 
-  This library is free software; you can redistribute it and/or
+  GNU libmicrohttpd is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
 
-  This library is distributed in the hope that it will be useful,
+  GNU 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
   Lesser General Public License for more details.
@@ -77,7 +77,7 @@ isasciilower (char c)
 MHD_static_inline_ bool
 isasciiupper (char c)
 {
-  return (c >= 'A') && (c <= 'Z');
+  return (c <= 'Z') && (c >= 'A');
 }
 
 
@@ -107,7 +107,7 @@ isasciialpha (char c)
 MHD_static_inline_ bool
 isasciidigit (char c)
 {
-  return (c >= '0') && (c <= '9');
+  return (c <= '9') && (c >= '0');
 }
 
 
@@ -122,8 +122,8 @@ MHD_static_inline_ bool
 isasciixdigit (char c)
 {
   return isasciidigit (c) ||
-         ( (c >= 'A') && (c <= 'F') ) ||
-         ( (c >= 'a') && (c <= 'f') );
+         ( (c <= 'F') && (c >= 'A') ) ||
+         ( (c <= 'f') && (c >= 'a') );
 }
 
 
@@ -179,7 +179,7 @@ toasciiupper (char c)
 #endif /* Disable unused functions. */
 
 
-#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in MHD_str_to_uvalue_n_() */
+#if defined(MHD_FAVOR_SMALL_CODE) /* Used only in mhd_str_to_uvalue_n() */
 /**
  * Convert US-ASCII decimal digit to its value.
  *
@@ -510,12 +510,21 @@ toxdigitvalue (char c)
   }
   return -1;
 #else  /* MHD_FAVOR_SMALL_CODE */
-  if (isasciidigit (c))
-    return (unsigned char) (c - '0');
-  if ( (c >= 'A') && (c <= 'F') )
-    return (unsigned char) (c - 'A' + 10);
-  if ( (c >= 'a') && (c <= 'f') )
-    return (unsigned char) (c - 'a' + 10);
+  if (c <= 9)
+  {
+     if (c >= 0)
+       return (unsigned char) (c - '0');
+  }
+  else if (c <= 'F')
+  {
+    if (c >= 'A')
+      return (unsigned char) (c - 'A' + 10);
+  }
+  else if (c <= 'F')
+  {
+    if (c >= 'a')
+      return (unsigned char) (c - 'a' + 10);
+  }
 
   return -1;
 #endif /* MHD_FAVOR_SMALL_CODE */
@@ -549,7 +558,7 @@ charsequalcaseless (const char c1, const char c2)
  * @return boolean true if character is lower case letter,
  *         boolean false otherwise
  */
-#define isasciilower(c) (((char) (c)) >= 'a' && ((char) (c)) <= 'z')
+#define isasciilower(c) ((((char) (c)) >= 'a') && (((char) (c)) <= 'z'))
 
 
 /**
@@ -559,7 +568,7 @@ charsequalcaseless (const char c1, const char c2)
  * @return boolean true if character is upper case letter,
  *         boolean false otherwise
  */
-#define isasciiupper(c) (((char) (c)) >= 'A' && ((char) (c)) <= 'Z')
+#define isasciiupper(c) ((((char) (c)) <= 'Z') && (((char) (c)) >= 'A'))
 
 
 /**
@@ -579,7 +588,7 @@ charsequalcaseless (const char c1, const char c2)
  * @return boolean true if character is decimal digit, boolean false
  *         otherwise
  */
-#define isasciidigit(c) (((char) (c)) >= '0' && ((char) (c)) <= '9')
+#define isasciidigit(c) ((((char) (c)) <= '9') && (((char) (c)) >= '0'))
 
 
 /**
@@ -590,8 +599,8 @@ charsequalcaseless (const char c1, const char c2)
  *         boolean false otherwise
  */
 #define isasciixdigit(c) (isasciidigit ((c)) || \
-                          (((char) (c)) >= 'A' && ((char) (c)) <= 'F') || \
-                          (((char) (c)) >= 'a' && ((char) (c)) <= 'f') )
+                          (((char) (c)) <= 'F' && ((char) (c)) >= 'A') || \
+                          (((char) (c)) <= 'f' && ((char) (c)) >= 'a'))
 
 
 /**
@@ -669,15 +678,8 @@ charsequalcaseless (const char c1, const char c2)
 
 
 #ifndef MHD_FAVOR_SMALL_CODE
-/**
- * Check two strings for equality, ignoring case of US-ASCII letters.
- *
- * @param str1 first string to compare
- * @param str2 second string to compare
- * @return non-zero if two strings are equal, zero otherwise.
- */
-int
-MHD_str_equal_caseless_ (const char *str1,
+MHD_INTERNAL bool
+mhd_str_equal_caseless (const char *str1,
                          const char *str2)
 {
   while (0 != (*str1))
@@ -690,7 +692,7 @@ MHD_str_equal_caseless_ (const char *str1,
       str2++;
     }
     else
-      return 0;
+      return false;
   }
   return 0 == (*str2);
 }
@@ -699,19 +701,8 @@ MHD_str_equal_caseless_ (const char *str1,
 #endif /* ! MHD_FAVOR_SMALL_CODE */
 
 
-/**
- * Check two string for equality, ignoring case of US-ASCII letters and
- * checking not more than @a maxlen characters.
- * Compares up to first terminating null character, but not more than
- * first @a maxlen characters.
- *
- * @param str1 first string to compare
- * @param str2 second string to compare
- * @param maxlen maximum number of characters to compare
- * @return non-zero if two strings are equal, zero otherwise.
- */
-int
-MHD_str_equal_caseless_n_ (const char *const str1,
+MHD_INTERNAL bool
+mhd_str_equal_caseless_n (const char *const str1,
                            const char *const str2,
                            size_t maxlen)
 {
@@ -726,26 +717,16 @@ MHD_str_equal_caseless_n_ (const char *const str1,
     if (charsequalcaseless (c1, c2))
       continue;
     else
-      return 0;
+      return false;
   }
-  return ! 0;
+  return true;
 }
 
 
-/**
- * Check two string for equality, ignoring case of US-ASCII letters and
- * checking not more than @a len bytes.
- * Compares not more first than @a len bytes, including binary zero characters.
- * Comparison stops at first unmatched byte.
- * @param str1 first string to compare
- * @param str2 second string to compare
- * @param len number of characters to compare
- * @return non-zero if @a len bytes are equal, zero otherwise.
- */
-bool
-MHD_str_equal_caseless_bin_n_ (const char *const str1,
-                               const char *const str2,
-                               size_t len)
+MHD_INTERNAL bool
+mhd_str_equal_caseless_bin_n (const char *const str1,
+                              const char *const str2,
+                              size_t len)
 {
   size_t i;
 
@@ -762,21 +743,8 @@ MHD_str_equal_caseless_bin_n_ (const char *const str1,
 }
 
 
-/**
- * Check whether @a str has case-insensitive @a token.
- * Token could be surrounded by spaces and tabs and delimited by comma.
- * Match succeed if substring between start, end (of string) or comma
- * contains only case-insensitive token and optional spaces and tabs.
- * @warning token must not contain null-characters except optional
- *          terminating null-character.
- * @param str the string to check
- * @param token the token to find
- * @param token_len length of token, not including optional terminating
- *                  null-character.
- * @return non-zero if two strings are equal, zero otherwise.
- */
-bool
-MHD_str_has_token_caseless_ (const char *str,
+MHD_INTERNAL bool
+mhd_str_has_token_caseless (const char *str,
                              const char *const token,
                              size_t token_len)
 {
@@ -822,41 +790,13 @@ MHD_str_has_token_caseless_ (const char *str,
 }
 
 
-/**
- * Remove case-insensitive @a token from the @a str and put result
- * to the output @a buf.
- *
- * Tokens in @a str could be surrounded by spaces and tabs and delimited by
- * comma. The token match succeed if substring between start, end (of string)
- * or comma contains only case-insensitive token and optional spaces and tabs.
- * The quoted strings and comments are not supported by this function.
- *
- * The output string is normalised: empty tokens and repeated whitespaces
- * are removed, no whitespaces before commas, exactly one space is used after
- * each comma.
- *
- * @param str the string to process
- * @param str_len the length of the @a str, not including optional
- *                terminating null-character.
- * @param token the token to find
- * @param token_len the length of @a token, not including optional
- *                  terminating null-character.
- * @param[out] buf the output buffer, not null-terminated.
- * @param[in,out] buf_size pointer to the size variable, at input it
- *                         is the size of allocated buffer, at output
- *                         it is the size of the resulting string (can
- *                         be up to 50% larger than input) or negative value
- *                         if there is not enough space for the result
- * @return 'true' if token has been removed,
- *         'false' otherwise.
- */
-bool
-MHD_str_remove_token_caseless_ (const char *str,
-                                size_t str_len,
-                                const char *const token,
-                                const size_t token_len,
-                                char *buf,
-                                ssize_t *buf_size)
+MHD_INTERNAL bool
+mhd_str_remove_token_caseless (const char *restrict str,
+                               size_t str_len,
+                               const char *const restrict token,
+                               const size_t token_len,
+                               char *restrict buf,
+                               ssize_t *restrict buf_size)
 {
   const char *s1; /**< the "input" string / character */
   char *s2;       /**< the "output" string / character */
@@ -1003,40 +943,16 @@ MHD_str_remove_token_caseless_ (const char *str,
 }
 
 
-/**
- * Perform in-place case-insensitive removal of @a tokens from the @a str.
- *
- * Token could be surrounded by spaces and tabs and delimited by comma.
- * The token match succeed if substring between start, end (of the string), or
- * comma contains only case-insensitive token and optional spaces and tabs.
- * The quoted strings and comments are not supported by this function.
- *
- * The input string must be normalised: empty tokens and repeated whitespaces
- * are removed, no whitespaces before commas, exactly one space is used after
- * each comma. The string is updated in-place.
- *
- * Behavior is undefined is the input string in not normalised.
- *
- * @param[in,out] str the string to update
- * @param[in,out] str_len the length of the @a str, not including optional
- *                        terminating null-character, not null-terminated
- * @param tokens the token to find
- * @param tokens_len the length of @a tokens, not including optional
- *                   terminating null-character.
- * @return 'true' if any token has been removed,
- *         'false' otherwise.
- */
-bool
-MHD_str_remove_tokens_caseless_ (char *str,
-                                 size_t *str_len,
-                                 const char *const tokens,
-                                 const size_t tokens_len)
+MHD_INTERNAL bool
+mhd_str_remove_tokens_caseless (char *restrict str,
+                                size_t *restrict str_len,
+                                const char *const restrict tkns,
+                                const size_t tokens_len)
 {
-  const char *const t = tokens;   /**< a short alias for @a tokens */
   size_t pt;                      /**< position in @a tokens */
   bool token_removed;
 
-  mhd_assert (NULL == memchr (tokens, 0, tokens_len));
+  mhd_assert (NULL == memchr (tkns, 0, tokens_len));
 
   token_removed = false;
   pt = 0;
@@ -1048,36 +964,36 @@ MHD_str_remove_tokens_caseless_ (char *str,
 
     /* Skip any initial whitespaces and empty tokens in 'tokens' */
     while ( (pt < tokens_len) &&
-            ((' ' == t[pt]) || ('\t' == t[pt]) || (',' == t[pt])) )
+            ((' ' == tkns[pt]) || ('\t' == tkns[pt]) || (',' == tkns[pt])) )
       pt++;
 
     if (pt >= tokens_len)
       break; /* No more tokens, nothing to remove */
 
     /* Found non-whitespace char which is not a comma */
-    tkn = t + pt;
+    tkn = tkns + pt;
     do
     {
       do
       {
         pt++;
       } while (pt < tokens_len &&
-               (' ' != t[pt] && '\t' != t[pt] && ',' != t[pt]));
+               (' ' != tkns[pt] && '\t' != tkns[pt] && ',' != tkns[pt]));
       /* Found end of the token string, space, tab, or comma */
-      tkn_len = pt - (size_t) (tkn - t);
+      tkn_len = pt - (size_t) (tkn - tkns);
 
       /* Skip all spaces and tabs */
-      while (pt < tokens_len && (' ' == t[pt] || '\t' == t[pt]))
+      while (pt < tokens_len && (' ' == tkns[pt] || '\t' == tkns[pt]))
         pt++;
       /* Found end of the token string or non-whitespace char */
-    } while (pt < tokens_len && ',' != t[pt]);
+    } while (pt < tokens_len && ',' != tkns[pt]);
 
     /* 'tkn' is the input token with 'tkn_len' chars */
     mhd_assert (0 != tkn_len);
 
     if (*str_len == tkn_len)
     {
-      if (MHD_str_equal_caseless_bin_n_ (str, tkn, tkn_len))
+      if (mhd_str_equal_caseless_bin_n (str, tkn, tkn_len))
       {
         *str_len = 0;
         token_removed = true;
@@ -1104,7 +1020,7 @@ MHD_str_remove_tokens_caseless_ (char *str,
         mhd_assert (pr >= pw);
         mhd_assert ((*str_len) >= (pr + tkn_len));
         if ( ( ((*str_len) == (pr + tkn_len)) || (',' == str[pr + tkn_len]) ) &&
-             MHD_str_equal_caseless_bin_n_ (str + pr, tkn, tkn_len) )
+             mhd_str_equal_caseless_bin_n (str + pr, tkn, tkn_len) )
         {
           /* current token in the input string matches the 'tkn', skip it */
           mhd_assert ((*str_len == pr + tkn_len) || \
@@ -1179,19 +1095,9 @@ MHD_str_remove_tokens_caseless_ (char *str,
 #ifndef MHD_FAVOR_SMALL_CODE
 /* Use individual function for each case */
 
-/**
- * Convert decimal US-ASCII digits in string to number in uint_fast64_t.
- * Conversion stopped at first non-digit character.
- *
- * @param str string to convert
- * @param[out] out_val pointer to uint_fast64_t to store result of conversion
- * @return non-zero number of characters processed on succeed,
- *         zero if no digit is found, resulting value is larger
- *         then possible to store in uint_fast64_t or @a out_val is NULL
- */
-size_t
-MHD_str_to_uint64_ (const char *str,
-                    uint_fast64_t *out_val)
+MHD_INTERNAL size_t
+mhd_str_to_uint64 (const char *restrict str,
+                    uint_fast64_t *restrict out_val)
 {
   const char *const start = str;
   uint_fast64_t res;
@@ -1203,13 +1109,15 @@ MHD_str_to_uint64_ (const char *str,
   do
   {
     const int digit = (unsigned char) (*str) - '0';
-    if ( (res > (UINT64_MAX / 10)) ||
-         ( (res == (UINT64_MAX / 10)) &&
-           ((uint_fast64_t) digit > (UINT64_MAX % 10)) ) )
-      return 0;
+    uint_fast64_t prev_res = res;
 
     res *= 10;
+    if (res / 10 != prev_res)
+      return 0;
     res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
+      return 0;
+
     str++;
   } while (isasciidigit (*str));
 
@@ -1218,23 +1126,10 @@ MHD_str_to_uint64_ (const char *str,
 }
 
 
-/**
- * Convert not more then @a maxlen decimal US-ASCII digits in string to
- * number in uint_fast64_t.
- * Conversion stopped at first non-digit character or after @a maxlen
- * digits.
- *
- * @param str string to convert
- * @param maxlen maximum number of characters to process
- * @param[out] out_val pointer to uint_fast64_t to store result of conversion
- * @return non-zero number of characters processed on succeed,
- *         zero if no digit is found, resulting value is larger
- *         then possible to store in uint_fast64_t or @a out_val is NULL
- */
-size_t
-MHD_str_to_uint64_n_ (const char *str,
+MHD_INTERNAL size_t
+mhd_str_to_uint64_n (const char *restrict str,
                       size_t maxlen,
-                      uint_fast64_t *out_val)
+                      uint_fast64_t *restrict out_val)
 {
   uint_fast64_t res;
   size_t i;
@@ -1247,14 +1142,14 @@ MHD_str_to_uint64_n_ (const char *str,
   do
   {
     const int digit = (unsigned char) str[i] - '0';
-
-    if ( (res > (UINT64_MAX / 10)) ||
-         ( (res == (UINT64_MAX / 10)) &&
-           ((uint_fast64_t) digit > (UINT64_MAX % 10)) ) )
-      return 0;
+    uint_fast64_t prev_res = res;
 
     res *= 10;
+    if (res / 10 != prev_res)
+      return 0;
     res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
+      return 0;
     i++;
   } while ( (i < maxlen) &&
             isasciidigit (str[i]) );
@@ -1264,19 +1159,9 @@ MHD_str_to_uint64_n_ (const char *str,
 }
 
 
-/**
- * Convert hexadecimal US-ASCII digits in string to number in uint_fast32_t.
- * Conversion stopped at first non-digit character.
- *
- * @param str string to convert
- * @param[out] out_val pointer to uint_fast32_t to store result of conversion
- * @return non-zero number of characters processed on succeed,
- *         zero if no digit is found, resulting value is larger
- *         then possible to store in uint_fast32_t or @a out_val is NULL
- */
-size_t
-MHD_strx_to_uint32_ (const char *str,
-                     uint_fast32_t *out_val)
+MHD_INTERNAL size_t
+mhd_strx_to_uint32 (const char *restrict str,
+                     uint_fast32_t *restrict out_val)
 {
   const char *const start = str;
   uint_fast32_t res;
@@ -1289,15 +1174,15 @@ MHD_strx_to_uint32_ (const char *str,
   digit = toxdigitvalue (*str);
   while (digit >= 0)
   {
-    if ( (res < (UINT32_MAX / 16)) ||
-         ((res == (UINT32_MAX / 16)) &&
-          ( (uint_fast32_t) digit <= (UINT32_MAX % 16)) ) )
-    {
-      res *= 16;
-      res += (unsigned int) digit;
-    }
-    else
+    uint_fast32_t prev_res = res;
+
+    res *= 16;
+    if (res / 16 != prev_res)
+      return 0;
+    res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
       return 0;
+
     str++;
     digit = toxdigitvalue (*str);
   }
@@ -1308,23 +1193,10 @@ MHD_strx_to_uint32_ (const char *str,
 }
 
 
-/**
- * Convert not more then @a maxlen hexadecimal US-ASCII digits in string
- * to number in uint_fast32_t.
- * Conversion stopped at first non-digit character or after @a maxlen
- * digits.
- *
- * @param str string to convert
- * @param maxlen maximum number of characters to process
- * @param[out] out_val pointer to uint_fast32_t to store result of conversion
- * @return non-zero number of characters processed on succeed,
- *         zero if no digit is found, resulting value is larger
- *         then possible to store in uint_fast32_t or @a out_val is NULL
- */
-size_t
-MHD_strx_to_uint32_n_ (const char *str,
+MHD_INTERNAL size_t
+mhd_strx_to_uint32_n (const char *restrict str,
                        size_t maxlen,
-                       uint_fast32_t *out_val)
+                       uint_fast32_t *restrict out_val)
 {
   size_t i;
   uint_fast32_t res;
@@ -1336,9 +1208,13 @@ MHD_strx_to_uint32_n_ (const char *str,
   i = 0;
   while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
   {
-    if ( (res > (UINT32_MAX / 16)) ||
-         ((res == (UINT32_MAX / 16)) &&
-          ( (uint_fast32_t) digit > (UINT32_MAX % 16)) ) )
+    uint_fast32_t prev_res = res;
+
+    res *= 16;
+    if (res / 16 != prev_res)
+      return 0;
+    res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
       return 0;
 
     res *= 16;
@@ -1362,9 +1238,9 @@ MHD_strx_to_uint32_n_ (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-MHD_strx_to_uint64_ (const char *str,
-                     uint_fast64_t *out_val)
+MHD_INTERNAL size_t
+mhd_strx_to_uint64 (const char *restrict str,
+                     uint_fast64_t *restrict out_val)
 {
   const char *const start = str;
   uint_fast64_t res;
@@ -1376,15 +1252,15 @@ MHD_strx_to_uint64_ (const char *str,
   digit = toxdigitvalue (*str);
   while (digit >= 0)
   {
-    if ( (res < (UINT64_MAX / 16)) ||
-         ((res == (UINT64_MAX / 16)) &&
-          ( (uint_fast64_t) digit <= (UINT64_MAX % 16)) ) )
-    {
-      res *= 16;
-      res += (unsigned int) digit;
-    }
-    else
+    uint_fast64_t prev_res = res;
+
+    res *= 16;
+    if (res / 16 != prev_res)
+      return 0;
+    res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
       return 0;
+
     str++;
     digit = toxdigitvalue (*str);
   }
@@ -1408,10 +1284,10 @@ MHD_strx_to_uint64_ (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-MHD_strx_to_uint64_n_ (const char *str,
+MHD_INTERNAL size_t
+mhd_strx_to_uint64_n (const char *restrict str,
                        size_t maxlen,
-                       uint_fast64_t *out_val)
+                       uint_fast64_t *restrict out_val)
 {
   size_t i;
   uint_fast64_t res;
@@ -1423,13 +1299,14 @@ MHD_strx_to_uint64_n_ (const char *str,
   i = 0;
   while (i < maxlen && (digit = toxdigitvalue (str[i])) >= 0)
   {
-    if ( (res > (UINT64_MAX / 16)) ||
-         ((res == (UINT64_MAX / 16)) &&
-          ( (uint_fast64_t) digit > (UINT64_MAX % 16)) ) )
-      return 0;
+    uint_fast64_t prev_res = res;
 
     res *= 16;
+    if (res / 16 != prev_res)
+      return 0;
     res += (unsigned int) digit;
+    if (res < (unsigned int) digit)
+      return 0;
     i++;
   }
 
@@ -1458,10 +1335,10 @@ MHD_strx_to_uint64_n_ (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
  */
-size_t
-MHD_str_to_uvalue_n_ (const char *str,
+MHD_INTERNAL size_t
+mhd_str_to_uvalue_n (const char *restrict str,
                       size_t maxlen,
-                      void *out_val,
+                      void *restrict out_val,
                       size_t val_size,
                       uint_fast64_t max_val,
                       unsigned int base)
@@ -1510,8 +1387,8 @@ MHD_str_to_uvalue_n_ (const char *str,
 #endif /* MHD_FAVOR_SMALL_CODE */
 
 
-size_t
-MHD_uint32_to_strx (uint_fast32_t val,
+MHD_INTERNAL size_t
+mhd_uint32_to_strx (uint_fast32_t val,
                     char *buf,
                     size_t buf_size)
 {
@@ -1544,8 +1421,8 @@ MHD_uint32_to_strx (uint_fast32_t val,
 
 
 #ifndef MHD_FAVOR_SMALL_CODE
-size_t
-MHD_uint16_to_str (uint_fast16_t val,
+MHD_INTERNAL size_t
+mhd_uint16_to_str (uint_fast16_t val,
                    char *buf,
                    size_t buf_size)
 {
@@ -1585,14 +1462,14 @@ MHD_uint16_to_str (uint_fast16_t val,
 #endif /* !MHD_FAVOR_SMALL_CODE */
 
 
-size_t
-MHD_uint64_to_str (uint_fast64_t val,
+MHD_INTERNAL size_t
+mhd_uint64_to_str (uint_fast64_t val,
                    char *buf,
                    size_t buf_size)
 {
   char *chr;  /**< pointer to the current printed digit */
   /* The biggest printable number is 18446744073709551615 */
-  uint_fast64_t divisor = UINT64_C (10000000000000000000);
+  uint_fast64_t divisor = (uint_fast64_t) 10000000000000000000U;
   int digit;
 
   chr = buf;
@@ -1623,8 +1500,8 @@ MHD_uint64_to_str (uint_fast64_t val,
 }
 
 
-size_t
-MHD_uint8_to_str_pad (uint8_t val,
+MHD_INTERNAL size_t
+mhd_uint8_to_str_pad (uint8_t val,
                       uint8_t min_digits,
                       char *buf,
                       size_t buf_size)
@@ -1670,10 +1547,10 @@ MHD_uint8_to_str_pad (uint8_t val,
 }
 
 
-size_t
-MHD_bin_to_hex (const void *bin,
+MHD_INTERNAL size_t
+mhd_bin_to_hex (const void *restrict bin,
                 size_t size,
-                char *hex)
+                char *restrict hex)
 {
   size_t i;
 
@@ -1690,26 +1567,25 @@ MHD_bin_to_hex (const void *bin,
 }
 
 
-size_t
-MHD_bin_to_hex_z (const void *bin,
+MHD_INTERNAL size_t
+mhd_bin_to_hex_z (const void *restrict bin,
                   size_t size,
-                  char *hex)
+                  char *restrict hex)
 {
   size_t res;
 
-  res = MHD_bin_to_hex (bin, size, hex);
+  res = mhd_bin_to_hex (bin, size, hex);
   hex[res] = 0;
 
   return res;
 }
 
 
-size_t
-MHD_hex_to_bin (const char *hex,
+MHD_INTERNAL size_t
+mhd_hex_to_bin (const char *restrict hex,
                 size_t len,
-                void *bin)
+                void *restrict bin)
 {
-  uint8_t *const out = (uint8_t *) bin;
   size_t r;
   size_t w;
 
@@ -1723,7 +1599,7 @@ MHD_hex_to_bin (const char *hex,
     const int l = toxdigitvalue (hex[r++]);
     if (0 > l)
       return 0;
-    out[w++] = (uint8_t) ((unsigned int) l);
+    ((uint8_t *)bin)[w++] = (uint8_t) ((unsigned int) l);
   }
   while (r < len)
   {
@@ -1731,8 +1607,9 @@ MHD_hex_to_bin (const char *hex,
     const int l = toxdigitvalue (hex[r++]);
     if ((0 > h) || (0 > l))
       return 0;
-    out[w++] = (uint8_t) ( ((uint8_t) (((uint8_t) ((unsigned int) h)) << 4))
-                           | ((uint8_t) ((unsigned int) l)) );
+    ((uint8_t *)bin)[w++] = (uint8_t) ( ((uint8_t) (((uint8_t)
+                                       ((unsigned int) h)) << 4))
+                                       | ((uint8_t) ((unsigned int) l)) );
   }
   mhd_assert (len == r);
   mhd_assert ((len + 1) / 2 == w);
@@ -1740,8 +1617,8 @@ MHD_hex_to_bin (const char *hex,
 }
 
 
-size_t
-MHD_str_pct_decode_strict_n_ (const char *pct_encoded,
+MHD_INTERNAL size_t
+mhd_str_pct_decode_strict_n (const char *pct_encoded,
                               size_t pct_encoded_len,
                               char *decoded,
                               size_t buf_size)
@@ -1750,7 +1627,7 @@ MHD_str_pct_decode_strict_n_ (const char *pct_encoded,
   bool broken;
   size_t res;
 
-  res = MHD_str_pct_decode_lenient_n_ (pct_encoded, pct_encoded_len, decoded,
+  res = mhd_str_pct_decode_lenient_n (pct_encoded, pct_encoded_len, decoded,
                                        buf_size, &broken);
   if (broken)
     return 0;
@@ -1823,8 +1700,8 @@ MHD_str_pct_decode_strict_n_ (const char *pct_encoded,
 }
 
 
-size_t
-MHD_str_pct_decode_lenient_n_ (const char *pct_encoded,
+MHD_INTERNAL size_t
+mhd_str_pct_decode_lenient_n (const char *pct_encoded,
                                size_t pct_encoded_len,
                                char *decoded,
                                size_t buf_size,
@@ -1922,14 +1799,14 @@ MHD_str_pct_decode_lenient_n_ (const char *pct_encoded,
 }
 
 
-size_t
-MHD_str_pct_decode_in_place_strict_ (char *str)
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_strict (char *str)
 {
 #ifdef MHD_FAVOR_SMALL_CODE
   size_t res;
   bool broken;
 
-  res = MHD_str_pct_decode_in_place_lenient_ (str, &broken);
+  res = mhd_str_pct_decode_in_place_lenient (str, &broken);
   if (broken)
   {
     res = 0;
@@ -1978,8 +1855,8 @@ MHD_str_pct_decode_in_place_strict_ (char *str)
 }
 
 
-size_t
-MHD_str_pct_decode_in_place_lenient_ (char *str,
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_lenient (char *str,
                                       bool *broken_encoding)
 {
 #ifdef MHD_FAVOR_SMALL_CODE
@@ -1987,7 +1864,7 @@ MHD_str_pct_decode_in_place_lenient_ (char *str,
   size_t res;
 
   len = strlen (str);
-  res = MHD_str_pct_decode_lenient_n_ (str, len, str, len, broken_encoding);
+  res = mhd_str_pct_decode_lenient_n (str, len, str, len, broken_encoding);
   str[res] = 0;
 
   return res;
@@ -2055,8 +1932,8 @@ MHD_str_pct_decode_in_place_lenient_ (char *str,
 
 
 #ifdef DAUTH_SUPPORT
-bool
-MHD_str_equal_quoted_bin_n (const char *quoted,
+MHD_INTERNAL bool
+mhd_str_equal_quoted_bin_n (const char *quoted,
                             size_t quoted_len,
                             const char *unquoted,
                             size_t unquoted_len)
@@ -2085,8 +1962,8 @@ MHD_str_equal_quoted_bin_n (const char *quoted,
 }
 
 
-bool
-MHD_str_equal_caseless_quoted_bin_n (const char *quoted,
+MHD_INTERNAL bool
+mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
                                      size_t quoted_len,
                                      const char *unquoted,
                                      size_t unquoted_len)
@@ -2115,8 +1992,8 @@ MHD_str_equal_caseless_quoted_bin_n (const char *quoted,
 }
 
 
-size_t
-MHD_str_unquote (const char *quoted,
+MHD_INTERNAL size_t
+mhd_str_unquote (const char *quoted,
                  size_t quoted_len,
                  char *result)
 {
@@ -2144,8 +2021,8 @@ MHD_str_unquote (const char *quoted,
 
 #if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
 
-size_t
-MHD_str_quote (const char *unquoted,
+MHD_INTERNAL size_t
+mhd_str_quote (const char *unquoted,
                size_t unquoted_len,
                char *result,
                size_t buf_size)
@@ -2224,23 +2101,23 @@ MHD_str_quote (const char *unquoted,
 #endif /* MHD_BASE64_FUNC_VERSION < 1 || MHD_BASE64_FUNC_VERSION > 3 */
 
 #if MHD_BASE64_FUNC_VERSION == 3
-#define MHD_base64_map_type_ int
+#define mhd_base64_map_type int
 #else  /* MHD_BASE64_FUNC_VERSION < 3 */
-#define MHD_base64_map_type_ int8_t
+#define mhd_base64_map_type int8_t
 #endif /* MHD_BASE64_FUNC_VERSION < 3 */
 
 #if MHD_BASE64_FUNC_VERSION == 1
-static MHD_base64_map_type_
+static mhd_base64_map_type
 base64_char_to_value_ (uint8_t c)
 {
   if ('Z' >= c)
   {
     if ('A' <= c)
-      return (MHD_base64_map_type_) ((c - 'A') + 0);
+      return (mhd_base64_map_type) ((c - 'A') + 0);
     if ('0' <= c)
     {
       if ('9' >= c)
-        return (MHD_base64_map_type_) ((c - '0') + 52);
+        return (mhd_base64_map_type) ((c - '0') + 52);
       if ('=' == c)
         return -2;
       return -1;
@@ -2252,7 +2129,7 @@ base64_char_to_value_ (uint8_t c)
     return -1;
   }
   if (('z' >= c) && ('a' <= c))
-    return (MHD_base64_map_type_) ((c - 'a') + 26);
+    return (mhd_base64_map_type) ((c - 'a') + 26);
   return -1;
 }
 
@@ -2263,14 +2140,14 @@ base64_char_to_value_ (uint8_t c)
 MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
 
 
-size_t
-MHD_base64_to_bin_n (const char *base64,
+MHD_INTERNAL size_t
+mhd_base64_to_bin_n (const char *base64,
                      size_t base64_len,
                      void *bin,
                      size_t bin_size)
 {
 #if MHD_BASE64_FUNC_VERSION >= 2
-  static const MHD_base64_map_type_ map[] = {
+  static const mhd_base64_map_type map[] = {
     /* -1 = invalid char, -2 = padding
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
     NUL,  SOH,  STX,  ETX,  EOT,  ENQ,  ACK,  BEL,  */
@@ -2370,10 +2247,10 @@ MHD_base64_to_bin_n (const char *base64,
 #endif /* MHD_BASE64_FUNC_VERSION == 2 */
     if (1)
     {
-      const MHD_base64_map_type_ v1 = base64_char_to_value_ (in[i + 0]);
-      const MHD_base64_map_type_ v2 = base64_char_to_value_ (in[i + 1]);
-      const MHD_base64_map_type_ v3 = base64_char_to_value_ (in[i + 2]);
-      const MHD_base64_map_type_ v4 = base64_char_to_value_ (in[i + 3]);
+      const mhd_base64_map_type v1 = base64_char_to_value_ (in[i + 0]);
+      const mhd_base64_map_type v2 = base64_char_to_value_ (in[i + 1]);
+      const mhd_base64_map_type v3 = base64_char_to_value_ (in[i + 2]);
+      const mhd_base64_map_type v4 = base64_char_to_value_ (in[i + 3]);
       if ((0 > v1) || (0 > v2) || (0 > v3) || (0 > v4))
         return 0;
       out[j + 0] = (uint8_t) (((uint8_t) (((uint8_t) v1) << 2))
@@ -2391,10 +2268,10 @@ MHD_base64_to_bin_n (const char *base64,
 #endif /* MHD_BASE64_FUNC_VERSION == 2 */
   if (1)
   { /* The last four chars block */
-    const MHD_base64_map_type_ v1 = base64_char_to_value_ (in[i + 0]);
-    const MHD_base64_map_type_ v2 = base64_char_to_value_ (in[i + 1]);
-    const MHD_base64_map_type_ v3 = base64_char_to_value_ (in[i + 2]);
-    const MHD_base64_map_type_ v4 = base64_char_to_value_ (in[i + 3]);
+    const mhd_base64_map_type v1 = base64_char_to_value_ (in[i + 0]);
+    const mhd_base64_map_type v2 = base64_char_to_value_ (in[i + 1]);
+    const mhd_base64_map_type v3 = base64_char_to_value_ (in[i + 2]);
+    const mhd_base64_map_type v4 = base64_char_to_value_ (in[i + 3]);
     if ((0 > v1) || (0 > v2))
       return 0; /* Invalid char or padding at first two positions */
     mhd_assert (j < bin_size);
@@ -2435,6 +2312,6 @@ MHD_base64_to_bin_n (const char *base64,
 MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
 
 
-#undef MHD_base64_map_type_
+#undef mhd_base64_map_type
 
 #endif /* BAUTH_SUPPORT */

+ 75 - 73
src/mhd2/mhd_str.h

@@ -1,13 +1,13 @@
 /*
   This file is part of libmicrohttpd
-  Copyright (C) 2015-2024 Karlson2k (Evgeny Grin)
+  Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
 
-  This library is free software; you can redistribute it and/or
+  GNU libmicrohttpd is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
 
-  This library is distributed in the hope that it will be useful,
+  GNU 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
   Lesser General Public License for more details.
@@ -30,7 +30,9 @@
 #include "sys_base_types.h"
 #include "sys_bool_type.h"
 #include "mhd_str_macros.h"
-
+#ifdef MHD_FAVOR_SMALL_CODE
+#  include "mhd_limits.h"
+#endif
 /*
  * Block of functions/macros that use US-ASCII charset as required by HTTP
  * standards. Not affected by current locale settings.
@@ -42,9 +44,9 @@
  *
  * @param str1 first string to compare
  * @param str2 second string to compare
- * @return non-zero if two strings are equal, zero otherwise.
+ * @return 'true' if two strings are equal, 'false' otherwise.
  */
-int
+MHD_INTERNAL bool
 mhd_str_equal_caseless (const char *str1,
                         const char *str2);
 
@@ -63,9 +65,9 @@ mhd_str_equal_caseless (const char *str1,
  * @param str1 first string to compare
  * @param str2 second string to compare
  * @param maxlen maximum number of characters to compare
- * @return non-zero if two strings are equal, zero otherwise.
+ * @return 'true' if two strings are equal, 'false' otherwise.
  */
-int
+MHD_INTERNAL bool
 mhd_str_equal_caseless_n (const char *const str1,
                           const char *const str2,
                           size_t maxlen);
@@ -79,9 +81,9 @@ mhd_str_equal_caseless_n (const char *const str1,
  * @param str1 first string to compare
  * @param str2 second string to compare
  * @param len number of characters to compare
- * @return non-zero if @a len bytes are equal, zero otherwise.
+ * @return 'true' if two strings are equal, 'false' otherwise.
  */
-bool
+MHD_INTERNAL bool
 mhd_str_equal_caseless_bin_n (const char *const str1,
                               const char *const str2,
                               size_t len);
@@ -99,7 +101,7 @@ mhd_str_equal_caseless_bin_n (const char *const str1,
  * @param a the statically allocated string to compare
  * @param s the string to compare
  * @param len number of characters to compare
- * @return non-zero if @a len bytes are equal, zero otherwise.
+ * @return 'true' if two strings are equal, 'false' otherwise.
  */
 #define mhd_str_equal_caseless_s_bin_n(a,s,l) \
         ((mhd_SSTR_LEN (a) == (l)) \
@@ -118,8 +120,8 @@ mhd_str_equal_caseless_bin_n (const char *const str1,
  *                  null-character.
  * @return non-zero if two strings are equal, zero otherwise.
  */
-bool
-mhd_str_has_token_caseless (const char *str,
+MHD_INTERNAL bool
+mhd_str_has_token_caseless (const char *restrict str,
                             const char *const token,
                             size_t token_len);
 
@@ -165,13 +167,13 @@ mhd_str_has_token_caseless (const char *str,
  * @return 'true' if token has been removed,
  *         'false' otherwise.
  */
-bool
-mhd_str_remove_token_caseless (const char *str,
+MHD_INTERNAL bool
+mhd_str_remove_token_caseless (const char *restrict str,
                                size_t str_len,
-                               const char *const token,
+                               const char *const restrict token,
                                const size_t token_len,
-                               char *buf,
-                               ssize_t *buf_size);
+                               char *restrict buf,
+                               ssize_t *restrict buf_size);
 
 
 /**
@@ -197,10 +199,10 @@ mhd_str_remove_token_caseless (const char *str,
  * @return 'true' if any token has been removed,
  *         'false' otherwise.
  */
-bool
-mhd_str_remove_tokens_caseless (char *str,
-                                size_t *str_len,
-                                const char *const tokens,
+MHD_INTERNAL bool
+mhd_str_remove_tokens_caseless (char *restrict str,
+                                size_t *restrict str_len,
+                                const char *const restrict tkns,
                                 const size_t tokens_len);
 
 
@@ -217,9 +219,9 @@ mhd_str_remove_tokens_caseless (char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-mhd_str_to_uint64 (const char *str,
-                   uint_fast64_t *out_val);
+MHD_INTERNAL size_t
+mhd_str_to_uint64 (const char *restrict str,
+                   uint_fast64_t *restrict out_val);
 
 /**
  * Convert not more then @a maxlen decimal US-ASCII digits in string to
@@ -234,10 +236,10 @@ mhd_str_to_uint64 (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-mhd_str_to_uint64_n (const char *str,
+MHD_INTERNAL size_t
+mhd_str_to_uint64_n (const char *restrict str,
                      size_t maxlen,
-                     uint_fast64_t *out_val);
+                     uint_fast64_t *restrict out_val);
 
 
 /**
@@ -250,9 +252,9 @@ mhd_str_to_uint64_n (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast32_t or @a out_val is NULL
  */
-size_t
-mhd_strx_to_uint32 (const char *str,
-                    uint_fast32_t *out_val);
+MHD_INTERNAL size_t
+mhd_strx_to_uint32 (const char *restrict str,
+                    uint_fast32_t *restrict out_val);
 
 
 /**
@@ -268,10 +270,10 @@ mhd_strx_to_uint32 (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast32_t or @a out_val is NULL
  */
-size_t
-mhd_strx_to_uint32_n (const char *str,
+MHD_INTERNAL size_t
+mhd_strx_to_uint32_n (const char *restrict str,
                       size_t maxlen,
-                      uint_fast32_t *out_val);
+                      uint_fast32_t *restrict out_val);
 
 
 /**
@@ -284,9 +286,9 @@ mhd_strx_to_uint32_n (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-mhd_strx_to_uint64 (const char *str,
-                    uint_fast64_t *out_val);
+MHD_INTERNAL size_t
+mhd_strx_to_uint64 (const char *restrict str,
+                    uint_fast64_t *restrict out_val);
 
 
 /**
@@ -302,10 +304,10 @@ mhd_strx_to_uint64 (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then possible to store in uint_fast64_t or @a out_val is NULL
  */
-size_t
-mhd_strx_to_uint64_n (const char *str,
+MHD_INTERNAL size_t
+mhd_strx_to_uint64_n (const char *restrict str,
                       size_t maxlen,
-                      uint_fast64_t *out_val);
+                      uint_fast64_t *restrict out_val);
 
 #else  /* MHD_FAVOR_SMALL_CODE */
 /* Use one universal function and macros to reduce size */
@@ -327,10 +329,10 @@ mhd_strx_to_uint64_n (const char *str,
  *         zero if no digit is found, resulting value is larger
  *         then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
  */
-size_t
-mhd_str_to_uvalue_n (const char *str,
+MHD_INTERNAL size_t
+mhd_str_to_uvalue_n (const char *restrict str,
                      size_t maxlen,
-                     void *out_val,
+                     void *restrict out_val,
                      size_t val_size,
                      uint_fast64_t max_val,
                      unsigned int base);
@@ -387,7 +389,7 @@ mhd_str_to_uvalue_n (const char *str,
  * @return number of characters has been put to the @a buf,
  *         zero if buffer is too small (buffer may be modified).
  */
-size_t
+MHD_INTERNAL size_t
 mhd_uint32_to_strx (uint_fast32_t val,
                     char *buf,
                     size_t buf_size);
@@ -403,13 +405,13 @@ mhd_uint32_to_strx (uint_fast32_t val,
  * @return number of characters has been put to the @a buf,
  *         zero if buffer is too small (buffer may be modified).
  */
-size_t
+MHD_INTERNAL size_t
 mhd_uint16_to_str (uint_fast16_t val,
                    char *buf,
                    size_t buf_size);
 
 #else  /* MHD_FAVOR_SMALL_CODE */
-#define MHD_uint16_to_str(v,b,s) MHD_uint64_to_str (v,b,s)
+#define mhd_uint16_to_str(v,b,s) mhd_uint64_to_str (v,b,s)
 #endif /* MHD_FAVOR_SMALL_CODE */
 
 
@@ -422,7 +424,7 @@ mhd_uint16_to_str (uint_fast16_t val,
  * @return number of characters has been put to the @a buf,
  *         zero if buffer is too small (buffer may be modified).
  */
-size_t
+MHD_INTERNAL size_t
 mhd_uint64_to_str (uint_fast64_t val,
                    char *buf,
                    size_t buf_size);
@@ -443,7 +445,7 @@ mhd_uint64_to_str (uint_fast64_t val,
  * @return number of characters has been put to the @a buf,
  *         zero if buffer is too small (buffer may be modified).
  */
-size_t
+MHD_INTERNAL size_t
 mhd_uint8_to_str_pad (uint8_t val,
                       uint8_t min_digits,
                       char *buf,
@@ -459,10 +461,10 @@ mhd_uint8_to_str_pad (uint8_t val,
  * @param[out] hex the output buffer, should be at least 2 * @a size
  * @return The number of characters written to the output buffer.
  */
-size_t
-mhd_bin_to_hex (const void *bin,
+MHD_INTERNAL size_t
+mhd_bin_to_hex (const void *restrict bin,
                 size_t size,
-                char *hex);
+                char *restrict hex);
 
 /**
  * Convert @a size bytes from input binary data to lower case
@@ -473,10 +475,10 @@ mhd_bin_to_hex (const void *bin,
  * @return The number of characters written to the output buffer,
  *         not including terminating zero.
  */
-size_t
-mhd_bin_to_hex_z (const void *bin,
+MHD_INTERNAL size_t
+mhd_bin_to_hex_z (const void *restrict bin,
                   size_t size,
-                  char *hex);
+                  char *restrict hex);
 
 /**
  * Convert hexadecimal digits to binary data.
@@ -491,10 +493,10 @@ mhd_bin_to_hex_z (const void *bin,
  * @return the number of bytes written to the output buffer,
  *         zero if found any character which is not hexadecimal digits
  */
-size_t
-mhd_hex_to_bin (const char *hex,
+MHD_INTERNAL size_t
+mhd_hex_to_bin (const char *restrict hex,
                 size_t len,
-                void *bin);
+                void *restrict bin);
 
 /**
  * Decode string with percent-encoded characters as defined by
@@ -514,7 +516,7 @@ mhd_hex_to_bin (const char *hex,
  *         by less than two hexadecimal digits) or output buffer is too
  *         small to hold the result
  */
-size_t
+MHD_INTERNAL size_t
 mhd_str_pct_decode_strict_n_ (const char *pct_encoded,
                               size_t pct_encoded_len,
                               char *decoded,
@@ -542,7 +544,7 @@ mhd_str_pct_decode_strict_n_ (const char *pct_encoded,
  * @return the number of characters written to the output buffer or
  *         zero if output buffer is too small to hold the result
  */
-size_t
+MHD_INTERNAL size_t
 mhd_str_pct_decode_lenient_n_ (const char *pct_encoded,
                                size_t pct_encoded_len,
                                char *decoded,
@@ -563,8 +565,8 @@ mhd_str_pct_decode_lenient_n_ (const char *pct_encoded,
  *                    truncated to zero length if broken encoding is found
  * @return the number of character in decoded string
  */
-size_t
-MHD_str_pct_decode_in_place_strict_ (char *str);
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_strict (char *str);
 
 
 /**
@@ -585,8 +587,8 @@ MHD_str_pct_decode_in_place_strict_ (char *str);
  *                             optional, can be NULL
  * @return the number of character in decoded string
  */
-size_t
-MHD_str_pct_decode_in_place_lenient_ (char *str,
+MHD_INTERNAL size_t
+mhd_str_pct_decode_in_place_lenient (char *str,
                                       bool *broken_encoding);
 
 #ifdef DAUTH_SUPPORT
@@ -609,7 +611,7 @@ MHD_str_pct_decode_in_place_lenient_ (char *str,
  *         non-zero if two strings are equal after unquoting of the
  *         first string.
  */
-bool
+MHD_INTERNAL bool
 mhd_str_equal_quoted_bin_n (const char *quoted,
                             size_t quoted_len,
                             const char *unquoted,
@@ -631,8 +633,8 @@ mhd_str_equal_quoted_bin_n (const char *quoted,
  *         non-zero if two strings are equal after unquoting of the
  *         first string.
  */
-#define MHD_str_equal_quoted_s_bin_n(q,l,u) \
-        MHD_str_equal_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
+#define mhd_str_equal_quoted_s_bin_n(q,l,u) \
+        mhd_str_equal_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
 
 /**
  * Check two strings for equality, "unquoting" the first string from quoted
@@ -654,7 +656,7 @@ mhd_str_equal_quoted_bin_n (const char *quoted,
  *         non-zero if two strings are caseless equal after unquoting of the
  *         first string.
  */
-bool
+MHD_INTERNAL bool
 mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
                                      size_t quoted_len,
                                      const char *unquoted,
@@ -677,8 +679,8 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
  *         non-zero if two strings are caseless equal after unquoting of the
  *         first string.
  */
-#define MHD_str_equal_caseless_quoted_s_bin_n(q,l,u) \
-        MHD_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
+#define mhd_str_equal_caseless_quoted_s_bin_n(q,l,u) \
+        mhd_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
 
 /**
  * Convert string from quoted to unquoted form as specified by
@@ -695,7 +697,7 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
  *         zero if last backslash is not followed by any character (or
  *         @a quoted_len is zero).
  */
-size_t
+MHD_INTERNAL size_t
 mhd_str_unquote (const char *quoted,
                  size_t quoted_len,
                  char *result);
@@ -719,7 +721,7 @@ mhd_str_unquote (const char *quoted,
  *         @a unquoted_len, zero if @a unquoted_len is zero or if quoted
  *         string is larger than @a buf_size.
  */
-size_t
+MHD_INTERNAL size_t
 mhd_str_quote (const char *unquoted,
                size_t unquoted_len,
                char *result,
@@ -737,7 +739,7 @@ mhd_str_quote (const char *unquoted,
  *         @a enc_size is valid (properly padded),
  *         undefined value smaller then @a enc_size if @a enc_size is not valid
  */
-#define MHD_base64_max_dec_size_(enc_size) (((enc_size) / 4) * 3)
+#define mhd_base64_max_dec_size(enc_size) (((enc_size) / 4) * 3)
 
 /**
  * Convert Base64 encoded string to binary data.
@@ -756,7 +758,7 @@ mhd_str_quote (const char *unquoted,
  *         (base64_len / 4 * 3 - 2), (base64_len / 4 * 3 - 1) or
  *         (base64_len / 4 * 3), depending on the number of padding characters.
  */
-size_t
+MHD_INTERNAL size_t
 mhd_base64_to_bin_n (const char *base64,
                      size_t base64_len,
                      void *bin,

+ 70 - 0
src/mhd2/request_funcs.c

@@ -0,0 +1,70 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/request_funcs.c
+ * @brief  The definition of the request internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_request.h"
+#include "mhd_connection.h"
+#include "request_funcs.h"
+#include "stream_funcs.h"
+#include "mhd_dlinked_list.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
+                               enum MHD_ValueKind kind,
+                               const struct MHD_String *restrict name,
+                               const struct MHD_StringNullable *restrict value)
+{
+  struct mhd_RequestField *f;
+
+  f = (struct mhd_RequestField *)
+       mhd_stream_alloc_memory (sizeof(struct mhd_RequestField));
+  if (NULL == f)
+    return false;
+
+  f->field.nv.name = name;
+  f->field.nv.value = value;
+  f->field.kind = kind;
+  mhd_DLINKEDL_INIT_LINKS(f, fields);
+
+  mhd_DLINKEDL_INS_LAST(&(c->rq),f,fields);
+
+  return true;
+}
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_stream_add_field (struct MHD_Connection *restrict c,
+                      enum MHD_ValueKind kind,
+                      const struct MHD_String *restrict name,
+                      const struct MHD_String *restrict value)
+{
+  struct MHD_StringNullable value2;
+
+  value2.len = value->len;
+  value2.cstr = value->cstr;
+
+  return mhd_stream_add_field_nullable(c, kind, name, &value2);
+}

+ 78 - 0
src/mhd2/request_funcs.h

@@ -0,0 +1,78 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/request_funcs.h
+ * @brief  The declaration of the request internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_REQUEST_FUNCS_H
+#define MHD_REQUEST_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+#include "mhd_str_types.h"
+#include "mhd_public_api.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Add field to the request.
+ * The memory allocated in the request memory pool
+ *
+ * @param c the connection to use
+ * @param kind the kind of the field to add
+ * @param name the name of the field to add, the string is not copied,
+ *             only copied the pointer value
+ * @param value the value of the field to add, the string is not copied,
+ *              only copied the pointer value
+ * @return true if succeed,
+ *         false if memory cannot be allocated
+ */
+MHD_INTERNAL bool
+mhd_stream_add_field (struct MHD_Connection *restrict c,
+                      enum MHD_ValueKind kind,
+                      const struct MHD_String *restrict name,
+                      const struct MHD_String *restrict value)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Add field to the request.
+ * The memory allocated in the request memory pool
+ * The value can have NULL string ("no value").
+ *
+ * @param c the connection to use
+ * @param kind the kind of the field to add
+ * @param name the name of the field to add, the string is not copied,
+ *             only copied the pointer value
+ * @param value the value of the field to add, the string is not copied,
+ *              only copied the pointer value
+ * @return true if succeed,
+ *         false if memory cannot be allocated
+ */
+MHD_INTERNAL bool
+mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
+                               enum MHD_ValueKind kind,
+                               const struct MHD_String *restrict name,
+                               const struct MHD_StringNullable *restrict value)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_REQUEST_FUNCS_H */

+ 68 - 0
src/mhd2/request_get_value.c

@@ -0,0 +1,68 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/request_get_value.c
+ * @brief  The implementation of MHD_request_get_value*() functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "request_get_value.h"
+#include "sys_base_types.h"
+#include <string.h>
+
+#include "mhd_request.h"
+
+#include "mhd_public_api.h"
+
+#include "mhd_dlinked_list.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_String *
+mhd_request_get_value_len (struct MHD_Request *MHD_RESTRICT request,
+                           enum MHD_ValueKind kind,
+                           size_t key_len,
+                           const char *restrict key)
+{
+  struct mhd_RequestField *f;
+
+  for (f = mhd_DLINKEDL_GET_FIRST(request, fields); NULL != f;
+       f = mhd_DLINKEDL_GET_NEXT(f, fields))
+  {
+    if ((key_len == f->field.nv.name.len) &&
+        (kind == f->field.kind) &&
+        (0 == memcmp(key, f->field.nv.name.cstr)))
+      return &(f->field.nv.value);
+  }
+  return NULL;
+}
+
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_String *
+MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request,
+                       enum MHD_ValueKind kind,
+                       const char *MHD_RESTRICT key)
+{
+  size_t len;
+  len = strlen(key);
+  return mhd_request_get_value_len(request, kind, len, key);
+}

+ 56 - 0
src/mhd2/request_get_value.h

@@ -0,0 +1,56 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/request_get_value.h
+ * @brief  The declaration of internal mhd_request_get_value* functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_REQUEST_GET_VALUE_H
+#define MHD_REQUEST_GET_VALUE_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+#include "mhd_public_api.h"
+
+/**
+ * Get a particular header (or other kind of request data) value.
+ * If multiple values match the kind, return any one of them.
+ *
+ * The returned pointer is valid until the response is queued.
+ * If the data is needed beyond this point, it should be copied.
+ *
+ * @param request request to get values from
+ * @param kind what kind of value are we looking for
+ * @param key the header to look for, empty to lookup 'trailing' value
+ *            without a key
+ * @return NULL if no such item was found
+ * @ingroup request
+ */
+MHD_INTERNAL const struct MHD_String *
+mhd_request_get_value_len (struct MHD_Request *restrict request,
+                           enum MHD_ValueKind kind,
+                           size_t key_len,
+                           const char *restrict key)
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
+
+#endif /* ! MHD_REQUEST_GET_VALUE_H */

+ 54 - 21
src/mhd2/response_add_header.c

@@ -34,6 +34,54 @@
 #include "mhd_public_api.h"
 
 
+MHD_INTERNAL
+MHD_FN_PAR_NONNULL_ (1)
+MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_IN_SIZE_(3,2)
+MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_CSTR_ (5) MHD_FN_PAR_IN_SIZE_(5,4) bool
+response_add_header_no_check (
+  struct MHD_Response *response,
+  size_t name_len,
+  const char name[MHD_FN_PAR_DYN_ARR_SIZE_(name_len)],
+  size_t value_len,
+  const char value[MHD_FN_PAR_DYN_ARR_SIZE_(value_len)])
+{
+  char *buf;
+  struct mhd_ResponseHeader *new_hdr;
+
+  buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2);
+  if (NULL == buf)
+    return false;
+
+  new_hdr = (struct mhd_ResponseHeader *) buf;
+  buf += sizeof(struct mhd_ResponseHeader);
+  memcpy (buf, name, name_len);
+  buf[name_len] = 0;
+  new_hdr->name.cstr = buf;
+  new_hdr->name.len = name_len;
+  buf += name_len + 1;
+  memcpy (buf, value, value_len);
+  buf[value_len] = 0;
+  new_hdr->value.cstr = buf;
+  new_hdr->value.len = value_len;
+
+  mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
+  return true;
+}
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_remove_all_headers (struct MHD_Response *restrict r)
+{
+  struct mhd_ResponseHeader *hdr;
+
+  for (hdr = mhd_DLINKEDL_GET_LAST(r, headers); NULL != hdr;
+       hdr = mhd_DLINKEDL_GET_LAST(r, headers))
+  {
+    mhd_DLINKEDL_DEL(r, hdr, headers);
+    free (hdr);
+  }
+}
+
+
 static enum MHD_StatusCode
 response_add_header_int (struct MHD_Response *response,
                          const char *name,
@@ -41,8 +89,6 @@ response_add_header_int (struct MHD_Response *response,
 {
   const size_t name_len = strlen (name);
   const size_t value_len = strlen (name);
-  char *buf;
-  struct mhd_ResponseHeader *new_hdr;
 
   if (response->frozen) /* Re-check with the lock held */
     return MHD_SC_TOO_LATE;
@@ -57,22 +103,9 @@ response_add_header_int (struct MHD_Response *response,
       (NULL != memchr (value, '\r', value_len)))
     return MHD_SC_RESP_HEADER_VALUE_INVALID;
 
-  buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2);
-  if (NULL == buf)
+  if (!response_add_header_no_check(response, name_len, name, value_len, value))
     return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED;
 
-  new_hdr = (struct mhd_ResponseHeader *) buf;
-  buf += sizeof(struct mhd_ResponseHeader);
-  memcpy (buf, name, name_len + 1);
-  new_hdr->name.cstr = buf;
-  new_hdr->name.len = name_len;
-  buf += name_len + 1;
-  memcpy (buf, value, value_len + 1);
-  new_hdr->value.cstr = buf;
-  new_hdr->value.len = value_len;
-
-  mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
-
   return MHD_SC_OK;
 }
 
@@ -85,7 +118,7 @@ MHD_response_add_header (struct MHD_Response *response,
                          const char *name,
                          const char *value)
 {
-  bool need_lock;
+  bool need_unlock;
   enum MHD_StatusCode res;
 
   if (response->frozen)
@@ -93,19 +126,19 @@ MHD_response_add_header (struct MHD_Response *response,
 
   if (response->reuse.reusable)
   {
-    need_lock = true;
+    need_unlock = true;
     if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
       return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
-    mhd_assert (1 == response->reuse.counter);
+    mhd_assert (1 == mhd_atomic_counter_get(&(response->reuse.counter)));
   }
   else
-    need_lock = false;
+    need_unlock = false;
 
   // TODO: add special processing for "Date", "Connection", "Content-Length"
 
   res = response_add_header_int (response, name, value);
 
-  if (need_lock)
+  if (need_unlock)
     mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
 
   return res;

+ 44 - 0
src/mhd2/response_add_header.h

@@ -0,0 +1,44 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/response_add_header.h
+ * @brief  The declarations of the response header manipulation internal
+ *         functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_ADD_HEADER_H
+#define MHD_RESPONSE_ADD_HEADER_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Remove all response headers
+ * @param r the response to use
+ */
+MHD_INTERNAL void
+mhd_response_remove_all_headers (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+
+#endif /* ! MHD_RESPONSE_ADD_HEADER_H */

+ 88 - 0
src/mhd2/response_destroy.c

@@ -0,0 +1,88 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/response_destroy.c
+ * @brief  The declarations of internal functions for response deletion
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "response_destroy.h"
+#include "mhd_response.h"
+
+#include "mhd_assert.h"
+#include "mhd_atomic_counter.h"
+
+#include "mhd_public_api.h"
+
+#include "response_add_header.h"
+#include "response_funcs.h"
+
+/**
+ * Perform full response de-initialisation, with cleaning-up / freeing
+ * all content data and headers.
+ * The response settings (if any) must be already freed.
+ * @param r the response to free
+ */
+static MHD_FN_PAR_NONNULL_ (1) void
+response_full_detinit (struct MHD_Response *restrict r)
+{
+  mhd_response_remove_all_headers (r);
+  if (r->reuse.reusable)
+    mhd_response_deinit_reusable(r);
+  mhd_response_deinit_content_data(r);
+  free (r);
+}
+
+
+MHD_INTERNAL void
+mhd_response_dec_use_count (struct MHD_Response *restrict r)
+{
+  mhd_assert(r->frozen);
+
+  if (r->reuse.reusable)
+  {
+    if (0 != mhd_atomic_counter_dec_get(&(r->reuse.counter)))
+      return; /* The response is still used somewhere */
+  }
+
+  response_full_detinit (r);
+}
+
+
+MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1) void
+MHD_response_destroy (struct MHD_Response *response)
+{
+  if (! response->frozen)
+  {
+    /* This response has been never used for actions */
+    mhd_assert (NULL != response->settings);
+    free (response->settings);
+#ifndef NDEBUG
+    /* Decrement counter to avoid triggering assert in deinit function */
+    mhd_assert (0 == mhd_atomic_counter_dec_get(&(response->reuse.counter)));
+#endif
+    response_full_detinit (response);
+    return;
+  }
+
+  mhd_response_dec_use_count (response);
+}

+ 43 - 0
src/mhd2/response_destroy.h

@@ -0,0 +1,43 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/response_destroy.h
+ * @brief  The declarations of internal functions for response deletion
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_DESTROY_H
+#define MHD_RESPONSE_DESTROY_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+/**
+ * Free/destroy non-reusable response, decrement use count for reusable
+ * response and free/destroy if it is not used any more.
+ * @param response the response to manipulate
+ */
+MHD_INTERNAL void
+mhd_response_dec_use_count (struct MHD_Response *restrict response);
+
+
+#endif /* ! MHD_RESPONSE_DESTROY_H */

+ 28 - 54
src/mhd2/response_from.c

@@ -34,6 +34,7 @@
 
 #include "compat_calloc.h"
 #include "sys_malloc.h"
+#include "sys_file_fd.h"
 
 #include "mhd_public_api.h"
 
@@ -43,15 +44,7 @@
 
 #include "mhd_assert.h"
 
-#include <limits.h>
-
-#ifndef ULONG_MAX
-#  define ULONG_MAX ((unsigned long) ~((unsigned long) 0))
-#endif
-
-#ifndef SIZE_MAX
-#  define ULONG_MAX ((unsigned long) ~((unsigned long) 0))
-#endif
+#include "mhd_limits.h"
 
 static struct MHD_Response *
 response_create_basic (enum MHD_HTTP_StatusCode sc,
@@ -76,7 +69,7 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
 
       r->sc = sc;
-      r->size = cntn_size;
+      r->cntn_size = cntn_size;
       r->free.cb = free_cb;
       r->free.cls = free_cb_cls;
       r->settings = s;
@@ -88,37 +81,18 @@ response_create_basic (enum MHD_HTTP_StatusCode sc,
   return NULL; /* Failure exit point */
 }
 
-
-static void
-response_destroy_unfrozen (struct MHD_Response *restrict r)
-{
-  mhd_assert (NULL != r->settings);
-  mhd_assert (! r->frozen);
-
-  // TODO: remove headers
-
-  if (mhd_RESPONSE_CONTENT_INVALID != r->cntn_type)
-  { /* Free callback has been set correctly already */
-    if (mhd_RESPONSE_CONTENT_IOVEC == r->cntn_type)
-      free (r->cntn.iovec.iov);
-    else if (mhd_RESPONSE_CONTENT_FILE == r->cntn_type)
-      close (r->cntn.file.fd);
-    /* For #mhd_RESPONSE_CONTENT_IOVEC clean-up performed by callback
-       for both modes: internal copy and external cleanup */
-    if (NULL != r->free.cb)
-      r->free.cb (r->free.cls);
-  }
-  free (r->settings);
-  free (r);
-}
-
-
-MHD_INTERNAL void
-mhd_response_dec_use_count (struct MHD_Response *restrict response)
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_deinit_content_data (struct MHD_Response *restrict r)
 {
-  (void) response;
-  // TODO: implement
-  return;
+  mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
+  if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype)
+    free (r->cntn.iovec.iov);
+  else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
+    close (r->cntn.file.fd);
+  /* For #mhd_RESPONSE_CONTENT_IOVEC clean-up performed by callback
+     for both modes: internal copy and external cleanup */
+  if (NULL != r->free.cb)
+    r->free.cb (r->free.cls);
 }
 
 
@@ -133,7 +107,7 @@ MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
   res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls);
   if (NULL != res)
   {
-    res->cntn_type = mhd_RESPONSE_CONTENT_CALLBACK;
+    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK;
     res->cntn.dyn.cb = dyn_cont;
     res->cntn.dyn.cls = dyn_cont_cls;
   }
@@ -160,7 +134,7 @@ MHD_response_from_buffer (
   res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls);
   if (NULL != res)
   {
-    res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
     res->cntn.buf = (0 != buffer_size) ?
                     (const unsigned char *) buffer : empty_buf;
   }
@@ -207,7 +181,7 @@ MHD_response_from_buffer_copy (
 
   if (NULL != res)
   {
-    res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
     res->cntn.buf = buf_copy;
   }
   return res;
@@ -255,8 +229,8 @@ MHD_response_from_iovec (
     {
       size_t i_add;
 
-      i_add = (size_t) (iov[i].iov_len / MHD_IOV_ELMN_MAX_SIZE);
-      if (0 != iov[i].iov_len % MHD_IOV_ELMN_MAX_SIZE)
+      i_add = (size_t) (iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE);
+      if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE)
         i_add++;
       i_cp += i_add;
       if (i_cp < i_add)
@@ -271,7 +245,7 @@ MHD_response_from_iovec (
     res = response_create_basic (sc, 0, free_cb, free_cb_cls);
     if (NULL != res)
     {
-      res->cntn_type = mhd_RESPONSE_CONTENT_BUFFER;
+      res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
       res->cntn.buf = empty_buf;
     }
     return res;
@@ -299,17 +273,17 @@ MHD_response_from_iovec (
       if (0 == element_size)
         continue;         /* skip zero-sized elements */
 #if defined(MHD_WINSOCK_SOCKETS) && defined(_WIN64)
-      while (MHD_IOV_ELMN_MAX_SIZE < element_size)
+      while (mhd_IOV_ELMN_MAX_SIZE < element_size)
       {
         iov_copy[i_cp].iov_base = (char *) mhd_DROP_CONST (buf);
-        iov_copy[i_cp].iov_len = MHD_IOV_ELMN_MAX_SIZE;
-        buf += MHD_IOV_ELMN_MAX_SIZE;
-        element_size -= MHD_IOV_ELMN_MAX_SIZE;
+        iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE;
+        buf += mhd_IOV_ELMN_MAX_SIZE;
+        element_size -= mhd_IOV_ELMN_MAX_SIZE;
         i_cp++;
       }
 #endif /* MHD_WINSOCK_SOCKETS && _WIN64 */
       iov_copy[i_cp].iov_base = mhd_DROP_CONST (buf);
-      iov_copy[i_cp].iov_len = (mhd_iov_size) element_size;
+      iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size;
       i_cp++;
     }
     mhd_assert (num_copy_elements == i_cp);
@@ -318,7 +292,7 @@ MHD_response_from_iovec (
     res = response_create_basic (sc, total_size, free_cb, free_cb_cls);
     if (NULL != res)
     {
-      res->cntn_type = mhd_RESPONSE_CONTENT_IOVEC;
+      res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC;
       res->cntn.iovec.iov = iov_copy;
       res->cntn.iovec.cnt = i_cp;
       return res;
@@ -340,7 +314,7 @@ MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
   res = response_create_basic (sc, size, NULL, NULL);
   if (NULL != res)
   {
-    res->cntn_type = mhd_RESPONSE_CONTENT_FILE;
+    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
     res->cntn.file.fd = fd;
     res->cntn.file.offset = offset;
     res->cntn.file.is_pipe = false; /* Not necessary */
@@ -358,7 +332,7 @@ MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
   res = response_create_basic (sc, MHD_SIZE_UNKNOWN, NULL, NULL);
   if (NULL != res)
   {
-    res->cntn_type = mhd_RESPONSE_CONTENT_FILE;
+    res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
     res->cntn.file.fd = fd;
     res->cntn.file.offset = 0; /* Not necessary */
     res->cntn.file.is_pipe = true;

+ 8 - 0
src/mhd2/response_from.h

@@ -32,5 +32,13 @@
 
 struct MHD_Response; /* forward declaration */
 
+/**
+ * Deinit / free / cleanup content data of the response
+ * @param r the response to use
+ */
+MHD_INTERNAL void
+mhd_response_deinit_content_data (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
 
 #endif /* ! MHD_RESPONSE_FROM_H */

+ 44 - 6
src/mhd2/response_funcs.c

@@ -26,11 +26,48 @@
 
 #include "mhd_sys_options.h"
 
+#include "sys_malloc.h"
+
 #include "sys_null_macro.h"
 #include "mhd_response.h"
 #include "response_funcs.h"
 #include "mhd_locks.h"
 
+
+#include "mhd_atomic_counter.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) bool
+response_make_reusable (struct MHD_Response *restrict r)
+{
+  mhd_assert (! r->reuse.reusable);
+  mhd_assert (! r->frozen);
+  mhd_assert (NULL != r->settings);
+
+  if (mhd_mutex_init(&(r->reuse.settings_lock)))
+  {
+    if (mhd_atomic_counter_init(&(r->reuse.counter), 1))
+    {
+      r->reuse.reusable = true;
+      return true;
+    }
+    (void) mhd_mutex_destroy(&(r->reuse.settings_lock));
+  }
+  return false;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) void
+mhd_response_deinit_reusable (struct MHD_Response *restrict r)
+{
+  mhd_assert(r->reuse.reusable);
+  mhd_assert(0 == mhd_atomic_counter_get(&(r->reuse.counter)));
+
+  mhd_atomic_counter_deinit(&(r->reuse.counter));
+  mhd_mutex_destroy_chk(&(r->reuse.settings_lock));
+}
+
+
 static void
 response_set_properties (struct MHD_Response *restrict r)
 {
@@ -47,12 +84,13 @@ response_set_properties (struct MHD_Response *restrict r)
   else
   {
     r->cfg.conn_close = s->conn_close;
-    r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->size);
+    r->cfg.chunked = s->chunked_enc || (MHD_SIZE_UNKNOWN == r->cntn_size);
   }
   r->cfg.mode_1_0 = s->http_1_0_server;
   r->cfg.cnt_len_by_app = s->insanity_header_content_length; // TODO: set only if "content-lengh" header is used
 
   r->settings = NULL;
+  free (s);
 }
 
 
@@ -64,22 +102,22 @@ response_set_properties (struct MHD_Response *restrict r)
 MHD_INTERNAL void
 mhd_response_check_frozen_freeze (struct MHD_Response *restrict response)
 {
-  bool need_lock;
+  bool need_unlock;
   if (response->frozen)
     return;
 
   if (response->reuse.reusable)
   {
-    need_lock = true;
+    need_unlock = true;
     mhd_mutex_lock_chk (&(response->reuse.settings_lock));
-    mhd_assert (1 == response->reuse.counter);
+    mhd_assert (1 == mhd_atomic_counter_get(&(response->reuse.counter)));
   }
   else
-    need_lock = false;
+    need_unlock = false;
 
   if (! response->frozen)/* Re-check under the lock */
     response_set_properties (response);
 
-  if (need_lock)
+  if (need_unlock)
     mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
 }

+ 20 - 7
src/mhd2/response_funcs.h

@@ -28,24 +28,37 @@
 #define MHD_RESPONSE_FUNCS_H 1
 
 #include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+
 
 struct MHD_Response; /* forward declaration */
 
 /**
- * Check whether response is "frozen" (modifications blocked) and "freeze"
- * it if not frozen before
- * @param response the response to manipulate
+ * Make response re-usable, initialise all required data
+ * @param r the response to make re-usable
+ * @return 'true' if succeed, 'false' if failed
+ */
+MHD_INTERNAL bool
+response_make_reusable (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
+
+/**
+ * De-initialise re-usability data
+ * @param r the response to de-initialise re-usability data
  */
 MHD_INTERNAL void
-mhd_response_check_frozen_freeze (struct MHD_Response *restrict response);
+mhd_response_deinit_reusable (struct MHD_Response *restrict r)
+MHD_FN_PAR_NONNULL_ (1);
+
 
 /**
- * Free/destroy non-reusable response, decrement use count for reusable
- * response and free/destroy if it is not used any more.
+ * Check whether response is "frozen" (modifications blocked) and "freeze"
+ * it if not frozen before
  * @param response the response to manipulate
  */
 MHD_INTERNAL void
-mhd_response_dec_use_count (struct MHD_Response *restrict response);
+mhd_response_check_frozen_freeze (struct MHD_Response *restrict r);
 
 
 #endif /* ! MHD_RESPONSE_FUNCS_H */

+ 14 - 26
src/mhd2/response_set_options.c

@@ -10,13 +10,14 @@
 
 
 #include "mhd_sys_options.h"
+#include "response_set_options.h"
 #include "sys_base_types.h"
+#include "sys_bool_type.h"
 #include "response_options.h"
 #include "mhd_response.h"
 #include "mhd_public_api.h"
-#include "sys_bool_type.h"
 #include "mhd_locks.h"
-
+#include "mhd_assert.h"
 
 /**
  * Internal version of the #MHD_response_set_options()
@@ -26,13 +27,13 @@
  * @param options_max_num the maximum number of @a options to use
  * @return #MHD_SC_OK on success, error code otherwise
  */
-static enum MHD_StatusCode
+static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
 response_set_options_int (
-  struct MHD_Response *volatile response,
-  const struct MHD_ResponseOptionAndValue *volatile options,
+  struct MHD_Response *restrict response,
+  const struct MHD_ResponseOptionAndValue *restrict options,
   size_t options_max_num)
 {
-  struct ResponseOptions *volatile settings;
+  struct ResponseOptions *restrict settings;
   size_t i;
 
   if (response->frozen)
@@ -55,20 +56,8 @@ response_set_options_int (
       else if (response->reuse.reusable)
         continue; /* This flag has been set before */
       else
-      {
-        if (mhd_mutex_init(&(response->reuse.settings_lock)))
-        {
-          if (mhd_mutex_init_short(&(response->reuse.cnt_lock)))
-          {
-            response->reuse.counter = 1;
-            response->reuse.reusable = true;
-            continue;
-          }
-          mhd_mutex_destroy_chk(&(response->reuse.settings_lock));
-        }
-        return MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
-      }
-      settings->reusable = option->val.reusable;
+        if (! response_make_reusable(response))
+          return MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
       continue;
     case MHD_R_O_HEAD_ONLY_RESPONSE:
       settings->head_only_response = option->val.head_only_response;
@@ -106,25 +95,24 @@ MHD_response_set_options (struct MHD_Response *response,
                           const struct MHD_ResponseOptionAndValue *options,
                           size_t options_max_num)
 {
-  bool need_lock;
+  bool need_unlock;
   enum MHD_StatusCode res;
 
   if (response->frozen)
     return MHD_SC_TOO_LATE;
 
+  need_unlock = false;
   if (response->reuse.reusable)
   {
-    need_lock = true;
+    need_unlock = true;
     if (! mhd_mutex_lock(&(response->reuse.settings_lock)))
       return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
-    mhd_assert (1 == response->reuse.counter);
+    mhd_assert (1 == mhd_atomic_counter_get(&(response->reuse.counter)));
   }
-  else
-    need_lock = false;
 
   res = response_set_options_int(response, options, options_max_num);
 
-  if (need_lock)
+  if (need_unlock)
     mhd_mutex_unlock_chk(&(response->reuse.settings_lock));
 
   return res;

+ 35 - 0
src/mhd2/response_set_options.h

@@ -0,0 +1,35 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/response_set_options.h
+ * @brief  The declarations of the internal response functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_RESPONSE_SET_OPTIONS_H
+#define MHD_RESPONSE_SET_OPTIONS_H 1
+
+#include "mhd_sys_options.h"
+
+struct MHD_Response; /* forward declaration */
+
+
+#endif /* ! MHD_RESPONSE_SET_OPTIONS_H */

+ 371 - 0
src/mhd2/stream_funcs.c

@@ -0,0 +1,371 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_funcs.c
+ * @brief  The definition of the stream internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "mhd_connection.h"
+#include "mhd_public_api.h"
+#include "stream_funcs.h"
+#include "mhd_assert.h"
+#include "mhd_mempool.h"
+#include "mhd_str.h"
+#include "mhd_str_macros.h"
+#include "request_get_value.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void *
+mhd_stream_alloc_memory (struct MHD_Connection *restrict c,
+                         size_t size)
+{
+  struct MemoryPool *const restrict pool = c->pool;     /* a short alias */
+  size_t need_to_be_freed = 0; /**< The required amount of additional free memory */
+  void *res;
+
+  res = MHD_pool_try_alloc (pool,
+                            size,
+                            &need_to_be_freed);
+  if (NULL != res)
+    return res;
+
+  if (MHD_pool_is_resizable_inplace (pool,
+                                     c->write_buffer,
+                                     c->write_buffer_size))
+  {
+    if (c->write_buffer_size - c->write_buffer_append_offset >=
+        need_to_be_freed)
+    {
+      char *buf;
+      const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
+      buf = MHD_pool_reallocate (pool,
+                                 c->write_buffer,
+                                 c->write_buffer_size,
+                                 new_buf_size);
+      mhd_assert (c->write_buffer == buf);
+      mhd_assert (c->write_buffer_append_offset <= new_buf_size);
+      mhd_assert (c->write_buffer_send_offset <= new_buf_size);
+      c->write_buffer_size = new_buf_size;
+      c->write_buffer = buf;
+    }
+    else
+      return NULL;
+  }
+  else if (MHD_pool_is_resizable_inplace (pool,
+                                          c->read_buffer,
+                                          c->read_buffer_size))
+  {
+    if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
+    {
+      char *buf;
+      const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
+      buf = MHD_pool_reallocate (pool,
+                                 c->read_buffer,
+                                 c->read_buffer_size,
+                                 new_buf_size);
+      mhd_assert (c->read_buffer == buf);
+      mhd_assert (c->read_buffer_offset <= new_buf_size);
+      c->read_buffer_size = new_buf_size;
+      c->read_buffer = buf;
+    }
+    else
+      return NULL;
+  }
+  else
+    return NULL;
+  res = MHD_pool_allocate (pool, size, true);
+  mhd_assert (NULL != res); /* It has been checked that pool has enough space */
+  return res;
+}
+
+
+#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
+/**
+ * A reasonable headers size (excluding request line) that should be sufficient
+ * for most requests.
+ * If incoming data buffer free space is not enough to process the complete
+ * header (the request line and all headers) and the headers size is larger than
+ * this size then the status code 431 "Request Header Fields Too Large" is
+ * returned to the client.
+ * The larger headers are processed by MHD if enough space is available.
+ */
+#  define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
+#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
+
+#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
+/**
+ * A reasonable request target (the request URI) size that should be sufficient
+ * for most requests.
+ * If incoming data buffer free space is not enough to process the complete
+ * header (the request line and all headers) and the request target size is
+ * larger than this size then the status code 414 "URI Too Long" is
+ * returned to the client.
+ * The larger request targets are processed by MHD if enough space is available.
+ * The value chosen according to RFC 9112 Section 3, paragraph 5
+ */
+#  define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
+#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
+/**
+ * A reasonable headers size (excluding request line) that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 431 "Request Header Fields Too Large" if headers size
+ * is smaller then this value.
+ */
+#  define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
+#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
+/**
+ * A reasonable request target (the request URI) size that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 414 "URI Too Long" if the request target size is smaller then
+ * this value.
+ */
+#  define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
+#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
+/**
+ * A reasonable request method string size that should be sufficient
+ * for basic simple requests.
+ * When no space left in the receiving buffer try to avoid replying with
+ * the status code 501 "Not Implemented" if the request method size is
+ * smaller then this value.
+ */
+#  define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
+#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
+
+#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
+/**
+ * A reasonable minimal chunk line length.
+ * When no space left in the receiving buffer reply with 413 "Content Too Large"
+ * if the chunk line length is larger than this value.
+ */
+#  define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
+#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_IN_SIZE_(4,3) unsigned int
+mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
+                                         enum MHD_ProcRecvDataStage stage,
+                                         size_t add_element_size,
+                                         const char *restrict add_element)
+{
+  size_t method_size;
+  size_t uri_size;
+  size_t opt_headers_size;
+  size_t host_field_line_size;
+
+  mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
+  mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
+  mhd_assert ((0 == add_element_size) || (NULL != add_element));
+
+  if (MHD_CONNECTION_HEADERS_RECEIVED > c->state)
+  {
+    mhd_assert (NULL != c->rq.field_lines.start);
+    opt_headers_size =
+      (size_t) ((c->read_buffer + c->read_buffer_offset)
+                - c->rq.field_lines.start);
+  }
+  else
+    opt_headers_size = c->rq.field_lines.size;
+
+  /* The read buffer is fully used by the request line, the field lines
+     (headers) and internal information.
+     The return status code works as a suggestion for the client to reduce
+     one of the request elements. */
+
+  if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
+      (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
+  {
+    /* Request could be re-tried easily with smaller chunk sizes */
+    return MHD_HTTP_STATUS_CONTENT_TOO_LARGE;
+  }
+
+  host_field_line_size = 0;
+  /* The "Host:" field line is mandatory.
+     The total size of the field lines (headers) cannot be smaller than
+     the size of the "Host:" field line. */
+  if ((MHD_PROC_RECV_HEADERS == stage)
+      && (0 != add_element_size))
+  {
+    static const size_t header_host_key_len =
+      mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
+    const bool is_host_header =
+      (header_host_key_len + 1 <= add_element_size)
+      && ( (0 == add_element[header_host_key_len])
+           || (':' == add_element[header_host_key_len]) )
+      && mhd_str_equal_caseless_bin_n (MHD_HTTP_HEADER_HOST,
+                                        add_element,
+                                        header_host_key_len);
+    if (is_host_header)
+    {
+      const bool is_parsed = ! (
+        (MHD_CONNECTION_HEADERS_RECEIVED > c->state) &&
+        (add_element_size == c->read_buffer_offset) &&
+        (c->read_buffer == add_element) );
+      size_t actual_element_size;
+
+      mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
+      /* The actual size should be larger due to CRLF or LF chars,
+         however the exact termination sequence is not known here and
+         as perfect precision is not required, to simplify the code
+         assume the minimal length. */
+      if (is_parsed)
+        actual_element_size = add_element_size + 1;  /* "1" for LF */
+      else
+        actual_element_size = add_element_size;
+
+      host_field_line_size = actual_element_size;
+      mhd_assert (opt_headers_size >= actual_element_size);
+      opt_headers_size -= actual_element_size;
+    }
+  }
+  if (0 == host_field_line_size)
+  {
+    static const size_t host_field_name_len =
+      mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
+    struct MHD_String *host_value;
+    host_value = mhd_request_get_value_len(&(c->rq),
+                                           MHD_VK_HEADER,
+                                           MHD_HTTP_HEADER_HOST,
+                                           host_field_name_len);
+    if (NULL != host_value)
+    {
+      /* Calculate the minimal size of the field line: no space between
+         colon and the field value, line terminated by LR */
+      host_field_line_size =
+        host_field_name_len + host_value->len + 2; /* "2" for ':' and LF */
+
+      /* The "Host:" field could be added by application */
+      if (opt_headers_size >= host_field_line_size)
+      {
+        opt_headers_size -= host_field_line_size;
+        /* Take into account typical space after colon and CR at the end of the line */
+        if (opt_headers_size >= 2)
+          opt_headers_size -= 2;
+      }
+      else
+        host_field_line_size = 0; /* No "Host:" field line set by the client */
+    }
+  }
+
+  uri_size = c->rq.req_target_len;
+  if (mhd_HTTP_METHOD_OTHER != c->rq.http_mthd)
+    method_size = 0; /* Do not recommend shorter request method */
+  else
+  {
+    mhd_assert (NULL != c->rq.method);
+    method_size = strlen (c->rq.method);
+  }
+
+  if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
+  {
+    /* Typically the easiest way to reduce request header size is
+       a removal of some optional headers. */
+    if (opt_headers_size > (uri_size / 8))
+    {
+      if ((opt_headers_size / 2) > method_size)
+        return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+      else
+        return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+    }
+    else
+    { /* Request target is MUCH larger than headers */
+      if ((uri_size / 16) > method_size)
+        return MHD_HTTP_STATUS_URI_TOO_LONG;
+      else
+        return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+    }
+  }
+  if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
+  {
+    /* If request target size if larger than maximum reasonable size
+       recommend client to reduce the request target size (length). */
+    if ((uri_size / 16) > method_size)
+      return MHD_HTTP_STATUS_URI_TOO_LONG;     /* Request target is MUCH larger than headers */
+    else
+      return MHD_HTTP_STATUS_NOT_IMPLEMENTED;  /* The length of the HTTP request method is unreasonably large */
+  }
+
+  /* The read buffer is too small to handle reasonably large requests */
+
+  if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
+  {
+    /* Recommend application to retry with minimal headers */
+    if ((opt_headers_size * 4) > uri_size)
+    {
+      if (opt_headers_size > method_size)
+        return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+      else
+        return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+    }
+    else
+    { /* Request target is significantly larger than headers */
+      if (uri_size > method_size * 4)
+        return MHD_HTTP_STATUS_URI_TOO_LONG;
+      else
+        return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+    }
+  }
+  if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
+  {
+    /* Recommend application to retry with a shorter request target */
+    if (uri_size > method_size * 4)
+      return MHD_HTTP_STATUS_URI_TOO_LONG;
+    else
+      return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
+  }
+
+  if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
+  {
+    /* The request target (URI) and headers are (reasonably) very small.
+       Some non-standard long request method is used. */
+    /* The last resort response as it means "the method is not supported
+       by the server for any URI". */
+    return MHD_HTTP_STATUS_NOT_IMPLEMENTED;
+  }
+
+  /* The almost impossible situation: all elements are small, but cannot
+     fit the buffer. The application set the buffer size to
+     critically low value? */
+
+  if ((1 < opt_headers_size) || (1 < uri_size))
+  {
+    if (opt_headers_size >= uri_size)
+      return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+    else
+      return MHD_HTTP_STATUS_URI_TOO_LONG;
+  }
+
+  /* Nothing to reduce in the request.
+     Reply with some status. */
+  if (0 != host_field_line_size)
+    return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
+
+  return MHD_HTTP_STATUS_URI_TOO_LONG;
+}

+ 86 - 0
src/mhd2/stream_funcs.h

@@ -0,0 +1,86 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_funcs.h
+ * @brief  The declaration of the stream internal functions
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_FUNCS_H
+#define MHD_STREAM_FUNCS_H 1
+
+#include "mhd_sys_options.h"
+#include "sys_base_types.h"
+
+struct MHD_Connection; /* forward declaration */
+
+
+/**
+ * The stage of input data processing.
+ * Used for out-of-memory (in the pool) handling.
+ */
+enum MHD_FIXED_ENUM_ MHD_ProcRecvDataStage
+{
+  MHD_PROC_RECV_INIT,        /**< No data HTTP request data have been processed yet */
+  MHD_PROC_RECV_METHOD,      /**< Processing/receiving the request HTTP method */
+  MHD_PROC_RECV_URI,         /**< Processing/receiving the request URI */
+  MHD_PROC_RECV_HTTPVER,     /**< Processing/receiving the request HTTP version string */
+  MHD_PROC_RECV_HEADERS,     /**< Processing/receiving the request HTTP headers */
+  MHD_PROC_RECV_COOKIE,      /**< Processing the received request cookie header */
+  MHD_PROC_RECV_BODY_NORMAL, /**< Processing/receiving the request non-chunked body */
+  MHD_PROC_RECV_BODY_CHUNKED,/**< Processing/receiving the request chunked body */
+  MHD_PROC_RECV_FOOTERS      /**< Processing/receiving the request footers */
+};
+
+/**
+ * Allocate memory from connection's memory pool.
+ * If memory pool doesn't have enough free memory but read or write buffer
+ * have some unused memory, the size of the buffer will be reduced as needed.
+ * @param connection the connection to use
+ * @param size the size of allocated memory area
+ * @return pointer to allocated memory region in the pool or
+ *         NULL if no memory is available
+ */
+MHD_INTERNAL void *
+mhd_stream_alloc_memory (struct MHD_Connection *restrict connection,
+                         size_t size)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Select the HTTP error status code for "out of receive buffer space" error.
+ * @param c the connection to process
+ * @param stage the current stage of request receiving
+ * @param add_element_size the size of the @a add_element;
+ *                         zero if @a add_element is NULL
+ * @param add_element the optional pointer to the element failed to be processed
+ *                    or added, the meaning of the element depends on
+ *                    the @a stage. Could be not zero-terminated and can
+ *                    contain binary zeros. Can be NULL.
+ * @return the HTTP error code to use in the error reply
+ */
+MHD_INTERNAL unsigned int
+mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
+                                         enum MHD_ProcRecvDataStage stage,
+                                         size_t add_element_size,
+                                         const char *restrict add_element)
+MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_IN_SIZE_(4,3);
+
+#endif /* ! MHD_STREAM_FUNCS_H */

+ 2149 - 0
src/mhd2/stream_process_request.c

@@ -0,0 +1,2149 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_process_request.c
+ * @brief  The implementation of internal functions for requests parsing
+ *         and processing
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+#include "sys_base_types.h"
+
+#include "mhd_str_macros.h"
+
+#include <string.h>
+
+#include "mhd_connection.h"
+
+#include "mhd_str_types.h"
+#include "daemon_logger.h"
+
+#include "request_funcs.h"
+
+/**
+ * Response text used when the request (http header) is
+ * malformed.
+ */
+#define ERR_RSP_REQUEST_MALFORMED \
+        "<html><head><title>Request malformed</title></head>" \
+        "<body>HTTP request is syntactically incorrect.</body></html>"
+
+/**
+ * Response text used when the request HTTP version is too old.
+ */
+#define ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD \
+        "<html>" \
+        "<head><title>Requested HTTP version is not supported</title></head>" \
+        "<body>Requested HTTP version is too old and not " \
+        "supported.</body></html>"
+/**
+ * Response text used when the request HTTP version is not supported.
+ */
+#define ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED \
+        "<html>" \
+        "<head><title>Requested HTTP version is not supported</title></head>" \
+        "<body>Requested HTTP version is not supported.</body></html>"
+
+/**
+ * Response text used when the request HTTP header has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#define ERR_RSP_BARE_CR_IN_HEADER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Request HTTP header has bare CR character without " \
+        "following LF character.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request HTTP footer has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#define ERR_RSP_BARE_CR_IN_FOOTER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Request HTTP footer has bare CR character without " \
+        "following LF character.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request HTTP header has bare LF character
+ * without CR character.
+ */
+#define ERR_RSP_BARE_LF_IN_HEADER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Request HTTP header has bare LF character without " \
+        "preceding CR character.</body>" \
+        "</html>"
+/**
+ * Response text used when the request HTTP footer has bare LF character
+ * without CR character.
+ */
+#define ERR_RSP_BARE_LF_IN_FOOTER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Request HTTP footer has bare LF character without " \
+        "preceding CR character.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request line has more then two whitespaces.
+ */
+#define ERR_RSP_RQ_LINE_TOO_MANY_WSP \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>The request line has more then two whitespaces.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request line has invalid characters in URI.
+ */
+#define ERR_RSP_RQ_TARGET_INVALID_CHAR \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has invalid characters in " \
+        "the request-target.</body>" \
+        "</html>"
+
+/**
+ * Response text used when line folding is used in request headers.
+ */
+#define ERR_RSP_OBS_FOLD \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Obsolete line folding is used in HTTP request header.</body>" \
+        "</html>"
+
+/**
+ * Response text used when line folding is used in request footers.
+ */
+#define ERR_RSP_OBS_FOLD_FOOTER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>Obsolete line folding is used in HTTP request footer.</body>" \
+        "</html>"
+
+/**
+ * Response text used when request header has no colon character.
+ */
+#define ERR_RSP_HEADER_WITHOUT_COLON \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request header line has no colon character.</body>" \
+        "</html>"
+
+/**
+ * Response text used when request footer has no colon character.
+ */
+#define ERR_RSP_FOOTER_WITHOUT_COLON \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request footer line has no colon character.</body>" \
+        "</html>"
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first header line.
+ */
+#define ERR_RSP_WSP_BEFORE_HEADER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has whitespace between the request line and " \
+        "the first header.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first footer line.
+ */
+#define ERR_RSP_WSP_BEFORE_FOOTER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>First HTTP footer line has whitespace at the first " \
+        "position.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#define ERR_RSP_WSP_IN_HEADER_NAME \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has whitespace before the first colon " \
+        "in header line.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#define ERR_RSP_WSP_IN_FOOTER_NAME \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has whitespace before the first colon " \
+        "in footer line.</body>" \
+        "</html>"
+/**
+ * Response text used when request header has invalid character.
+ */
+#define ERR_RSP_INVALID_CHR_IN_HEADER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has invalid character in header.</body>" \
+        "</html>"
+
+/**
+ * Response text used when request header has invalid character.
+ */
+#define ERR_RSP_INVALID_CHR_IN_FOOTER \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request has invalid character in footer.</body>" \
+        "</html>"
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#define ERR_RSP_EMPTY_HEADER_NAME \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request header has empty header name.</body>" \
+        "</html>"
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#define ERR_RSP_EMPTY_FOOTER_NAME \
+        "<html>" \
+        "<head><title>Request broken</title></head>" \
+        "<body>HTTP request footer has empty footer name.</body>" \
+        "</html>"
+
+/**
+ * Response text used when the request header is too big to be processed.
+ */
+#define ERR_RSP_REQUEST_HEADER_TOO_BIG \
+        "<html>" \
+        "<head><title>Request too big</title></head>" \
+        "<body><p>The total size of the request headers, which includes the " \
+        "request target and the request field lines, exceeds the memory " \
+        "constraints of this web server.</p>" \
+        "<p>The request could be re-tried with shorter field lines, a shorter " \
+        "request target or a shorter request method token.</p></body>" \
+        "</html>"
+
+/**
+ * Get whether bare LF in HTTP header and other protocol elements
+ * should be treated as the line termination depending on the configured
+ * strictness level.
+ * RFC 9112, section 2.2
+ */
+#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
+
+/**
+ * The valid length of any HTTP version string
+ */
+#define HTTP_VER_LEN (mhd_SSTR_LEN(MHD_HTTP_VERSION_1_1_STR))
+
+/**
+ * Detect HTTP version, send error response if version is not supported
+ *
+ * @param connection the connection
+ * @param http_string the pointer to HTTP version string
+ * @param len the length of @a http_string in bytes
+ * @return true if HTTP version is correct and supported,
+ *         false if HTTP version is not correct or unsupported.
+ */
+static bool
+parse_http_version (struct MHD_Connection *restrict connection,
+                    const char *restrict http_string,
+                    size_t len)
+{
+  const char *const h = http_string; /**< short alias */
+  mhd_assert (NULL != http_string);
+
+  /* String must start with 'HTTP/d.d', case-sensetive match.
+   * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
+  if ((HTTP_VER_LEN != len) ||
+      ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
+      ('/' != h[4])
+      || ('.' != h[6]))
+  {
+    connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+    transmit_error_response_static (connection,
+                                    MHD_HTTP_STATUS_BAD_REQUEST,
+                                    ERR_RSP_REQUEST_MALFORMED);
+    return false;
+  }
+  if (1 == h[5] - '0')
+  {
+    /* HTTP/1.x */
+    if (1 == h[7] - '0')
+    {
+      connection->rq.http_ver = MHD_HTTP_VERSION_1_1;
+      return true;
+    }
+    else if (0 == h[7] - '0')
+    {
+      connection->rq.http_ver = MHD_HTTP_VERSION_1_0;
+      return true;
+    }
+    else
+      connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+
+  }
+  else if (0 == h[5] - '0')
+  {
+    /* Too old major version */
+    connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+    transmit_error_response_static (connection,
+                                    MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+                                    ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD);
+    return false;
+  }
+  else if ((2 == h[5] - '0') &&
+      (('0' > h[7]) || ('9' < h[7])))
+    connection->rq.http_ver = MHD_HTTP_VERSION_2;
+  else
+    connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+
+  transmit_error_response_static (connection,
+                                  MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
+                                  ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED);
+  return false;
+}
+
+
+#ifndef MHD_MAX_EMPTY_LINES_SKIP
+/**
+ * The maximum number of ignored empty line before the request line
+ * at default "strictness" level.
+ */
+#define MHD_MAX_EMPTY_LINES_SKIP 1024
+#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
+
+
+/**
+ * Find and parse the request line.
+ * @param c the connection to process
+ * @return true if request line completely processed (or unrecoverable error
+ *         found) and state is changed,
+ *         false if not enough data yet in the receive buffer
+ */
+static MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_get_request_line_inner (struct MHD_Connection *restrict c)
+{
+  size_t p; /**< The current processing position */
+  const int discp_lvl = c->daemon->req_cfg.strictnees;
+  /* Allow to skip one or more empty lines before the request line.
+     RFC 9112, section 2.2 */
+  const bool skip_empty_lines = (1 >= discp_lvl);
+  /* Allow to skip more then one empty line before the request line.
+     RFC 9112, section 2.2 */
+  const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
+  /* Allow to skip unlimited number of empty lines before the request line.
+     RFC 9112, section 2.2 */
+  const bool skip_unlimited_empty_lines =
+    (skip_empty_lines && (-3 >= discp_lvl));
+  /* Treat bare LF as the end of the line.
+     RFC 9112, section 2.2 */
+  const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+  /* Treat tab as whitespace delimiter.
+     RFC 9112, section 3 */
+  const bool tab_as_wsp = (0 >= discp_lvl);
+  /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
+     RFC 9112, section 3 */
+  const bool other_wsp_as_wsp = (-1 >= discp_lvl);
+  /* Treat continuous whitespace block as a single space.
+     RFC 9112, section 3 */
+  const bool wsp_blocks = (-1 >= discp_lvl);
+  /* Parse whitespace in URI, special parsing of the request line.
+     RFC 9112, section 3.2 */
+  const bool wsp_in_uri = (0 >= discp_lvl);
+  /* Keep whitespace in URI, give app URI with whitespace instead of
+     automatic redirect to fixed URI.
+     Violates RFC 9112, section 3.2 */
+  const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+  /* Keep bare CR character as is.
+     Violates RFC 9112, section 2.2 */
+  const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
+  /* Treat bare CR as space; replace it with space before processing.
+     RFC 9112, section 2.2 */
+  const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+
+  mhd_assert (MHD_CONNECTION_INIT == c->state || \
+              MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+  mhd_assert (NULL == c->rq.method || \
+              MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+  mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
+              MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+  mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
+              0 != c->rq.hdrs.rq_line.proc_pos);
+
+  if (0 == c->read_buffer_offset)
+  {
+    mhd_assert (MHD_CONNECTION_INIT == c->state);
+    return false; /* No data to process */
+  }
+  p = c->rq.hdrs.rq_line.proc_pos;
+  mhd_assert (p <= c->read_buffer_offset);
+
+  /* Skip empty lines, if any (and if allowed) */
+  /* See RFC 9112, section 2.2 */
+  if ((0 == p)
+      && (skip_empty_lines))
+  {
+    /* Skip empty lines before the request line.
+       See RFC 9112, section 2.2 */
+    bool is_empty_line;
+    mhd_assert (MHD_CONNECTION_INIT == c->state);
+    mhd_assert (NULL == c->rq.method);
+    mhd_assert (NULL == c->rq.url);
+    mhd_assert (0 == c->rq.url_len);
+    mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+    mhd_assert (0 == c->rq.req_target_len);
+    mhd_assert (NULL == c->rq.version);
+    do
+    {
+      is_empty_line = false;
+      if ('\r' == c->read_buffer[0])
+      {
+        if (1 == c->read_buffer_offset)
+          return false; /* Not enough data yet */
+        if ('\n' == c->read_buffer[1])
+        {
+          is_empty_line = true;
+          c->read_buffer += 2;
+          c->read_buffer_size -= 2;
+          c->read_buffer_offset -= 2;
+          c->rq.hdrs.rq_line.skipped_empty_lines++;
+        }
+      }
+      else if (('\n' == c->read_buffer[0]) &&
+               (bare_lf_as_crlf))
+      {
+        is_empty_line = true;
+        c->read_buffer += 1;
+        c->read_buffer_size -= 1;
+        c->read_buffer_offset -= 1;
+        c->rq.hdrs.rq_line.skipped_empty_lines++;
+      }
+      if (is_empty_line)
+      {
+        if ((! skip_unlimited_empty_lines) &&
+            (((unsigned int) ((skip_several_empty_lines) ?
+                              MHD_MAX_EMPTY_LINES_SKIP : 1)) <
+             c->rq.hdrs.rq_line.skipped_empty_lines))
+        {
+          connection_close_error (c,
+                                  _ ("Too many meaningless extra empty lines " \
+                                     "received before the request"));
+          return true; /* Process connection closure */
+        }
+        if (0 == c->read_buffer_offset)
+          return false;  /* No more data to process */
+      }
+    } while (is_empty_line);
+  }
+  /* All empty lines are skipped */
+
+  c->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
+  /* Read and parse the request line */
+  mhd_assert (1 <= c->read_buffer_offset);
+
+  while (p < c->read_buffer_offset)
+  {
+    char *const restrict read_buffer = c->read_buffer;
+    const char chr = read_buffer[p];
+    bool end_of_line;
+    /*
+       The processing logic is different depending on the configured strictness:
+
+       When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
+       processed BEFORE processing of the current character.
+       When whitespace BLOCKS are ALLOWED, the end of the whitespace is
+       processed AFTER processing of the current character.
+
+       When space char in the URI is ALLOWED, the delimiter between the URI and
+       the HTTP version string is processed only at the END of the line.
+       When space in the URI is NOT ALLOWED, the delimiter between the URI and
+       the HTTP version string is processed as soon as the FIRST whitespace is
+       found after URI start.
+     */
+
+    end_of_line = false;
+
+    mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+                (c->rq.hdrs.rq_line.last_ws_end > \
+                 c->rq.hdrs.rq_line.last_ws_start));
+    mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
+                (0 != c->rq.hdrs.rq_line.last_ws_end));
+
+    /* Check for the end of the line */
+    if ('\r' == chr)
+    {
+      if (p + 1 == c->read_buffer_offset)
+      {
+        c->rq.hdrs.rq_line.proc_pos = p;
+        return false; /* Not enough data yet */
+      }
+      else if ('\n' == read_buffer[p + 1])
+        end_of_line = true;
+      else
+      {
+        /* Bare CR alone */
+        /* Must be rejected or replaced with space char.
+           See RFC 9112, section 2.2 */
+        if (bare_cr_as_sp)
+        {
+          read_buffer[p] = ' ';
+          c->rq.num_cr_sp_replaced++;
+          continue; /* Re-start processing of the current character */
+        }
+        else if (! bare_cr_keep)
+        {
+          /* A quick simple check whether this line looks like an HTTP request */
+          if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+              (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+          {
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_BARE_CR_IN_HEADER);
+          }
+          else
+            connection_close_error (c,
+                                    _ ("Bare CR characters are not allowed " \
+                                       "in the request line.\n"));
+          return true; /* Error in the request */
+        }
+      }
+    }
+    else if ('\n' == chr)
+    {
+      /* Bare LF may be recognised as a line delimiter.
+         See RFC 9112, section 2.2 */
+      if (bare_lf_as_crlf)
+        end_of_line = true;
+      else
+      {
+        /* While RFC does not enforce error for bare LF character,
+           if this char is not treated as a line delimiter, it should be
+           rejected to avoid any security weakness due to request smuggling. */
+        /* A quick simple check whether this line looks like an HTTP request */
+        if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+            (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+        {
+          transmit_error_response_static (c,
+                                          MHD_HTTP_STATUS_BAD_REQUEST,
+                                          ERR_RSP_BARE_LF_IN_HEADER);
+        }
+        else
+          connection_close_error (c,
+                                  _ ("Bare LF characters are not allowed " \
+                                     "in the request line.\n"));
+        return true; /* Error in the request */
+      }
+    }
+
+    if (end_of_line)
+    {
+      /* Handle the end of the request line */
+
+      if (NULL != c->rq.method)
+      {
+        if (wsp_in_uri)
+        {
+          /* The end of the URI and the start of the HTTP version string
+             should be determined now. */
+          mhd_assert (NULL == c->rq.version);
+          mhd_assert (0 == c->rq.req_target_len);
+          if (0 != c->rq.hdrs.rq_line.last_ws_end)
+          {
+            /* Determine the end and the length of the URI */
+            if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+            {
+              read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
+              c->rq.req_target_len =
+                c->rq.hdrs.rq_line.last_ws_start
+                - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
+            }
+            else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
+                      c->rq.hdrs.rq_line.last_ws_end) &&
+                     (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
+            {
+              /* Found only HTTP method and HTTP version and more than one
+                 whitespace between them. Assume zero-length URI. */
+              mhd_assert (wsp_blocks);
+              c->rq.hdrs.rq_line.last_ws_start++;
+              read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
+              c->rq.hdrs.rq_line.rq_tgt =
+                read_buffer + c->rq.hdrs.rq_line.last_ws_start;
+              c->rq.req_target_len = 0;
+              c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+              c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+            }
+            /* Determine the start of the HTTP version string */
+            if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+            {
+              c->rq.version = read_buffer + c->rq.hdrs.rq_line.last_ws_end;
+            }
+          }
+        }
+        else
+        {
+          /* The end of the URI and the start of the HTTP version string
+             should be already known. */
+          if ((NULL == c->rq.version)
+              && (NULL != c->rq.hdrs.rq_line.rq_tgt)
+              && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
+                                                - read_buffer))
+              && (0 != read_buffer[(size_t)
+                                      (c->rq.hdrs.rq_line.rq_tgt
+                                       - read_buffer) - 1]))
+          {
+            /* Found only HTTP method and HTTP version and more than one
+               whitespace between them. Assume zero-length URI. */
+            size_t uri_pos;
+            mhd_assert (wsp_blocks);
+            mhd_assert (0 == c->rq.req_target_len);
+            uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer) - 1;
+            mhd_assert (uri_pos < p);
+            c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
+            read_buffer[uri_pos] = 0;  /* Zero terminate the URI */
+            c->rq.hdrs.rq_line.rq_tgt = read_buffer + uri_pos;
+            c->rq.req_target_len = 0;
+            c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+            c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+          }
+        }
+
+        if (NULL != c->rq.version)
+        {
+          mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+          if (! parse_http_version (c, c->rq.version,
+                                    p
+                                    - (size_t) (c->rq.version
+                                                - read_buffer)))
+          {
+            mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
+            return true; /* Unsupported / broken HTTP version */
+          }
+          read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
+          if ('\r' == chr)
+          {
+            p++; /* Consume CR */
+            mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
+          }
+          p++; /* Consume LF */
+          c->read_buffer += p;
+          c->read_buffer_size -= p;
+          c->read_buffer_offset -= p;
+          mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
+                      c->rq.req_target_len);
+          mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+                      (0 != c->rq.req_target_len));
+          mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+                      ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
+                                 - c->rq.hdrs.rq_line.rq_tgt) < \
+                       c->rq.req_target_len));
+          mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+                      (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
+                       c->rq.hdrs.rq_line.rq_tgt));
+          return true; /* The request line is successfully parsed */
+        }
+      }
+      /* Error in the request line */
+
+      /* A quick simple check whether this line looks like an HTTP request */
+      if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+          (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+      {
+        transmit_error_response_static (c,
+                                        MHD_HTTP_STATUS_BAD_REQUEST,
+                                        ERR_RSP_REQUEST_MALFORMED);
+      }
+      else
+        connection_close_error (c,
+                                _ ("The request line is malformed.\n"));
+
+      return true;
+    }
+
+    /* Process possible end of the previously found whitespace delimiter */
+    if ((! wsp_blocks) &&
+        (p == c->rq.hdrs.rq_line.last_ws_end) &&
+        (0 != c->rq.hdrs.rq_line.last_ws_end))
+    {
+      /* Previous character was a whitespace char and whitespace blocks
+         are not allowed. */
+      /* The current position is the next character after
+         a whitespace delimiter */
+      if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+      {
+        /* The current position is the start of the URI */
+        mhd_assert (0 == c->rq.req_target_len);
+        mhd_assert (NULL == c->rq.version);
+        c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
+        /* Reset the whitespace marker */
+        c->rq.hdrs.rq_line.last_ws_start = 0;
+        c->rq.hdrs.rq_line.last_ws_end = 0;
+      }
+      else
+      {
+        /* It was a whitespace after the start of the URI */
+        if (! wsp_in_uri)
+        {
+          mhd_assert ((0 != c->rq.req_target_len) || \
+                      (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
+          mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
+          c->rq.version = read_buffer + p;
+          /* Reset the whitespace marker */
+          c->rq.hdrs.rq_line.last_ws_start = 0;
+          c->rq.hdrs.rq_line.last_ws_end = 0;
+        }
+      }
+    }
+
+    /* Process the current character.
+       Is it not the end of the line.  */
+    if ((' ' == chr)
+        || (('\t' == chr) && (tab_as_wsp))
+        || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
+    {
+      /* A whitespace character */
+      if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
+          (p != c->rq.hdrs.rq_line.last_ws_end) ||
+          (! wsp_blocks))
+      {
+        /* Found first whitespace char of the new whitespace block */
+        if (NULL == c->rq.method)
+        {
+          /* Found the end of the HTTP method string */
+          mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start);
+          mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end);
+          mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+          mhd_assert (0 == c->rq.req_target_len);
+          mhd_assert (NULL == c->rq.version);
+          if (0 == p)
+          {
+            connection_close_error (c,
+                                    _ ("The request line starts with "
+                                       "a whitespace.\n"));
+            return true; /* Error in the request */
+          }
+          read_buffer[p] = 0; /* Zero-terminate the request method string */
+          c->rq.method = read_buffer;
+          parse_http_std_method (c, c->rq.method, p);
+        }
+        else
+        {
+          /* A whitespace after the start of the URI */
+          if (! wsp_in_uri)
+          {
+            /* Whitespace in URI is not allowed to be parsed */
+            if (NULL == c->rq.version)
+            {
+              mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+              /* This is a delimiter between URI and HTTP version string */
+              read_buffer[p] = 0; /* Zero-terminate request URI string */
+              mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt   \
+                                     - read_buffer)) <= p);
+              c->rq.req_target_len =
+                p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
+            }
+            else
+            {
+              /* This is a delimiter AFTER version string */
+
+              /* A quick simple check whether this line looks like an HTTP request */
+              if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
+                  (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
+              {
+                transmit_error_response_static (c,
+                                                MHD_HTTP_STATUS_BAD_REQUEST,
+                                                ERR_RSP_RQ_LINE_TOO_MANY_WSP);
+              }
+              else
+                connection_close_error (c,
+                                        _ ("The request line has more than "
+                                           "two whitespaces.\n"));
+              return true; /* Error in the request */
+            }
+          }
+          else
+          {
+            /* Whitespace in URI is allowed to be parsed */
+            if (0 != c->rq.hdrs.rq_line.last_ws_end)
+            {
+              /* The whitespace after the start of the URI has been found already */
+              c->rq.hdrs.rq_line.num_ws_in_uri +=
+                c->rq.hdrs.rq_line.last_ws_end
+                - c->rq.hdrs.rq_line.last_ws_start;
+            }
+          }
+        }
+        c->rq.hdrs.rq_line.last_ws_start = p;
+        c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
+      }
+      else
+      {
+        /* Continuation of the whitespace block */
+        mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end);
+        mhd_assert (0 != p);
+        c->rq.hdrs.rq_line.last_ws_end = p + 1;
+      }
+    }
+    else
+    {
+      /* Non-whitespace char, not the end of the line */
+      mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+                  (c->rq.hdrs.rq_line.last_ws_end == p) || \
+                  wsp_in_uri);
+
+      if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
+          (0 != c->rq.hdrs.rq_line.last_ws_end) &&
+          (wsp_blocks))
+      {
+        /* The end of the whitespace block */
+        if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+        {
+          /* This is the first character of the URI */
+          mhd_assert (0 == c->rq.req_target_len);
+          mhd_assert (NULL == c->rq.version);
+          c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
+          /* Reset the whitespace marker */
+          c->rq.hdrs.rq_line.last_ws_start = 0;
+          c->rq.hdrs.rq_line.last_ws_end = 0;
+        }
+        else
+        {
+          if (! wsp_in_uri)
+          {
+            /* This is the first character of the HTTP version */
+            mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+            mhd_assert ((0 != c->rq.req_target_len) || \
+                        (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
+            mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
+            c->rq.version = read_buffer + p;
+            /* Reset the whitespace marker */
+            c->rq.hdrs.rq_line.last_ws_start = 0;
+            c->rq.hdrs.rq_line.last_ws_end = 0;
+          }
+        }
+      }
+
+      /* Handle other special characters */
+      if ('?' == chr)
+      {
+        if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
+            (NULL != c->rq.hdrs.rq_line.rq_tgt))
+        {
+          c->rq.hdrs.rq_line.rq_tgt_qmark = read_buffer + p;
+        }
+      }
+      else if ((0xb == chr) || (0xc == chr))
+      {
+        /* VT or LF characters */
+        mhd_assert (! other_wsp_as_wsp);
+        if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
+            (NULL == c->rq.version) &&
+            (wsp_in_uri))
+        {
+          c->rq.hdrs.rq_line.num_ws_in_uri++;
+        }
+        else
+        {
+          connection_close_error (c,
+                                  _ ("Invalid character is in the "
+                                     "request line.\n"));
+          return true; /* Error in the request */
+        }
+      }
+      else if (0 == chr)
+      {
+        /* NUL character */
+        connection_close_error (c,
+                                _ ("The NUL character is in the "
+                                   "request line.\n"));
+        return true; /* Error in the request */
+      }
+    }
+
+    p++;
+  }
+
+  c->rq.hdrs.rq_line.proc_pos = p;
+  return false; /* Not enough data yet */
+}
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_get_request_line (struct MHD_Connection *restrict c)
+{
+  const int discp_lvl = c->daemon->req_cfg.strictnees;
+  /* Parse whitespace in URI, special parsing of the request line */
+  const bool wsp_in_uri = (0 >= discp_lvl);
+  /* Keep whitespace in URI, give app URI with whitespace instead of
+     automatic redirect to fixed URI */
+  const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+
+  if (! get_request_line_inner (c))
+  {
+    /* End of the request line has not been found yet */
+    mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
+    if ((NULL != c->rq.version) &&
+        (HTTP_VER_LEN <
+         (c->rq.hdrs.rq_line.proc_pos
+          - (size_t) (c->rq.version - c->read_buffer))))
+    {
+      c->rq.http_ver = MHD_HTTP_VERSION_INVALID;
+      transmit_error_response_static (c,
+                                      MHD_HTTP_STATUS_BAD_REQUEST,
+                                      ERR_RSP_REQUEST_MALFORMED);
+      return true; /* Error in the request */
+    }
+    return false;
+  }
+  if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
+    return true; /* Error in the request */
+
+  mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+  mhd_assert (NULL == c->rq.url);
+  mhd_assert (0 == c->rq.url_len);
+  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+  if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
+  {
+    if (! wsp_in_uri)
+    {
+      transmit_error_response_static (c,
+                                      MHD_HTTP_STATUS_BAD_REQUEST,
+                                      ERR_RSP_RQ_TARGET_INVALID_CHAR);
+      return true; /* Error in the request */
+    }
+    if (! wsp_in_uri_keep)
+    {
+      send_redirect_fixed_rq_target (c);
+      return true; /* Error in the request */
+    }
+  }
+  if (! process_request_target (c))
+    return true; /* Error in processing */
+
+  c->state = MHD_CONNECTION_REQ_LINE_RECEIVED;
+  return true;
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_switch_to_rq_headers_processing (struct MHD_Connection *restrict c)
+{
+  c->rq.field_lines.start = c->read_buffer;
+  memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+  c->state = MHD_CONNECTION_REQ_HEADERS_RECEIVING;
+}
+
+/**
+ * Send error reply when receive buffer space exhausted while receiving or
+ * storing the request headers
+ * @param c the connection to handle
+ * @param add_header the optional pointer to the current header string being
+ *                   processed or the header failed to be added.
+ *                   Could be not zero-terminated and can contain binary zeros.
+ *                   Can be NULL.
+ * @param add_header_size the size of the @a add_header
+ */
+static void
+handle_req_headers_no_space (struct MHD_Connection *restrict c,
+                             const char *restrict add_header,
+                             size_t add_header_size)
+{
+  unsigned int err_code;
+
+  err_code = get_no_space_err_status_code (c,
+                                           MHD_PROC_RECV_HEADERS,
+                                           add_header,
+                                           add_header_size);
+  transmit_error_response_static (c,
+                                  err_code,
+                                  ERR_RSP_REQUEST_HEADER_TOO_BIG);
+}
+
+
+/**
+ * Results of header line reading
+ */
+enum mhd_HdrLineReadRes
+{
+  /**
+   * Not enough data yet
+   */
+  MHD_HDR_LINE_READING_NEED_MORE_DATA = 0,
+  /**
+   * New header line has been read
+   */
+  MHD_HDR_LINE_READING_GOT_HEADER,
+  /**
+   * Error in header data, error response has been queued
+   */
+  MHD_HDR_LINE_READING_DATA_ERROR,
+  /**
+   * Found the end of the request header (end of field lines)
+   */
+  MHD_HDR_LINE_READING_GOT_END_OF_HEADER
+} _MHD_FIXED_ENUM;
+
+
+/**
+ * Find the end of the request header line and make basic header parsing.
+ * Handle errors and header folding.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ *                        if false then headers are processed
+ * @param[out] hdr_name the name of the parsed header (field)
+ * @param[out] hdr_name the value of the parsed header (field)
+ * @return true if request header line completely processed,
+ *         false if not enough data yet in the receive buffer
+ */
+static enum MHD_HdrLineReadRes_
+get_req_header (struct MHD_Connection *restrict c,
+                bool process_footers,
+                struct MHD_String *restrict hdr_name,
+                struct MHD_String *restrict hdr_value)
+{
+  const int discp_lvl = c->daemon->req_cfg.strictnees;
+  /* Treat bare LF as the end of the line.
+     RFC 9112, section 2.2-3
+     Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
+     Bare LF is processed as end of the line or rejected as broken request. */
+  const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
+  /* Keep bare CR character as is.
+     Violates RFC 9112, section 2.2-4 */
+  const bool bare_cr_keep = (-3 >= discp_lvl);
+  /* Treat bare CR as space; replace it with space before processing.
+     RFC 9112, section 2.2-4 */
+  const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+  /* Treat NUL as space; replace it with space before processing.
+     RFC 9110, section 5.5-5 */
+  const bool nul_as_sp = (-1 >= discp_lvl);
+  /* Allow folded header lines.
+     RFC 9112, section 5.2-4 */
+  const bool allow_folded = (0 >= discp_lvl);
+  /* Do not reject headers with the whitespace at the start of the first line.
+     When allowed, the first line with whitespace character at the first
+     position is ignored (as well as all possible line foldings of the first
+     line).
+     RFC 9112, section 2.2-8 */
+  const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
+  /* Allow whitespace in header (field) name.
+     Violates RFC 9110, section 5.1-2 */
+  const bool allow_wsp_in_name = (-2 >= discp_lvl);
+  /* Allow zero-length header (field) name.
+     Violates RFC 9110, section 5.1-2 */
+  const bool allow_empty_name = (-2 >= discp_lvl);
+  /* Allow whitespace before colon.
+     Violates RFC 9112, section 5.1-2 */
+  const bool allow_wsp_before_colon = (-3 >= discp_lvl);
+  /* Do not abort the request when header line has no colon, just skip such
+     bad lines.
+     RFC 9112, section 5-1 */
+  const bool allow_line_without_colon = (-2 >= discp_lvl);
+
+  size_t p; /**< The position of the currently processed character */
+
+  (void) process_footers; /* Unused parameter in non-debug and no messages */
+
+  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+               MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+              c->state);
+
+  p = c->rq.hdrs.hdr.proc_pos;
+
+  mhd_assert (p <= c->read_buffer_offset);
+  while (p < c->read_buffer_offset)
+  {
+    char *const restrict read_buffer = c->read_buffer;
+    const char chr = read_buffer[p];
+    bool end_of_line;
+
+    mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+                (c->rq.hdrs.hdr.name_len < p));
+    mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
+    mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+                (c->rq.hdrs.hdr.name_end_found));
+    mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+                (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
+    mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+                (0 != c->rq.hdrs.hdr.name_len));
+    mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+                (0 == c->rq.hdrs.hdr.name_len) || \
+                (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
+    mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+                (0 == c->rq.hdrs.hdr.value_start) || \
+                (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
+
+    /* Check for the end of the line */
+    if ('\r' == chr)
+    {
+      if (0 != p)
+      {
+        /* Line is not empty, need to check for possible line folding */
+        if (p + 2 >= c->read_buffer_offset)
+          break; /* Not enough data yet to check for folded line */
+      }
+      else
+      {
+        /* Line is empty, no need to check for possible line folding */
+        if (p + 2 > c->read_buffer_offset)
+          break; /* Not enough data yet to check for the end of the line */
+      }
+      if ('\n' == read_buffer[p + 1])
+        end_of_line = true;
+      else
+      {
+        /* Bare CR alone */
+        /* Must be rejected or replaced with space char.
+           See RFC 9112, section 2.2-4 */
+        if (bare_cr_as_sp)
+        {
+          read_buffer[p] = ' ';
+          c->rq.num_cr_sp_replaced++;
+          continue; /* Re-start processing of the current character */
+        }
+        else if (! bare_cr_keep)
+        {
+          if (! process_footers)
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_BARE_CR_IN_HEADER);
+          else
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_BARE_CR_IN_FOOTER);
+          return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+        }
+        end_of_line = false;
+      }
+    }
+    else if ('\n' == chr)
+    {
+      /* Bare LF may be recognised as a line delimiter.
+         See RFC 9112, section 2.2-3 */
+      if (bare_lf_as_crlf)
+      {
+        if (0 != p)
+        {
+          /* Line is not empty, need to check for possible line folding */
+          if (p + 1 >= c->read_buffer_offset)
+            break; /* Not enough data yet to check for folded line */
+        }
+        end_of_line = true;
+      }
+      else
+      {
+        if (! process_footers)
+          transmit_error_response_static (c,
+                                          MHD_HTTP_STATUS_BAD_REQUEST,
+                                          ERR_RSP_BARE_LF_IN_HEADER);
+        else
+          transmit_error_response_static (c,
+                                          MHD_HTTP_STATUS_BAD_REQUEST,
+                                          ERR_RSP_BARE_LF_IN_FOOTER);
+        return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+      }
+    }
+    else
+      end_of_line = false;
+
+    if (end_of_line)
+    {
+      /* Handle the end of the line */
+      /**
+       *  The full length of the line, including CRLF (or bare LF).
+       */
+      const size_t line_len = p + (('\r' == chr) ? 2 : 1);
+      char next_line_char;
+      mhd_assert (line_len <= c->read_buffer_offset);
+
+      if (0 == p)
+      {
+        /* Zero-length header line. This is the end of the request header
+           section.
+           RFC 9112, Section 2.1-1 */
+        mhd_assert (! c->rq.hdrs.hdr.starts_with_ws);
+        mhd_assert (! c->rq.hdrs.hdr.name_end_found);
+        mhd_assert (0 == c->rq.hdrs.hdr.name_len);
+        mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
+        mhd_assert (0 == c->rq.hdrs.hdr.value_start);
+        /* Consume the line with CRLF (or bare LF) */
+        c->read_buffer += line_len;
+        c->read_buffer_offset -= line_len;
+        c->read_buffer_size -= line_len;
+        return MHD_HDR_LINE_READING_GOT_END_OF_HEADER;
+      }
+
+      mhd_assert (line_len < c->read_buffer_offset);
+      mhd_assert (0 != line_len);
+      mhd_assert ('\n' == read_buffer[line_len - 1]);
+      next_line_char = read_buffer[line_len];
+      if ((' ' == next_line_char) ||
+          ('\t' == next_line_char))
+      {
+        /* Folded line */
+        if (! allow_folded)
+        {
+          if (! process_footers)
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_OBS_FOLD);
+          else
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_OBS_FOLD_FOOTER);
+
+          return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+        }
+        /* Replace CRLF (or bare LF) character(s) with space characters.
+           See RFC 9112, Section 5.2-4 */
+        read_buffer[p] = ' ';
+        if ('\r' == chr)
+          read_buffer[p + 1] = ' ';
+        continue; /* Re-start processing of the current character */
+      }
+      else
+      {
+        /* It is not a folded line, it's the real end of the non-empty line */
+        bool skip_line = false;
+        mhd_assert (0 != p);
+        if (c->rq.hdrs.hdr.starts_with_ws)
+        {
+          /* This is the first line and it starts with whitespace. This line
+             must be discarded completely.
+             See RFC 9112, Section 2.2-8 */
+          mhd_assert (allow_wsp_at_start);
+
+          MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
+                       "Whitespace-prefixed first header line " \
+                       "has been skipped.");
+          skip_line = true;
+        }
+        else if (! c->rq.hdrs.hdr.name_end_found)
+        {
+          if (! allow_line_without_colon)
+          {
+            if (! process_footers)
+              transmit_error_response_static (c,
+                                              MHD_HTTP_STATUS_BAD_REQUEST,
+                                              ERR_RSP_HEADER_WITHOUT_COLON);
+            else
+              transmit_error_response_static (c,
+                                              MHD_HTTP_STATUS_BAD_REQUEST,
+                                              ERR_RSP_FOOTER_WITHOUT_COLON);
+
+            return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+          }
+          /* Skip broken line completely */
+          c->rq.skipped_broken_lines++;
+          skip_line = true;
+        }
+        if (skip_line)
+        {
+          /* Skip the entire line */
+          c->read_buffer += line_len;
+          c->read_buffer_offset -= line_len;
+          c->read_buffer_size -= line_len;
+          p = 0;
+          /* Reset processing state */
+          memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+          /* Start processing of the next line */
+          continue;
+        }
+        else
+        {
+          /* This line should be valid header line */
+          size_t value_len;
+          mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
+
+          hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */
+          hdr_name->len = c->rq.hdrs.hdr.name_len;
+          mhd_assert (0 == hdr_name->cstr[hdr_name->len]);
+
+          if (0 == c->rq.hdrs.hdr.value_start)
+          {
+            c->rq.hdrs.hdr.value_start = p;
+            read_buffer[p] = 0;
+            value_len = 0;
+          }
+          else if (0 != c->rq.hdrs.hdr.ws_start)
+          {
+            mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+            mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start);
+            read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
+            value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
+          }
+          else
+          {
+            mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+            read_buffer[p] = 0;
+            value_len = p - c->rq.hdrs.hdr.value_start;
+          }
+          hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start;
+          hdr_value->len = value_len;
+          mhd_assert (0 == hdr_value->cstr[hdr_value->len]);
+          /* Consume the entire line */
+          c->read_buffer += line_len;
+          c->read_buffer_offset -= line_len;
+          c->read_buffer_size -= line_len;
+          return MHD_HDR_LINE_READING_GOT_HEADER;
+        }
+      }
+    }
+    else if ((' ' == chr) || ('\t' == chr))
+    {
+      if (0 == p)
+      {
+        if (! allow_wsp_at_start)
+        {
+          if (! process_footers)
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_WSP_BEFORE_HEADER);
+          else
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_WSP_BEFORE_FOOTER);
+          return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+        }
+        c->rq.hdrs.hdr.starts_with_ws = true;
+      }
+      else if ((! c->rq.hdrs.hdr.name_end_found) &&
+               (! c->rq.hdrs.hdr.starts_with_ws))
+      {
+        /* Whitespace in header name / between header name and colon */
+        if (allow_wsp_in_name || allow_wsp_before_colon)
+        {
+          if (0 == c->rq.hdrs.hdr.ws_start)
+            c->rq.hdrs.hdr.ws_start = p;
+        }
+        else
+        {
+          if (! process_footers)
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_WSP_IN_HEADER_NAME);
+          else
+            transmit_error_response_static (c,
+                                            MHD_HTTP_STATUS_BAD_REQUEST,
+                                            ERR_RSP_WSP_IN_FOOTER_NAME);
+
+          return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+        }
+      }
+      else
+      {
+        /* Whitespace before/inside/after header (field) value */
+        if (0 == c->rq.hdrs.hdr.ws_start)
+          c->rq.hdrs.hdr.ws_start = p;
+      }
+    }
+    else if (0 == chr)
+    {
+      if (! nul_as_sp)
+      {
+        if (! process_footers)
+          transmit_error_response_static (c,
+                                          MHD_HTTP_STATUS_BAD_REQUEST,
+                                          ERR_RSP_INVALID_CHR_IN_HEADER);
+        else
+          transmit_error_response_static (c,
+                                          MHD_HTTP_STATUS_BAD_REQUEST,
+                                          ERR_RSP_INVALID_CHR_IN_FOOTER);
+
+        return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+      }
+      read_buffer[p] = ' ';
+      continue; /* Re-start processing of the current character */
+    }
+    else
+    {
+      /* Not a whitespace, not the end of the header line */
+      mhd_assert ('\r' != chr);
+      mhd_assert ('\n' != chr);
+      mhd_assert ('\0' != chr);
+      if ((! c->rq.hdrs.hdr.name_end_found) &&
+          (! c->rq.hdrs.hdr.starts_with_ws))
+      {
+        /* Processing the header (field) name */
+        if (':' == chr)
+        {
+          if (0 == c->rq.hdrs.hdr.ws_start)
+            c->rq.hdrs.hdr.name_len = p;
+          else
+          {
+            mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+            if (! allow_wsp_before_colon)
+            {
+              if (! process_footers)
+                transmit_error_response_static (c,
+                                                MHD_HTTP_STATUS_BAD_REQUEST,
+                                                ERR_RSP_WSP_IN_HEADER_NAME);
+              else
+                transmit_error_response_static (c,
+                                                MHD_HTTP_STATUS_BAD_REQUEST,
+                                                ERR_RSP_WSP_IN_FOOTER_NAME);
+              return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+            }
+            c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
+#ifndef MHD_FAVOR_SMALL_CODE
+            c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+          }
+          if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
+          {
+            if (! process_footers)
+              transmit_error_response_static (c,
+                                              MHD_HTTP_STATUS_BAD_REQUEST,
+                                              ERR_RSP_EMPTY_HEADER_NAME);
+            else
+              transmit_error_response_static (c,
+                                              MHD_HTTP_STATUS_BAD_REQUEST,
+                                              ERR_RSP_EMPTY_FOOTER_NAME);
+            return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+          }
+          c->rq.hdrs.hdr.name_end_found = true;
+          read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
+        }
+        else
+        {
+          if (0 != c->rq.hdrs.hdr.ws_start)
+          {
+            /* End of the whitespace in header (field) name */
+            mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+            if (! allow_wsp_in_name)
+            {
+              if (! process_footers)
+                transmit_error_response_static (c,
+                                                MHD_HTTP_STATUS_BAD_REQUEST,
+                                                ERR_RSP_WSP_IN_HEADER_NAME);
+              else
+                transmit_error_response_static (c,
+                                                MHD_HTTP_STATUS_BAD_REQUEST,
+                                                ERR_RSP_WSP_IN_FOOTER_NAME);
+
+              return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+            }
+#ifndef MHD_FAVOR_SMALL_CODE
+            c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+          }
+        }
+      }
+      else
+      {
+        /* Processing the header (field) value */
+        if (0 == c->rq.hdrs.hdr.value_start)
+          c->rq.hdrs.hdr.value_start = p;
+#ifndef MHD_FAVOR_SMALL_CODE
+        c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+      }
+#ifdef MHD_FAVOR_SMALL_CODE
+      c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* MHD_FAVOR_SMALL_CODE */
+    }
+    p++;
+  }
+  c->rq.hdrs.hdr.proc_pos = p;
+  return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
+}
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_get_req_headers (struct MHD_Connection *restrict c, bool process_footers)
+{
+  do
+  {
+    struct MHD_String hdr_name;
+    struct MHD_String hdr_value;
+    enum mhd_HdrLineReadRes res;
+
+    mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+                 MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+                c->state);
+
+#ifndef NDEBUG
+    hdr_name.cstr = NULL;
+    hdr_value.cstr = NULL;
+#endif /* ! NDEBUG */
+    res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
+    if (MHD_HDR_LINE_READING_GOT_HEADER == res)
+    {
+      mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+                   MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+                  c->state);
+      mhd_assert (NULL != hdr_name.cstr);
+      mhd_assert (NULL != hdr_value.cstr);
+      /* Values must be zero-terminated and must not have binary zeros */
+      mhd_assert (strlen (hdr_name.cstr) == hdr_name.len);
+      mhd_assert (strlen (hdr_value.cstr) == hdr_value.len);
+      /* Values must not have whitespaces at the start or at the end */
+      mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' '));
+      mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t'));
+      mhd_assert ((hdr_name.len == 0) || \
+                  (hdr_name.cstr[hdr_name.len - 1] != ' '));
+      mhd_assert ((hdr_name.len == 0) || \
+                  (hdr_name.cstr[hdr_name.len - 1] != '\t'));
+      mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' '));
+      mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t'));
+      mhd_assert ((hdr_value.len == 0) || \
+                  (hdr_value.cstr[hdr_value.len - 1] != ' '));
+      mhd_assert ((hdr_value.len == 0) || \
+                  (hdr_value.cstr[hdr_value.len - 1] != '\t'));
+
+      if (! mhd_stream_add_field (c,
+                                  process_footers ?
+                                  MHD_VK_FOOTER : MHD_VK_HEADER,
+                                  &hdr_name,
+                                  &hdr_value))
+      {
+        size_t add_element_size;
+
+        mhd_assert (hdr_name.cstr < hdr_value.cstr);
+
+        if (!process_footers)
+          MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+                       "Failed to allocate memory in the connection memory " \
+                       "pool to store header.");
+        else
+          MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
+                       "Failed to allocate memory in the connection memory " \
+                       "pool to store footer.");
+
+        add_element_size = hdr_value.len
+                           + (size_t) (hdr_value.cstr - hdr_name.cstr);
+
+        if (! process_footers)
+          handle_req_headers_no_space (c, hdr_name.cstr, add_element_size);
+        else
+          handle_req_footers_no_space (c, hdr_name.cstr, add_element_size);
+
+        mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
+        return true;
+      }
+      /* Reset processing state */
+      reset_rq_header_processing_state (c);
+      mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+                   MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+                  c->state);
+      /* Read the next header (field) line */
+      continue;
+    }
+    else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
+    {
+      mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+                   MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+                  c->state);
+      return false;
+    }
+    else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
+    {
+      mhd_assert ((process_footers ? \
+                   MHD_CONNECTION_FOOTERS_RECEIVING : \
+                   MHD_CONNECTION_REQ_HEADERS_RECEIVING) < c->state);
+      mhd_assert (c->stop_with_error);
+      mhd_assert (c->discard_request);
+      return true;
+    }
+    mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res);
+    break;
+  } while (1);
+
+  if (1 == c->rq.num_cr_sp_replaced)
+  {
+    if (! process_footers)
+      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                   "One bare CR character has been replaced with space " \
+                   "in the request line or in the request headers.");
+    else
+      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
+                   "One bare CR character has been replaced with space " \
+                   "in the request footers.");
+  }
+  else if (0 != c->rq.num_cr_sp_replaced)
+  {
+    if (! process_footers)
+      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     MHD_LOG_FMT("%" PRIuFAST64 " bare CR characters have " \
+                                 "been replaced with spaces in the request " \
+                                 "line and/or in the request headers."), \
+                     (uint_fast64_t) c->rq.num_cr_sp_replaced);
+    else
+      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     MHD_LOG_FMT("%" PRIuFAST64 " bare CR characters have " \
+                                 "been replaced with spaces in the request " \
+                                 "footers."), \
+                     (uint_fast64_t) c->rq.num_cr_sp_replaced);
+
+
+  }
+  if (1 == c->rq.skipped_broken_lines)
+  {
+    if (! process_footers)
+      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
+                   "One header line without colon has been skipped.");
+    else
+      MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
+                   "One footer line without colon has been skipped.");
+  }
+  else if (0 != c->rq.skipped_broken_lines)
+  {
+    if (! process_footers)
+      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     MHD_LOG_FMT("%" PRIu64 " header lines without colons "
+                                 "have been skipped."),
+                     (uint_fast64_t) c->rq.skipped_broken_lines);
+    else
+      MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
+                     MHD_LOG_FMT("%" PRIu64 " footer lines without colons "
+                                 "have been skipped."),
+                     (uint_fast64_t) c->rq.skipped_broken_lines);
+  }
+
+  mhd_assert (c->rq.method < c->read_buffer);
+  if (! process_footers)
+  {
+    c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
+    mhd_assert (NULL != c->rq.field_lines.start);
+    c->rq.field_lines.size =
+      (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
+    if ('\r' == *(c->read_buffer - 2))
+      c->rq.field_lines.size--;
+    c->state = MHD_CONNECTION_HEADERS_RECEIVED;
+
+    if (mhd_BUF_INC_SIZE > c->read_buffer_size)
+    {
+      /* Try to re-use some of the last bytes of the request header */
+      /* Do this only if space in the read buffer is limited AND
+         amount of read ahead data is small. */
+      /**
+       *  The position of the terminating NUL after the last character of
+       *  the last header element.
+       */
+      const char *last_elmnt_end;
+      size_t shift_back_size;
+      struct mhd_RequestField *header;
+      header = mhd_DLINKEDL_GET_LAST(&(c->rq), fields);
+      if (NULL != header)
+        last_elmnt_end =
+          header->field.nv.value.cstr + header->field.nv.value.len;
+      else
+        last_elmnt_end = c->rq.version + HTTP_VER_LEN;
+      mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
+      shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
+      if (0 != c->read_buffer_offset)
+        memmove (c->read_buffer - shift_back_size,
+                 c->read_buffer,
+                 c->read_buffer_offset);
+      c->read_buffer -= shift_back_size;
+      c->read_buffer_size += shift_back_size;
+    }
+  }
+  else
+    c->state = MHD_CONNECTION_FOOTERS_RECEIVED;
+
+  return true;
+}
+
+
+#ifdef COOKIE_SUPPORT
+
+/**
+ * Cookie parsing result
+ */
+enum _MHD_ParseCookie
+{
+  MHD_PARSE_COOKIE_OK = MHD_YES,      /**< Success or no cookies in headers */
+  MHD_PARSE_COOKIE_OK_LAX = 2,        /**< Cookies parsed, but workarounds used */
+  MHD_PARSE_COOKIE_MALFORMED = -1,    /**< Invalid cookie header */
+  MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO /**< Not enough memory in the pool */
+};
+
+
+/**
+ * Parse the cookies string (see RFC 6265).
+ *
+ * Try to parse the cookies string even if it is not strictly formed
+ * as specified by RFC 6265.
+ *
+ * @param str the string to parse, without leading whitespaces
+ * @param str_len the size of the @a str, not including mandatory
+ *                zero-termination
+ * @param connection the connection to add parsed cookies
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_(2)
+MHD_FN_PAR_INOUT_SIZE_(2,1) enum _MHD_ParseCookie
+parse_cookies_string (const size_t str_len,
+                      char *restrict str,
+                      struct MHD_Connection *restrict connection)
+{
+  size_t i;
+  bool non_strict;
+  /* Skip extra whitespaces and empty cookies */
+  const bool allow_wsp_empty = (0 >= connection->daemon->req_cfg.strictnees);
+  /* Allow whitespaces around '=' character */
+  const bool wsp_around_eq = (-3 >= connection->daemon->req_cfg.strictnees);
+  /* Allow whitespaces in quoted cookie value */
+  const bool wsp_in_quoted = (-2 >= connection->daemon->req_cfg.strictnees);
+  /* Allow tab as space after semicolon between cookies */
+  const bool tab_as_sp = (0 >= connection->daemon->req_cfg.strictnees);
+  /* Allow no space after semicolon between cookies */
+  const bool allow_no_space = (0 >= connection->daemon->req_cfg.strictnees);
+
+  non_strict = false;
+  i = 0;
+  while (i < str_len)
+  {
+    size_t name_start;
+    size_t name_len;
+    size_t value_start;
+    size_t value_len;
+    bool val_quoted;
+    /* Skip any whitespaces and empty cookies */
+    while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
+    {
+      if (! allow_wsp_empty)
+        return MHD_PARSE_COOKIE_MALFORMED;
+      non_strict = true;
+      i++;
+      if (i == str_len)
+        return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+    }
+    /* 'i' must point to the first char of cookie-name */
+    name_start = i;
+    /* Find the end of the cookie-name */
+    do
+    {
+      const char l = str[i];
+      if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
+          (';' == l) || (0 == l))
+        break;
+    } while (str_len > ++i);
+    name_len = i - name_start;
+    /* Skip any whitespaces */
+    while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+    {
+      if (! wsp_around_eq)
+        return MHD_PARSE_COOKIE_MALFORMED;
+      non_strict = true;
+      i++;
+    }
+    if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
+      return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
+    /* 'i' must point to the '=' char */
+    mhd_assert ('=' == str[i]);
+    i++;
+    /* Skip any whitespaces */
+    while (str_len > i && (' ' == str[i] || '\t' == str[i]))
+    {
+      if (! wsp_around_eq)
+        return MHD_PARSE_COOKIE_MALFORMED;
+      non_strict = true;
+      i++;
+    }
+    /* 'i' must point to the first char of cookie-value */
+    if (str_len == i)
+    {
+      value_start = 0;
+      value_len = 0;
+#ifndef NDEBUG
+      val_quoted = false; /* This assignment used in assert */
+#endif
+    }
+    else
+    {
+      bool valid_cookie;
+      val_quoted = ('"' == str[i]);
+      if (val_quoted)
+        i++;
+      value_start = i;
+      /* Find the end of the cookie-value */
+      while (str_len > i)
+      {
+        const char l = str[i];
+        if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
+            ('\\' == l) || (0 == l))
+          break;
+        if ((' ' == l) || ('\t' == l))
+        {
+          if (! val_quoted)
+            break;
+          if (! wsp_in_quoted)
+            return MHD_PARSE_COOKIE_MALFORMED;
+          non_strict = true;
+        }
+        i++;
+      }
+      value_len = i - value_start;
+      if (val_quoted)
+      {
+        if ((str_len == i) || ('"' != str[i]))
+          return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
+        i++;
+      }
+      /* Skip any whitespaces */
+      if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
+      {
+        do
+        {
+          i++;
+        } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
+        /* Whitespace at the end? */
+        if (str_len > i)
+        {
+          if (! allow_wsp_empty)
+            return MHD_PARSE_COOKIE_MALFORMED;
+          non_strict = true;
+        }
+      }
+      if (str_len == i)
+        valid_cookie = true;
+      else if (';' == str[i])
+        valid_cookie = true;
+      else
+        valid_cookie = false;
+
+      if (! valid_cookie)
+        return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
+    }
+    mhd_assert (0 != name_len);
+    str[name_start + name_len] = 0; /* Zero-terminate the name */
+    if (0 != value_len)
+    {
+      struct MHD_String name;
+      struct MHD_String value;
+      mhd_assert (value_start + value_len <= str_len);
+      name.cstr = str + name_start;
+      name.len = name_len;
+      str[value_start + value_len] = 0; /* Zero-terminate the value */
+      value.cstr = str + value_start;
+      value.len = value_len;
+      if (! mhd_stream_add_field(connection,
+                                 MHD_VK_COOKIE,
+                                 &name,
+                                 &value))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    else
+    {
+      struct MHD_String name;
+      struct MHD_String value;
+      name.cstr = str + name_start;
+      name.len = name_len;
+      value.cstr = ""
+      value.len = 0;
+      if (! mhd_stream_add_field(connection,
+                                 MHD_VK_COOKIE,
+                                 &name,
+                                 &value))
+        return MHD_PARSE_COOKIE_NO_MEMORY;
+    }
+    if (str_len > i)
+    {
+      mhd_assert (0 == str[i] || ';' == str[i]);
+      mhd_assert (! val_quoted || ';' == str[i]);
+      mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
+      i++;
+      if (str_len == i)
+      { /* No next cookie after semicolon */
+        if (! allow_wsp_empty)
+          return MHD_PARSE_COOKIE_MALFORMED;
+        non_strict = true;
+      }
+      else if (' ' != str[i])
+      {/* No space after semicolon */
+        if (('\t' == str[i]) && tab_as_sp)
+          i++;
+        else if (! allow_no_space)
+          return MHD_PARSE_COOKIE_MALFORMED;
+        non_strict = true;
+      }
+      else
+      {
+        i++;
+        if (str_len == i)
+        {
+          if (! allow_wsp_empty)
+            return MHD_PARSE_COOKIE_MALFORMED;
+          non_strict = true;
+        }
+      }
+    }
+  }
+  return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
+}
+
+
+/**
+ * Parse the cookie header (see RFC 6265).
+ *
+ * @param connection connection to parse header of
+ * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
+ */
+static enum _MHD_ParseCookie
+parse_cookie_header (struct MHD_Connection *connection)
+{
+  struct MHD_String *hvalue;
+  char *cpy;
+  size_t i;
+  enum _MHD_ParseCookie parse_res;
+  struct mhd_RequestField *const saved_tail =
+    connection->rq.fields.last;
+  const bool allow_partially_correct_cookie =
+    (1 >= connection->daemon->req_cfg.strictnees);
+
+  hvalue = mhd_request_get_value_len(&(connection->rq),
+                                     MHD_VK_HEADER,
+                                     mhd_SSTR_LEN (MHD_HTTP_HEADER_COOKIE),
+                                     MHD_HTTP_HEADER_COOKIE);
+  if (NULL == hvalue)
+    return MHD_PARSE_COOKIE_OK;
+  if (0 == hvalue->len)
+    return MHD_PARSE_COOKIE_OK;
+
+  cpy = mhd_stream_alloc_memory (connection,
+                                 hvalue->len + 1);
+  if (NULL == cpy)
+    parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
+  else
+  {
+    memcpy (cpy,
+            hdr,
+            hdr_len);
+    cpy[hdr_len] = '\0';
+
+    i = 0;
+    /* Skip all initial whitespaces */
+    while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
+      i++;
+
+    parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
+  }
+
+  switch (parse_res)
+  {
+  case MHD_PARSE_COOKIE_OK:
+    break;
+  case MHD_PARSE_COOKIE_OK_LAX:
+#ifdef HAVE_MESSAGES
+    if (saved_tail != connection->rq.headers_received_tail)
+      MHD_DLOG (connection->daemon,
+                _ ("The Cookie header has been parsed, but it is not fully "
+                   "compliant with the standard.\n"));
+#endif /* HAVE_MESSAGES */
+    break;
+  case MHD_PARSE_COOKIE_MALFORMED:
+    if (saved_tail != connection->rq.headers_received_tail)
+    {
+      if (! allow_partially_correct_cookie)
+      {
+        /* Remove extracted values from partially broken cookie */
+        /* Memory remains allocated until the end of the request processing */
+        connection->rq.headers_received_tail = saved_tail;
+        saved_tail->next = NULL;
+#ifdef HAVE_MESSAGES
+        MHD_DLOG (connection->daemon,
+                  _ ("The Cookie header has been ignored as it contains "
+                     "malformed data.\n"));
+#endif /* HAVE_MESSAGES */
+      }
+#ifdef HAVE_MESSAGES
+      else
+        MHD_DLOG (connection->daemon,
+                  _ ("The Cookie header has been only partially parsed as it "
+                     "contains malformed data.\n"));
+#endif /* HAVE_MESSAGES */
+    }
+#ifdef HAVE_MESSAGES
+    else
+      MHD_DLOG (connection->daemon,
+                _ ("The Cookie header has malformed data.\n"));
+#endif /* HAVE_MESSAGES */
+    break;
+  case MHD_PARSE_COOKIE_NO_MEMORY:
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("Not enough memory in the connection pool to "
+                 "parse client cookies!\n"));
+#endif /* HAVE_MESSAGES */
+    break;
+  default:
+    mhd_assert (0);
+    break;
+  }
+#ifndef HAVE_MESSAGES
+  (void) saved_tail; /* Mute compiler warning */
+#endif /* ! HAVE_MESSAGES */
+
+  return parse_res;
+}
+
+
+#endif /* COOKIE_SUPPORT */
+
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
+mhd_parse_connection_headers (struct MHD_Connection *restrict c)
+{
+  const char *clen;
+  const char *enc;
+  size_t val_len;
+
+#ifdef COOKIE_SUPPORT
+  if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
+  {
+    handle_req_cookie_no_space (connection);
+    return;
+  }
+#endif /* COOKIE_SUPPORT */
+  if ( (-3 < connection->daemon->client_discipline) &&
+       (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
+       (MHD_NO ==
+        MHD_lookup_connection_value_n (connection,
+                                       MHD_VK_HEADER,
+                                       MHD_HTTP_HEADER_HOST,
+                                       mhd_SSTR_LEN (
+                                         MHD_HTTP_HEADER_HOST),
+                                       NULL,
+                                       NULL)) )
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("Received HTTP/1.1 request without `Host' header.\n"));
+#endif
+    transmit_error_response_static (connection,
+                                    MHD_HTTP_STATUS_BAD_REQUEST,
+                                    REQUEST_LACKS_HOST);
+    return;
+  }
+
+  /* The presence of the request body is indicated by "Content-Length:" or
+     "Transfer-Encoding:" request headers.
+     Unless one of these two headers is used, the request has no request body.
+     See RFC9112, Section 6, paragraph 4. */
+  connection->rq.remaining_upload_size = 0;
+  if (MHD_NO !=
+      MHD_lookup_connection_value_n (connection,
+                                     MHD_VK_HEADER,
+                                     MHD_HTTP_HEADER_TRANSFER_ENCODING,
+                                     mhd_SSTR_LEN (
+                                       MHD_HTTP_HEADER_TRANSFER_ENCODING),
+                                     &enc,
+                                     NULL))
+  {
+    if (! MHD_str_equal_caseless_ (enc,
+                                   "chunked"))
+    {
+      transmit_error_response_static (connection,
+                                      MHD_HTTP_STATUS_BAD_REQUEST,
+                                      REQUEST_UNSUPPORTED_TR_ENCODING);
+      return;
+    }
+    else if (MHD_NO !=
+             MHD_lookup_connection_value_n (connection,
+                                            MHD_VK_HEADER,
+                                            MHD_HTTP_HEADER_CONTENT_LENGTH,
+                                            mhd_SSTR_LEN ( \
+                                              MHD_HTTP_HEADER_CONTENT_LENGTH),
+                                            NULL,
+                                            NULL))
+    {
+      /* TODO: add individual settings */
+      if (1 <= connection->daemon->client_discipline)
+      {
+        transmit_error_response_static (connection,
+                                        MHD_HTTP_STATUS_BAD_REQUEST,
+                                        REQUEST_LENGTH_WITH_TR_ENCODING);
+        return;
+      }
+      else
+      {
+        /* Must close connection after reply to prevent potential attack */
+        connection->keepalive = MHD_CONN_MUST_CLOSE;
+#ifdef HAVE_MESSAGES
+        MHD_DLOG (connection->daemon,
+                  _ ("The 'Content-Length' request header is ignored "
+                     "as chunked Transfer-Encoding is used "
+                     "for this request.\n"));
+#endif /* HAVE_MESSAGES */
+      }
+    }
+    connection->rq.have_chunked_upload = true;
+    connection->rq.remaining_upload_size = MHD_SIZE_UNKNOWN;
+  }
+  else if (MHD_NO !=
+           MHD_lookup_connection_value_n (connection,
+                                          MHD_VK_HEADER,
+                                          MHD_HTTP_HEADER_CONTENT_LENGTH,
+                                          mhd_SSTR_LEN (
+                                            MHD_HTTP_HEADER_CONTENT_LENGTH),
+                                          &clen,
+                                          &val_len))
+  {
+    size_t num_digits;
+
+    num_digits = MHD_str_to_uint64_n_ (clen,
+                                       val_len,
+                                       &connection->rq.remaining_upload_size);
+
+    if (((0 == num_digits) &&
+         (0 != val_len) &&
+         ('0' <= clen[0]) && ('9' >= clen[0]))
+        || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
+    {
+      connection->rq.remaining_upload_size = 0;
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                _ ("Too large value of 'Content-Length' header. " \
+                   "Closing connection.\n"));
+#endif
+      transmit_error_response_static (connection,
+                                      MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+                                      REQUEST_CONTENTLENGTH_TOOLARGE);
+    }
+    else if ((val_len != num_digits) ||
+             (0 == num_digits))
+    {
+      connection->rq.remaining_upload_size = 0;
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                _ ("Failed to parse 'Content-Length' header. " \
+                   "Closing connection.\n"));
+#endif
+      transmit_error_response_static (connection,
+                                      MHD_HTTP_STATUS_BAD_REQUEST,
+                                      REQUEST_CONTENTLENGTH_MALFORMED);
+    }
+  }
+}

+ 81 - 0
src/mhd2/stream_process_request.h

@@ -0,0 +1,81 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_process_request.h
+ * @brief  The declarations of internal functions for requests parsing
+ *         and processing
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_PROCESS_REQUEST_H
+#define MHD_STREAM_PROCESS_STATES_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Find and parse the request line.
+ * @param c the connection to process
+ * @return true if request line completely processed (or unrecoverable error
+ *         found) and state is changed,
+ *         false if not enough data yet in the receive buffer
+ */
+MHD_INTERNAL bool
+mhd_get_request_line (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Switch to request headers (field lines) processing state.
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_switch_to_rq_headers_processing (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Find the end of the request headers and make basic header parsing.
+ * Advance to the next state when done, handle errors.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ *                        if false then headers are processed
+ * @return true if request headers reading finished (either successfully
+ *         or with error),
+ *         false if not enough data yet in the receive buffer
+ */
+MHD_INTERNAL bool
+mhd_get_req_headers (struct MHD_Connection *restrict c, bool process_footers)
+MHD_FN_PAR_NONNULL_ALL_;
+
+/**
+ * Parse the various headers; figure out the size
+ * of the upload and make sure the headers follow
+ * the protocol.  Advance to the appropriate state.
+ *
+ * @param c the connection to process
+ */
+MHD_INTERNAL void
+mhd_parse_connection_headers (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_STREAM_PROCESS_STATES_H */

+ 359 - 0
src/mhd2/stream_process_states.c

@@ -0,0 +1,359 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_process_states.h
+ * @brief  The definitions of internal functions for processing
+ *         stream states
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include "sys_bool_type.h"
+
+#include "mhd_connection.h"
+#include "stream_process_states.h"
+
+
+MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
+mhd_conn_process_data (struct MHD_Connection *restrict c)
+{
+  struct MHD_Daemon *const restrict d = c->daemon;
+
+  enum MHD_Result ret;
+
+  /* 'daemon' is not used if epoll is not available and asserts are disabled */
+  (void) d; /* Mute compiler warning */
+
+  if ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) ||
+      (0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
+  {
+    mhd_assert ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) || \
+                mhd_SOCKET_ERR_IS_HARD(c->sk_discnt_err));
+    if ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) ||
+        (mhd_SOCKET_ERR_NOT_CHECKED == c->sk_discnt_err))
+      c->sk_discnt_err = mhd_socket_error_get_from_socket(c->socket_fd);
+    mhd_conn_pre_close_skt_err (c);
+    return false;
+  }
+
+  while (true) // TODO: support suspend
+  {
+#ifdef HTTPS_SUPPORT
+    // TODO: support TLS, handshake
+#endif /* HTTPS_SUPPORT */
+    switch (c->state)
+    {
+    case MHD_CONNECTION_INIT:
+    case MHD_CONNECTION_REQ_LINE_RECEIVING:
+      if (mhd_get_request_line (c))
+      {
+        mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
+        mhd_assert ((MHD_IS_HTTP_VER_SUPPORTED (c->rq.http_ver)) \
+                    || (c->discard_request));
+        continue;
+      }
+      mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= c->state);
+      break;
+    case MHD_CONNECTION_REQ_LINE_RECEIVED:
+      mhd_switch_to_rq_headers_processing (c);
+      mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED != c->state);
+      continue;
+    case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+      if (mhd_get_req_headers (c, false))
+      {
+        mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < c->state);
+        mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == c->state) || \
+                    (c->discard_request));
+        continue;
+      }
+      mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state);
+      break;
+    case MHD_CONNECTION_HEADERS_RECEIVED:
+      mhd_parse_connection_headers (c);
+      if (MHD_CONNECTION_HEADERS_RECEIVED != c->state)
+        continue;
+      c->state = MHD_CONNECTION_HEADERS_PROCESSED;
+      if (c->suspended)
+        break;
+      continue;
+    case MHD_CONNECTION_HEADERS_PROCESSED:
+      call_connection_handler (c);
+      if (MHD_CONNECTION_HEADERS_PROCESSED != c->state)
+        continue;
+      if (c->suspended)
+        continue;
+
+      if ( (NULL == c->rp.response) &&
+           (need_100_continue (c)) &&
+           /* If the client is already sending the payload (body)
+              there is no need to send "100 Continue" */
+           (0 == c->read_buffer_offset) )
+      {
+        c->state = MHD_CONNECTION_CONTINUE_SENDING;
+        break;
+      }
+      if ( (NULL != c->rp.response) &&
+           (0 != c->rq.remaining_upload_size) )
+      {
+        /* we refused (no upload allowed!) */
+        c->rq.remaining_upload_size = 0;
+        /* force close, in case client still tries to upload... */
+        c->discard_request = true;
+      }
+      c->state = (0 == c->rq.remaining_upload_size)
+                          ? MHD_CONNECTION_FULL_REQ_RECEIVED
+                          : MHD_CONNECTION_BODY_RECEIVING;
+      if (c->suspended)
+        break;
+      continue;
+    case MHD_CONNECTION_CONTINUE_SENDING:
+      if (c->continue_message_write_offset ==
+          mhd_SSTR_LEN (HTTP_100_CONTINUE))
+      {
+        c->state = MHD_CONNECTION_BODY_RECEIVING;
+        continue;
+      }
+      break;
+    case MHD_CONNECTION_BODY_RECEIVING:
+      mhd_assert (0 != c->rq.remaining_upload_size);
+      mhd_assert (! c->discard_request);
+      mhd_assert (NULL == c->rp.response);
+      if (0 != c->read_buffer_offset)
+      {
+        process_request_body (c);           /* loop call */
+        if (MHD_CONNECTION_BODY_RECEIVING != c->state)
+          continue;
+      }
+      /* Modify here when queueing of the response during data processing
+         will be supported */
+      mhd_assert (! c->discard_request);
+      mhd_assert (NULL == c->rp.response);
+      if (0 == c->rq.remaining_upload_size)
+      {
+        c->state = MHD_CONNECTION_BODY_RECEIVED;
+        continue;
+      }
+      break;
+    case MHD_CONNECTION_BODY_RECEIVED:
+      mhd_assert (! c->discard_request);
+      mhd_assert (NULL == c->rp.response);
+      if (0 == c->rq.remaining_upload_size)
+      {
+        if (c->rq.have_chunked_upload)
+        {
+          /* Reset counter variables reused for footers */
+          c->rq.num_cr_sp_replaced = 0;
+          c->rq.skipped_broken_lines = 0;
+          reset_rq_header_processing_state (c);
+          c->state = MHD_CONNECTION_FOOTERS_RECEIVING;
+        }
+        else
+          c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+        continue;
+      }
+      break;
+    case MHD_CONNECTION_FOOTERS_RECEIVING:
+      if (get_req_headers (c, true))
+      {
+        mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < c->state);
+        mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == c->state) || \
+                    (c->discard_request));
+        continue;
+      }
+      mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == c->state);
+      break;
+    case MHD_CONNECTION_FOOTERS_RECEIVED:
+      /* The header, the body, and the footers of the request has been received,
+       * switch to the final processing of the request. */
+      c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+      continue;
+    case MHD_CONNECTION_FULL_REQ_RECEIVED:
+      call_connection_handler (c);     /* "final" call */
+      if (c->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
+        continue;
+      if (NULL == c->rp.response)
+        break;                  /* try again next time */
+      /* Response is ready, start reply */
+      c->state = MHD_CONNECTION_START_REPLY;
+      continue;
+    case MHD_CONNECTION_START_REPLY:
+      mhd_assert (NULL != c->rp.response);
+      connection_switch_from_recv_to_send (c);
+      if (MHD_NO == build_header_response (c))
+      {
+        /* oops - close! */
+        CONNECTION_CLOSE_ERROR (c,
+                                _ ("Closing connection (failed to create "
+                                   "response header).\n"));
+        continue;
+      }
+      c->state = MHD_CONNECTION_HEADERS_SENDING;
+      break;
+
+    case MHD_CONNECTION_HEADERS_SENDING:
+      /* no default action */
+      break;
+    case MHD_CONNECTION_HEADERS_SENT:
+      mhd_assert (0 && "Not implemented yet");
+#if 0 //def UPGRADE_SUPPORT // TODO: upgrade support
+      if (NULL != c->rp.response->upgrade_handler)
+      {
+        c->state = MHD_CONNECTION_UPGRADE;
+        /* This connection is "upgraded".  Pass socket to application. */
+        if (MHD_NO ==
+            MHD_response_execute_upgrade_ (c->rp.response,
+                                           connection))
+        {
+          /* upgrade failed, fail hard */
+          CONNECTION_CLOSE_ERROR (connection,
+                                  NULL);
+          continue;
+        }
+        /* Response is not required anymore for this connection. */
+        if (1)
+        {
+          struct MHD_Response *const resp = c->rp.response;
+
+          c->rp.response = NULL;
+          MHD_destroy_response (resp);
+        }
+        continue;
+      }
+#endif /* UPGRADE_SUPPORT */
+
+      if (c->rp.props.send_reply_body)
+      {
+        if (c->rp.props.chunked)
+          c->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
+        else
+          c->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
+      }
+      else
+        c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+      continue;
+    case MHD_CONNECTION_NORMAL_BODY_READY:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (! c->rp.props.chunked);
+      /* nothing to do here */
+      break;
+    case MHD_CONNECTION_NORMAL_BODY_UNREADY:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (! c->rp.props.chunked);
+      if (0 == c->rp.response->cntn_size)
+      {
+        if (c->rp.props.chunked)
+          c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+        else
+          c->state = MHD_CONNECTION_FULL_REPLY_SENT;
+        continue;
+      }
+      if (MHD_NO != try_ready_normal_body (c))
+      {
+        c->state = MHD_CONNECTION_NORMAL_BODY_READY;
+        /* Buffering for flushable socket was already enabled*/
+
+        break;
+      }
+      /* mutex was already unlocked by "try_ready_normal_body */
+      /* not ready, no socket action */
+      break;
+    case MHD_CONNECTION_CHUNKED_BODY_READY:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (c->rp.props.chunked);
+      /* nothing to do here */
+      break;
+    case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (c->rp.props.chunked);
+      if ( (0 == c->rp.response->cntn_size) ||
+           (c->rp.rsp_cntn_read_pos ==
+            c->rp.response->cntn_size) )
+      {
+        c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
+        continue;
+      }
+      if (1)
+      { /* pseudo-branch for local variables scope */
+        bool finished;
+        if (MHD_NO != try_ready_chunked_body (c, &finished))
+        {
+          c->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
+                              MHD_CONNECTION_CHUNKED_BODY_READY;
+          continue;
+        }
+        /* mutex was already unlocked by try_ready_chunked_body */
+      }
+      break;
+    case MHD_CONNECTION_CHUNKED_BODY_SENT:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (c->rp.props.chunked);
+      mhd_assert (c->write_buffer_send_offset <= \
+                  c->write_buffer_append_offset);
+
+      if (MHD_NO == build_connection_chunked_response_footer (c))
+      {
+        /* oops - close! */
+        CONNECTION_CLOSE_ERROR (c,
+                                _ ("Closing connection (failed to create " \
+                                   "response footer)."));
+        continue;
+      }
+      mhd_assert (c->write_buffer_send_offset < \
+                  c->write_buffer_append_offset);
+      c->state = MHD_CONNECTION_FOOTERS_SENDING;
+      continue;
+    case MHD_CONNECTION_FOOTERS_SENDING:
+      mhd_assert (c->rp.props.send_reply_body);
+      mhd_assert (c->rp.props.chunked);
+      /* no default action */
+      break;
+    case MHD_CONNECTION_FULL_REPLY_SENT:
+      // FIXME: support MHD_HTTP_STATUS_PROCESSING ?
+      /* Reset connection after complete reply */
+      connection_reset (c,
+                        MHD_CONN_USE_KEEPALIVE == c->keepalive &&
+                        ! c->read_closed &&
+                        ! c->discard_request);
+      continue;
+    case MHD_CONNECTION_CLOSED:
+      cleanup_connection (c);
+      return MHD_NO;
+#if 0 // def UPGRADE_SUPPORT
+    case MHD_CONNECTION_UPGRADE:
+      return MHD_YES;     /* keep open */
+#endif /* UPGRADE_SUPPORT */
+    default:
+      mhd_assert (0);
+      break;
+    }
+    break;
+  }
+  if (connection_check_timedout (c))
+  {
+    MHD_connection_close_ (c,
+                           MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
+    return MHD_YES;
+  }
+  mhd_conn_update_active_state (c);
+  /* MHD_connection_update_event_loop_info (c);*/
+
+  return true;
+}

+ 48 - 0
src/mhd2/stream_process_states.h

@@ -0,0 +1,48 @@
+/*
+  This file is part of GNU libmicrohttpd
+  Copyright (C) 2024 Evgeny Grin (Karlson2k)
+
+  GNU libmicrohttpd is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  GNU 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
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+*/
+
+/**
+ * @file src/mhd2/stream_process_states.h
+ * @brief  The declarations of internal functions for processing
+ *         stream states
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#ifndef MHD_STREAM_PROCESS_STATES_H
+#define MHD_STREAM_PROCESS_STATES_H 1
+
+#include "mhd_sys_options.h"
+
+#include "sys_bool_type.h"
+
+struct MHD_Connection; /* forward declaration */
+
+/**
+ * Process states and the data for the connection
+ * For HTTP/1.1 connection is equal stream
+ * @param c the connection to process
+ * @return true if states and data has been successfully processed,
+ *         false if connection needs to be closed
+ */
+MHD_INTERNAL bool
+mhd_conn_process_data (struct MHD_Connection *restrict c)
+MHD_FN_PAR_NONNULL_ALL_;
+
+#endif /* ! MHD_STREAM_PROCESS_STATES_H */

+ 11 - 0
src/mhd2/sys_base_types.h

@@ -48,6 +48,9 @@
 #ifdef HAVE_CRTDEFS_H
 #  include <crtdefs.h> /* W32-specific header */
 #endif
+#ifdef HAVE_INTTYPES_H
+#  include <inttypes.h>
+#endif
 
 #ifndef HAVE_SSIZE_T
 #  if defined(HAVE_PTRDIFF_T)
@@ -65,4 +68,12 @@ typedef intptr_t ssize_t;
 #  endif
 #endif /* ! HAVE_SSIZE_T */
 
+#ifndef PRIuFAST64
+#  ifdef PRIu64
+#    define PRIuFAST64 PRIu64
+#  else
+#    define PRIuFAST64 "llu"
+#  endif
+#endif
+
 #endif /* ! MHD_SYS_BASE_TYPES_H */

+ 59 - 0
src/mhd2/sys_ip_headers.h

@@ -53,4 +53,63 @@
 #  include <ws2tcpip.h>
 #endif
 
+#ifdef IPPROTO_TCP
+#  if defined(TCP_CORK)
+/**
+ * Value of TCP_CORK or TCP_NOPUSH
+ */
+#    define mhd_TCP_CORK_NOPUSH TCP_CORK
+#  elif defined(TCP_NOPUSH)
+/**
+ * Value of TCP_CORK or TCP_NOPUSH
+ */
+#    define mhd_TCP_CORK_NOPUSH TCP_NOPUSH
+#  endif /* TCP_NOPUSH */
+#endif /* IPPROTO_TCP */
+
+#ifdef mhd_TCP_CORK_NOPUSH
+#  ifdef __linux__
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+#    define mhd_CORK_RESET_PUSH_DATA 1
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ * even if TCP_CORK/TCP_NOPUSH was in switched off state.
+ */
+#    define mhd_CORK_RESET_PUSH_DATA_ALWAYS 1
+#endif /* __linux__ */
+#if (defined(__FreeBSD__) && \
+  ((__FreeBSD__ + 0) >= 5 || (__FreeBSD_version + 0) >= 450000)) || \
+  (defined(__FreeBSD_kernel_version) && \
+  (__FreeBSD_kernel_version + 0) >= 450000)
+/* FreeBSD pushes data to the network with reset of TCP_NOPUSH
+ * starting from version 4.5. */
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+#define mhd_CORK_RESET_PUSH_DATA 1
+#endif /* __FreeBSD_version >= 450000 */
+#ifdef __OpenBSD__
+/* OpenBSD took implementation from FreeBSD */
+/**
+ * Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
+ */
+#define mhd_CORK_RESET_PUSH_DATA 1
+#endif /* __OpenBSD__ */
+#endif /* MHD_TCP_CORK_NOPUSH */
+
+#ifdef __linux__
+/**
+ * Indicate that set of TCP_NODELAY push data to the network
+ */
+#  define mhd_NODELAY_SET_PUSH_DATA 1
+/**
+ * Indicate that set of TCP_NODELAY push data to the network even
+ * if TCP_DELAY was already set and regardless of TCP_CORK / TCP_NOPUSH state
+ */
+#  define mhd_NODELAY_SET_PUSH_DATA_ALWAYS 1
+#endif /* __linux__ */
+
+
 #endif /* ! MHD_SYS_IP_HEADERS_H */

+ 45 - 11
src/mhd2/sys_sockets_headers.h

@@ -49,33 +49,45 @@
 #endif
 
 #if defined(HAVE_SOCK_NONBLOCK) && ! defined(MHD_WINSOCK_SOCKETS)
-#  define MHD_SOCK_NONBLOCK SOCK_NONBLOCK
+#  define mhd_SOCK_NONBLOCK SOCK_NONBLOCK
 #else
-#  define MHD_SOCK_NONBLOCK (0)
+#  define mhd_SOCK_NONBLOCK (0)
 #endif
 
 #if defined(SOCK_CLOEXEC) && ! defined(MHD_WINSOCK_SOCKETS)
-#  define MHD_SOCK_CLOEXEC SOCK_CLOEXEC
+#  define mhd_SOCK_CLOEXEC SOCK_CLOEXEC
 #else
-#  define MHD_SOCK_CLOEXEC (0)
+#  define mhd_SOCK_CLOEXEC (0)
 #endif
 
 #if defined(SOCK_NOSIGPIPE) && ! defined(MHD_WINSOCK_SOCKETS)
-#  define MHD_SOCK_NOSIGPIPE SOCK_NOSIGPIPE
+#  define mhd_SOCK_NOSIGPIPE SOCK_NOSIGPIPE
 #else
-#  define MHD_SOCK_NOSIGPIPE (0)
+#  define mhd_SOCK_NOSIGPIPE (0)
 #endif
 
 #if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS)
-#  define MHD_MSG_NOSIGNAL MSG_NOSIGNAL
+#  define mhd_MSG_NOSIGNAL MSG_NOSIGNAL
 #else
-#  define MHD_MSG_NOSIGNAL (0)
+#  define mhd_MSG_NOSIGNAL (0)
 #endif
 
-#if defined(MSG_NOSIGNAL) && ! defined(MHD_WINSOCK_SOCKETS)
-#  define MHD_MSG_NOSIGNAL MSG_MORE
+#ifdef MSG_MORE
+#  ifdef __linux__
+/* MSG_MORE signal kernel to buffer outbond data and works like
+ * TCP_CORK per call without actually setting TCP_CORK value.
+ * It's known to work on Linux. Add more OSes if they are compatible. */
+/**
+ * Indicate MSG_MORE is usable for buffered send().
+ */
+#    define mhd_USE_MSG_MORE 1
+#  endif /* __linux__ */
+#endif /* MSG_MORE */
+
+#ifdef mhd_USE_MSG_MORE
+#  define mhd_MSG_MORE MSG_MORE
 #else
-#  define MHD_MSG_NOSIGNAL (0)
+#  define mhd_MSG_MORE (0)
 #endif
 
 
@@ -128,4 +140,26 @@ typedef int mhd_SCKT_SEND_SIZE;
 #  define MHD_ACCEPTED_DOES_NOT_INHERIT_NONBLOCK 1
 #endif
 
+
+#if defined(MHD_socket_nosignal_) || \
+  (defined(SOL_SOCKET) && defined(SO_NOSIGPIPE))
+/**
+ * Indicate that SIGPIPE can be suppressed by MHD for normal send() by flags
+ * or socket options.
+ * If this macro is undefined, MHD cannot suppress SIGPIPE for socket functions
+ * so sendfile() or writev() calls are avoided in application threads.
+ */
+#  define mhd_SEND_SPIPE_SUPPRESS_POSSIBLE   1
+#endif /* MHD_WINSOCK_SOCKETS || MHD_socket_nosignal_ || MSG_NOSIGNAL */
+
+
+#if ! defined(MHD_WINSOCK_SOCKETS)
+/**
+ * Indicate that suppression of SIGPIPE is required for some network
+ * system calls.
+ */
+#  define mhd_SEND_SPIPE_SUPPRESS_NEEDED     1
+#endif
+
+
 #endif /* ! MHD_SYS_SOCKETS_HEADERS_H */

+ 0 - 156
src/microhttpd/mhd_limits.h

@@ -1,156 +0,0 @@
-/*
-  This file is part of libmicrohttpd
-  Copyright (C) 2015-2022 Karlson2k (Evgeny Grin)
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License as published by the Free Software Foundation; either
-  version 2.1 of the License, or (at your option) any later version.
-
-  This library 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
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/**
- * @file microhttpd/mhd_limits.h
- * @brief  limits values definitions
- * @author Karlson2k (Evgeny Grin)
- */
-
-#ifndef MHD_LIMITS_H
-#define MHD_LIMITS_H
-
-#include "platform.h"
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif /* HAVE_LIMITS_H */
-
-#define MHD_UNSIGNED_TYPE_MAX_(type) ((type) - 1)
-/* Assume 8 bits per byte, no padding bits. */
-#define MHD_SIGNED_TYPE_MAX_(type) \
-  ( (type) ((( ((type) 1) << (sizeof(type) * 8 - 2)) - 1) * 2 + 1) )
-#define MHD_TYPE_IS_SIGNED_(type) (((type) 0)>((type) - 1))
-
-#ifndef INT_MAX
-#ifdef __INT_MAX__
-#define INT_MAX __INT_MAX__
-#else  /* ! __UINT_MAX__ */
-#define INT_MAX MHD_SIGNED_TYPE_MAX_ (int)
-#endif /* ! __UINT_MAX__ */
-#endif /* !UINT_MAX */
-
-#ifndef UINT_MAX
-#ifdef __UINT_MAX__
-#define UINT_MAX __UINT_MAX__
-#else  /* ! __UINT_MAX__ */
-#define UINT_MAX MHD_UNSIGNED_TYPE_MAX_ (unsigned int)
-#endif /* ! __UINT_MAX__ */
-#endif /* !UINT_MAX */
-
-#ifndef LONG_MAX
-#ifdef __LONG_MAX__
-#define LONG_MAX __LONG_MAX__
-#else  /* ! __LONG_MAX__ */
-#define LONG_MAX MHD_SIGNED_TYPE_MAX (long)
-#endif /* ! __LONG_MAX__ */
-#endif /* !LONG_MAX */
-
-#ifndef ULLONG_MAX
-#ifdef ULONGLONG_MAX
-#define ULLONG_MAX ULONGLONG_MAX
-#else  /* ! ULONGLONG_MAX */
-#define ULLONG_MAX MHD_UNSIGNED_TYPE_MAX_ (MHD_UNSIGNED_LONG_LONG)
-#endif /* ! ULONGLONG_MAX */
-#endif /* !ULLONG_MAX */
-
-#ifndef INT32_MAX
-#ifdef __INT32_MAX__
-#define INT32_MAX __INT32_MAX__
-#else  /* ! __INT32_MAX__ */
-#define INT32_MAX ((int32_t) 0x7FFFFFFF)
-#endif /* ! __INT32_MAX__ */
-#endif /* !INT32_MAX */
-
-#ifndef UINT32_MAX
-#ifdef __UINT32_MAX__
-#define UINT32_MAX __UINT32_MAX__
-#else  /* ! __UINT32_MAX__ */
-#define UINT32_MAX ((int32_t) 0xFFFFFFFF)
-#endif /* ! __UINT32_MAX__ */
-#endif /* !UINT32_MAX */
-
-#ifndef UINT64_MAX
-#ifdef __UINT64_MAX__
-#define UINT64_MAX __UINT64_MAX__
-#else  /* ! __UINT64_MAX__ */
-#define UINT64_MAX ((uint64_t) 0xFFFFFFFFFFFFFFFF)
-#endif /* ! __UINT64_MAX__ */
-#endif /* !UINT64_MAX */
-
-#ifndef INT64_MAX
-#ifdef __INT64_MAX__
-#define INT64_MAX __INT64_MAX__
-#else  /* ! __INT64_MAX__ */
-#define INT64_MAX ((int64_t) 0x7FFFFFFFFFFFFFFF)
-#endif /* ! __UINT64_MAX__ */
-#endif /* !INT64_MAX */
-
-#ifndef SIZE_MAX
-#ifdef __SIZE_MAX__
-#define SIZE_MAX __SIZE_MAX__
-#elif defined(UINTPTR_MAX)
-#define SIZE_MAX UINTPTR_MAX
-#else  /* ! __SIZE_MAX__ */
-#define SIZE_MAX MHD_UNSIGNED_TYPE_MAX_ (size_t)
-#endif /* ! __SIZE_MAX__ */
-#endif /* !SIZE_MAX */
-
-#ifndef SSIZE_MAX
-#ifdef __SSIZE_MAX__
-#define SSIZE_MAX __SSIZE_MAX__
-#elif defined(INTPTR_MAX)
-#define SSIZE_MAX INTPTR_MAX
-#else
-#define SSIZE_MAX MHD_SIGNED_TYPE_MAX_ (ssize_t)
-#endif
-#endif /* ! SSIZE_MAX */
-
-#ifndef OFF_T_MAX
-#ifdef OFF_MAX
-#define OFF_T_MAX OFF_MAX
-#elif defined(OFFT_MAX)
-#define OFF_T_MAX OFFT_MAX
-#elif defined(__APPLE__) && defined(__MACH__)
-#define OFF_T_MAX INT64_MAX
-#else
-#define OFF_T_MAX MHD_SIGNED_TYPE_MAX_ (off_t)
-#endif
-#endif /* !OFF_T_MAX */
-
-#if defined(_LARGEFILE64_SOURCE) && ! defined(OFF64_T_MAX)
-#define OFF64_T_MAX MHD_SIGNED_TYPE_MAX_ (uint64_t)
-#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */
-
-#ifndef TIME_T_MAX
-#define TIME_T_MAX ((time_t)              \
-                    (MHD_TYPE_IS_SIGNED_ (time_t) ?    \
-                     MHD_SIGNED_TYPE_MAX_ (time_t) : \
-                     MHD_UNSIGNED_TYPE_MAX_ (time_t)))
-#endif /* !TIME_T_MAX */
-
-#ifndef TIMEVAL_TV_SEC_MAX
-#ifndef _WIN32
-#define TIMEVAL_TV_SEC_MAX TIME_T_MAX
-#else  /* _WIN32 */
-#define TIMEVAL_TV_SEC_MAX LONG_MAX
-#endif /* _WIN32 */
-#endif /* !TIMEVAL_TV_SEC_MAX */
-
-#endif /* MHD_LIMITS_H */

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.