mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
Still WIP
This commit is contained in:
+9
-1
@@ -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],
|
||||
[],
|
||||
|
||||
+216
-89
@@ -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
|
||||
{
|
||||
/* 100 "Continue". RFC9110, Section 15.2.1. */
|
||||
MHD_HTTP_STATUS_CONTINUE = 100
|
||||
,
|
||||
/* 101 "Switching Protocols". RFC9110, Section 15.2.2. */
|
||||
MHD_HTTP_STATUS_SWITCHING_PROTOCOLS = 101
|
||||
,
|
||||
/* 102 "Processing". RFC2518. */
|
||||
MHD_HTTP_STATUS_PROCESSING = 102
|
||||
,
|
||||
/* 103 "Early Hints". RFC8297. */
|
||||
MHD_HTTP_STATUS_EARLY_HINTS = 103
|
||||
,
|
||||
|
||||
MHD_HTTP_STATUS_CONTINUE = 100
|
||||
/* 200 "OK". RFC9110, Section 15.3.1. */
|
||||
MHD_HTTP_STATUS_OK = 200
|
||||
,
|
||||
MHD_HTTP_STATUS_SWITCHING_PROTOCOLS = 101
|
||||
/* 201 "Created". RFC9110, Section 15.3.2. */
|
||||
MHD_HTTP_STATUS_CREATED = 201
|
||||
,
|
||||
MHD_HTTP_STATUS_PROCESSING = 102
|
||||
,
|
||||
MHD_HTTP_STATUS_OK = 200
|
||||
,
|
||||
MHD_HTTP_STATUS_CREATED = 201
|
||||
,
|
||||
MHD_HTTP_STATUS_ACCEPTED = 202
|
||||
/* 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 */
|
||||
,
|
||||
MHD_HTTP_STATUS_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450 /* IANA: unused */
|
||||
,
|
||||
|
||||
/* 451 "Unavailable For Legal Reasons". RFC7725. */
|
||||
MHD_HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451
|
||||
,
|
||||
MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500
|
||||
,
|
||||
MHD_HTTP_STATUS_NOT_IMPLEMENTED = 501
|
||||
,
|
||||
MHD_HTTP_STATUS_BAD_GATEWAY = 502
|
||||
,
|
||||
MHD_HTTP_STATUS_SERVICE_UNAVAILABLE = 503
|
||||
,
|
||||
MHD_HTTP_STATUS_GATEWAY_TIMEOUT = 504
|
||||
,
|
||||
MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
,
|
||||
MHD_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506
|
||||
,
|
||||
MHD_HTTP_STATUS_INSUFFICIENT_STORAGE = 507
|
||||
,
|
||||
MHD_HTTP_STATUS_LOOP_DETECTED = 508
|
||||
,
|
||||
MHD_HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509 /* IANA: unused */
|
||||
,
|
||||
MHD_HTTP_STATUS_NOT_EXTENDED = 510
|
||||
,
|
||||
MHD_HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
|
||||
/* 500 "Internal Server Error". RFC9110, Section 15.6.1. */
|
||||
MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500
|
||||
,
|
||||
/* 501 "Not Implemented". RFC9110, Section 15.6.2. */
|
||||
MHD_HTTP_STATUS_NOT_IMPLEMENTED = 501
|
||||
,
|
||||
/* 502 "Bad Gateway". RFC9110, Section 15.6.3. */
|
||||
MHD_HTTP_STATUS_BAD_GATEWAY = 502
|
||||
,
|
||||
/* 503 "Service Unavailable". RFC9110, Section 15.6.4. */
|
||||
MHD_HTTP_STATUS_SERVICE_UNAVAILABLE = 503
|
||||
,
|
||||
/* 504 "Gateway Timeout". RFC9110, Section 15.6.5. */
|
||||
MHD_HTTP_STATUS_GATEWAY_TIMEOUT = 504
|
||||
,
|
||||
/* 505 "HTTP Version Not Supported". RFC9110, Section 15.6.6. */
|
||||
MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
|
||||
,
|
||||
/* 506 "Variant Also Negotiates". RFC2295. */
|
||||
MHD_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506
|
||||
,
|
||||
/* 507 "Insufficient Storage". RFC4918. */
|
||||
MHD_HTTP_STATUS_INSUFFICIENT_STORAGE = 507
|
||||
,
|
||||
/* 508 "Loop Detected". RFC5842. */
|
||||
MHD_HTTP_STATUS_LOOP_DETECTED = 508
|
||||
,
|
||||
|
||||
/* 510 "Not Extended". (OBSOLETED) RFC2774; status-change-http-experiments-to-historic. */
|
||||
MHD_HTTP_STATUS_NOT_EXTENDED = 510
|
||||
,
|
||||
/* 511 "Network Authentication Required". RFC6585. */
|
||||
MHD_HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
,
|
||||
|
||||
|
||||
/* Not registered non-standard codes */
|
||||
/* 449 "Reply With". MS IIS extension. */
|
||||
MHD_HTTP_STATUS_RETRY_WITH = 449
|
||||
,
|
||||
|
||||
/* 450 "Blocked by Windows Parental Controls". MS extension. */
|
||||
MHD_HTTP_STATUS_BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS = 450
|
||||
,
|
||||
|
||||
/* 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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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.
|
||||
* Settings for requests processing
|
||||
*/
|
||||
MHD_RequestCallback req_cb;
|
||||
|
||||
/**
|
||||
* The closure for @a req_cb
|
||||
*/
|
||||
void *req_cb_cls;
|
||||
struct mhd_DaemonRequestProcessingSettings req_cfg;
|
||||
|
||||
/* Other data */
|
||||
|
||||
|
||||
+15
-6
@@ -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
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+13
-32
@@ -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
|
||||
{
|
||||
/**
|
||||
* The field data
|
||||
*/
|
||||
struct MHD_NameValueKind field;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
const char *header;
|
||||
|
||||
/**
|
||||
* The length of the @a header, not including the final zero termination.
|
||||
*/
|
||||
size_t header_size;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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
@@ -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
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
Copyright (C) 2017-2023 Karlson2k (Evgeny Grin), Full re-write of buffering and
|
||||
pushing, many bugs fixes, optimisations, sendfile() porting
|
||||
Copyright (C) 2017-2024 Karlson2k (Evgeny Grin), Full re-write of buffering
|
||||
and pushing, many bugs fixes, optimisations,
|
||||
sendfile() porting
|
||||
Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers
|
||||
|
||||
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.
|
||||
@@ -21,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file microhttpd/mhd_send.c
|
||||
* @file src/mhd2/mhd_send.c
|
||||
* @brief Implementation of send() wrappers and helper functions.
|
||||
* @author Karlson2k (Evgeny Grin)
|
||||
* @author ng0 (N. Gillmann)
|
||||
@@ -34,47 +35,63 @@
|
||||
* large a chunk as possible to the socket. Alternatively,
|
||||
* use madvise(..., MADV_SEQUENTIAL). */
|
||||
|
||||
#include "mhd_sys_options.h"
|
||||
|
||||
#include "mhd_send.h"
|
||||
#ifdef MHD_LINUX_SOLARIS_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#endif /* MHD_LINUX_SOLARIS_SENDFILE */
|
||||
#include "sys_sockets_headers.h"
|
||||
#include "sys_ip_headers.h"
|
||||
#include "mhd_sockets_macros.h"
|
||||
#include "daemon_logger.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mhd_iovec.h"
|
||||
#ifdef HAVE_LINUX_SENDFILE
|
||||
# include <sys/sendfile.h>
|
||||
#endif /* HAVE_LINUX_SENDFILE */
|
||||
|
||||
#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/uio.h>
|
||||
#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
/* For FreeBSD version identification */
|
||||
#include <sys/param.h>
|
||||
# include <sys/param.h>
|
||||
#endif /* HAVE_SYS_PARAM_H */
|
||||
#ifdef HAVE_SYSCONF
|
||||
#include <unistd.h>
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_SYSCONF */
|
||||
#include "mhd_assert.h"
|
||||
|
||||
#include "mhd_limits.h"
|
||||
|
||||
#ifdef MHD_VECT_SEND
|
||||
#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
|
||||
defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
|
||||
defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
|
||||
#define _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
|
||||
#endif /* (!HAVE_SENDMSG || !MSG_NOSIGNAL) &&
|
||||
MHD_SEND_SPIPE_SUPPRESS_POSSIBLE && MHD_SEND_SPIPE_SUPPRESS_NEEDED */
|
||||
#endif /* MHD_VECT_SEND */
|
||||
#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
|
||||
defined(MHD_WINSOCK_SOCKETS)
|
||||
# define mhd_USE_VECT_SEND 1
|
||||
#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */
|
||||
|
||||
|
||||
#ifdef mhd_USE_VECT_SEND
|
||||
# if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL)) && \
|
||||
defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
|
||||
defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
|
||||
# define mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1
|
||||
# endif /* (!HAVE_SENDMSG || !MSG_NOSIGNAL) &&
|
||||
mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && mhd_SEND_SPIPE_SUPPRESS_NEEDED */
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
/**
|
||||
* sendfile() chuck size
|
||||
*/
|
||||
#define MHD_SENFILE_CHUNK_ (0x20000)
|
||||
#define mhd_SENFILE_CHUNK_SIZE (0x20000)
|
||||
|
||||
/**
|
||||
* sendfile() chuck size for thread-per-connection
|
||||
*/
|
||||
#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
|
||||
#define mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C (0x200000)
|
||||
|
||||
#ifdef HAVE_FREEBSD_SENDFILE
|
||||
#ifdef SF_FLAGS
|
||||
#if defined(HAVE_FREEBSD_SENDFILE) && defined(SF_FLAGS)
|
||||
/**
|
||||
* FreeBSD sendfile() flags
|
||||
*/
|
||||
@@ -101,16 +118,19 @@ freebsd_sendfile_init_ (void)
|
||||
else
|
||||
{
|
||||
freebsd_sendfile_flags_ =
|
||||
SF_FLAGS ((uint16_t) ((MHD_SENFILE_CHUNK_ + sys_page_size - 1)
|
||||
/ sys_page_size), SF_NODISKIO);
|
||||
SF_FLAGS ((uint_fast16_t) \
|
||||
((mhd_SENFILE_CHUNK_SIZE + sys_page_size - 1) / sys_page_size) \
|
||||
& 0xFFFFU, SF_NODISKIO);
|
||||
freebsd_sendfile_flags_thd_p_c_ =
|
||||
SF_FLAGS ((uint16_t) ((MHD_SENFILE_CHUNK_THR_P_C_ + sys_page_size - 1)
|
||||
/ sys_page_size), SF_NODISKIO);
|
||||
SF_FLAGS ((uint_fast16_t) \
|
||||
((mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C + sys_page_size - 1) \
|
||||
/ sys_page_size) & 0xFFFFU, SF_NODISKIO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* SF_FLAGS */
|
||||
#else /* ! HAVE_FREEBSD_SENDFILE || ! SF_FLAGS */
|
||||
# define freebsd_sendfile_init_() (void)0
|
||||
#endif /* HAVE_FREEBSD_SENDFILE */
|
||||
|
||||
|
||||
@@ -128,11 +148,11 @@ iov_max_init_ (void)
|
||||
mhd_iov_max_ = (unsigned long) res;
|
||||
else
|
||||
{
|
||||
#if defined(IOV_MAX)
|
||||
# if defined(IOV_MAX)
|
||||
mhd_iov_max_ = IOV_MAX;
|
||||
#else /* ! IOV_MAX */
|
||||
# else /* ! IOV_MAX */
|
||||
mhd_iov_max_ = 8; /* Should be the safe limit */
|
||||
#endif /* ! IOV_MAX */
|
||||
# endif /* ! IOV_MAX */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,46 +160,44 @@ iov_max_init_ (void)
|
||||
/**
|
||||
* IOV_MAX (run-time) value
|
||||
*/
|
||||
#define _MHD_IOV_MAX mhd_iov_max_
|
||||
#elif defined(IOV_MAX)
|
||||
# define mhd_IOV_MAX mhd_iov_max_
|
||||
#else /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
|
||||
# define iov_max_init_() (void)0
|
||||
# if defined(IOV_MAX)
|
||||
|
||||
/**
|
||||
* IOV_MAX (static) value
|
||||
*/
|
||||
#define _MHD_IOV_MAX IOV_MAX
|
||||
#endif /* HAVE_SYSCONF && _SC_IOV_MAX */
|
||||
# define mhd_IOV_MAX IOV_MAX
|
||||
# endif /* IOV_MAX */
|
||||
#endif /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */
|
||||
|
||||
|
||||
/**
|
||||
* Initialises static variables
|
||||
*/
|
||||
void
|
||||
MHD_send_init_static_vars_ (void)
|
||||
mhd_send_init_static_vars (void)
|
||||
{
|
||||
#ifdef HAVE_FREEBSD_SENDFILE
|
||||
/* FreeBSD 11 and later allow to specify read-ahead size
|
||||
* and handles SF_NODISKIO differently.
|
||||
* SF_FLAGS defined only on FreeBSD 11 and later. */
|
||||
#ifdef SF_FLAGS
|
||||
freebsd_sendfile_init_ ();
|
||||
#endif /* SF_FLAGS */
|
||||
#endif /* HAVE_FREEBSD_SENDFILE */
|
||||
#if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX)
|
||||
|
||||
iov_max_init_ ();
|
||||
#endif /* HAVE_SYSCONF && _SC_IOV_MAX */
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MHD_connection_set_nodelay_state_ (struct MHD_Connection *connection,
|
||||
bool nodelay_state)
|
||||
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
|
||||
mhd_connection_set_nodelay_state (struct MHD_Connection *connection,
|
||||
bool nodelay_state)
|
||||
{
|
||||
#ifdef TCP_NODELAY
|
||||
const MHD_SCKT_OPT_BOOL_ off_val = 0;
|
||||
const MHD_SCKT_OPT_BOOL_ on_val = 1;
|
||||
static const mhd_SCKT_OPT_BOOL off_val = 0;
|
||||
static const mhd_SCKT_OPT_BOOL on_val = 1;
|
||||
int err_code;
|
||||
|
||||
if (_MHD_YES == connection->is_nonip)
|
||||
if (mhd_T_IS_YES(connection->is_nonip))
|
||||
return false;
|
||||
|
||||
if (0 == setsockopt (connection->socket_fd,
|
||||
@@ -188,121 +206,76 @@ MHD_connection_set_nodelay_state_ (struct MHD_Connection *connection,
|
||||
(const void *) (nodelay_state ? &on_val : &off_val),
|
||||
sizeof (off_val)))
|
||||
{
|
||||
connection->sk_nodelay = nodelay_state;
|
||||
connection->sk_nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
err_code = MHD_socket_get_error_ ();
|
||||
if (MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_EINVAL_) ||
|
||||
MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_ENOPROTOOPT_) ||
|
||||
MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_ENOTSOCK_))
|
||||
err_code = mhd_SCKT_GET_LERR ();
|
||||
if ((mhd_T_IS_NOT_YES(connection->is_nonip)) &&
|
||||
(mhd_SCKT_ERR_IS_EINVAL (err_code) ||
|
||||
mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) ||
|
||||
mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
|
||||
{
|
||||
if (_MHD_UNKNOWN == connection->is_nonip)
|
||||
connection->is_nonip = _MHD_YES;
|
||||
#ifdef HAVE_MESSAGES
|
||||
else
|
||||
{
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Setting %s option to %s state failed "
|
||||
"for TCP/IP socket %d: %s\n"),
|
||||
"TCP_NODELAY",
|
||||
nodelay_state ? _ ("ON") : _ ("OFF"),
|
||||
(int) connection->socket_fd,
|
||||
MHD_socket_strerr_ (err_code));
|
||||
}
|
||||
#endif /* HAVE_MESSAGES */
|
||||
connection->is_nonip = mhd_T_YES;
|
||||
}
|
||||
#ifdef HAVE_MESSAGES
|
||||
else
|
||||
{
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Setting %s option to %s state failed: %s\n"),
|
||||
"TCP_NODELAY",
|
||||
nodelay_state ? _ ("ON") : _ ("OFF"),
|
||||
MHD_socket_strerr_ (err_code));
|
||||
MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \
|
||||
"Failed to set required TCP_NODELAY option for the socket.");
|
||||
}
|
||||
#endif /* HAVE_MESSAGES */
|
||||
|
||||
#else /* ! TCP_NODELAY */
|
||||
(void) connection; (void) nodelay_state; /* Mute compiler warnings */
|
||||
(void) nodelay_state; /* Mute compiler warnings */
|
||||
connection->sk_nodelay = mhd_T_NO;
|
||||
#endif /* ! TCP_NODELAY */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set required cork state for connection socket
|
||||
*
|
||||
* The function automatically updates sk_corked state.
|
||||
*
|
||||
* @param connection the connection to manipulate
|
||||
* @param cork_state the requested new state of socket
|
||||
* @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)
|
||||
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
|
||||
mhd_connection_set_cork_state (struct MHD_Connection *connection,
|
||||
bool cork_state)
|
||||
{
|
||||
#if defined(MHD_TCP_CORK_NOPUSH)
|
||||
const MHD_SCKT_OPT_BOOL_ off_val = 0;
|
||||
const MHD_SCKT_OPT_BOOL_ on_val = 1;
|
||||
#if defined(mhd_TCP_CORK_NOPUSH)
|
||||
static const mhd_SCKT_OPT_BOOL off_val = 0;
|
||||
static const mhd_SCKT_OPT_BOOL on_val = 1;
|
||||
int err_code;
|
||||
|
||||
if (_MHD_YES == connection->is_nonip)
|
||||
if (mhd_T_IS_YES(connection->is_nonip))
|
||||
return false;
|
||||
|
||||
if (0 == setsockopt (connection->socket_fd,
|
||||
IPPROTO_TCP,
|
||||
MHD_TCP_CORK_NOPUSH,
|
||||
mhd_TCP_CORK_NOPUSH,
|
||||
(const void *) (cork_state ? &on_val : &off_val),
|
||||
sizeof (off_val)))
|
||||
{
|
||||
connection->sk_corked = cork_state;
|
||||
connection->sk_corked = cork_state ? mhd_T_YES : mhd_T_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
err_code = MHD_socket_get_error_ ();
|
||||
if (MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_EINVAL_) ||
|
||||
MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_ENOPROTOOPT_) ||
|
||||
MHD_SCKT_ERR_IS_ (err_code, MHD_SCKT_ENOTSOCK_))
|
||||
err_code = mhd_SCKT_GET_LERR ();
|
||||
if ((mhd_T_IS_NOT_YES(connection->is_nonip)) &&
|
||||
(mhd_SCKT_ERR_IS_EINVAL (err_code) ||
|
||||
mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) ||
|
||||
mhd_SCKT_ERR_IS_NOTSOCK (err_code)))
|
||||
{
|
||||
if (_MHD_UNKNOWN == connection->is_nonip)
|
||||
connection->is_nonip = _MHD_YES;
|
||||
#ifdef HAVE_MESSAGES
|
||||
else
|
||||
{
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Setting %s option to %s state failed "
|
||||
"for TCP/IP socket %d: %s\n"),
|
||||
#ifdef TCP_CORK
|
||||
"TCP_CORK",
|
||||
#else /* ! TCP_CORK */
|
||||
"TCP_NOPUSH",
|
||||
#endif /* ! TCP_CORK */
|
||||
cork_state ? _ ("ON") : _ ("OFF"),
|
||||
(int) connection->socket_fd,
|
||||
MHD_socket_strerr_ (err_code));
|
||||
}
|
||||
#endif /* HAVE_MESSAGES */
|
||||
connection->is_nonip = mhd_T_YES;
|
||||
}
|
||||
#ifdef HAVE_MESSAGES
|
||||
else
|
||||
{
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Setting %s option to %s state failed: %s\n"),
|
||||
#ifdef TCP_CORK
|
||||
"TCP_CORK",
|
||||
#else /* ! TCP_CORK */
|
||||
"TCP_NOPUSH",
|
||||
#endif /* ! TCP_CORK */
|
||||
cork_state ? _ ("ON") : _ ("OFF"),
|
||||
MHD_socket_strerr_ (err_code));
|
||||
# ifdef TCP_CORK
|
||||
MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
|
||||
"Failed to set required TCP_CORK option for the socket.");
|
||||
# else
|
||||
MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \
|
||||
"Failed to set required TCP_NOPUSH option for the socket.");
|
||||
# endif
|
||||
}
|
||||
#endif /* HAVE_MESSAGES */
|
||||
|
||||
#else /* ! MHD_TCP_CORK_NOPUSH */
|
||||
(void) connection; (void) cork_state; /* Mute compiler warnings. */
|
||||
#endif /* ! MHD_TCP_CORK_NOPUSH */
|
||||
#else /* ! mhd_TCP_CORK_NOPUSH */
|
||||
(void) cork_state; /* Mute compiler warnings. */
|
||||
connection->sk_corked = mhd_T_NO;
|
||||
#endif /* ! mhd_TCP_CORK_NOPUSH */
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -326,8 +299,11 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
* Final piece is indicated by push_data == true. */
|
||||
const bool buffer_data = (! push_data);
|
||||
|
||||
if (_MHD_YES == connection->is_nonip)
|
||||
if (mhd_T_IS_YES(connection->is_nonip))
|
||||
return;
|
||||
|
||||
// TODO: support inheriting of TCP_NODELAY and TCP_NOPUSH
|
||||
|
||||
/* The goal is to minimise the total number of additional sys-calls
|
||||
* before and after send().
|
||||
* The following tricky (over-)complicated algorithm typically use zero,
|
||||
@@ -336,67 +312,72 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
if (buffer_data)
|
||||
{
|
||||
/* Need to buffer data if possible. */
|
||||
#ifdef MHD_USE_MSG_MORE
|
||||
#ifdef mhd_USE_MSG_MORE
|
||||
if (plain_send)
|
||||
return; /* Data is buffered by send() with MSG_MORE flag.
|
||||
* No need to check or change anything. */
|
||||
#else /* ! MHD_USE_MSG_MORE */
|
||||
#else /* ! mhd_USE_MSG_MORE */
|
||||
(void) plain_send; /* Mute compiler warning. */
|
||||
#endif /* ! MHD_USE_MSG_MORE */
|
||||
#endif /* ! mhd_USE_MSG_MORE */
|
||||
|
||||
#ifdef MHD_TCP_CORK_NOPUSH
|
||||
if (_MHD_ON == connection->sk_corked)
|
||||
#ifdef mhd_TCP_CORK_NOPUSH
|
||||
if (mhd_T_IS_YES(connection->sk_corked))
|
||||
return; /* The connection was already corked. */
|
||||
|
||||
if (MHD_connection_set_cork_state_ (connection, true))
|
||||
/* Prefer 'cork' over 'no delay' as the 'cork' buffers better, regardless
|
||||
* of the number of received ACKs. */
|
||||
if (mhd_connection_set_cork_state (connection, true))
|
||||
return; /* The connection has been corked. */
|
||||
|
||||
/* Failed to cork the connection.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
#endif /* MHD_TCP_CORK_NOPUSH */
|
||||
if (_MHD_OFF == connection->sk_nodelay)
|
||||
#endif /* mhd_TCP_CORK_NOPUSH */
|
||||
if (mhd_T_IS_NO(connection->sk_nodelay))
|
||||
return; /* TCP_NODELAY was not set for the socket.
|
||||
* Nagle's algorithm will buffer some data. */
|
||||
|
||||
/* Try to reset TCP_NODELAY state for the socket.
|
||||
* Ignore possible error as no other options exist to
|
||||
* buffer data. */
|
||||
MHD_connection_set_nodelay_state_ (connection, false);
|
||||
mhd_connection_set_nodelay_state (connection, false);
|
||||
/* TCP_NODELAY has been (hopefully) reset for the socket.
|
||||
* Nagle's algorithm will buffer some data. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Need to push data after send() */
|
||||
/* If additional sys-call is required prefer to make it after the send()
|
||||
* as the next send() may consume only part of the prepared data and
|
||||
/* Need to push data after the next send() */
|
||||
/* If additional sys-call is required, prefer to make it only after the send()
|
||||
* (if possible) as this send() may consume only part of the prepared data and
|
||||
* more send() calls will be used. */
|
||||
#ifdef MHD_TCP_CORK_NOPUSH
|
||||
#ifdef _MHD_CORK_RESET_PUSH_DATA
|
||||
#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
#ifdef mhd_TCP_CORK_NOPUSH
|
||||
# ifdef mhd_CORK_RESET_PUSH_DATA
|
||||
# ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
/* Data can be pushed immediately by uncorking socket regardless of
|
||||
* cork state before. */
|
||||
/* This is typical for Linux, no other kernel with
|
||||
* such behavior are known so far. */
|
||||
* such behaviour are known so far. */
|
||||
|
||||
/* No need to check the current state of TCP_CORK / TCP_NOPUSH
|
||||
* as reset of cork will push the data anyway. */
|
||||
return; /* Data may be pushed by resetting of
|
||||
* TCP_CORK / TCP_NOPUSH after send() */
|
||||
#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
# else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
/* Reset of TCP_CORK / TCP_NOPUSH will push the data
|
||||
* only if socket is corked. */
|
||||
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
|
||||
/* Data can be pushed immediately by setting TCP_NODELAY regardless
|
||||
* of TCP_NODDELAY or corking state before. */
|
||||
|
||||
/* Dead code currently, no known kernels with such behavior. */
|
||||
/* Dead code currently, no known kernels with such behaviour and without
|
||||
* pushing by uncorking. */
|
||||
return; /* Data may be pushed by setting of TCP_NODELAY after send().
|
||||
No need to make extra sys-calls before send().*/
|
||||
#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA
|
||||
/* These next comment blocks are just generic description for the possible
|
||||
* choices for the code below. */
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA
|
||||
/* Setting of TCP_NODELAY will push the data only if
|
||||
* both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */
|
||||
|
||||
@@ -404,31 +385,32 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
* socket was corked before or by setting TCP_NODELAY if
|
||||
* socket was not corked and TCP_NODELAY was not set before. */
|
||||
|
||||
/* Dead code currently as Linux is the only kernel that push
|
||||
* data by setting of TCP_NODELAY and Linux push data always. */
|
||||
#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
|
||||
/* This combination not possible currently as Linux is the only kernel that
|
||||
* pushes data by setting of TCP_NODELAY and Linux pushes data always
|
||||
* by TCP_NODELAY, regardless previous TCP_NODELAY state. */
|
||||
# else /* ! mhd_NODELAY_SET_PUSH_DATA */
|
||||
/* Data can be pushed immediately by uncorking socket or
|
||||
* can be pushed by send() on uncorked socket if
|
||||
* TCP_NODELAY was set *before*. */
|
||||
|
||||
/* This is typical FreeBSD behavior. */
|
||||
#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
|
||||
/* This is typical modern FreeBSD and OpenBSD behaviour. */
|
||||
# endif /* ! mhd_NODELAY_SET_PUSH_DATA */
|
||||
|
||||
if (_MHD_ON == connection->sk_corked)
|
||||
if (mhd_T_IS_YES(connection->sk_corked))
|
||||
return; /* Socket is corked. Data can be pushed by resetting of
|
||||
* TCP_CORK / TCP_NOPUSH after send() */
|
||||
else if (_MHD_OFF == connection->sk_corked)
|
||||
else if (mhd_T_IS_NO(connection->sk_corked))
|
||||
{
|
||||
/* The socket is not corked. */
|
||||
if (_MHD_ON == connection->sk_nodelay)
|
||||
if (mhd_T_IS_YES(connection->sk_nodelay))
|
||||
return; /* TCP_NODELAY was already set,
|
||||
* data will be pushed automatically by the next send() */
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA
|
||||
else if (_MHD_UNKNOWN == connection->sk_nodelay)
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA
|
||||
else if (mhd_T_IS_MAYBE(connection->sk_nodelay))
|
||||
{
|
||||
/* Setting TCP_NODELAY may push data.
|
||||
/* Setting TCP_NODELAY may push data NOW.
|
||||
* Cork socket here and uncork after send(). */
|
||||
if (MHD_connection_set_cork_state_ (connection, true))
|
||||
if (mhd_connection_set_cork_state (connection, true))
|
||||
return; /* The connection has been corked.
|
||||
* Data can be pushed by resetting of
|
||||
* TCP_CORK / TCP_NOPUSH after send() */
|
||||
@@ -438,41 +420,40 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
* Really unlikely to happen on TCP connections */
|
||||
/* Have to set TCP_NODELAY.
|
||||
* If TCP_NODELAY real system state was OFF then
|
||||
* already buffered data may be pushed here, but this is unlikely
|
||||
* to happen as it is only a backup solution when corking has failed.
|
||||
* already buffered data may be pushed NOW, but it is unlikely
|
||||
* to happen as this is only a backup solution when corking has failed.
|
||||
* Ignore possible error here as no other options exist to
|
||||
* push data. */
|
||||
MHD_connection_set_nodelay_state_ (connection, true);
|
||||
mhd_connection_set_nodelay_state (connection, true);
|
||||
/* TCP_NODELAY has been (hopefully) set for the socket.
|
||||
* The data will be pushed by the next send(). */
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif /* _MHD_NODELAY_SET_PUSH_DATA */
|
||||
# endif /* mhd_NODELAY_SET_PUSH_DATA */
|
||||
else
|
||||
{
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA
|
||||
/* TCP_NODELAY was switched off and
|
||||
* the socket is not corked. */
|
||||
#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
|
||||
/* Socket is not corked and TCP_NODELAY was not set or unknown. */
|
||||
#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA
|
||||
/* The socket is not corked and TCP_NODELAY is switched off. */
|
||||
# else /* ! mhd_NODELAY_SET_PUSH_DATA */
|
||||
/* The socket is not corked and TCP_NODELAY is not set or unknown. */
|
||||
# endif /* ! mhd_NODELAY_SET_PUSH_DATA */
|
||||
|
||||
/* At least one additional sys-call is required. */
|
||||
/* At least one additional sys-call before send() is required. */
|
||||
/* Setting TCP_NODELAY is optimal here as data will be pushed
|
||||
* automatically by the next send() and no additional
|
||||
* sys-call are needed after the send(). */
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
return;
|
||||
else
|
||||
{
|
||||
/* Failed to set TCP_NODELAY for the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
/* Cork the socket here and make additional sys-call
|
||||
* to uncork the socket after send(). */
|
||||
* to uncork the socket after send(). This will push the data. */
|
||||
/* Ignore possible error here as no other options exist to
|
||||
* push data. */
|
||||
MHD_connection_set_cork_state_ (connection, true);
|
||||
mhd_connection_set_cork_state (connection, true);
|
||||
/* The connection has been (hopefully) corked.
|
||||
* Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH
|
||||
* after send() */
|
||||
@@ -480,60 +461,76 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Corked state is unknown. Need to make sys-call here otherwise
|
||||
/* Corked state is unknown. Need to make a sys-call here otherwise
|
||||
* data may not be pushed. */
|
||||
if (MHD_connection_set_cork_state_ (connection, true))
|
||||
if (mhd_connection_set_cork_state (connection, true))
|
||||
return; /* The connection has been corked.
|
||||
* Data can be pushed by resetting of
|
||||
* TCP_CORK / TCP_NOPUSH after send() */
|
||||
/* The socket cannot be corked.
|
||||
* Really unlikely to happen on TCP connections */
|
||||
if (_MHD_ON == connection->sk_nodelay)
|
||||
if (mhd_T_IS_YES(connection->sk_nodelay))
|
||||
return; /* TCP_NODELAY was already set,
|
||||
* data will be pushed by the next send() */
|
||||
|
||||
/* Have to set TCP_NODELAY. */
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA
|
||||
/* If TCP_NODELAY state was unknown (external connection) then
|
||||
* already buffered data may be pushed here, but this is unlikely
|
||||
* to happen as it is only a backup solution when corking has failed. */
|
||||
#endif /* _MHD_NODELAY_SET_PUSH_DATA */
|
||||
# endif /* mhd_NODELAY_SET_PUSH_DATA */
|
||||
/* Ignore possible error here as no other options exist to
|
||||
* push data. */
|
||||
MHD_connection_set_nodelay_state_ (connection, true);
|
||||
mhd_connection_set_nodelay_state (connection, true);
|
||||
/* TCP_NODELAY has been (hopefully) set for the socket.
|
||||
* The data will be pushed by the next send(). */
|
||||
return;
|
||||
#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
#else /* ! _MHD_CORK_RESET_PUSH_DATA */
|
||||
# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
# endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
# else /* ! mhd_CORK_RESET_PUSH_DATA */
|
||||
|
||||
# ifndef mhd_NODELAY_SET_PUSH_DATA
|
||||
/* Neither uncorking the socket or setting TCP_NODELAY
|
||||
* push the data immediately. */
|
||||
/* The only way to push the data is to use send() on uncorked
|
||||
* socket with TCP_NODELAY switched on . */
|
||||
|
||||
/* This is a typical *BSD (except FreeBSD) and Darwin behavior. */
|
||||
/* This is old FreeBSD and Darwin behaviour. */
|
||||
|
||||
/* Uncork socket if socket wasn't uncorked. */
|
||||
if (_MHD_OFF != connection->sk_corked)
|
||||
MHD_connection_set_cork_state_ (connection, false);
|
||||
if (mhd_T_IS_NOT_NO(connection->sk_corked))
|
||||
mhd_connection_set_cork_state (connection, false);
|
||||
|
||||
/* Set TCP_NODELAY if it wasn't set. */
|
||||
if (_MHD_ON != connection->sk_nodelay)
|
||||
MHD_connection_set_nodelay_state_ (connection, true);
|
||||
if (mhd_T_IS_NOT_YES(connection->sk_nodelay))
|
||||
mhd_connection_set_nodelay_state (connection, true);
|
||||
|
||||
return;
|
||||
#endif /* ! _MHD_CORK_RESET_PUSH_DATA */
|
||||
#else /* ! MHD_TCP_CORK_NOPUSH */
|
||||
# else /* mhd_NODELAY_SET_PUSH_DATA */
|
||||
/* Setting TCP_NODELAY push the data immediately. */
|
||||
|
||||
/* Dead code currently as Linux kernel is only kernel which push by
|
||||
* setting TCP_NODELAY. The same kernel push data by resetting TCP_CORK. */
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
|
||||
return; /* Data may be pushed by setting of TCP_NODELAY after send().
|
||||
No need to make extra sys-calls before send().*/
|
||||
# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
/* Cannot set TCP_NODELAY here as it would push data NOW.
|
||||
* Set TCP_NODELAY after the send(), together if uncorking if necessary. */
|
||||
# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
# endif /* mhd_NODELAY_SET_PUSH_DATA */
|
||||
# endif /* ! mhd_CORK_RESET_PUSH_DATA */
|
||||
#else /* ! mhd_TCP_CORK_NOPUSH */
|
||||
/* Buffering of data is controlled only by
|
||||
* Nagel's algorithm. */
|
||||
/* Set TCP_NODELAY if it wasn't set. */
|
||||
if (_MHD_ON != connection->sk_nodelay)
|
||||
MHD_connection_set_nodelay_state_ (connection, true);
|
||||
#endif /* ! MHD_TCP_CORK_NOPUSH */
|
||||
if (mhd_T_IS_NOT_YES(connection->sk_nodelay))
|
||||
mhd_connection_set_nodelay_state (connection, true);
|
||||
#endif /* ! mhd_TCP_CORK_NOPUSH */
|
||||
}
|
||||
|
||||
|
||||
#ifndef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
#ifndef mhd_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
/**
|
||||
* Send zero-sized data
|
||||
*
|
||||
@@ -546,27 +543,24 @@ pre_send_setopt (struct MHD_Connection *connection,
|
||||
* @return true if succeed, false if failed
|
||||
*/
|
||||
static bool
|
||||
zero_send_ (struct MHD_Connection *connection)
|
||||
zero_send (struct MHD_Connection *connection)
|
||||
{
|
||||
int dummy;
|
||||
static const int dummy;
|
||||
|
||||
if (_MHD_YES == connection->is_nonip)
|
||||
if (mhd_T_IS_YES(connection->is_nonip))
|
||||
return false;
|
||||
mhd_assert (_MHD_OFF == connection->sk_corked);
|
||||
mhd_assert (_MHD_ON == connection->sk_nodelay);
|
||||
mhd_assert (mhd_T_IS_NO(connection->sk_corked));
|
||||
mhd_assert (mhd_T_IS_YES(connection->sk_nodelay));
|
||||
dummy = 0; /* Mute compiler and analyzer warnings */
|
||||
if (0 == MHD_send_ (connection->socket_fd, &dummy, 0))
|
||||
if (0 == mhd_sys_send (connection->socket_fd, &dummy, 0))
|
||||
return true;
|
||||
#ifdef HAVE_MESSAGES
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Zero-send failed: %s\n"),
|
||||
MHD_socket_last_strerr_ () );
|
||||
#endif /* HAVE_MESSAGES */
|
||||
MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \
|
||||
"Failed to push the data by zero-sized send.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
#endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
|
||||
/**
|
||||
* Handle post-send setsockopt calls.
|
||||
@@ -587,48 +581,53 @@ post_send_setopt (struct MHD_Connection *connection,
|
||||
* Final piece is indicated by push_data == true. */
|
||||
const bool buffer_data = (! push_data);
|
||||
|
||||
if (_MHD_YES == connection->is_nonip)
|
||||
if (mhd_T_IS_YES(connection->is_nonip))
|
||||
return;
|
||||
if (buffer_data)
|
||||
return; /* Nothing to do after send(). */
|
||||
return; /* Nothing to do after the send(). */
|
||||
|
||||
#ifndef MHD_USE_MSG_MORE
|
||||
#ifndef mhd_USE_MSG_MORE
|
||||
(void) plain_send_next; /* Mute compiler warning */
|
||||
#endif /* ! MHD_USE_MSG_MORE */
|
||||
#endif /* ! mhd_USE_MSG_MORE */
|
||||
|
||||
/* Need to push data. */
|
||||
#ifdef MHD_TCP_CORK_NOPUSH
|
||||
#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
|
||||
#ifdef MHD_USE_MSG_MORE
|
||||
if (_MHD_OFF == connection->sk_corked)
|
||||
{
|
||||
if (_MHD_ON == connection->sk_nodelay)
|
||||
return; /* Data was already pushed by send(). */
|
||||
}
|
||||
/* This is Linux kernel. There are options:
|
||||
#ifdef mhd_TCP_CORK_NOPUSH
|
||||
if (mhd_T_IS_YES(connection->sk_nodelay) && \
|
||||
mhd_T_IS_NO(connection->sk_corked))
|
||||
return; /* Data has been already pushed by last send(). */
|
||||
|
||||
# ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS
|
||||
# ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS
|
||||
# ifdef mhd_USE_MSG_MORE
|
||||
/* This is Linux kernel.
|
||||
* The socket is corked (or unknown) or 'no delay' is not set (or unknown).
|
||||
* There are options:
|
||||
* * Push the data by setting of TCP_NODELAY (without change
|
||||
* of the cork on the socket),
|
||||
* * Push the data by resetting of TCP_CORK.
|
||||
* The optimal choice depends on the next final send functions
|
||||
* used on the same socket. If TCP_NODELAY wasn't set then push
|
||||
* data by setting TCP_NODELAY (TCP_NODELAY will not be removed
|
||||
* and is needed to push the data by send() without MSG_MORE).
|
||||
* used on the same socket.
|
||||
*
|
||||
* In general on Linux kernel TCP_NODELAY always enabled is preferred,
|
||||
* as buffering is controlled by MSG_MORE or cork/uncork.
|
||||
*
|
||||
* If next send function will not support MSG_MORE (like sendfile()
|
||||
* or TLS-connection) than push data by setting TCP_NODELAY
|
||||
* so the socket may remain corked (no additional sys-call before
|
||||
* next send()).
|
||||
*
|
||||
* If send()/sendmsg() will be used next than push data by
|
||||
* resetting of TCP_CORK so next send without MSG_MORE will push
|
||||
* data to the network (without additional sys-call to push data).
|
||||
* If next final send function will not support MSG_MORE (like
|
||||
* sendfile() or TLS-connection) than push data by setting
|
||||
* TCP_NODELAY so socket will remain corked (no additional
|
||||
* sys-call before next send()). */
|
||||
if ((_MHD_ON != connection->sk_nodelay) ||
|
||||
* resetting of TCP_CORK so next final send without MSG_MORE will push
|
||||
* data to the network (without additional sys-call to push data). */
|
||||
|
||||
if (mhd_T_IS_NOT_YES(connection->sk_nodelay) ||
|
||||
(! plain_send_next))
|
||||
{
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
return; /* Data has been pushed by TCP_NODELAY. */
|
||||
/* Failed to set TCP_NODELAY for the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
if (MHD_connection_set_cork_state_ (connection, false))
|
||||
if (mhd_connection_set_cork_state (connection, false))
|
||||
return; /* Data has been pushed by uncorking the socket. */
|
||||
/* Failed to uncork the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
@@ -637,53 +636,53 @@ post_send_setopt (struct MHD_Connection *connection,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MHD_connection_set_cork_state_ (connection, false))
|
||||
if (mhd_connection_set_cork_state (connection, false))
|
||||
return; /* Data has been pushed by uncorking the socket. */
|
||||
/* Failed to uncork the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
return; /* Data has been pushed by TCP_NODELAY. */
|
||||
/* Failed to set TCP_NODELAY for the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
|
||||
/* The socket cannot be uncorked, no way to push data */
|
||||
}
|
||||
#else /* ! MHD_USE_MSG_MORE */
|
||||
/* Use setting of TCP_NODELAY here to avoid sys-call
|
||||
* for corking the socket during sending of the next response. */
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
# else /* ! mhd_USE_MSG_MORE */
|
||||
/* Push data by setting TCP_NODELAY here as uncorking here
|
||||
* would require corking the socket before sending the next response. */
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
return; /* Data was pushed by TCP_NODELAY. */
|
||||
/* Failed to set TCP_NODELAY for the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
if (MHD_connection_set_cork_state_ (connection, false))
|
||||
if (mhd_connection_set_cork_state (connection, false))
|
||||
return; /* Data was pushed by uncorking the socket. */
|
||||
/* Failed to uncork the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
|
||||
/* The socket remains corked, no way to push data */
|
||||
#endif /* ! MHD_USE_MSG_MORE */
|
||||
#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
if (MHD_connection_set_cork_state_ (connection, false))
|
||||
# endif /* ! mhd_USE_MSG_MORE */
|
||||
# else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
if (mhd_connection_set_cork_state (connection, false))
|
||||
return; /* Data was pushed by uncorking the socket. */
|
||||
/* Failed to uncork the socket.
|
||||
* Really unlikely to happen on TCP connections. */
|
||||
return; /* Socket remains corked, no way to push data */
|
||||
#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
/* This is a typical *BSD or Darwin kernel. */
|
||||
|
||||
if (_MHD_OFF == connection->sk_corked)
|
||||
/* Socket remains corked, no way to push data */
|
||||
# endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */
|
||||
# else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
/* This is old FreeBSD or Darwin kernel. */
|
||||
|
||||
if (mhd_T_IS_NO(connection->sk_corked))
|
||||
{
|
||||
if (_MHD_ON == connection->sk_nodelay)
|
||||
return; /* Data was already pushed by send(). */
|
||||
mhd_assert(mhd_T_IS_NOT_YES(connection->sk_nodelay));
|
||||
|
||||
/* Unlikely to reach this code.
|
||||
* TCP_NODELAY should be turned on before send(). */
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
{
|
||||
/* TCP_NODELAY has been set on uncorked socket.
|
||||
* Use zero-send to push the data. */
|
||||
if (zero_send_ (connection))
|
||||
if (zero_send (connection))
|
||||
return; /* The data has been pushed by zero-send. */
|
||||
}
|
||||
|
||||
@@ -692,234 +691,173 @@ post_send_setopt (struct MHD_Connection *connection,
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _MHD_CORK_RESET_PUSH_DATA
|
||||
enum MHD_tristate old_cork_state = connection->sk_corked;
|
||||
#endif /* _MHD_CORK_RESET_PUSH_DATA */
|
||||
#ifdef mhd_CORK_RESET_PUSH_DATA
|
||||
enum mhd_Tristate old_cork_state = connection->sk_corked;
|
||||
#endif /* mhd_CORK_RESET_PUSH_DATA */
|
||||
/* The socket is corked or cork state is unknown. */
|
||||
|
||||
if (MHD_connection_set_cork_state_ (connection, false))
|
||||
if (mhd_connection_set_cork_state (connection, false))
|
||||
{
|
||||
#ifdef _MHD_CORK_RESET_PUSH_DATA
|
||||
/* FreeBSD kernel */
|
||||
if (_MHD_OFF == old_cork_state)
|
||||
#ifdef mhd_CORK_RESET_PUSH_DATA
|
||||
/* Modern FreeBSD or OpenBSD kernel */
|
||||
if (mhd_T_IS_YES(old_cork_state))
|
||||
return; /* Data has been pushed by uncorking the socket. */
|
||||
#endif /* _MHD_CORK_RESET_PUSH_DATA */
|
||||
#endif /* mhd_CORK_RESET_PUSH_DATA */
|
||||
|
||||
/* Unlikely to reach this code.
|
||||
* The data should be pushed by uncorking (FreeBSD) or
|
||||
* the socket should be uncorked before send(). */
|
||||
if ((_MHD_ON == connection->sk_nodelay) ||
|
||||
(MHD_connection_set_nodelay_state_ (connection, true)))
|
||||
if (mhd_T_IS_YES(connection->sk_nodelay) ||
|
||||
(mhd_connection_set_nodelay_state (connection, true)))
|
||||
{
|
||||
/* TCP_NODELAY is turned ON on uncorked socket.
|
||||
* Use zero-send to push the data. */
|
||||
if (zero_send_ (connection))
|
||||
if (zero_send (connection))
|
||||
return; /* The data has been pushed by zero-send. */
|
||||
}
|
||||
}
|
||||
/* The socket remains corked. Data cannot be pushed. */
|
||||
/* Data cannot be pushed. */
|
||||
}
|
||||
#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
#else /* ! MHD_TCP_CORK_NOPUSH */
|
||||
#endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */
|
||||
#else /* ! mhd_TCP_CORK_NOPUSH */
|
||||
/* Corking is not supported. Buffering is controlled
|
||||
* by TCP_NODELAY only. */
|
||||
mhd_assert (_MHD_ON != connection->sk_corked);
|
||||
if (_MHD_ON == connection->sk_nodelay)
|
||||
mhd_assert (mhd_T_IS_NOT_YES(connection->sk_corked));
|
||||
if (mhd_T_IS_YES(connection->sk_nodelay))
|
||||
return; /* Data was already pushed by send(). */
|
||||
|
||||
/* Unlikely to reach this code.
|
||||
* TCP_NODELAY should be turned on before send(). */
|
||||
if (MHD_connection_set_nodelay_state_ (connection, true))
|
||||
if (mhd_connection_set_nodelay_state (connection, true))
|
||||
{
|
||||
/* TCP_NODELAY has been set.
|
||||
* Use zero-send to push the data. */
|
||||
if (zero_send_ (connection))
|
||||
* Use zero-send to try to push the data. */
|
||||
if (zero_send (connection))
|
||||
return; /* The data has been pushed by zero-send. */
|
||||
}
|
||||
|
||||
/* Failed to push the data. */
|
||||
#endif /* ! MHD_TCP_CORK_NOPUSH */
|
||||
#ifdef HAVE_MESSAGES
|
||||
MHD_DLOG (connection->daemon,
|
||||
_ ("Failed to push the data from buffers to the network. "
|
||||
"Client may experience some delay "
|
||||
"(usually in range 200ms - 5 sec).\n"));
|
||||
#endif /* HAVE_MESSAGES */
|
||||
#endif /* ! mhd_TCP_CORK_NOPUSH */
|
||||
MHD_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \
|
||||
"Failed to force flush the last part of the response header " \
|
||||
"or the response content that might have been buffered by " \
|
||||
"the kernel. The client may experience some delay (usually " \
|
||||
"in range 200ms - 5 sec).");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
MHD_send_data_ (struct MHD_Connection *connection,
|
||||
const char *buffer,
|
||||
size_t buffer_size,
|
||||
bool push_data)
|
||||
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2)
|
||||
MHD_FN_PAR_OUT_ (5) enum mhd_SocketError
|
||||
mhd_plain_send (struct MHD_Connection *restrict c,
|
||||
size_t buf_size,
|
||||
const char buf[MHD_FN_PAR_DYN_ARR_SIZE_(buf_size)],
|
||||
bool push_data,
|
||||
size_t *restrict sent)
|
||||
{
|
||||
MHD_socket s = connection->socket_fd;
|
||||
ssize_t ret;
|
||||
#ifdef HTTPS_SUPPORT
|
||||
const bool tls_conn = (connection->daemon->options & MHD_USE_TLS);
|
||||
#else /* ! HTTPS_SUPPORT */
|
||||
const bool tls_conn = false;
|
||||
#endif /* ! HTTPS_SUPPORT */
|
||||
/* plaintext transmission */
|
||||
ssize_t res;
|
||||
bool full_buf_sent;
|
||||
|
||||
if ( (MHD_INVALID_SOCKET == s) ||
|
||||
(MHD_CONNECTION_CLOSED == connection->state) )
|
||||
if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
|
||||
{
|
||||
return MHD_ERR_NOTCONN_;
|
||||
}
|
||||
|
||||
if (buffer_size > SSIZE_MAX)
|
||||
{
|
||||
buffer_size = SSIZE_MAX; /* Max return value */
|
||||
buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
|
||||
push_data = false; /* Incomplete send */
|
||||
}
|
||||
|
||||
pre_send_setopt (c, true, push_data);
|
||||
#ifdef mhd_USE_MSG_MORE
|
||||
res = mhd_sys_send4 (s,
|
||||
buffer,
|
||||
buffer_size,
|
||||
push_data ? 0 : MSG_MORE);
|
||||
#else
|
||||
res = mhd_sys_send4 (c,
|
||||
buf,
|
||||
buf_size,
|
||||
0);
|
||||
#endif
|
||||
|
||||
if (0 >= res)
|
||||
{
|
||||
enum mhd_SocketError err;
|
||||
|
||||
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 'send-ready' */
|
||||
(((unsigned int) c->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
|
||||
return err;
|
||||
}
|
||||
*sent = (size_t) res;
|
||||
|
||||
full_buf_sent = (buf_size == (size_t) res);
|
||||
|
||||
if (! full_buf_sent)
|
||||
c->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
|
||||
(((unsigned int) c->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
|
||||
/* If there is a need to push the data from network buffers
|
||||
* call post_send_setopt(). */
|
||||
/* It's unknown whether sendfile() (or other send function without
|
||||
* MSG_MORE support) will be used for the next reply so assume
|
||||
* that next sending will be the same, like this call. */
|
||||
if ( push_data && full_buf_sent)
|
||||
post_send_setopt (c, false, push_data);
|
||||
|
||||
return mhd_SOCKET_ERR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2)
|
||||
MHD_FN_PAR_OUT_ (5) 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_Socket s = connection->socket_fd;
|
||||
|
||||
enum mhd_SocketError ret;
|
||||
const bool tls_conn = false; // TODO: TLS support
|
||||
|
||||
mhd_assert (MHD_INVALID_SOCKET != s);
|
||||
mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
|
||||
|
||||
if (tls_conn)
|
||||
{
|
||||
#ifdef HTTPS_SUPPORT
|
||||
pre_send_setopt (connection, (! tls_conn), push_data);
|
||||
ret = gnutls_record_send (connection->tls_session,
|
||||
buffer,
|
||||
buffer_size);
|
||||
if (GNUTLS_E_AGAIN == ret)
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
if (GNUTLS_E_INTERRUPTED == ret)
|
||||
return MHD_ERR_AGAIN_;
|
||||
if ( (GNUTLS_E_ENCRYPTION_FAILED == ret) ||
|
||||
(GNUTLS_E_INVALID_SESSION == ret) ||
|
||||
(GNUTLS_E_COMPRESSION_FAILED == ret) ||
|
||||
(GNUTLS_E_EXPIRED == ret) ||
|
||||
(GNUTLS_E_HASH_FAILED == ret) )
|
||||
return MHD_ERR_TLS_;
|
||||
if ( (GNUTLS_E_PUSH_ERROR == ret) ||
|
||||
(GNUTLS_E_INTERNAL_ERROR == ret) ||
|
||||
(GNUTLS_E_CRYPTODEV_IOCTL_ERROR == ret) ||
|
||||
(GNUTLS_E_CRYPTODEV_DEVICE_ERROR == ret) )
|
||||
return MHD_ERR_PIPE_;
|
||||
#if defined(GNUTLS_E_PREMATURE_TERMINATION)
|
||||
if (GNUTLS_E_PREMATURE_TERMINATION == ret)
|
||||
return MHD_ERR_CONNRESET_;
|
||||
#elif defined(GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
|
||||
if (GNUTLS_E_UNEXPECTED_PACKET_LENGTH == ret)
|
||||
return MHD_ERR_CONNRESET_;
|
||||
#endif /* GNUTLS_E_UNEXPECTED_PACKET_LENGTH */
|
||||
if (GNUTLS_E_MEMORY_ERROR == ret)
|
||||
return MHD_ERR_NOMEM_;
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Treat any other error as hard error. */
|
||||
return MHD_ERR_NOTCONN_;
|
||||
}
|
||||
#ifdef EPOLL_SUPPORT
|
||||
/* Unlike non-TLS connections, do not reset "write-ready" if
|
||||
* sent amount smaller than provided amount, as TLS
|
||||
* connections may break data into smaller parts for sending. */
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
#else /* ! HTTPS_SUPPORT */
|
||||
ret = MHD_ERR_NOTCONN_;
|
||||
#endif /* ! HTTPS_SUPPORT */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* plaintext transmission */
|
||||
if (buffer_size > MHD_SCKT_SEND_MAX_SIZE_)
|
||||
{
|
||||
buffer_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */
|
||||
push_data = false; /* Incomplete send */
|
||||
}
|
||||
|
||||
pre_send_setopt (connection, (! tls_conn), push_data);
|
||||
#ifdef MHD_USE_MSG_MORE
|
||||
ret = MHD_send4_ (s,
|
||||
buffer,
|
||||
buffer_size,
|
||||
push_data ? 0 : MSG_MORE);
|
||||
#else
|
||||
ret = MHD_send4_ (s,
|
||||
buffer,
|
||||
buffer_size,
|
||||
0);
|
||||
#endif
|
||||
|
||||
if (0 > ret)
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
/* EAGAIN, no longer write-ready */
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
if (MHD_SCKT_ERR_IS_EINTR_ (err))
|
||||
return MHD_ERR_AGAIN_;
|
||||
if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
|
||||
return MHD_ERR_CONNRESET_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
|
||||
return MHD_ERR_PIPE_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EOPNOTSUPP_))
|
||||
return MHD_ERR_OPNOTSUPP_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_))
|
||||
return MHD_ERR_NOTCONN_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINVAL_))
|
||||
return MHD_ERR_INVAL_;
|
||||
if (MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err))
|
||||
return MHD_ERR_NOMEM_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
|
||||
return MHD_ERR_BADF_;
|
||||
/* Treat any other error as a hard error. */
|
||||
return MHD_ERR_NOTCONN_;
|
||||
}
|
||||
#ifdef EPOLL_SUPPORT
|
||||
else if (buffer_size > (size_t) ret)
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
mhd_assert (0 && "Not implemented yet");
|
||||
return mhd_SOCKET_ERR_OTHER;
|
||||
}
|
||||
|
||||
/* If there is a need to push the data from network buffers
|
||||
* call post_send_setopt(). */
|
||||
/* If TLS connection is used then next final send() will be
|
||||
* without MSG_MORE support. If non-TLS connection is used
|
||||
* it's unknown whether sendfile() will be used or not so
|
||||
* assume that next call will be the same, like this call. */
|
||||
if ( (push_data) &&
|
||||
(buffer_size == (size_t) ret) )
|
||||
post_send_setopt (connection, (! tls_conn), push_data);
|
||||
|
||||
return ret;
|
||||
return mhd_plain_send(connection, buf_size, buf, push_data, sent);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
const char *header,
|
||||
size_t header_size,
|
||||
bool never_push_hdr,
|
||||
const char *body,
|
||||
size_t body_size,
|
||||
bool complete_response)
|
||||
MHD_INTERNAL MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_NONNULL_(3)
|
||||
MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) 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,
|
||||
size_t body_size,
|
||||
const char *restrict body,
|
||||
bool complete_response,
|
||||
size_t *restrict sent)
|
||||
{
|
||||
ssize_t ret;
|
||||
mhd_iov_ret_type res;
|
||||
bool send_error;
|
||||
bool push_hdr;
|
||||
bool push_body;
|
||||
MHD_socket s = connection->socket_fd;
|
||||
#ifndef _WIN32
|
||||
#define _MHD_SEND_VEC_MAX MHD_SCKT_SEND_MAX_SIZE_
|
||||
#else /* ! _WIN32 */
|
||||
#define _MHD_SEND_VEC_MAX UINT32_MAX
|
||||
#endif /* ! _WIN32 */
|
||||
#ifdef MHD_VECT_SEND
|
||||
MHD_Socket s = connection->socket_fd;
|
||||
#ifdef mhd_USE_VECT_SEND
|
||||
#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
|
||||
struct iovec vector[2];
|
||||
#ifdef HAVE_SENDMSG
|
||||
@@ -934,25 +872,22 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
|
||||
no_vec = false;
|
||||
#ifdef HTTPS_SUPPORT
|
||||
no_vec = no_vec || (connection->daemon->options & MHD_USE_TLS);
|
||||
no_vec = no_vec || (false); // TODO: TLS support
|
||||
#endif /* HTTPS_SUPPORT */
|
||||
#if (! defined(HAVE_SENDMSG) || ! defined(MSG_NOSIGNAL) ) && \
|
||||
defined(MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE) && \
|
||||
defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED)
|
||||
defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \
|
||||
defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED)
|
||||
no_vec = no_vec || (! connection->daemon->sigpipe_blocked &&
|
||||
! connection->sk_spipe_suppress);
|
||||
#endif /* (!HAVE_SENDMSG || ! MSG_NOSIGNAL) &&
|
||||
MHD_SEND_SPIPE_SEND_SUPPRESS_POSSIBLE &&
|
||||
MHD_SEND_SPIPE_SUPPRESS_NEEDED */
|
||||
#endif /* MHD_VECT_SEND */
|
||||
mhd_SEND_SPIPE_SUPPRESS_POSSIBLE &&
|
||||
mhd_SEND_SPIPE_SUPPRESS_NEEDED */
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
mhd_assert ( (NULL != body) || (0 == body_size) );
|
||||
|
||||
if ( (MHD_INVALID_SOCKET == s) ||
|
||||
(MHD_CONNECTION_CLOSED == connection->state) )
|
||||
{
|
||||
return MHD_ERR_NOTCONN_;
|
||||
}
|
||||
mhd_assert (MHD_INVALID_SOCKET != s);
|
||||
mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
|
||||
|
||||
push_body = complete_response;
|
||||
|
||||
@@ -965,7 +900,7 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
else
|
||||
{
|
||||
if (1400 > (header_size + body_size))
|
||||
push_hdr = false; /* Do not push the header as complete
|
||||
push_hdr = false; /* Do not push the header as complete
|
||||
* reply is already ready and the whole
|
||||
* reply most probably will fit into
|
||||
* the single IP packet. */
|
||||
@@ -980,76 +915,70 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
if (complete_response && (0 == body_size))
|
||||
push_hdr = true; /* The header alone is equal to the whole response. */
|
||||
|
||||
if (
|
||||
#ifdef MHD_VECT_SEND
|
||||
(no_vec) ||
|
||||
(0 == body_size) ||
|
||||
((size_t) SSIZE_MAX <= header_size) ||
|
||||
((size_t) _MHD_SEND_VEC_MAX < header_size)
|
||||
#ifdef _WIN32
|
||||
|| ((size_t) UINT_MAX < header_size)
|
||||
#endif /* _WIN32 */
|
||||
#else /* ! MHD_VECT_SEND */
|
||||
true
|
||||
#endif /* ! MHD_VECT_SEND */
|
||||
)
|
||||
{
|
||||
ret = MHD_send_data_ (connection,
|
||||
header,
|
||||
header_size,
|
||||
push_hdr);
|
||||
#ifndef mhd_USE_VECT_SEND
|
||||
no_vec = (no_vec || true);
|
||||
#else /* mhd_USE_VECT_SEND */
|
||||
no_vec = (no_vec || (0 == body_size));
|
||||
no_vec = (no_vec || ((sizeof(mhd_iov_elmn_size) <= sizeof(size_t)) &&
|
||||
(((size_t) mhd_IOV_ELMN_MAX_SIZE) < header_size)));
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
if ( (header_size == (size_t) ret) &&
|
||||
((size_t) SSIZE_MAX > header_size) &&
|
||||
(0 != body_size) &&
|
||||
(connection->sk_nonblck) )
|
||||
|
||||
|
||||
if (no_vec)
|
||||
{
|
||||
enum mhd_SocketError ret;
|
||||
ret = mhd_send_data (connection,
|
||||
header,
|
||||
header_size,
|
||||
push_hdr,
|
||||
sent);
|
||||
|
||||
// TODO: check 'send-ready'
|
||||
if ((mhd_SOCKET_ERR_NO_ERROR == ret) &&
|
||||
(header_size == *sent) &&
|
||||
(0 != body_size) &&
|
||||
(header_size < header_size + body_size) &&
|
||||
(connection->sk_nonblck))
|
||||
{
|
||||
ssize_t ret2;
|
||||
size_t sent_b;
|
||||
/* The header has been sent completely.
|
||||
* Try to send the reply body without waiting for
|
||||
* the next round. */
|
||||
/* Make sure that sum of ret + ret2 will not exceed SSIZE_MAX as
|
||||
* function needs to return positive value if succeed. */
|
||||
if ( (((size_t) SSIZE_MAX) - ((size_t) ret)) < body_size)
|
||||
{
|
||||
body_size = (((size_t) SSIZE_MAX) - ((size_t) ret));
|
||||
complete_response = false;
|
||||
push_body = complete_response;
|
||||
}
|
||||
|
||||
ret2 = MHD_send_data_ (connection,
|
||||
body,
|
||||
body_size,
|
||||
push_body);
|
||||
if (0 < ret2)
|
||||
return ret + ret2; /* Total data sent */
|
||||
if (MHD_ERR_AGAIN_ == ret2)
|
||||
return ret;
|
||||
ret = mhd_send_data (connection,
|
||||
body,
|
||||
body_size,
|
||||
push_body,
|
||||
&sent_b);
|
||||
|
||||
return ret2; /* Error code */
|
||||
if (mhd_SOCKET_ERR_NO_ERROR == ret)
|
||||
*sent += sent_b;
|
||||
else if (mhd_SOCKET_ERR_IS_HARD(ret))
|
||||
return ret; /* Unrecoverable error */
|
||||
|
||||
return mhd_SOCKET_ERR_NO_ERROR; /* The header has been sent successfully */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#ifdef MHD_VECT_SEND
|
||||
#ifdef mhd_USE_VECT_SEND
|
||||
|
||||
if ( ((size_t) SSIZE_MAX <= body_size) ||
|
||||
((size_t) SSIZE_MAX < (header_size + body_size)) )
|
||||
if (header_size > (header_size + body_size))
|
||||
{
|
||||
/* Return value limit */
|
||||
body_size = SSIZE_MAX - header_size;
|
||||
body_size = SIZE_MAX - header_size;
|
||||
complete_response = false;
|
||||
push_body = complete_response;
|
||||
}
|
||||
#if (SSIZE_MAX != _MHD_SEND_VEC_MAX) || (_MHD_SEND_VEC_MAX + 0 == 0)
|
||||
if (((size_t) _MHD_SEND_VEC_MAX <= body_size) ||
|
||||
((size_t) _MHD_SEND_VEC_MAX < (header_size + body_size)))
|
||||
if (((mhd_iov_ret_type) (header_size + body_size)) < 0 ||
|
||||
((size_t)(mhd_iov_ret_type) (header_size + body_size)) !=
|
||||
(header_size + body_size))
|
||||
{
|
||||
/* Send total amount limit */
|
||||
body_size = _MHD_SEND_VEC_MAX - header_size;
|
||||
/* Send sys-call total amount limit */
|
||||
body_size = mhd_IOV_RET_MAX_SIZE - header_size;
|
||||
complete_response = false;
|
||||
push_body = complete_response;
|
||||
}
|
||||
#endif /* SSIZE_MAX != _MHD_SEND_VEC_MAX */
|
||||
|
||||
pre_send_setopt (connection,
|
||||
#ifdef HAVE_SENDMSG
|
||||
@@ -1058,84 +987,75 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
false,
|
||||
#endif /* ! HAVE_SENDMSG */
|
||||
push_hdr || push_body);
|
||||
send_error = false;
|
||||
#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
|
||||
vector[0].iov_base = _MHD_DROP_CONST (header);
|
||||
vector[0].iov_base = mhd_DROP_CONST (header);
|
||||
vector[0].iov_len = header_size;
|
||||
vector[1].iov_base = _MHD_DROP_CONST (body);
|
||||
vector[1].iov_base = mhd_DROP_CONST (body);
|
||||
vector[1].iov_len = body_size;
|
||||
|
||||
#if defined(HAVE_SENDMSG)
|
||||
memset (&msg, 0, sizeof(msg));
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = vector;
|
||||
msg.msg_iovlen = 2;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
ret = sendmsg (s, &msg, MSG_NOSIGNAL_OR_ZERO);
|
||||
res = sendmsg (s, &msg, mhd_MSG_NOSIGNAL |
|
||||
((push_hdr || push_body) ? 0 : mhd_MSG_MORE));
|
||||
#elif defined(HAVE_WRITEV)
|
||||
ret = writev (s, vector, 2);
|
||||
res = writev (s, vector, 2);
|
||||
#endif /* HAVE_WRITEV */
|
||||
if (0 < res)
|
||||
*sent = (size_t) res;
|
||||
else
|
||||
send_error = true;
|
||||
#endif /* HAVE_SENDMSG || HAVE_WRITEV */
|
||||
#ifdef _WIN32
|
||||
if ((size_t) UINT_MAX < body_size)
|
||||
if (((mhd_iov_elmn_size) body_size) != body_size)
|
||||
{
|
||||
/* Send item size limit */
|
||||
body_size = UINT_MAX;
|
||||
body_size = mhd_IOV_ELMN_MAX_SIZE;
|
||||
complete_response = false;
|
||||
push_body = complete_response;
|
||||
}
|
||||
vector[0].buf = (char *) _MHD_DROP_CONST (header);
|
||||
vector[0].buf = (char *) mhd_DROP_CONST (header);
|
||||
vector[0].len = (unsigned long) header_size;
|
||||
vector[1].buf = (char *) _MHD_DROP_CONST (body);
|
||||
vector[1].buf = (char *) mhd_DROP_CONST (body);
|
||||
vector[1].len = (unsigned long) body_size;
|
||||
|
||||
ret = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
|
||||
if (0 == ret)
|
||||
ret = (ssize_t) vec_sent;
|
||||
res = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL);
|
||||
if (0 == res)
|
||||
*sent = (size_t) vec_sent;
|
||||
else
|
||||
ret = -1;
|
||||
send_error = true;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
if (0 > ret)
|
||||
if (send_error)
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
enum mhd_SocketError err;
|
||||
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
/* EAGAIN, no longer write-ready */
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
if (MHD_SCKT_ERR_IS_EINTR_ (err))
|
||||
return MHD_ERR_AGAIN_;
|
||||
if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
|
||||
return MHD_ERR_CONNRESET_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
|
||||
return MHD_ERR_PIPE_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EOPNOTSUPP_))
|
||||
return MHD_ERR_OPNOTSUPP_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_))
|
||||
return MHD_ERR_NOTCONN_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINVAL_))
|
||||
return MHD_ERR_INVAL_;
|
||||
if (MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err))
|
||||
return MHD_ERR_NOMEM_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
|
||||
return MHD_ERR_BADF_;
|
||||
/* Treat any other error as a hard error. */
|
||||
return MHD_ERR_NOTCONN_;
|
||||
err = mhd_socket_error_get_from_sys_err(mhd_SCKT_GET_LERR ());
|
||||
|
||||
if (mhd_SOCKET_ERR_AGAIN == err)
|
||||
connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
|
||||
(((unsigned int) connection->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
|
||||
return err;
|
||||
}
|
||||
#ifdef EPOLL_SUPPORT
|
||||
else if ((header_size + body_size) > (size_t) ret)
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
if ((header_size + body_size) > *sent)
|
||||
connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
|
||||
(((unsigned int) connection->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
|
||||
/* If there is a need to push the data from network buffers
|
||||
* call post_send_setopt(). */
|
||||
if ( (push_body) &&
|
||||
((header_size + body_size) == (size_t) ret) )
|
||||
((header_size + body_size) == *sent) )
|
||||
{
|
||||
/* Complete reply has been sent. */
|
||||
/* If TLS connection is used then next final send() will be
|
||||
@@ -1145,39 +1065,37 @@ MHD_send_hdr_and_body_ (struct MHD_Connection *connection,
|
||||
* the same, like for this response. */
|
||||
post_send_setopt (connection,
|
||||
#ifdef HAVE_SENDMSG
|
||||
true,
|
||||
true, /* Assume the same type of the send function */
|
||||
#else /* ! HAVE_SENDMSG */
|
||||
false,
|
||||
false, /* Assume the same type of the send function */
|
||||
#endif /* ! HAVE_SENDMSG */
|
||||
true);
|
||||
}
|
||||
else if ( (push_hdr) &&
|
||||
(header_size <= (size_t) ret))
|
||||
(header_size <= *sent))
|
||||
{
|
||||
/* The header has been sent completely and there is a
|
||||
* need to push the header data. */
|
||||
/* Luckily the type of send function will be used next is known. */
|
||||
post_send_setopt (connection,
|
||||
#if defined(_MHD_HAVE_SENDFILE)
|
||||
MHD_resp_sender_std == connection->rp.resp_sender,
|
||||
#else /* ! _MHD_HAVE_SENDFILE */
|
||||
true,
|
||||
#endif /* ! _MHD_HAVE_SENDFILE */
|
||||
true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
#else /* ! MHD_VECT_SEND */
|
||||
mhd_assert (false);
|
||||
return MHD_ERR_CONNRESET_; /* Unreachable. Mute warnings. */
|
||||
#endif /* ! MHD_VECT_SEND */
|
||||
return mhd_SOCKET_ERR_NO_ERROR;
|
||||
#else /* ! mhd_USE_VECT_SEND */
|
||||
mhd_assert (0 && "Should be unreachable");
|
||||
return mhd_SOCKET_ERR_INTERNAL; /* Unreachable. Mute warnings. */
|
||||
#endif /* ! mhd_USE_VECT_SEND */
|
||||
}
|
||||
|
||||
|
||||
#if defined(_MHD_HAVE_SENDFILE)
|
||||
ssize_t
|
||||
MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
#if defined(MHD_USE_SENDFILE) // TODO: adapt, update
|
||||
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)
|
||||
{
|
||||
#ifdef 0
|
||||
ssize_t ret;
|
||||
const int file_fd = connection->rp.response->fd;
|
||||
uint64_t left;
|
||||
@@ -1203,23 +1121,23 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
#endif /* HAVE_DARWIN_SENDFILE */
|
||||
const bool used_thr_p_c =
|
||||
MHD_D_IS_USING_THREAD_PER_CONN_ (connection->daemon);
|
||||
const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ :
|
||||
MHD_SENFILE_CHUNK_;
|
||||
const size_t chunk_size = used_thr_p_c ? mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C :
|
||||
mhd_SENFILE_CHUNK_SIZE;
|
||||
size_t send_size = 0;
|
||||
bool push_data;
|
||||
mhd_assert (MHD_resp_sender_sendfile == connection->rp.resp_sender);
|
||||
mhd_assert (mhd_resp_sender_sendfile == connection->rp.resp_sender);
|
||||
mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
|
||||
|
||||
offsetu64 = connection->rp.rsp_write_position
|
||||
offsetu64 = connection->rp.rsp_cntn_read_pos
|
||||
+ connection->rp.response->fd_off;
|
||||
if (max_off_t < offsetu64)
|
||||
{ /* Retry to send with standard 'send()'. */
|
||||
connection->rp.resp_sender = MHD_resp_sender_std;
|
||||
connection->rp.resp_sender = mhd_resp_sender_std;
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
|
||||
left = connection->rp.response->total_size
|
||||
- connection->rp.rsp_write_position;
|
||||
- connection->rp.rsp_cntn_read_pos;
|
||||
|
||||
if ( (uint64_t) SSIZE_MAX < left)
|
||||
left = SSIZE_MAX;
|
||||
@@ -1254,7 +1172,7 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
#endif /* HAVE_SENDFILE64 */
|
||||
if (0 > ret)
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
const int err = mhd_socket_get_error ();
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
@@ -1275,14 +1193,14 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
to fall back to 'SEND'; see also this thread for info on
|
||||
odd libc/Linux behavior with sendfile:
|
||||
http://lists.gnu.org/archive/html/libmicrohttpd/2011-02/msg00015.html */
|
||||
connection->rp.resp_sender = MHD_resp_sender_std;
|
||||
connection->rp.resp_sender = mhd_resp_sender_std;
|
||||
return MHD_ERR_AGAIN_;
|
||||
#else /* HAVE_SOLARIS_SENDFILE */
|
||||
if ( (EAFNOSUPPORT == err) ||
|
||||
(EINVAL == err) ||
|
||||
(EOPNOTSUPP == err) )
|
||||
{ /* Retry with standard file reader. */
|
||||
connection->rp.resp_sender = MHD_resp_sender_std;
|
||||
connection->rp.resp_sender = mhd_resp_sender_std;
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
if ( (ENOTCONN == err) ||
|
||||
@@ -1311,7 +1229,7 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
&sent_bytes,
|
||||
flags))
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
const int err = mhd_socket_get_error ();
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
|
||||
MHD_SCKT_ERR_IS_EINTR_ (err) ||
|
||||
(EBUSY == err) )
|
||||
@@ -1324,7 +1242,7 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
}
|
||||
/* Some unrecoverable error. Possibly file FD is not suitable
|
||||
* for sendfile(). Retry with standard send(). */
|
||||
connection->rp.resp_sender = MHD_resp_sender_std;
|
||||
connection->rp.resp_sender = mhd_resp_sender_std;
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
mhd_assert (0 < sent_bytes);
|
||||
@@ -1339,7 +1257,7 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
NULL,
|
||||
0))
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
const int err = mhd_socket_get_error ();
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err) ||
|
||||
MHD_SCKT_ERR_IS_EINTR_ (err))
|
||||
{
|
||||
@@ -1358,7 +1276,7 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
(EOPNOTSUPP == err) )
|
||||
{ /* This file FD is not suitable for sendfile().
|
||||
* Retry with standard send(). */
|
||||
connection->rp.resp_sender = MHD_resp_sender_std;
|
||||
connection->rp.resp_sender = mhd_resp_sender_std;
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
return MHD_ERR_BADF_; /* Return hard error. */
|
||||
@@ -1378,12 +1296,17 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
post_send_setopt (connection, false, push_data);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
(void) connection; (void) sent;
|
||||
mhd_assert (0 && "Not implemented yet");
|
||||
return mhd_SOCKET_ERR_INTERNAL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif /* _MHD_HAVE_SENDFILE */
|
||||
#endif /* MHD_USE_SENDFILE */
|
||||
|
||||
#if defined(MHD_VECT_SEND)
|
||||
#if defined(mhd_USE_VECT_SEND)
|
||||
|
||||
|
||||
/**
|
||||
@@ -1397,14 +1320,18 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
|
||||
* 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
|
||||
* @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
|
||||
*/
|
||||
static ssize_t
|
||||
send_iov_nontls (struct MHD_Connection *connection,
|
||||
struct MHD_iovec_track_ *const r_iov,
|
||||
bool push_data)
|
||||
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
|
||||
send_iov_nontls (struct MHD_Connection *restrict connection,
|
||||
struct mhd_iovec_track *const restrict r_iov,
|
||||
bool push_data,
|
||||
size_t *restrict sent)
|
||||
{
|
||||
ssize_t res;
|
||||
bool send_error;
|
||||
size_t items_to_send;
|
||||
#ifdef HAVE_SENDMSG
|
||||
struct msghdr msg;
|
||||
@@ -1413,46 +1340,53 @@ send_iov_nontls (struct MHD_Connection *connection,
|
||||
DWORD cnt_w;
|
||||
#endif /* MHD_WINSOCK_SOCKETS */
|
||||
|
||||
mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
|
||||
// TODO: assert for non-TLS
|
||||
|
||||
if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
|
||||
(MHD_CONNECTION_CLOSED == connection->state) )
|
||||
{
|
||||
return MHD_ERR_NOTCONN_;
|
||||
}
|
||||
mhd_assert (MHD_INVALID_SOCKET != connection->socket_fd);
|
||||
mhd_assert (MHD_CONNECTION_CLOSED != connection->state);
|
||||
|
||||
send_error = false;
|
||||
items_to_send = r_iov->cnt - r_iov->sent;
|
||||
#ifdef _MHD_IOV_MAX
|
||||
if (_MHD_IOV_MAX < items_to_send)
|
||||
#ifdef mhd_IOV_MAX
|
||||
if (mhd_IOV_MAX < items_to_send)
|
||||
{
|
||||
mhd_assert (0 < _MHD_IOV_MAX);
|
||||
if (0 == _MHD_IOV_MAX)
|
||||
return MHD_ERR_NOTCONN_; /* Should never happen */
|
||||
items_to_send = _MHD_IOV_MAX;
|
||||
mhd_assert (0 < mhd_IOV_MAX);
|
||||
if (0 == mhd_IOV_MAX)
|
||||
return mhd_SOCKET_ERR_INTERNAL; /* Should never happen */
|
||||
items_to_send = mhd_IOV_MAX;
|
||||
push_data = false; /* Incomplete response */
|
||||
}
|
||||
#endif /* _MHD_IOV_MAX */
|
||||
#endif /* mhd_IOV_MAX */
|
||||
#ifdef HAVE_SENDMSG
|
||||
memset (&msg, 0, sizeof(struct msghdr));
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = r_iov->iov + r_iov->sent;
|
||||
msg.msg_iovlen = items_to_send;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
pre_send_setopt (connection, true, push_data);
|
||||
#ifdef MHD_USE_MSG_MORE
|
||||
res = sendmsg (connection->socket_fd, &msg,
|
||||
MSG_NOSIGNAL_OR_ZERO | (push_data ? 0 : MSG_MORE));
|
||||
#else /* ! MHD_USE_MSG_MORE */
|
||||
res = sendmsg (connection->socket_fd, &msg, MSG_NOSIGNAL_OR_ZERO);
|
||||
#endif /* ! MHD_USE_MSG_MORE */
|
||||
MSG_NOSIGNAL_OR_ZERO | (push_data ? 0 : mhd_MSG_MORE));
|
||||
if (0 < res)
|
||||
*sent = (size_t) res;
|
||||
else
|
||||
send_error = true;
|
||||
#elif defined(HAVE_WRITEV)
|
||||
pre_send_setopt (connection, true, push_data);
|
||||
pre_send_setopt (connection, false, push_data);
|
||||
res = writev (connection->socket_fd, r_iov->iov + r_iov->sent,
|
||||
items_to_send);
|
||||
if (0 < res)
|
||||
*sent = (size_t) res;
|
||||
else
|
||||
send_error = true;
|
||||
#elif defined(MHD_WINSOCK_SOCKETS)
|
||||
#ifdef _WIN64
|
||||
if (items_to_send > UINT32_MAX)
|
||||
if (items_to_send > ULONG_MAX)
|
||||
{
|
||||
cnt_w = UINT32_MAX;
|
||||
cnt_w = ULONG_MAX;
|
||||
push_data = false; /* Incomplete response */
|
||||
}
|
||||
else
|
||||
@@ -1465,50 +1399,31 @@ send_iov_nontls (struct MHD_Connection *connection,
|
||||
(LPWSABUF) (r_iov->iov + r_iov->sent),
|
||||
cnt_w,
|
||||
&bytes_sent, 0, NULL, NULL))
|
||||
res = (ssize_t) bytes_sent;
|
||||
*sent = (size_t) bytes_sent;
|
||||
else
|
||||
res = -1;
|
||||
send_error = true;
|
||||
#else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_WINSOCK_SOCKETS */
|
||||
#error No vector-send function available
|
||||
#endif
|
||||
|
||||
if (0 > res)
|
||||
if (send_error)
|
||||
{
|
||||
const int err = MHD_socket_get_error_ ();
|
||||
enum mhd_SocketError err;
|
||||
|
||||
if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
/* EAGAIN --- no longer write-ready */
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
return MHD_ERR_AGAIN_;
|
||||
}
|
||||
if (MHD_SCKT_ERR_IS_EINTR_ (err))
|
||||
return MHD_ERR_AGAIN_;
|
||||
if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
|
||||
return MHD_ERR_CONNRESET_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
|
||||
return MHD_ERR_PIPE_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EOPNOTSUPP_))
|
||||
return MHD_ERR_OPNOTSUPP_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_))
|
||||
return MHD_ERR_NOTCONN_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINVAL_))
|
||||
return MHD_ERR_INVAL_;
|
||||
if (MHD_SCKT_ERR_IS_LOW_RESOURCES_ (err))
|
||||
return MHD_ERR_NOMEM_;
|
||||
if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EBADF_))
|
||||
return MHD_ERR_BADF_;
|
||||
/* Treat any other error as a hard error. */
|
||||
return MHD_ERR_NOTCONN_;
|
||||
err = mhd_socket_error_get_from_sys_err(mhd_SCKT_GET_LERR ());
|
||||
|
||||
if (mhd_SOCKET_ERR_AGAIN == err)
|
||||
connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
|
||||
(((unsigned int) connection->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Some data has been sent */
|
||||
if (1)
|
||||
{
|
||||
size_t track_sent = (size_t) res;
|
||||
size_t track_sent = (size_t) *sent;
|
||||
/* Adjust the internal tracking information for the iovec to
|
||||
* take this last send into account. */
|
||||
while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent))
|
||||
@@ -1522,29 +1437,28 @@ send_iov_nontls (struct MHD_Connection *connection,
|
||||
post_send_setopt (connection, true, push_data);
|
||||
else
|
||||
{
|
||||
#ifdef EPOLL_SUPPORT
|
||||
connection->epoll_state &=
|
||||
~((enum MHD_EpollState) MHD_EPOLL_STATE_WRITE_READY);
|
||||
#endif /* EPOLL_SUPPORT */
|
||||
connection->sk_ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */
|
||||
(((unsigned int) connection->sk_ready)
|
||||
& (~mhd_SOCKET_NET_STATE_SEND_READY));
|
||||
if (0 != track_sent)
|
||||
{
|
||||
mhd_assert (r_iov->cnt > r_iov->sent);
|
||||
/* The last iov element has been partially sent */
|
||||
r_iov->iov[r_iov->sent].iov_base =
|
||||
(void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + track_sent);
|
||||
r_iov->iov[r_iov->sent].iov_len -= (MHD_iov_size_) track_sent;
|
||||
r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) track_sent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return mhd_SOCKET_ERR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#endif /* MHD_VECT_SEND */
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
|
||||
defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
|
||||
defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
|
||||
|
||||
/**
|
||||
@@ -1559,99 +1473,112 @@ send_iov_nontls (struct MHD_Connection *connection,
|
||||
* 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
|
||||
* @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
|
||||
*/
|
||||
static ssize_t
|
||||
send_iov_emu (struct MHD_Connection *connection,
|
||||
struct MHD_iovec_track_ *const r_iov,
|
||||
bool push_data)
|
||||
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4) enum mhd_SocketError
|
||||
send_iov_emu (struct MHD_Connection *restrict connection,
|
||||
struct mhd_iovec_track *const restrict r_iov,
|
||||
bool push_data,
|
||||
size_t *restrict sent)
|
||||
{
|
||||
const bool non_blk = connection->sk_nonblck;
|
||||
size_t total_sent;
|
||||
ssize_t res;
|
||||
size_t sent_elements;
|
||||
size_t max_elelements_to_sent;
|
||||
|
||||
mhd_assert (NULL != r_iov->iov);
|
||||
total_sent = 0;
|
||||
sent_elements = 0;
|
||||
max_elelements_to_sent = 8; /* Do not make too many sys-calls for just one connection */
|
||||
do
|
||||
{
|
||||
if ((size_t) SSIZE_MAX - total_sent < r_iov->iov[r_iov->sent].iov_len)
|
||||
return (ssize_t) total_sent; /* return value would overflow */
|
||||
enum mhd_SocketError res;
|
||||
size_t sent_el_size;
|
||||
|
||||
res = MHD_send_data_ (connection,
|
||||
if (total_sent > (size_t)(r_iov->iov[r_iov->sent].iov_len + total_sent))
|
||||
break; /* return value would overflow */
|
||||
|
||||
res = mhd_send_data (connection,
|
||||
r_iov->iov[r_iov->sent].iov_base,
|
||||
r_iov->iov[r_iov->sent].iov_len,
|
||||
push_data && (r_iov->cnt == r_iov->sent + 1));
|
||||
if (0 > res)
|
||||
push_data && (r_iov->cnt == r_iov->sent + 1),
|
||||
&sent_el_size);
|
||||
if (mhd_SOCKET_ERR_NO_ERROR == res)
|
||||
{
|
||||
/* Result is an error */
|
||||
if (0 == total_sent)
|
||||
return res; /* Nothing was sent, return result as is */
|
||||
return res; /* Nothing was sent, return error as is */
|
||||
|
||||
if (MHD_ERR_AGAIN_ == res)
|
||||
return (ssize_t) total_sent; /* Return the amount of the sent data */
|
||||
if (mhd_SOCKET_ERR_IS_HARD(res))
|
||||
return res; /* Any kind of a hard error */
|
||||
|
||||
return res; /* Any kind of a hard error */
|
||||
break; /* Return the amount of the sent data */
|
||||
}
|
||||
|
||||
total_sent += (size_t) res;
|
||||
total_sent += sent_el_size;
|
||||
|
||||
if (r_iov->iov[r_iov->sent].iov_len != (size_t) res)
|
||||
if (r_iov->iov[r_iov->sent].iov_len != sent_el_size)
|
||||
{
|
||||
const size_t sent = (size_t) res;
|
||||
/* Incomplete buffer has been sent.
|
||||
* Adjust buffer of the last element. */
|
||||
r_iov->iov[r_iov->sent].iov_base =
|
||||
(void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + sent);
|
||||
r_iov->iov[r_iov->sent].iov_len -= (MHD_iov_size_) sent;
|
||||
(void *) ((uint8_t *) r_iov->iov[r_iov->sent].iov_base + sent_el_size);
|
||||
r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) sent_el_size;
|
||||
|
||||
return (ssize_t) total_sent;
|
||||
break; /* Return the amount of the sent data */
|
||||
}
|
||||
/* The iov element has been completely sent */
|
||||
r_iov->sent++;
|
||||
} while ((r_iov->cnt > r_iov->sent) && (non_blk));
|
||||
} while ((r_iov->cnt > r_iov->sent) && 0 != (--max_elelements_to_sent) &&
|
||||
(non_blk));
|
||||
|
||||
return (ssize_t) total_sent;
|
||||
mhd_assert (0 != total_sent);
|
||||
*sent = total_sent;
|
||||
return mhd_SOCKET_ERR_NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
|
||||
|| _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
|
||||
|| mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
|
||||
|
||||
ssize_t
|
||||
MHD_send_iovec_ (struct MHD_Connection *connection,
|
||||
struct MHD_iovec_track_ *const r_iov,
|
||||
bool push_data)
|
||||
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_ (4) 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)
|
||||
{
|
||||
#ifdef MHD_VECT_SEND
|
||||
#ifdef mhd_USE_VECT_SEND
|
||||
#if defined(HTTPS_SUPPORT) || \
|
||||
defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
bool use_iov_send = true;
|
||||
#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* MHD_VECT_SEND */
|
||||
#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
mhd_assert (NULL != connection->rp.resp_iov.iov);
|
||||
mhd_assert (NULL != connection->rp.response->data_iov);
|
||||
mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == \
|
||||
connection->rp.response->cntn_dtype);
|
||||
mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent);
|
||||
#ifdef MHD_VECT_SEND
|
||||
#ifdef mhd_USE_VECT_SEND
|
||||
#if defined(HTTPS_SUPPORT) || \
|
||||
defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
#ifdef HTTPS_SUPPORT
|
||||
use_iov_send = use_iov_send &&
|
||||
(0 == (connection->daemon->options & MHD_USE_TLS));
|
||||
use_iov_send = use_iov_send &
|
||||
(true); // TODO: TLS support
|
||||
#endif /* HTTPS_SUPPORT */
|
||||
#ifdef _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
|
||||
#ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED
|
||||
use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked ||
|
||||
connection->sk_spipe_suppress);
|
||||
#endif /* _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
if (use_iov_send)
|
||||
#endif /* HTTPS_SUPPORT || _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* HTTPS_SUPPORT || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
return send_iov_nontls (connection, r_iov, push_data);
|
||||
#endif /* MHD_VECT_SEND */
|
||||
#endif /* mhd_USE_VECT_SEND */
|
||||
|
||||
#if ! defined(MHD_VECT_SEND) || defined(HTTPS_SUPPORT) || \
|
||||
defined(_MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
#if ! defined(mhd_USE_VECT_SEND) || defined(HTTPS_SUPPORT) || \
|
||||
defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED)
|
||||
return send_iov_emu (connection, r_iov, push_data);
|
||||
#endif /* !MHD_VECT_SEND || HTTPS_SUPPORT
|
||||
|| _MHD_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
#endif /* !mhd_USE_VECT_SEND || HTTPS_SUPPORT
|
||||
|| mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \
|
||||
defined(MHD_WINSOCK_SOCKETS)
|
||||
#define MHD_VECT_SEND 1
|
||||
#endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_WINSOCK_SOCKETS */
|
||||
#include "sys_base_types.h"
|
||||
#include "sys_bool_type.h"
|
||||
#include "mhd_socket_error.h"
|
||||
|
||||
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 */
|
||||
@@ -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;
|
||||
}
|
||||
+51
-14
@@ -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
|
||||
*/
|
||||
@@ -92,22 +112,16 @@ 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
|
||||
*/
|
||||
mhd_SOCKET_ERR_TLS
|
||||
,
|
||||
/**
|
||||
* Other socket error
|
||||
*/
|
||||
mhd_SOCKET_ERR_OTHER
|
||||
,
|
||||
/**
|
||||
* Internal (MHD) error
|
||||
* Not actually reported by the OS
|
||||
*/
|
||||
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 */
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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_INTERNAL MHD_FN_PAR_NONNULL_ (1) void
|
||||
mhd_response_deinit_content_data (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)
|
||||
{
|
||||
(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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
/**
|
||||
* 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_deinit_reusable (struct MHD_Response *restrict r)
|
||||
MHD_FN_PAR_NONNULL_ (1);
|
||||
|
||||
|
||||
/**
|
||||
* 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_check_frozen_freeze (struct MHD_Response *restrict response);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
mhd_response_check_frozen_freeze (struct MHD_Response *restrict r);
|
||||
|
||||
|
||||
#endif /* ! MHD_RESPONSE_FUNCS_H */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user