Still WIP

This commit is contained in:
Evgeny Grin (Karlson2k)
2024-07-10 07:48:16 +02:00
parent b916770d7a
commit b81de0bd22
63 changed files with 6700 additions and 2000 deletions
+9 -1
View File
@@ -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
View File
@@ -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
View File
@@ -29,13 +29,17 @@
#include "mhd_action.h"
#include "mhd_request.h"
#include "mhd_public_api.h"
#include "daemon_logger.h"
MHD_EXTERN_ MHD_FN_RETURNS_NONNULL_ MHD_FN_PAR_NONNULL_ALL_
MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_
const struct MHD_Action *
MHD_action_suspend (struct MHD_Request *request)
{
struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
if (mhd_UPLOAD_ACTION_NO_ACTION != head_act->act)
return (const struct MHD_Action *) NULL;
head_act->act = mhd_ACTION_SUSPEND;
return head_act;
}
@@ -48,7 +52,7 @@ MHD_action_from_response (struct MHD_Request *request,
{
struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
if (mhd_ACTION_NO_ACTION != head_act->act)
return NULL;
return (const struct MHD_Action *) NULL;
if (NULL == response)
return (const struct MHD_Action *) NULL;
@@ -70,18 +74,18 @@ MHD_action_process_upload (struct MHD_Request *request,
{
struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
if (mhd_ACTION_NO_ACTION != head_act->act)
return NULL;
return (const struct MHD_Action *) NULL;
if (0 == large_buffer_size)
{
if (NULL != uc_full)
return NULL;
return (const struct MHD_Action *) NULL;
if (NULL == uc_inc)
return NULL;
return (const struct MHD_Action *) NULL;
}
else
{
if (NULL == uc_full)
return NULL;
return (const struct MHD_Action *) NULL;
}
head_act->act = mhd_ACTION_UPLOAD;
@@ -108,9 +112,9 @@ MHD_action_post_processor (struct MHD_Request *request,
{
struct MHD_Action *const restrict head_act = &(request->app_act.head_act);
if (mhd_ACTION_NO_ACTION != head_act->act)
return NULL;
return (const struct MHD_Action *) NULL;
if (NULL == done_cb)
return NULL;
return (const struct MHD_Action *) NULL;
head_act->act = mhd_ACTION_POST_PROCESS;
head_act->data.post_process.pp_buffer_size = pp_buffer_size;
@@ -131,7 +135,7 @@ MHD_upload_action_suspend (struct MHD_Request *request)
{
struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
return NULL;
return (const struct MHD_UploadAction *) NULL;
upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
@@ -146,7 +150,7 @@ MHD_upload_action_from_response (struct MHD_Request *request,
{
struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
return NULL;
return (const struct MHD_UploadAction *) NULL;
upl_act->act = mhd_UPLOAD_ACTION_SUSPEND;
upl_act->data.response = response;
@@ -160,7 +164,7 @@ MHD_upload_action_continue (struct MHD_Request *request)
{
struct MHD_Action *const restrict upl_act = &(request->app_act.upl_act);
if (mhd_UPLOAD_ACTION_NO_ACTION != upl_act->act)
return NULL;
return (const struct MHD_UploadAction *) NULL;
upl_act->act = mhd_UPLOAD_ACTION_CONTINUE;
+180
View File
@@ -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 */
+157
View File
@@ -0,0 +1,157 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/conn_data_recv.c
* @brief The implementation of data receiving functions for connection
* @author Karlson2k (Evgeny Grin)
*
* Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
* and other contributors.
*/
#include "conn_data_recv.h"
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
#include "sys_base_types.h"
#include "mhd_assert.h"
#include "mhd_connection.h"
#include "mhd_recv.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_data_recv (struct MHD_Connection restrict *c,
bool has_err)
{
ssize_t bytes_read;
uint_fast64_t dummy_buf;
void *buf;
size_t buf_size;
size_t received;
enum mhd_SocketError res;
mhd_assert (MHD_CONNECTION_CLOSED != c->state);
mhd_assert (NULL != c->read_buffer);
mhd_assert (c->read_buffer_size > c->read_buffer_offset);
mhd_assert (! has_err || \
(0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)));
mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
has_err);
// TODO: TLS support: handshake/transport layer
buf = c->read_buffer + c->read_buffer_offset;
buf_size = c->read_buffer_size - c->read_buffer_offset;
res = mhd_recv (c,buf_size, buf, &received);
if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
{
/* Handle errors */
if (has_err && ! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
{
/* Re-try last time to detect the error */
uint_fast64_t dummy_buf;
res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received);
}
if (mhd_SOCKET_ERR_IS_HARD (res))
{
c->sk_discnt_err = res;
c->sk_ready =
(enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
| mhd_SOCKET_NET_STATE_ERROR_READY);
}
return;
}
if (0 == bytes_read)
c->sk_rmt_shut_wr = true;
c->read_buffer_offset += (size_t) bytes_read;
MHD_update_last_activity_ (c); // TODO: centralise activity update
return;
}
#if 0 // TODO: report disconnect
if ((bytes_read < 0) || socket_error)
{
if (MHD_ERR_CONNRESET_ == bytes_read)
{
if ( (MHD_CONNECTION_INIT < c->state) &&
(MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (c->daemon,
_ ("Socket has been disconnected when reading request.\n"));
#endif
c->discard_request = true;
}
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_READ_ERROR);
return;
}
#ifdef HAVE_MESSAGES
if (MHD_CONNECTION_INIT != c->state)
MHD_DLOG (c->daemon,
_ ("Connection socket is closed when reading " \
"request due to the error: %s\n"),
(bytes_read < 0) ? str_conn_error_ (bytes_read) :
"detected c closure");
#endif
CONNECTION_CLOSE_ERROR (c,
NULL);
return;
}
#if 0 // TODO: handle remote shut WR
if (0 == bytes_read)
{ /* Remote side closed c. */ // FIXME: Actually NOT!
c->sk_rmt_shut_wr = true;
if ( (MHD_CONNECTION_INIT < c->state) &&
(MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (c->daemon,
_ ("Connection was closed by remote side with incomplete "
"request.\n"));
#endif
c->discard_request = true;
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_CLIENT_ABORT);
}
else if (MHD_CONNECTION_INIT == c->state)
/* This termination code cannot be reported to the application
* because application has not been informed yet about this request */
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
else
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_WITH_ERROR);
return;
}
#endif
#endif
+50
View File
@@ -0,0 +1,50 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/conn_data_recv.h
* @brief The definition of data receiving functions for connection
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_CONN_DATA_RECV_H
#define MHD_CONN_DATA_RECV_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
struct MHD_Connection; /* forward declarations */
/**
* Perform data receiving for the connection and try to detect the socket error
* type.
*
* @param c the connection to use
* @param has_err if 'true' then just check for the network error
* type is performed
*/
MHD_INTERNAL void
mhd_conn_data_recv (struct MHD_Connection restrict *c,
bool has_err)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_CONN_DATA_RECV_H */
+279
View File
@@ -0,0 +1,279 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/conn_data_send.c
* @brief The implementation of data sending functions for connection
* @author Karlson2k (Evgeny Grin)
*
* Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
* and other contributors.
*/
#include "mhd_sys_options.h"
#include "conn_data_send.h"
#include "sys_bool_type.h"
#include "sys_base_types.h"
#include "mhd_str_macros.h"
#include "mhd_socket_error.h"
#include "mhd_assert.h"
#include "mhd_connection.h"
#include "mhd_send.h"
/**
* Check if we are done sending the write-buffer.
* If so, transition into "next_state".
*
* @param connection connection to check write status for
* @param next_state the next state to transition to
* @return #MHD_NO if we are not done, #MHD_YES if we are
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
check_write_done (struct MHD_Connection *restrict connection,
enum MHD_CONNECTION_STATE next_state)
{
// TODO: integrate into states processing
if ( (connection->write_buffer_append_offset !=
connection->write_buffer_send_offset)
/* || data_in_tls_buffers == true */
)
return false;
connection->write_buffer_append_offset = 0;
connection->write_buffer_send_offset = 0;
connection->state = next_state;
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_conn_data_send (struct MHD_Connection restrict *c,
bool has_err)
{
static const char http_100_continue_msg[] =
mdh_HTTP_1_1_100_CONTINUE_REPLY;
static const size_t http_100_continue_msg_len =
mhd_SSTR_LEN(mdh_HTTP_1_1_100_CONTINUE_REPLY);
enum mhd_SocketError res;
size_t sent;
// TODO: assert check suspended
#ifdef HTTPS_SUPPORT
// TODO: TLS support, handshake
#endif /* HTTPS_SUPPORT */
res = mhd_SOCKET_ERR_INTERNAL;
if (MHD_CONNECTION_CONTINUE_SENDING == c->state)
{
res = mhd_send_data (c,
http_100_continue_msg
[c->continue_message_write_offset],
http_100_continue_msg_len
- c->continue_message_write_offset,
true,
&sent);
if (mhd_SOCKET_ERR_NO_ERROR != res)
c->continue_message_write_offset += sent;
}
else if (MHD_CONNECTION_HEADERS_SENDING == c->state)
{
struct MHD_Response *const restrict resp = c->rp.response;
const size_t wb_ready = c->write_buffer_append_offset
- c->write_buffer_send_offset;
mhd_assert (c->write_buffer_append_offset >= \
c->write_buffer_send_offset);
mhd_assert (NULL != resp);
mhd_assert ((MHD_CONN_MUST_UPGRADE != c->keepalive) || \
(! c->rp.props.send_reply_body));
// TODO: support body generating alongside with header sending
if ((c->rp.props.send_reply_body) &&
(mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) &&
(! c->rp.props.chunked) )
{
/* Send response headers alongside the response body, if the body
* data is available. */
mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
res = mhd_send_hdr_and_body (c,
c->write_buffer +
c->write_buffer_send_offset,
wb_ready,
false,
(const char *) resp->cntn.buf,
resp->cntn_size,
true,
&sent);
}
else
{
/* This is response for HEAD request or reply body is not allowed
* for any other reason or reply body is dynamically generated. */
/* Do not send the body data even if it's available. */
res = mhd_send_hdr_and_body (c,
c->write_buffer +
c->write_buffer_send_offset,
wb_ready,
false,
NULL,
0,
((0 == resp->cntn_size) ||
(! c->rp.props.send_reply_body)),
&sent);
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
{
if (sent > wb_ready)
{
/* The complete header and some response data have been sent,
* update both offsets. */
mhd_assert (0 == c->rp.rsp_cntn_read_pos);
mhd_assert (! c->rp.props.chunked);
mhd_assert (c->rp.props.send_reply_body);
c->write_buffer_send_offset += wb_ready;
c->rp.rsp_cntn_read_pos = sent - wb_ready;
}
else
c->write_buffer_send_offset += sent;
mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state);
// TODO: move it to data processing
check_write_done (c,
MHD_CONNECTION_HEADERS_SENT);
}
}
else if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
(MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
{
struct MHD_Response *const restrict resp = c->rp.response;
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size);
mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \
(mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc));
if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
{
mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
res = mhd_send_data (c,
(const char *) resp->cntn.buf +
c->rp.rsp_cntn_read_pos,
c->rp.rsp_cntn_read_pos - resp->cntn_size,
true,
&sent);
}
else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
{
mhd_assert (c->write_buffer_append_offset > \
c->write_buffer_send_offset);
res = mhd_send_data (c,
c->write_buffer + c->write_buffer_send_offset,
c->write_buffer_append_offset
- c->write_buffer_send_offset,
true,
&sent);
}
else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
{
mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype);
res = mhd_send_iovec (c,
&c->rp.resp_iov,
true,
&sent);
}
#if defined(MHD_USE_SENDFILE)
else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
{
mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype);
res = mhd_send_sendfile (c, &sent);
}
#endif /* MHD_USE_SENDFILE */
else
{
mhd_assert (0 && "Should be unreachable");
res = mhd_SOCKET_ERR_INTERNAL;
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
{
if (mhd_REPLY_CNTN_LOC_CONN_BUF != c->rp.cntn_loc)
c->rp.rsp_cntn_read_pos += sent;
else
c->write_buffer_send_offset += sent;
}
if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)
{
// TODO: move it to data processing
check_write_done (c,
(c->rp.response->cntn_size ==
c->rp.rsp_cntn_read_pos) ?
MHD_CONNECTION_CHUNKED_BODY_SENT :
MHD_CONNECTION_CHUNKED_BODY_UNREADY);
}
}
else if (MHD_CONNECTION_FOOTERS_SENDING == c->state)
{
res = mhd_send_data (c,
c->write_buffer +
c->write_buffer_send_offset,
c->write_buffer_append_offset
- c->write_buffer_send_offset,
true,
&sent);
if (mhd_SOCKET_ERR_NO_ERROR != res)
{
c->write_buffer_send_offset += sent;
// TODO: move it to data processing
check_write_done (c,
MHD_CONNECTION_FULL_REPLY_SENT);
}
}
else
{
mhd_assert (0 && "Should be unreachable");
res = mhd_SOCKET_ERR_INTERNAL;
}
if (mhd_SOCKET_ERR_NO_ERROR != res)
{
MHD_update_last_activity_ (c); // TODO: centralise activity mark updates
}
else if (mhd_SOCKET_ERR_IS_HARD(res))
{
c->sk_discnt_err = res;
c->sk_ready =
(enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
| mhd_SOCKET_NET_STATE_ERROR_READY);
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/conn_data_send.h
* @brief The definition of data sending functions for connection
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_CONN_DATA_SEND_H
#define MHD_CONN_DATA_SEND_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
struct MHD_Connection; /* forward declarations */
/**
* Perform data sending for the connection and try to detect the socket error
* type.
*
* @param c the connection to use
* @param has_err if 'true' then just check for the network error
* type is performed
*/
MHD_INTERNAL void
mhd_conn_data_send (struct MHD_Connection restrict *c,
bool has_err)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_CONN_DATA_SEND_H */
+4 -1
View File
@@ -42,6 +42,8 @@
#include "sys_sockets_types.h"
#include "sys_sockets_headers.h"
#include <string.h>
#include "compat_calloc.h"
#include "mhd_panic.h"
@@ -59,6 +61,7 @@
#include "events_process.h"
#include "response_from.h"
#include "response_destroy.h"
/**
@@ -736,7 +739,7 @@ MHD_daemon_add_connection (struct MHD_Daemon *daemon,
for (i = 0; i < daemon->threading.hier.pool.num; ++i)
{
struct MHD_Daemon *const volatile worker =
struct MHD_Daemon *const restrict worker =
daemon->threading.hier.pool.workers
+ (i + offset) % daemon->threading.hier.pool.num;
if (worker->conns.block_new)
+2 -2
View File
@@ -93,8 +93,8 @@ MHD_daemon_create (MHD_RequestCallback req_cb,
s->fd_number_limit = MHD_INVALID_SOCKET;
d->log_params.v_log_cb = mhd_logger_default;
d->req_cb = req_cb;
d->req_cb_cls = req_cb_cls;
d->req_cfg.cb = req_cb;
d->req_cfg.cb_cls = req_cb_cls;
d->settings = s;
return d;
+10
View File
@@ -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
View File
@@ -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 */
-303
View File
@@ -1,303 +0,0 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/data_process.c
* @brief The implementation of data receiving, sending and processing
* functions for connection
* @author Karlson2k (Evgeny Grin)
*
* Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
* contributors.
*/
#include "mhd_sys_options.h"
#include "data_process.h"
#include "sys_bool_type.h"
#include "sys_base_types.h"
#include "mhd_assert.h"
#include "mhd_daemon.h"
#include "mhd_connection.h"
#include "mhd_recv.h"
#include "daemon_logger.h"
/**
* Perform data receiving for the connection and try to detect the socket error
* type.
*
* @param c the connection to use
* @param has_err if 'true' then just check for the network error
* type is performed
*/
void
conn_process_recv (struct MHD_Connection restrict *c,
bool has_err)
{
ssize_t bytes_read;
uint_fast64_t dummy_buf;
void *buf;
size_t buf_size;
size_t received;
enum mhd_SocketError res;
mhd_assert (MHD_CONNECTION_CLOSED != c->state);
mhd_assert ((NULL != c->read_buffer) || has_err);
mhd_assert ((c->read_buffer_size > c->read_buffer_offset) || has_err);
mhd_assert (! has_err || \
(0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)));
mhd_assert ((0 == (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)) || \
has_err);
// TODO: TLS support: handshake/transport layer
if (! has_err ||
(NULL != c->read_buffer) || (c->read_buffer_size > c->read_buffer_offset))
{
buf = c->read_buffer + c->read_buffer_offset;
buf_size = c->read_buffer_size - c->read_buffer_offset;
}
else
{
buf = &dummy_buf;
buf_size = sizeof(buf);
}
res = mhd_recv (c,buf_size, buf, &received);
if ((mhd_SOCKET_ERR_NO_ERROR != res) || has_err)
{
/* Handle errors */
if (! mhd_SOCKET_ERR_IS_HARD (res) && c->sk_nonblck)
{
/* Re-try last time to detect the error */
res = mhd_recv (c, &dummy_buf, sizeof(dummy_buf), &received);
}
if (mhd_SOCKET_ERR_IS_HARD (res))
{
c->sk_discnt_err = res;
c->sk_ready =
(enum mhd_SocketNetState) (((unsigned int) c->sk_ready)
| mhd_SOCKET_NET_STATE_ERROR_READY);
}
return;
}
if (0 == bytes_read)
c->sk_rmt_shut_wr = true;
c->read_buffer_offset += (size_t) bytes_read;
MHD_update_last_activity_ (c); // TODO: centralise activity update
return;
}
#if 0 // TODO: report disconnect
if ((bytes_read < 0) || socket_error)
{
if (MHD_ERR_CONNRESET_ == bytes_read)
{
if ( (MHD_CONNECTION_INIT < c->state) &&
(MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (c->daemon,
_ ("Socket has been disconnected when reading request.\n"));
#endif
c->discard_request = true;
}
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_READ_ERROR);
return;
}
#ifdef HAVE_MESSAGES
if (MHD_CONNECTION_INIT != c->state)
MHD_DLOG (c->daemon,
_ ("Connection socket is closed when reading " \
"request due to the error: %s\n"),
(bytes_read < 0) ? str_conn_error_ (bytes_read) :
"detected c closure");
#endif
CONNECTION_CLOSE_ERROR (c,
NULL);
return;
}
#if 0 // TODO: handle remote shut WR
if (0 == bytes_read)
{ /* Remote side closed c. */ // FIXME: Actually NOT!
c->sk_rmt_shut_wr = true;
if ( (MHD_CONNECTION_INIT < c->state) &&
(MHD_CONNECTION_FULL_REQ_RECEIVED > c->state) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (c->daemon,
_ ("Connection was closed by remote side with incomplete "
"request.\n"));
#endif
c->discard_request = true;
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_CLIENT_ABORT);
}
else if (MHD_CONNECTION_INIT == c->state)
/* This termination code cannot be reported to the application
* because application has not been informed yet about this request */
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_COMPLETED_OK);
else
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_WITH_ERROR);
return;
}
#endif
#endif
MHD_INTERNAL bool
mhd_connection_process_recv_send_data (struct MHD_Connection restrict *c)
{
const bool send_ready_state_known =
((0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)) ||
mhd_D_IS_USING_EDGE_TRIG (c->daemon));
const bool has_sock_err =
(0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk_ready));
bool data_processed;
bool need_send;
data_processed = false;
if ( ((0 != (mhd_SOCKET_NET_STATE_RECV_READY & c->sk_ready)) &&
((0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info))
|| has_sock_err))
|| (has_sock_err && c->sk_nonblck))
{
conn_process_recv (c, has_sock_err);
if (! conn_process_data (c))
return false;
data_processed = true;
}
need_send = ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) &&
(0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info)));
if (0 != (MHD_EVENT_LOOP_INFO_WRITE & c->event_loop_info))
{
/* Perform sending if:
* + connection is ready for sending or
* + just formed send data, connection send ready status is not known and
* connection socket is non-blocking */
/* Assuming that after finishing receiving phase, connection send buffers
should have some space as sending was performed before receiving or has
not been performed yet. */
if ((0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk_ready)) ||
((data_processed && ! send_ready_state_known && c->sk_nonblck) ||
(has_sock_err && c->sk_nonblck)))
{
conn_process_send (c, has_sock_err);
if (! conn_process_data (c))
return false;
data_processed = true;
}
}
if (! data_processed)
return conn_process_data (c);
return true;
#if 0 // TODO: re-implement fasttrack as a single unified buffer sending
if (! force_close)
{
/* No need to check value of 'ret' here as closed connection
* cannot be in MHD_EVENT_LOOP_INFO_WRITE state. */
if ( (MHD_EVENT_LOOP_INFO_WRITE == c->event_loop_info) &&
write_ready)
{
MHD_connection_handle_write (c);
ret = MHD_connection_handle_idle (c);
states_info_processed = true;
}
}
else
{
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_WITH_ERROR);
return MHD_connection_handle_idle (c);
}
if (! states_info_processed)
{ /* Connection is not read or write ready, but external conditions
* may be changed and need to be processed. */
ret = MHD_connection_handle_idle (c);
}
/* Fast track for fast connections. */
/* If full request was read by single read_handler() invocation
and headers were completely prepared by single MHD_connection_handle_idle()
then try not to wait for next sockets polling and send response
immediately.
As writeability of socket was not checked and it may have
some data pending in system buffers, use this optimization
only for non-blocking sockets. */
/* No need to check 'ret' as connection is always in
* MHD_CONNECTION_CLOSED state if 'ret' is equal 'MHD_NO'. */
else if (on_fasttrack && c->sk_nonblck)
{
if (MHD_CONNECTION_HEADERS_SENDING == c->state)
{
MHD_connection_handle_write (c);
/* Always call 'MHD_connection_handle_idle()' after each read/write. */
ret = MHD_connection_handle_idle (c);
}
/* If all headers were sent by single write_handler() and
* response body is prepared by single MHD_connection_handle_idle()
* call - continue. */
if ((MHD_CONNECTION_NORMAL_BODY_READY == c->state) ||
(MHD_CONNECTION_CHUNKED_BODY_READY == c->state))
{
MHD_connection_handle_write (c);
ret = MHD_connection_handle_idle (c);
}
}
/* All connection's data and states are processed for this turn.
* If connection already has more data to be processed - use
* zero timeout for next select()/poll(). */
/* Thread-per-connection do not need global zero timeout as
* connections are processed individually. */
/* Note: no need to check for read buffer availability for
* TLS read-ready connection in 'read info' state as connection
* without space in read buffer will be marked as 'info block'. */
if ( (! c->daemon->data_already_pending) &&
(! MHD_D_IS_USING_THREAD_PER_CONN_ (c->daemon)) )
{
if (0 != (MHD_EVENT_LOOP_INFO_PROCESS & c->event_loop_info))
c->daemon->data_already_pending = true;
#ifdef HTTPS_SUPPORT
else if ( (c->tls_read_ready) &&
(0 != (MHD_EVENT_LOOP_INFO_READ & c->event_loop_info)) )
c->daemon->data_already_pending = true;
#endif /* HTTPS_SUPPORT */
}
return ret;
#endif
}
+15 -4
View File
@@ -40,6 +40,9 @@
#include "daemon_logger.h"
#include "mhd_connection.h"
#include "daemon_add_conn.h"
#include "daemon_funcs.h"
#include "conn_data_process.h"
MHD_FN_PAR_NONNULL_ (1) static void
@@ -209,15 +212,20 @@ daemon_accept_new_conns (struct MHD_Daemon *restrict d)
static bool
daemon_process_all_act_coons (struct MHD_Daemon *restrict d)
{
struct MHD_Connection *restrict c;
struct MHD_Connection *c;
mhd_assert (! mhd_D_HAS_WORKERS (d));
for (c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready); c != NULL;
c = mhd_DLINKEDL_GET_NEXT (c, proc_ready))
c = mhd_DLINKEDL_GET_FIRST (&(d->events),proc_ready);
while (NULL != c)
{
struct MHD_Connection *next;
next = mhd_DLINKEDL_GET_NEXT (c, proc_ready); /* The current connection can be closed */
if (! mhd_conn_process_recv_send_data(c))
mhd_conn_close_final(c);
c = next;
}
return false;
return true;
}
@@ -524,6 +532,9 @@ mhd_worker_all_events (void *cls)
while (! d->threading.stop_requested)
{
if (d->threading.resume_requested)
mhd_daemon_resume_conns (d);
if (! process_all_events_and_data (d))
break;
}
+2 -2
View File
@@ -44,9 +44,9 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_HTTP_ProtocolVersion
,
MHD_HTTP_VERSION_1_1 = 2
,
MHD_HTTP_VERSION_2_0 = 3
MHD_HTTP_VERSION_2 = 3
,
MHD_HTTP_VERSION_3_0 = 4
MHD_HTTP_VERSION_3 = 4
,
MHD_HTTP_VERSION_FUTURE = 255
};
+2 -2
View File
@@ -175,7 +175,7 @@ static const struct mhd_HttpStatusesBlock statuses[] = {
};
MHD_EXTERN_ MHD_FN_CONST_ const struct MHD_String *
MHD_HTTP_status_code_to_string (enum MHD_StatusCode code)
MHD_HTTP_status_code_to_string (enum MHD_HTTP_StatusCode code)
{
struct MHD_String *res;
const unsigned int code_i = (unsigned int) code;
@@ -193,7 +193,7 @@ MHD_HTTP_status_code_to_string (enum MHD_StatusCode code)
MHD_INTERNAL MHD_FN_CONST_ const struct MHD_String *
mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
mhd_HTTP_status_code_to_string_int (enum MHD_HTTP_StatusCode code)
{
static const struct MHD_String no_status =
mhd_MSTR_INIT ("Nonstandard Status");
+1 -1
View File
@@ -39,7 +39,7 @@
* @return pointer to MHD_String, never NULL.
*/
MHD_INTERNAL const struct MHD_String *
mhd_HTTP_status_code_to_string_int (enum MHD_StatusCode code)
mhd_HTTP_status_code_to_string_int (enum MHD_HTTP_StatusCode code)
MHD_FN_CONST_;
+73
View File
@@ -0,0 +1,73 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_atomic_counter.c
* @brief The definition of the atomic counter functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "mhd_atomic_counter.h"
#if defined(mhd_ATOMIC_BY_LOCKS)
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt)
{
mhd_ATOMIC_COUNTER_TYPE ret;
mhd_mutex_lock_chk(&(pcnt->lock));
ret = ++(pcnt->count);
mhd_mutex_unlock_chk(&(pcnt->lock));
mhd_assert (0 != ret); /* check for overflow */
return ret;
}
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt)
{
mhd_ATOMIC_COUNTER_TYPE ret;
mhd_mutex_lock_chk(&(pcnt->lock));
ret = --(pcnt->count);
mhd_mutex_unlock_chk(&(pcnt->lock));
mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); /* check for underflow */
return ret;
}
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt)
{
mhd_ATOMIC_COUNTER_TYPE ret;
mhd_mutex_lock_chk(&(pcnt->lock));
ret = pcnt->count;
mhd_mutex_unlock_chk(&(pcnt->lock));
return ret;
}
#endif /* mhd_ATOMIC_BY_LOCKS */
+203
View File
@@ -0,0 +1,203 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_atomic_counter.h
* @brief The definition of the atomic counter type and related functions
* declarations
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_ATOMIC_COUNTER_H
#define MHD_ATOMIC_COUNTER_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h" /* for size_t */
/* Use 'size_t' to make sure it would never overflow when used for
* MHD needs. */
/**
* The type used to contain the counter value.
* Always unsigned.
*/
#define mhd_ATOMIC_COUNTER_TYPE size_t
/**
* The maximum counter value
*/
#define mhd_ATOMIC_COUNTER_MAX \
((mhd_ATOMIC_COUNTER_TYPE)(~((mhd_ATOMIC_COUNTER_TYPE)0)))
#ifdef MHD_USE_THREADS
/**
* Atomic operations are based on locks
*/
# define mhd_ATOMIC_BY_LOCKS 1
#else /* ! MHD_USE_THREADS */
/**
* Atomic because single thread environment is used
*/
# define mhd_ATOMIC_SINGLE_THREAD 1
#endif /* ! MHD_USE_THREADS */
#if defined(mhd_ATOMIC_BY_LOCKS)
# include "mhd_locks.h"
# include "sys_bool_type.h"
/**
* The atomic counter
*/
struct mhd_AtomicCounter
{
/**
* Counter value.
* Must be read or written only with @a lock held.
*/
volatile mhd_ATOMIC_COUNTER_TYPE count;
/**
* The mutex.
*/
mhd_mutex lock;
};
#elif defined(mhd_ATOMIC_SINGLE_THREAD)
/**
* The atomic counter
*/
struct mhd_AtomicCounter
{
/**
* Counter value.
*/
volatile mhd_ATOMIC_COUNTER_TYPE count;
};
#endif /* mhd_ATOMIC_SINGLE_THREAD */
#if defined(mhd_ATOMIC_BY_LOCKS)
/**
* Initialise the counter to specified value.
* @param pcnt the pointer to the counter to initialise
* @param initial_value the initial value for the counter
* @return 'true' if succeed, "false' if failed
* @warning Must not be called for the counters that has been initialised
* already.
*/
# define mhd_atomic_counter_init(pcnt,initial_value) \
((pcnt)->count=(initial_value), mhd_mutex_init_short(&(pcnt->lock)))
/**
* Deinitialise the counter.
* @param pcnt the pointer to the counter to deinitialise
* @warning Must be called only for the counters that has been initialised.
*/
# define mhd_atomic_counter_deinit(pcnt) mhd_mutex_destroy_chk(&((pcnt)->lock))
/**
* Atomically increment the value of the counter
* @param pcnt the pointer to the counter to increment
*/
# define mhd_atomic_counter_inc(pcnt) do { \
mhd_mutex_lock_chk(&(pcnt->lock)); \
++(pcnt->count); \
mhd_mutex_unlock_chk(&(pcnt->lock)); } while (0)
/**
* Atomically increment the value of the counter and return the result
* @param pcnt the pointer to the counter to increment
* @return the final/resulting counter value
*/
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_inc_get (struct mhd_AtomicCounter *pcnt);
/**
* Atomically decrement the value of the counter and return the result
* @param pcnt the pointer to the counter to decrement
* @return the final/resulting counter value
*/
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_dec_get (struct mhd_AtomicCounter *pcnt);
/**
* Atomically get the value of the counter
* @param pcnt the pointer to the counter to get
* @return the counter value
*/
MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE
mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt);
#elif defined(mhd_ATOMIC_SINGLE_THREAD)
/**
* Initialise the counter to specified value.
* @param pcnt the pointer to the counter to initialise
* @param initial_value the initial value for the counter
* @return 'true' if succeed, "false' if failed
* @warning Must not be called for the counters that has been initialised
* already.
*/
# define mhd_atomic_counter_init(pcnt,initial_value) \
((pcnt)->count=(initial_value), (! 0))
/**
* Deinitialise the counter.
* @param pcnt the pointer to the counter to deinitialise
* @warning Must be called only for the counters that has been initialised.
*/
# define mhd_atomic_counter_deinit(pcnt) ((void)0)
/**
* Atomically increment the value of the counter
* @param pcnt the pointer to the counter to increment
*/
# define mhd_atomic_counter_inc(pcnt) do { ++(pcnt->count); } while (0)
/**
* Atomically increment the value of the counter and return the result
* @param pcnt the pointer to the counter to increment
* @return the final/resulting counter value
*/
# define mhd_atomic_counter_inc_get(pcnt) (++((pcnt)->count))
/**
* Atomically decrement the value of the counter and return the result
* @param pcnt the pointer to the counter to decrement
* @return the final/resulting counter value
*/
# define mhd_atomic_counter_dec_get(pcnt) (--((pcnt)->count))
/**
* Atomically get the value of the counter
* @param pcnt the pointer to the counter to get
* @return the counter value
*/
# define mhd_atomic_counter_get(pcnt) ((pcnt)->count)
#endif /* mhd_ATOMIC_SINGLE_THREAD */
#endif /* ! MHD_ATOMIC_COUNTER_H */
+81 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -289,7 +289,7 @@ mhd_itc_nonblocking (struct mhd_itc *pitc);
* @return non-zero if succeeded, zero otherwise
*/
# define mhd_itc_activate(itc) \
((0 < mhd_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ())
((0 < mhd_sys_send ((itc).sk[1], "", 1)) || mhd_SCKT_LERR_IS_EAGAIN ())
/**
* Return read FD of @a itc which can be used for poll(), select() etc.
+4
View File
@@ -28,6 +28,7 @@
#include "mhd_panic.h"
#include "mhd_mono_clock.h"
#include "mhd_socket_type.h"
#include "mhd_send.h"
#ifdef MHD_WINSOCK_SOCKETS
# include <winsock2.h>
#endif
@@ -46,12 +47,15 @@ mhd_lib_global_init (void)
MHD_PANIC ("Failed to initialise WinSock.");
}
#endif /* MHD_WINSOCK_SOCKETS */
MHD_monotonic_msec_counter_init();
mhd_send_init_static_vars();
}
void
mhd_lib_global_deinit (void)
{
MHD_monotonic_msec_counter_finish();
#if defined(MHD_WINSOCK_SOCKETS)
(void) WSACleanup ();
#endif /* MHD_WINSOCK_SOCKETS */
+177
View File
@@ -0,0 +1,177 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/mhd_limits.h
* @brief limits values definitions
* @author Karlson2k (Evgeny Grin)
*
* This file provides maximum types values as macros. Macros may not work
* in preprocessor expressions, while macros always work in compiler
* expressions.
* This file does not include <stdint.h> and other type-definitions files.
* For best portability, make sure that this file is included after required
* type-definitions files.
*/
#ifndef MHD_LIMITS_H
#define MHD_LIMITS_H
#include "mhd_sys_options.h"
#ifdef HAVE_LIMITS_H
# include <limits.h>
#endif /* HAVE_LIMITS_H */
#define mhd_UNSIGNED_TYPE_MAX(type) ((type)(~((type) 0)))
/* Assume 8 bits per byte, no padding bits. */
#define mhd_SIGNED_TYPE_MAX(type) \
( (type) ((( ((type) 1) << (sizeof(type) * 8 - 2)) - 1) * 2 + 1) )
/* The maximum value for signed type, based on knowledge of unsigned counterpart
type */
#define mhd_SIGNED_TYPE_MAX2(type,utype) ((type)(((utype)(~((utype)0))) >> 1))
#define mhd_IS_TYPE_SIGNED(type) (((type) 0) > ((type) - 1))
#if defined(__GNUC__) || defined(__clang__)
# define mhd_USE_PREDEF_LIMITS
#endif
#ifndef INT_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT_MAX__)
# define INT_MAX __INT_MAX__
# else /* ! __INT_MAX__ */
# define INT_MAX mhd_SIGNED_TYPE_MAX2 (int, unsigned int)
# endif /* ! __INT_MAX__ */
#endif /* ! INT_MAX */
#ifndef UINT_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT_MAX__)
# define UINT_MAX __UINT_MAX__
# else /* ! __UINT_MAX__ */
# define UINT_MAX mhd_UNSIGNED_TYPE_MAX (unsigned int)
# endif /* ! __UINT_MAX__ */
#endif /* !UINT_MAX */
#ifndef LONG_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__LONG_MAX__)
# define LONG_MAX __LONG_MAX__
# else /* ! __LONG_MAX__ */
# define LONG_MAX mhd_SIGNED_TYPE_MAX2 (long, unsigned long)
# endif /* ! __LONG_MAX__ */
#endif /* !LONG_MAX */
#ifndef ULONG_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__ULONG_MAX__)
# define ULONG_MAX ULONG_MAX
# else /* ! __ULONG_MAX__ */
# define ULONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long)
# endif /* ! __ULONG_MAX__ */
#endif /* !ULONG_MAX */
#ifndef ULLONG_MAX
# ifdef ULONGLONG_MAX
# define ULLONG_MAX ULONGLONG_MAX
# else /* ! ULONGLONG_MAX */
# define ULLONG_MAX mhd_UNSIGNED_TYPE_MAX (unsigned long long)
# endif /* ! ULONGLONG_MAX */
#endif /* !ULLONG_MAX */
#ifndef INT32_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT32_MAX__)
# define INT32_MAX __INT32_MAX__
# else /* ! __INT32_MAX__ */
# define INT32_MAX ((int32_t) 0x7FFFFFFF)
# endif /* ! __INT32_MAX__ */
#endif /* !INT32_MAX */
#ifndef UINT32_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT32_MAX__)
# define UINT32_MAX __UINT32_MAX__
# else /* ! __UINT32_MAX__ */
# define UINT32_MAX ((uint32_t) 0xFFFFFFFFU)
# endif /* ! __UINT32_MAX__ */
#endif /* !UINT32_MAX */
#ifndef INT64_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__INT64_MAX__)
# define INT64_MAX __INT64_MAX__
# else /* ! __INT64_MAX__ */
# define INT64_MAX ((int64_t) 0x7FFFFFFFFFFFFFFF)
# endif /* ! __UINT64_MAX__ */
#endif /* !INT64_MAX */
#ifndef UINT64_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__UINT64_MAX__)
# define UINT64_MAX __UINT64_MAX__
# else /* ! __UINT64_MAX__ */
# define UINT64_MAX ((uint64_t) 0xFFFFFFFFFFFFFFFFU)
# endif /* ! __UINT64_MAX__ */
#endif /* !UINT64_MAX */
#ifndef SIZE_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__SIZE_MAX__)
# define SIZE_MAX __SIZE_MAX__
# else /* ! __SIZE_MAX__ */
# define SIZE_MAX mhd_UNSIGNED_TYPE_MAX (size_t)
# endif /* ! __SIZE_MAX__ */
#endif /* !SIZE_MAX */
#ifndef SSIZE_MAX
# if defined(mhd_USE_PREDEF_LIMITS) && defined(__SSIZE_MAX__)
# define SSIZE_MAX __SSIZE_MAX__
# else
# define SSIZE_MAX mhd_SIGNED_TYPE_MAX2 (ssize_t, size_t)
# endif
#endif /* ! SSIZE_MAX */
#ifndef OFF_T_MAX
# ifdef OFF_MAX
# define OFF_T_MAX OFF_MAX
# elif defined(OFFT_MAX)
# define OFF_T_MAX OFFT_MAX
# elif defined(__APPLE__) && defined(__MACH__)
# define OFF_T_MAX INT64_MAX
# else
# define OFF_T_MAX mhd_SIGNED_TYPE_MAX (off_t)
# endif
#endif /* !OFF_T_MAX */
#if defined(_LARGEFILE64_SOURCE) && ! defined(OFF64_T_MAX)
# define OFF64_T_MAX mhd_SIGNED_TYPE_MAX (off64_t)
#endif /* _LARGEFILE64_SOURCE && !OFF64_T_MAX */
#ifndef TIME_T_MAX
# define TIME_T_MAX ((time_t) \
(mhd_IS_TYPE_SIGNED (time_t) ? \
mhd_SIGNED_TYPE_MAX (time_t) : \
mhd_UNSIGNED_TYPE_MAX (time_t)))
#endif /* !TIME_T_MAX */
#ifndef TIMEVAL_TV_SEC_MAX
# ifndef _WIN32
# define TIMEVAL_TV_SEC_MAX TIME_T_MAX
# else /* _WIN32 */
# define TIMEVAL_TV_SEC_MAX LONG_MAX
# endif /* _WIN32 */
#endif /* !TIMEVAL_TV_SEC_MAX */
#endif /* MHD_LIMITS_H */
+1 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+616 -689
View File
@@ -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 */
+78
View File
@@ -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
View File
@@ -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 */
+53 -7
View File
@@ -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
View File
@@ -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
View File
@@ -1,13 +1,13 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2015-2024 Karlson2k (Evgeny Grin)
Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
@@ -30,7 +30,9 @@
#include "sys_base_types.h"
#include "sys_bool_type.h"
#include "mhd_str_macros.h"
#ifdef MHD_FAVOR_SMALL_CODE
# include "mhd_limits.h"
#endif
/*
* Block of functions/macros that use US-ASCII charset as required by HTTP
* standards. Not affected by current locale settings.
@@ -42,9 +44,9 @@
*
* @param str1 first string to compare
* @param str2 second string to compare
* @return non-zero if two strings are equal, zero otherwise.
* @return 'true' if two strings are equal, 'false' otherwise.
*/
int
MHD_INTERNAL bool
mhd_str_equal_caseless (const char *str1,
const char *str2);
@@ -63,9 +65,9 @@ mhd_str_equal_caseless (const char *str1,
* @param str1 first string to compare
* @param str2 second string to compare
* @param maxlen maximum number of characters to compare
* @return non-zero if two strings are equal, zero otherwise.
* @return 'true' if two strings are equal, 'false' otherwise.
*/
int
MHD_INTERNAL bool
mhd_str_equal_caseless_n (const char *const str1,
const char *const str2,
size_t maxlen);
@@ -79,9 +81,9 @@ mhd_str_equal_caseless_n (const char *const str1,
* @param str1 first string to compare
* @param str2 second string to compare
* @param len number of characters to compare
* @return non-zero if @a len bytes are equal, zero otherwise.
* @return 'true' if two strings are equal, 'false' otherwise.
*/
bool
MHD_INTERNAL bool
mhd_str_equal_caseless_bin_n (const char *const str1,
const char *const str2,
size_t len);
@@ -99,7 +101,7 @@ mhd_str_equal_caseless_bin_n (const char *const str1,
* @param a the statically allocated string to compare
* @param s the string to compare
* @param len number of characters to compare
* @return non-zero if @a len bytes are equal, zero otherwise.
* @return 'true' if two strings are equal, 'false' otherwise.
*/
#define mhd_str_equal_caseless_s_bin_n(a,s,l) \
((mhd_SSTR_LEN (a) == (l)) \
@@ -118,8 +120,8 @@ mhd_str_equal_caseless_bin_n (const char *const str1,
* null-character.
* @return non-zero if two strings are equal, zero otherwise.
*/
bool
mhd_str_has_token_caseless (const char *str,
MHD_INTERNAL bool
mhd_str_has_token_caseless (const char *restrict str,
const char *const token,
size_t token_len);
@@ -165,13 +167,13 @@ mhd_str_has_token_caseless (const char *str,
* @return 'true' if token has been removed,
* 'false' otherwise.
*/
bool
mhd_str_remove_token_caseless (const char *str,
MHD_INTERNAL bool
mhd_str_remove_token_caseless (const char *restrict str,
size_t str_len,
const char *const token,
const char *const restrict token,
const size_t token_len,
char *buf,
ssize_t *buf_size);
char *restrict buf,
ssize_t *restrict buf_size);
/**
@@ -197,10 +199,10 @@ mhd_str_remove_token_caseless (const char *str,
* @return 'true' if any token has been removed,
* 'false' otherwise.
*/
bool
mhd_str_remove_tokens_caseless (char *str,
size_t *str_len,
const char *const tokens,
MHD_INTERNAL bool
mhd_str_remove_tokens_caseless (char *restrict str,
size_t *restrict str_len,
const char *const restrict tkns,
const size_t tokens_len);
@@ -217,9 +219,9 @@ mhd_str_remove_tokens_caseless (char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast64_t or @a out_val is NULL
*/
size_t
mhd_str_to_uint64 (const char *str,
uint_fast64_t *out_val);
MHD_INTERNAL size_t
mhd_str_to_uint64 (const char *restrict str,
uint_fast64_t *restrict out_val);
/**
* Convert not more then @a maxlen decimal US-ASCII digits in string to
@@ -234,10 +236,10 @@ mhd_str_to_uint64 (const char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast64_t or @a out_val is NULL
*/
size_t
mhd_str_to_uint64_n (const char *str,
MHD_INTERNAL size_t
mhd_str_to_uint64_n (const char *restrict str,
size_t maxlen,
uint_fast64_t *out_val);
uint_fast64_t *restrict out_val);
/**
@@ -250,9 +252,9 @@ mhd_str_to_uint64_n (const char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast32_t or @a out_val is NULL
*/
size_t
mhd_strx_to_uint32 (const char *str,
uint_fast32_t *out_val);
MHD_INTERNAL size_t
mhd_strx_to_uint32 (const char *restrict str,
uint_fast32_t *restrict out_val);
/**
@@ -268,10 +270,10 @@ mhd_strx_to_uint32 (const char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast32_t or @a out_val is NULL
*/
size_t
mhd_strx_to_uint32_n (const char *str,
MHD_INTERNAL size_t
mhd_strx_to_uint32_n (const char *restrict str,
size_t maxlen,
uint_fast32_t *out_val);
uint_fast32_t *restrict out_val);
/**
@@ -284,9 +286,9 @@ mhd_strx_to_uint32_n (const char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast64_t or @a out_val is NULL
*/
size_t
mhd_strx_to_uint64 (const char *str,
uint_fast64_t *out_val);
MHD_INTERNAL size_t
mhd_strx_to_uint64 (const char *restrict str,
uint_fast64_t *restrict out_val);
/**
@@ -302,10 +304,10 @@ mhd_strx_to_uint64 (const char *str,
* zero if no digit is found, resulting value is larger
* then possible to store in uint_fast64_t or @a out_val is NULL
*/
size_t
mhd_strx_to_uint64_n (const char *str,
MHD_INTERNAL size_t
mhd_strx_to_uint64_n (const char *restrict str,
size_t maxlen,
uint_fast64_t *out_val);
uint_fast64_t *restrict out_val);
#else /* MHD_FAVOR_SMALL_CODE */
/* Use one universal function and macros to reduce size */
@@ -327,10 +329,10 @@ mhd_strx_to_uint64_n (const char *str,
* zero if no digit is found, resulting value is larger
* then @a max_val, @a val_size is not 4/8 or @a out_val is NULL
*/
size_t
mhd_str_to_uvalue_n (const char *str,
MHD_INTERNAL size_t
mhd_str_to_uvalue_n (const char *restrict str,
size_t maxlen,
void *out_val,
void *restrict out_val,
size_t val_size,
uint_fast64_t max_val,
unsigned int base);
@@ -387,7 +389,7 @@ mhd_str_to_uvalue_n (const char *str,
* @return number of characters has been put to the @a buf,
* zero if buffer is too small (buffer may be modified).
*/
size_t
MHD_INTERNAL size_t
mhd_uint32_to_strx (uint_fast32_t val,
char *buf,
size_t buf_size);
@@ -403,13 +405,13 @@ mhd_uint32_to_strx (uint_fast32_t val,
* @return number of characters has been put to the @a buf,
* zero if buffer is too small (buffer may be modified).
*/
size_t
MHD_INTERNAL size_t
mhd_uint16_to_str (uint_fast16_t val,
char *buf,
size_t buf_size);
#else /* MHD_FAVOR_SMALL_CODE */
#define MHD_uint16_to_str(v,b,s) MHD_uint64_to_str (v,b,s)
#define mhd_uint16_to_str(v,b,s) mhd_uint64_to_str (v,b,s)
#endif /* MHD_FAVOR_SMALL_CODE */
@@ -422,7 +424,7 @@ mhd_uint16_to_str (uint_fast16_t val,
* @return number of characters has been put to the @a buf,
* zero if buffer is too small (buffer may be modified).
*/
size_t
MHD_INTERNAL size_t
mhd_uint64_to_str (uint_fast64_t val,
char *buf,
size_t buf_size);
@@ -443,7 +445,7 @@ mhd_uint64_to_str (uint_fast64_t val,
* @return number of characters has been put to the @a buf,
* zero if buffer is too small (buffer may be modified).
*/
size_t
MHD_INTERNAL size_t
mhd_uint8_to_str_pad (uint8_t val,
uint8_t min_digits,
char *buf,
@@ -459,10 +461,10 @@ mhd_uint8_to_str_pad (uint8_t val,
* @param[out] hex the output buffer, should be at least 2 * @a size
* @return The number of characters written to the output buffer.
*/
size_t
mhd_bin_to_hex (const void *bin,
MHD_INTERNAL size_t
mhd_bin_to_hex (const void *restrict bin,
size_t size,
char *hex);
char *restrict hex);
/**
* Convert @a size bytes from input binary data to lower case
@@ -473,10 +475,10 @@ mhd_bin_to_hex (const void *bin,
* @return The number of characters written to the output buffer,
* not including terminating zero.
*/
size_t
mhd_bin_to_hex_z (const void *bin,
MHD_INTERNAL size_t
mhd_bin_to_hex_z (const void *restrict bin,
size_t size,
char *hex);
char *restrict hex);
/**
* Convert hexadecimal digits to binary data.
@@ -491,10 +493,10 @@ mhd_bin_to_hex_z (const void *bin,
* @return the number of bytes written to the output buffer,
* zero if found any character which is not hexadecimal digits
*/
size_t
mhd_hex_to_bin (const char *hex,
MHD_INTERNAL size_t
mhd_hex_to_bin (const char *restrict hex,
size_t len,
void *bin);
void *restrict bin);
/**
* Decode string with percent-encoded characters as defined by
@@ -514,7 +516,7 @@ mhd_hex_to_bin (const char *hex,
* by less than two hexadecimal digits) or output buffer is too
* small to hold the result
*/
size_t
MHD_INTERNAL size_t
mhd_str_pct_decode_strict_n_ (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
@@ -542,7 +544,7 @@ mhd_str_pct_decode_strict_n_ (const char *pct_encoded,
* @return the number of characters written to the output buffer or
* zero if output buffer is too small to hold the result
*/
size_t
MHD_INTERNAL size_t
mhd_str_pct_decode_lenient_n_ (const char *pct_encoded,
size_t pct_encoded_len,
char *decoded,
@@ -563,8 +565,8 @@ mhd_str_pct_decode_lenient_n_ (const char *pct_encoded,
* truncated to zero length if broken encoding is found
* @return the number of character in decoded string
*/
size_t
MHD_str_pct_decode_in_place_strict_ (char *str);
MHD_INTERNAL size_t
mhd_str_pct_decode_in_place_strict (char *str);
/**
@@ -585,8 +587,8 @@ MHD_str_pct_decode_in_place_strict_ (char *str);
* optional, can be NULL
* @return the number of character in decoded string
*/
size_t
MHD_str_pct_decode_in_place_lenient_ (char *str,
MHD_INTERNAL size_t
mhd_str_pct_decode_in_place_lenient (char *str,
bool *broken_encoding);
#ifdef DAUTH_SUPPORT
@@ -609,7 +611,7 @@ MHD_str_pct_decode_in_place_lenient_ (char *str,
* non-zero if two strings are equal after unquoting of the
* first string.
*/
bool
MHD_INTERNAL bool
mhd_str_equal_quoted_bin_n (const char *quoted,
size_t quoted_len,
const char *unquoted,
@@ -631,8 +633,8 @@ mhd_str_equal_quoted_bin_n (const char *quoted,
* non-zero if two strings are equal after unquoting of the
* first string.
*/
#define MHD_str_equal_quoted_s_bin_n(q,l,u) \
MHD_str_equal_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
#define mhd_str_equal_quoted_s_bin_n(q,l,u) \
mhd_str_equal_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
/**
* Check two strings for equality, "unquoting" the first string from quoted
@@ -654,7 +656,7 @@ mhd_str_equal_quoted_bin_n (const char *quoted,
* non-zero if two strings are caseless equal after unquoting of the
* first string.
*/
bool
MHD_INTERNAL bool
mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
size_t quoted_len,
const char *unquoted,
@@ -677,8 +679,8 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
* non-zero if two strings are caseless equal after unquoting of the
* first string.
*/
#define MHD_str_equal_caseless_quoted_s_bin_n(q,l,u) \
MHD_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
#define mhd_str_equal_caseless_quoted_s_bin_n(q,l,u) \
mhd_str_equal_caseless_quoted_bin_n (q,l,u,mhd_SSTR_LEN (u))
/**
* Convert string from quoted to unquoted form as specified by
@@ -695,7 +697,7 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
* zero if last backslash is not followed by any character (or
* @a quoted_len is zero).
*/
size_t
MHD_INTERNAL size_t
mhd_str_unquote (const char *quoted,
size_t quoted_len,
char *result);
@@ -719,7 +721,7 @@ mhd_str_unquote (const char *quoted,
* @a unquoted_len, zero if @a unquoted_len is zero or if quoted
* string is larger than @a buf_size.
*/
size_t
MHD_INTERNAL size_t
mhd_str_quote (const char *unquoted,
size_t unquoted_len,
char *result,
@@ -737,7 +739,7 @@ mhd_str_quote (const char *unquoted,
* @a enc_size is valid (properly padded),
* undefined value smaller then @a enc_size if @a enc_size is not valid
*/
#define MHD_base64_max_dec_size_(enc_size) (((enc_size) / 4) * 3)
#define mhd_base64_max_dec_size(enc_size) (((enc_size) / 4) * 3)
/**
* Convert Base64 encoded string to binary data.
@@ -756,7 +758,7 @@ mhd_str_quote (const char *unquoted,
* (base64_len / 4 * 3 - 2), (base64_len / 4 * 3 - 1) or
* (base64_len / 4 * 3), depending on the number of padding characters.
*/
size_t
MHD_INTERNAL size_t
mhd_base64_to_bin_n (const char *base64,
size_t base64_len,
void *bin,
+70
View File
@@ -0,0 +1,70 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/request_funcs.c
* @brief The definition of the request internal functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "mhd_request.h"
#include "mhd_connection.h"
#include "request_funcs.h"
#include "stream_funcs.h"
#include "mhd_dlinked_list.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
enum MHD_ValueKind kind,
const struct MHD_String *restrict name,
const struct MHD_StringNullable *restrict value)
{
struct mhd_RequestField *f;
f = (struct mhd_RequestField *)
mhd_stream_alloc_memory (sizeof(struct mhd_RequestField));
if (NULL == f)
return false;
f->field.nv.name = name;
f->field.nv.value = value;
f->field.kind = kind;
mhd_DLINKEDL_INIT_LINKS(f, fields);
mhd_DLINKEDL_INS_LAST(&(c->rq),f,fields);
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_add_field (struct MHD_Connection *restrict c,
enum MHD_ValueKind kind,
const struct MHD_String *restrict name,
const struct MHD_String *restrict value)
{
struct MHD_StringNullable value2;
value2.len = value->len;
value2.cstr = value->cstr;
return mhd_stream_add_field_nullable(c, kind, name, &value2);
}
+78
View File
@@ -0,0 +1,78 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/request_funcs.h
* @brief The declaration of the request internal functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_REQUEST_FUNCS_H
#define MHD_REQUEST_FUNCS_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
#include "mhd_str_types.h"
#include "mhd_public_api.h"
struct MHD_Connection; /* forward declaration */
/**
* Add field to the request.
* The memory allocated in the request memory pool
*
* @param c the connection to use
* @param kind the kind of the field to add
* @param name the name of the field to add, the string is not copied,
* only copied the pointer value
* @param value the value of the field to add, the string is not copied,
* only copied the pointer value
* @return true if succeed,
* false if memory cannot be allocated
*/
MHD_INTERNAL bool
mhd_stream_add_field (struct MHD_Connection *restrict c,
enum MHD_ValueKind kind,
const struct MHD_String *restrict name,
const struct MHD_String *restrict value)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Add field to the request.
* The memory allocated in the request memory pool
* The value can have NULL string ("no value").
*
* @param c the connection to use
* @param kind the kind of the field to add
* @param name the name of the field to add, the string is not copied,
* only copied the pointer value
* @param value the value of the field to add, the string is not copied,
* only copied the pointer value
* @return true if succeed,
* false if memory cannot be allocated
*/
MHD_INTERNAL bool
mhd_stream_add_field_nullable (struct MHD_Connection *restrict c,
enum MHD_ValueKind kind,
const struct MHD_String *restrict name,
const struct MHD_StringNullable *restrict value)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_REQUEST_FUNCS_H */
+68
View File
@@ -0,0 +1,68 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/request_get_value.c
* @brief The implementation of MHD_request_get_value*() functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "request_get_value.h"
#include "sys_base_types.h"
#include <string.h>
#include "mhd_request.h"
#include "mhd_public_api.h"
#include "mhd_dlinked_list.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_String *
mhd_request_get_value_len (struct MHD_Request *MHD_RESTRICT request,
enum MHD_ValueKind kind,
size_t key_len,
const char *restrict key)
{
struct mhd_RequestField *f;
for (f = mhd_DLINKEDL_GET_FIRST(request, fields); NULL != f;
f = mhd_DLINKEDL_GET_NEXT(f, fields))
{
if ((key_len == f->field.nv.name.len) &&
(kind == f->field.kind) &&
(0 == memcmp(key, f->field.nv.name.cstr)))
return &(f->field.nv.value);
}
return NULL;
}
MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_String *
MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request,
enum MHD_ValueKind kind,
const char *MHD_RESTRICT key)
{
size_t len;
len = strlen(key);
return mhd_request_get_value_len(request, kind, len, key);
}
+56
View File
@@ -0,0 +1,56 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/request_get_value.h
* @brief The declaration of internal mhd_request_get_value* functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_REQUEST_GET_VALUE_H
#define MHD_REQUEST_GET_VALUE_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
#include "mhd_public_api.h"
/**
* Get a particular header (or other kind of request data) value.
* If multiple values match the kind, return any one of them.
*
* The returned pointer is valid until the response is queued.
* If the data is needed beyond this point, it should be copied.
*
* @param request request to get values from
* @param kind what kind of value are we looking for
* @param key the header to look for, empty to lookup 'trailing' value
* without a key
* @return NULL if no such item was found
* @ingroup request
*/
MHD_INTERNAL const struct MHD_String *
mhd_request_get_value_len (struct MHD_Request *restrict request,
enum MHD_ValueKind kind,
size_t key_len,
const char *restrict key)
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4);
#endif /* ! MHD_REQUEST_GET_VALUE_H */
+54 -21
View File
@@ -34,6 +34,54 @@
#include "mhd_public_api.h"
MHD_INTERNAL
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_IN_SIZE_(3,2)
MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_CSTR_ (5) MHD_FN_PAR_IN_SIZE_(5,4) bool
response_add_header_no_check (
struct MHD_Response *response,
size_t name_len,
const char name[MHD_FN_PAR_DYN_ARR_SIZE_(name_len)],
size_t value_len,
const char value[MHD_FN_PAR_DYN_ARR_SIZE_(value_len)])
{
char *buf;
struct mhd_ResponseHeader *new_hdr;
buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2);
if (NULL == buf)
return false;
new_hdr = (struct mhd_ResponseHeader *) buf;
buf += sizeof(struct mhd_ResponseHeader);
memcpy (buf, name, name_len);
buf[name_len] = 0;
new_hdr->name.cstr = buf;
new_hdr->name.len = name_len;
buf += name_len + 1;
memcpy (buf, value, value_len);
buf[value_len] = 0;
new_hdr->value.cstr = buf;
new_hdr->value.len = value_len;
mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) void
mhd_response_remove_all_headers (struct MHD_Response *restrict r)
{
struct mhd_ResponseHeader *hdr;
for (hdr = mhd_DLINKEDL_GET_LAST(r, headers); NULL != hdr;
hdr = mhd_DLINKEDL_GET_LAST(r, headers))
{
mhd_DLINKEDL_DEL(r, hdr, headers);
free (hdr);
}
}
static enum MHD_StatusCode
response_add_header_int (struct MHD_Response *response,
const char *name,
@@ -41,8 +89,6 @@ response_add_header_int (struct MHD_Response *response,
{
const size_t name_len = strlen (name);
const size_t value_len = strlen (name);
char *buf;
struct mhd_ResponseHeader *new_hdr;
if (response->frozen) /* Re-check with the lock held */
return MHD_SC_TOO_LATE;
@@ -57,22 +103,9 @@ response_add_header_int (struct MHD_Response *response,
(NULL != memchr (value, '\r', value_len)))
return MHD_SC_RESP_HEADER_VALUE_INVALID;
buf = malloc (sizeof(struct mhd_ResponseHeader) + name_len + value_len + 2);
if (NULL == buf)
if (!response_add_header_no_check(response, name_len, name, value_len, value))
return MHD_SC_RESPONSE_HEADER_MALLOC_FAILED;
new_hdr = (struct mhd_ResponseHeader *) buf;
buf += sizeof(struct mhd_ResponseHeader);
memcpy (buf, name, name_len + 1);
new_hdr->name.cstr = buf;
new_hdr->name.len = name_len;
buf += name_len + 1;
memcpy (buf, value, value_len + 1);
new_hdr->value.cstr = buf;
new_hdr->value.len = value_len;
mhd_DLINKEDL_INS_LAST (response, new_hdr, headers);
return MHD_SC_OK;
}
@@ -85,7 +118,7 @@ MHD_response_add_header (struct MHD_Response *response,
const char *name,
const char *value)
{
bool need_lock;
bool need_unlock;
enum MHD_StatusCode res;
if (response->frozen)
@@ -93,19 +126,19 @@ MHD_response_add_header (struct MHD_Response *response,
if (response->reuse.reusable)
{
need_lock = true;
need_unlock = true;
if (! mhd_mutex_lock (&(response->reuse.settings_lock)))
return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
mhd_assert (1 == response->reuse.counter);
mhd_assert (1 == mhd_atomic_counter_get(&(response->reuse.counter)));
}
else
need_lock = false;
need_unlock = false;
// TODO: add special processing for "Date", "Connection", "Content-Length"
res = response_add_header_int (response, name, value);
if (need_lock)
if (need_unlock)
mhd_mutex_unlock_chk (&(response->reuse.settings_lock));
return res;
+44
View File
@@ -0,0 +1,44 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/response_add_header.h
* @brief The declarations of the response header manipulation internal
* functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_RESPONSE_ADD_HEADER_H
#define MHD_RESPONSE_ADD_HEADER_H 1
#include "mhd_sys_options.h"
struct MHD_Response; /* forward declaration */
/**
* Remove all response headers
* @param r the response to use
*/
MHD_INTERNAL void
mhd_response_remove_all_headers (struct MHD_Response *restrict r)
MHD_FN_PAR_NONNULL_ (1);
#endif /* ! MHD_RESPONSE_ADD_HEADER_H */
+88
View File
@@ -0,0 +1,88 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/response_destroy.c
* @brief The declarations of internal functions for response deletion
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "response_destroy.h"
#include "mhd_response.h"
#include "mhd_assert.h"
#include "mhd_atomic_counter.h"
#include "mhd_public_api.h"
#include "response_add_header.h"
#include "response_funcs.h"
/**
* Perform full response de-initialisation, with cleaning-up / freeing
* all content data and headers.
* The response settings (if any) must be already freed.
* @param r the response to free
*/
static MHD_FN_PAR_NONNULL_ (1) void
response_full_detinit (struct MHD_Response *restrict r)
{
mhd_response_remove_all_headers (r);
if (r->reuse.reusable)
mhd_response_deinit_reusable(r);
mhd_response_deinit_content_data(r);
free (r);
}
MHD_INTERNAL void
mhd_response_dec_use_count (struct MHD_Response *restrict r)
{
mhd_assert(r->frozen);
if (r->reuse.reusable)
{
if (0 != mhd_atomic_counter_dec_get(&(r->reuse.counter)))
return; /* The response is still used somewhere */
}
response_full_detinit (r);
}
MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1) void
MHD_response_destroy (struct MHD_Response *response)
{
if (! response->frozen)
{
/* This response has been never used for actions */
mhd_assert (NULL != response->settings);
free (response->settings);
#ifndef NDEBUG
/* Decrement counter to avoid triggering assert in deinit function */
mhd_assert (0 == mhd_atomic_counter_dec_get(&(response->reuse.counter)));
#endif
response_full_detinit (response);
return;
}
mhd_response_dec_use_count (response);
}
+43
View File
@@ -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
View File
@@ -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;
+8
View File
@@ -32,5 +32,13 @@
struct MHD_Response; /* forward declaration */
/**
* Deinit / free / cleanup content data of the response
* @param r the response to use
*/
MHD_INTERNAL void
mhd_response_deinit_content_data (struct MHD_Response *restrict r)
MHD_FN_PAR_NONNULL_ (1);
#endif /* ! MHD_RESPONSE_FROM_H */
+44 -6
View File
@@ -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));
}
+22 -9
View File
@@ -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 */
+14 -26
View File
@@ -10,13 +10,14 @@
#include "mhd_sys_options.h"
#include "response_set_options.h"
#include "sys_base_types.h"
#include "sys_bool_type.h"
#include "response_options.h"
#include "mhd_response.h"
#include "mhd_public_api.h"
#include "sys_bool_type.h"
#include "mhd_locks.h"
#include "mhd_assert.h"
/**
* Internal version of the #MHD_response_set_options()
@@ -26,13 +27,13 @@
* @param options_max_num the maximum number of @a options to use
* @return #MHD_SC_OK on success, error code otherwise
*/
static enum MHD_StatusCode
static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode
response_set_options_int (
struct MHD_Response *volatile response,
const struct MHD_ResponseOptionAndValue *volatile options,
struct MHD_Response *restrict response,
const struct MHD_ResponseOptionAndValue *restrict options,
size_t options_max_num)
{
struct ResponseOptions *volatile settings;
struct ResponseOptions *restrict settings;
size_t i;
if (response->frozen)
@@ -55,20 +56,8 @@ response_set_options_int (
else if (response->reuse.reusable)
continue; /* This flag has been set before */
else
{
if (mhd_mutex_init(&(response->reuse.settings_lock)))
{
if (mhd_mutex_init_short(&(response->reuse.cnt_lock)))
{
response->reuse.counter = 1;
response->reuse.reusable = true;
continue;
}
mhd_mutex_destroy_chk(&(response->reuse.settings_lock));
}
return MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
}
settings->reusable = option->val.reusable;
if (! response_make_reusable(response))
return MHD_SC_RESPONSE_MUTEX_INIT_FAILED;
continue;
case MHD_R_O_HEAD_ONLY_RESPONSE:
settings->head_only_response = option->val.head_only_response;
@@ -106,25 +95,24 @@ MHD_response_set_options (struct MHD_Response *response,
const struct MHD_ResponseOptionAndValue *options,
size_t options_max_num)
{
bool need_lock;
bool need_unlock;
enum MHD_StatusCode res;
if (response->frozen)
return MHD_SC_TOO_LATE;
need_unlock = false;
if (response->reuse.reusable)
{
need_lock = true;
need_unlock = true;
if (! mhd_mutex_lock(&(response->reuse.settings_lock)))
return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED;
mhd_assert (1 == response->reuse.counter);
mhd_assert (1 == mhd_atomic_counter_get(&(response->reuse.counter)));
}
else
need_lock = false;
res = response_set_options_int(response, options, options_max_num);
if (need_lock)
if (need_unlock)
mhd_mutex_unlock_chk(&(response->reuse.settings_lock));
return res;
+35
View File
@@ -0,0 +1,35 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/response_set_options.h
* @brief The declarations of the internal response functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_RESPONSE_SET_OPTIONS_H
#define MHD_RESPONSE_SET_OPTIONS_H 1
#include "mhd_sys_options.h"
struct MHD_Response; /* forward declaration */
#endif /* ! MHD_RESPONSE_SET_OPTIONS_H */
+371
View File
@@ -0,0 +1,371 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_funcs.c
* @brief The definition of the stream internal functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "mhd_connection.h"
#include "mhd_public_api.h"
#include "stream_funcs.h"
#include "mhd_assert.h"
#include "mhd_mempool.h"
#include "mhd_str.h"
#include "mhd_str_macros.h"
#include "request_get_value.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void *
mhd_stream_alloc_memory (struct MHD_Connection *restrict c,
size_t size)
{
struct MemoryPool *const restrict pool = c->pool; /* a short alias */
size_t need_to_be_freed = 0; /**< The required amount of additional free memory */
void *res;
res = MHD_pool_try_alloc (pool,
size,
&need_to_be_freed);
if (NULL != res)
return res;
if (MHD_pool_is_resizable_inplace (pool,
c->write_buffer,
c->write_buffer_size))
{
if (c->write_buffer_size - c->write_buffer_append_offset >=
need_to_be_freed)
{
char *buf;
const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
buf = MHD_pool_reallocate (pool,
c->write_buffer,
c->write_buffer_size,
new_buf_size);
mhd_assert (c->write_buffer == buf);
mhd_assert (c->write_buffer_append_offset <= new_buf_size);
mhd_assert (c->write_buffer_send_offset <= new_buf_size);
c->write_buffer_size = new_buf_size;
c->write_buffer = buf;
}
else
return NULL;
}
else if (MHD_pool_is_resizable_inplace (pool,
c->read_buffer,
c->read_buffer_size))
{
if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
{
char *buf;
const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
buf = MHD_pool_reallocate (pool,
c->read_buffer,
c->read_buffer_size,
new_buf_size);
mhd_assert (c->read_buffer == buf);
mhd_assert (c->read_buffer_offset <= new_buf_size);
c->read_buffer_size = new_buf_size;
c->read_buffer = buf;
}
else
return NULL;
}
else
return NULL;
res = MHD_pool_allocate (pool, size, true);
mhd_assert (NULL != res); /* It has been checked that pool has enough space */
return res;
}
#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
/**
* A reasonable headers size (excluding request line) that should be sufficient
* for most requests.
* If incoming data buffer free space is not enough to process the complete
* header (the request line and all headers) and the headers size is larger than
* this size then the status code 431 "Request Header Fields Too Large" is
* returned to the client.
* The larger headers are processed by MHD if enough space is available.
*/
# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
/**
* A reasonable request target (the request URI) size that should be sufficient
* for most requests.
* If incoming data buffer free space is not enough to process the complete
* header (the request line and all headers) and the request target size is
* larger than this size then the status code 414 "URI Too Long" is
* returned to the client.
* The larger request targets are processed by MHD if enough space is available.
* The value chosen according to RFC 9112 Section 3, paragraph 5
*/
# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
/**
* A reasonable headers size (excluding request line) that should be sufficient
* for basic simple requests.
* When no space left in the receiving buffer try to avoid replying with
* the status code 431 "Request Header Fields Too Large" if headers size
* is smaller then this value.
*/
# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
/**
* A reasonable request target (the request URI) size that should be sufficient
* for basic simple requests.
* When no space left in the receiving buffer try to avoid replying with
* the status code 414 "URI Too Long" if the request target size is smaller then
* this value.
*/
# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
/**
* A reasonable request method string size that should be sufficient
* for basic simple requests.
* When no space left in the receiving buffer try to avoid replying with
* the status code 501 "Not Implemented" if the request method size is
* smaller then this value.
*/
# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
/**
* A reasonable minimal chunk line length.
* When no space left in the receiving buffer reply with 413 "Content Too Large"
* if the chunk line length is larger than this value.
*/
# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
MHD_INTERNAL MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_IN_SIZE_(4,3) unsigned int
mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
enum MHD_ProcRecvDataStage stage,
size_t add_element_size,
const char *restrict add_element)
{
size_t method_size;
size_t uri_size;
size_t opt_headers_size;
size_t host_field_line_size;
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
mhd_assert ((0 == add_element_size) || (NULL != add_element));
if (MHD_CONNECTION_HEADERS_RECEIVED > c->state)
{
mhd_assert (NULL != c->rq.field_lines.start);
opt_headers_size =
(size_t) ((c->read_buffer + c->read_buffer_offset)
- c->rq.field_lines.start);
}
else
opt_headers_size = c->rq.field_lines.size;
/* The read buffer is fully used by the request line, the field lines
(headers) and internal information.
The return status code works as a suggestion for the client to reduce
one of the request elements. */
if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
(MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
{
/* Request could be re-tried easily with smaller chunk sizes */
return MHD_HTTP_STATUS_CONTENT_TOO_LARGE;
}
host_field_line_size = 0;
/* The "Host:" field line is mandatory.
The total size of the field lines (headers) cannot be smaller than
the size of the "Host:" field line. */
if ((MHD_PROC_RECV_HEADERS == stage)
&& (0 != add_element_size))
{
static const size_t header_host_key_len =
mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
const bool is_host_header =
(header_host_key_len + 1 <= add_element_size)
&& ( (0 == add_element[header_host_key_len])
|| (':' == add_element[header_host_key_len]) )
&& mhd_str_equal_caseless_bin_n (MHD_HTTP_HEADER_HOST,
add_element,
header_host_key_len);
if (is_host_header)
{
const bool is_parsed = ! (
(MHD_CONNECTION_HEADERS_RECEIVED > c->state) &&
(add_element_size == c->read_buffer_offset) &&
(c->read_buffer == add_element) );
size_t actual_element_size;
mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
/* The actual size should be larger due to CRLF or LF chars,
however the exact termination sequence is not known here and
as perfect precision is not required, to simplify the code
assume the minimal length. */
if (is_parsed)
actual_element_size = add_element_size + 1; /* "1" for LF */
else
actual_element_size = add_element_size;
host_field_line_size = actual_element_size;
mhd_assert (opt_headers_size >= actual_element_size);
opt_headers_size -= actual_element_size;
}
}
if (0 == host_field_line_size)
{
static const size_t host_field_name_len =
mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
struct MHD_String *host_value;
host_value = mhd_request_get_value_len(&(c->rq),
MHD_VK_HEADER,
MHD_HTTP_HEADER_HOST,
host_field_name_len);
if (NULL != host_value)
{
/* Calculate the minimal size of the field line: no space between
colon and the field value, line terminated by LR */
host_field_line_size =
host_field_name_len + host_value->len + 2; /* "2" for ':' and LF */
/* The "Host:" field could be added by application */
if (opt_headers_size >= host_field_line_size)
{
opt_headers_size -= host_field_line_size;
/* Take into account typical space after colon and CR at the end of the line */
if (opt_headers_size >= 2)
opt_headers_size -= 2;
}
else
host_field_line_size = 0; /* No "Host:" field line set by the client */
}
}
uri_size = c->rq.req_target_len;
if (mhd_HTTP_METHOD_OTHER != c->rq.http_mthd)
method_size = 0; /* Do not recommend shorter request method */
else
{
mhd_assert (NULL != c->rq.method);
method_size = strlen (c->rq.method);
}
if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
{
/* Typically the easiest way to reduce request header size is
a removal of some optional headers. */
if (opt_headers_size > (uri_size / 8))
{
if ((opt_headers_size / 2) > method_size)
return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
else
{ /* Request target is MUCH larger than headers */
if ((uri_size / 16) > method_size)
return MHD_HTTP_STATUS_URI_TOO_LONG;
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
}
if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
{
/* If request target size if larger than maximum reasonable size
recommend client to reduce the request target size (length). */
if ((uri_size / 16) > method_size)
return MHD_HTTP_STATUS_URI_TOO_LONG; /* Request target is MUCH larger than headers */
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
/* The read buffer is too small to handle reasonably large requests */
if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
{
/* Recommend application to retry with minimal headers */
if ((opt_headers_size * 4) > uri_size)
{
if (opt_headers_size > method_size)
return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
else
{ /* Request target is significantly larger than headers */
if (uri_size > method_size * 4)
return MHD_HTTP_STATUS_URI_TOO_LONG;
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
}
if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
{
/* Recommend application to retry with a shorter request target */
if (uri_size > method_size * 4)
return MHD_HTTP_STATUS_URI_TOO_LONG;
else
return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
}
if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
{
/* The request target (URI) and headers are (reasonably) very small.
Some non-standard long request method is used. */
/* The last resort response as it means "the method is not supported
by the server for any URI". */
return MHD_HTTP_STATUS_NOT_IMPLEMENTED;
}
/* The almost impossible situation: all elements are small, but cannot
fit the buffer. The application set the buffer size to
critically low value? */
if ((1 < opt_headers_size) || (1 < uri_size))
{
if (opt_headers_size >= uri_size)
return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
else
return MHD_HTTP_STATUS_URI_TOO_LONG;
}
/* Nothing to reduce in the request.
Reply with some status. */
if (0 != host_field_line_size)
return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
return MHD_HTTP_STATUS_URI_TOO_LONG;
}
+86
View File
@@ -0,0 +1,86 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2022-2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_funcs.h
* @brief The declaration of the stream internal functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_STREAM_FUNCS_H
#define MHD_STREAM_FUNCS_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
struct MHD_Connection; /* forward declaration */
/**
* The stage of input data processing.
* Used for out-of-memory (in the pool) handling.
*/
enum MHD_FIXED_ENUM_ MHD_ProcRecvDataStage
{
MHD_PROC_RECV_INIT, /**< No data HTTP request data have been processed yet */
MHD_PROC_RECV_METHOD, /**< Processing/receiving the request HTTP method */
MHD_PROC_RECV_URI, /**< Processing/receiving the request URI */
MHD_PROC_RECV_HTTPVER, /**< Processing/receiving the request HTTP version string */
MHD_PROC_RECV_HEADERS, /**< Processing/receiving the request HTTP headers */
MHD_PROC_RECV_COOKIE, /**< Processing the received request cookie header */
MHD_PROC_RECV_BODY_NORMAL, /**< Processing/receiving the request non-chunked body */
MHD_PROC_RECV_BODY_CHUNKED,/**< Processing/receiving the request chunked body */
MHD_PROC_RECV_FOOTERS /**< Processing/receiving the request footers */
};
/**
* Allocate memory from connection's memory pool.
* If memory pool doesn't have enough free memory but read or write buffer
* have some unused memory, the size of the buffer will be reduced as needed.
* @param connection the connection to use
* @param size the size of allocated memory area
* @return pointer to allocated memory region in the pool or
* NULL if no memory is available
*/
MHD_INTERNAL void *
mhd_stream_alloc_memory (struct MHD_Connection *restrict connection,
size_t size)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Select the HTTP error status code for "out of receive buffer space" error.
* @param c the connection to process
* @param stage the current stage of request receiving
* @param add_element_size the size of the @a add_element;
* zero if @a add_element is NULL
* @param add_element the optional pointer to the element failed to be processed
* or added, the meaning of the element depends on
* the @a stage. Could be not zero-terminated and can
* contain binary zeros. Can be NULL.
* @return the HTTP error code to use in the error reply
*/
MHD_INTERNAL unsigned int
mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
enum MHD_ProcRecvDataStage stage,
size_t add_element_size,
const char *restrict add_element)
MHD_FN_PAR_NONNULL_(1) MHD_FN_PAR_IN_SIZE_(4,3);
#endif /* ! MHD_STREAM_FUNCS_H */
+2149
View File
@@ -0,0 +1,2149 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_process_request.c
* @brief The implementation of internal functions for requests parsing
* and processing
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
#include "sys_base_types.h"
#include "mhd_str_macros.h"
#include <string.h>
#include "mhd_connection.h"
#include "mhd_str_types.h"
#include "daemon_logger.h"
#include "request_funcs.h"
/**
* Response text used when the request (http header) is
* malformed.
*/
#define ERR_RSP_REQUEST_MALFORMED \
"<html><head><title>Request malformed</title></head>" \
"<body>HTTP request is syntactically incorrect.</body></html>"
/**
* Response text used when the request HTTP version is too old.
*/
#define ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD \
"<html>" \
"<head><title>Requested HTTP version is not supported</title></head>" \
"<body>Requested HTTP version is too old and not " \
"supported.</body></html>"
/**
* Response text used when the request HTTP version is not supported.
*/
#define ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED \
"<html>" \
"<head><title>Requested HTTP version is not supported</title></head>" \
"<body>Requested HTTP version is not supported.</body></html>"
/**
* Response text used when the request HTTP header has bare CR character
* without LF character (and CR is not allowed to be treated as whitespace).
*/
#define ERR_RSP_BARE_CR_IN_HEADER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Request HTTP header has bare CR character without " \
"following LF character.</body>" \
"</html>"
/**
* Response text used when the request HTTP footer has bare CR character
* without LF character (and CR is not allowed to be treated as whitespace).
*/
#define ERR_RSP_BARE_CR_IN_FOOTER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Request HTTP footer has bare CR character without " \
"following LF character.</body>" \
"</html>"
/**
* Response text used when the request HTTP header has bare LF character
* without CR character.
*/
#define ERR_RSP_BARE_LF_IN_HEADER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Request HTTP header has bare LF character without " \
"preceding CR character.</body>" \
"</html>"
/**
* Response text used when the request HTTP footer has bare LF character
* without CR character.
*/
#define ERR_RSP_BARE_LF_IN_FOOTER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Request HTTP footer has bare LF character without " \
"preceding CR character.</body>" \
"</html>"
/**
* Response text used when the request line has more then two whitespaces.
*/
#define ERR_RSP_RQ_LINE_TOO_MANY_WSP \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>The request line has more then two whitespaces.</body>" \
"</html>"
/**
* Response text used when the request line has invalid characters in URI.
*/
#define ERR_RSP_RQ_TARGET_INVALID_CHAR \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has invalid characters in " \
"the request-target.</body>" \
"</html>"
/**
* Response text used when line folding is used in request headers.
*/
#define ERR_RSP_OBS_FOLD \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Obsolete line folding is used in HTTP request header.</body>" \
"</html>"
/**
* Response text used when line folding is used in request footers.
*/
#define ERR_RSP_OBS_FOLD_FOOTER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>Obsolete line folding is used in HTTP request footer.</body>" \
"</html>"
/**
* Response text used when request header has no colon character.
*/
#define ERR_RSP_HEADER_WITHOUT_COLON \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request header line has no colon character.</body>" \
"</html>"
/**
* Response text used when request footer has no colon character.
*/
#define ERR_RSP_FOOTER_WITHOUT_COLON \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request footer line has no colon character.</body>" \
"</html>"
/**
* Response text used when the request has whitespace at the start
* of the first header line.
*/
#define ERR_RSP_WSP_BEFORE_HEADER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has whitespace between the request line and " \
"the first header.</body>" \
"</html>"
/**
* Response text used when the request has whitespace at the start
* of the first footer line.
*/
#define ERR_RSP_WSP_BEFORE_FOOTER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>First HTTP footer line has whitespace at the first " \
"position.</body>" \
"</html>"
/**
* Response text used when the whitespace found before colon (inside header
* name or between header name and colon).
*/
#define ERR_RSP_WSP_IN_HEADER_NAME \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has whitespace before the first colon " \
"in header line.</body>" \
"</html>"
/**
* Response text used when the whitespace found before colon (inside header
* name or between header name and colon).
*/
#define ERR_RSP_WSP_IN_FOOTER_NAME \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has whitespace before the first colon " \
"in footer line.</body>" \
"</html>"
/**
* Response text used when request header has invalid character.
*/
#define ERR_RSP_INVALID_CHR_IN_HEADER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has invalid character in header.</body>" \
"</html>"
/**
* Response text used when request header has invalid character.
*/
#define ERR_RSP_INVALID_CHR_IN_FOOTER \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request has invalid character in footer.</body>" \
"</html>"
/**
* Response text used when request header has zero-length header (filed) name.
*/
#define ERR_RSP_EMPTY_HEADER_NAME \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request header has empty header name.</body>" \
"</html>"
/**
* Response text used when request header has zero-length header (filed) name.
*/
#define ERR_RSP_EMPTY_FOOTER_NAME \
"<html>" \
"<head><title>Request broken</title></head>" \
"<body>HTTP request footer has empty footer name.</body>" \
"</html>"
/**
* Response text used when the request header is too big to be processed.
*/
#define ERR_RSP_REQUEST_HEADER_TOO_BIG \
"<html>" \
"<head><title>Request too big</title></head>" \
"<body><p>The total size of the request headers, which includes the " \
"request target and the request field lines, exceeds the memory " \
"constraints of this web server.</p>" \
"<p>The request could be re-tried with shorter field lines, a shorter " \
"request target or a shorter request method token.</p></body>" \
"</html>"
/**
* Get whether bare LF in HTTP header and other protocol elements
* should be treated as the line termination depending on the configured
* strictness level.
* RFC 9112, section 2.2
*/
#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
/**
* The valid length of any HTTP version string
*/
#define HTTP_VER_LEN (mhd_SSTR_LEN(MHD_HTTP_VERSION_1_1_STR))
/**
* Detect HTTP version, send error response if version is not supported
*
* @param connection the connection
* @param http_string the pointer to HTTP version string
* @param len the length of @a http_string in bytes
* @return true if HTTP version is correct and supported,
* false if HTTP version is not correct or unsupported.
*/
static bool
parse_http_version (struct MHD_Connection *restrict connection,
const char *restrict http_string,
size_t len)
{
const char *const h = http_string; /**< short alias */
mhd_assert (NULL != http_string);
/* String must start with 'HTTP/d.d', case-sensetive match.
* See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
if ((HTTP_VER_LEN != len) ||
('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
('/' != h[4])
|| ('.' != h[6]))
{
connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
transmit_error_response_static (connection,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_REQUEST_MALFORMED);
return false;
}
if (1 == h[5] - '0')
{
/* HTTP/1.x */
if (1 == h[7] - '0')
{
connection->rq.http_ver = MHD_HTTP_VERSION_1_1;
return true;
}
else if (0 == h[7] - '0')
{
connection->rq.http_ver = MHD_HTTP_VERSION_1_0;
return true;
}
else
connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
}
else if (0 == h[5] - '0')
{
/* Too old major version */
connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
transmit_error_response_static (connection,
MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD);
return false;
}
else if ((2 == h[5] - '0') &&
(('0' > h[7]) || ('9' < h[7])))
connection->rq.http_ver = MHD_HTTP_VERSION_2;
else
connection->rq.http_ver = MHD_HTTP_VERSION_INVALID;
transmit_error_response_static (connection,
MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED);
return false;
}
#ifndef MHD_MAX_EMPTY_LINES_SKIP
/**
* The maximum number of ignored empty line before the request line
* at default "strictness" level.
*/
#define MHD_MAX_EMPTY_LINES_SKIP 1024
#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
/**
* Find and parse the request line.
* @param c the connection to process
* @return true if request line completely processed (or unrecoverable error
* found) and state is changed,
* false if not enough data yet in the receive buffer
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
mhd_get_request_line_inner (struct MHD_Connection *restrict c)
{
size_t p; /**< The current processing position */
const int discp_lvl = c->daemon->req_cfg.strictnees;
/* Allow to skip one or more empty lines before the request line.
RFC 9112, section 2.2 */
const bool skip_empty_lines = (1 >= discp_lvl);
/* Allow to skip more then one empty line before the request line.
RFC 9112, section 2.2 */
const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
/* Allow to skip unlimited number of empty lines before the request line.
RFC 9112, section 2.2 */
const bool skip_unlimited_empty_lines =
(skip_empty_lines && (-3 >= discp_lvl));
/* Treat bare LF as the end of the line.
RFC 9112, section 2.2 */
const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
/* Treat tab as whitespace delimiter.
RFC 9112, section 3 */
const bool tab_as_wsp = (0 >= discp_lvl);
/* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
RFC 9112, section 3 */
const bool other_wsp_as_wsp = (-1 >= discp_lvl);
/* Treat continuous whitespace block as a single space.
RFC 9112, section 3 */
const bool wsp_blocks = (-1 >= discp_lvl);
/* Parse whitespace in URI, special parsing of the request line.
RFC 9112, section 3.2 */
const bool wsp_in_uri = (0 >= discp_lvl);
/* Keep whitespace in URI, give app URI with whitespace instead of
automatic redirect to fixed URI.
Violates RFC 9112, section 3.2 */
const bool wsp_in_uri_keep = (-2 >= discp_lvl);
/* Keep bare CR character as is.
Violates RFC 9112, section 2.2 */
const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
/* Treat bare CR as space; replace it with space before processing.
RFC 9112, section 2.2 */
const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
mhd_assert (MHD_CONNECTION_INIT == c->state || \
MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
mhd_assert (NULL == c->rq.method || \
MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd || \
0 != c->rq.hdrs.rq_line.proc_pos);
if (0 == c->read_buffer_offset)
{
mhd_assert (MHD_CONNECTION_INIT == c->state);
return false; /* No data to process */
}
p = c->rq.hdrs.rq_line.proc_pos;
mhd_assert (p <= c->read_buffer_offset);
/* Skip empty lines, if any (and if allowed) */
/* See RFC 9112, section 2.2 */
if ((0 == p)
&& (skip_empty_lines))
{
/* Skip empty lines before the request line.
See RFC 9112, section 2.2 */
bool is_empty_line;
mhd_assert (MHD_CONNECTION_INIT == c->state);
mhd_assert (NULL == c->rq.method);
mhd_assert (NULL == c->rq.url);
mhd_assert (0 == c->rq.url_len);
mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
mhd_assert (0 == c->rq.req_target_len);
mhd_assert (NULL == c->rq.version);
do
{
is_empty_line = false;
if ('\r' == c->read_buffer[0])
{
if (1 == c->read_buffer_offset)
return false; /* Not enough data yet */
if ('\n' == c->read_buffer[1])
{
is_empty_line = true;
c->read_buffer += 2;
c->read_buffer_size -= 2;
c->read_buffer_offset -= 2;
c->rq.hdrs.rq_line.skipped_empty_lines++;
}
}
else if (('\n' == c->read_buffer[0]) &&
(bare_lf_as_crlf))
{
is_empty_line = true;
c->read_buffer += 1;
c->read_buffer_size -= 1;
c->read_buffer_offset -= 1;
c->rq.hdrs.rq_line.skipped_empty_lines++;
}
if (is_empty_line)
{
if ((! skip_unlimited_empty_lines) &&
(((unsigned int) ((skip_several_empty_lines) ?
MHD_MAX_EMPTY_LINES_SKIP : 1)) <
c->rq.hdrs.rq_line.skipped_empty_lines))
{
connection_close_error (c,
_ ("Too many meaningless extra empty lines " \
"received before the request"));
return true; /* Process connection closure */
}
if (0 == c->read_buffer_offset)
return false; /* No more data to process */
}
} while (is_empty_line);
}
/* All empty lines are skipped */
c->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
/* Read and parse the request line */
mhd_assert (1 <= c->read_buffer_offset);
while (p < c->read_buffer_offset)
{
char *const restrict read_buffer = c->read_buffer;
const char chr = read_buffer[p];
bool end_of_line;
/*
The processing logic is different depending on the configured strictness:
When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
processed BEFORE processing of the current character.
When whitespace BLOCKS are ALLOWED, the end of the whitespace is
processed AFTER processing of the current character.
When space char in the URI is ALLOWED, the delimiter between the URI and
the HTTP version string is processed only at the END of the line.
When space in the URI is NOT ALLOWED, the delimiter between the URI and
the HTTP version string is processed as soon as the FIRST whitespace is
found after URI start.
*/
end_of_line = false;
mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
(c->rq.hdrs.rq_line.last_ws_end > \
c->rq.hdrs.rq_line.last_ws_start));
mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
(0 != c->rq.hdrs.rq_line.last_ws_end));
/* Check for the end of the line */
if ('\r' == chr)
{
if (p + 1 == c->read_buffer_offset)
{
c->rq.hdrs.rq_line.proc_pos = p;
return false; /* Not enough data yet */
}
else if ('\n' == read_buffer[p + 1])
end_of_line = true;
else
{
/* Bare CR alone */
/* Must be rejected or replaced with space char.
See RFC 9112, section 2.2 */
if (bare_cr_as_sp)
{
read_buffer[p] = ' ';
c->rq.num_cr_sp_replaced++;
continue; /* Re-start processing of the current character */
}
else if (! bare_cr_keep)
{
/* A quick simple check whether this line looks like an HTTP request */
if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
(mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
{
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_CR_IN_HEADER);
}
else
connection_close_error (c,
_ ("Bare CR characters are not allowed " \
"in the request line.\n"));
return true; /* Error in the request */
}
}
}
else if ('\n' == chr)
{
/* Bare LF may be recognised as a line delimiter.
See RFC 9112, section 2.2 */
if (bare_lf_as_crlf)
end_of_line = true;
else
{
/* While RFC does not enforce error for bare LF character,
if this char is not treated as a line delimiter, it should be
rejected to avoid any security weakness due to request smuggling. */
/* A quick simple check whether this line looks like an HTTP request */
if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
(mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
{
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_LF_IN_HEADER);
}
else
connection_close_error (c,
_ ("Bare LF characters are not allowed " \
"in the request line.\n"));
return true; /* Error in the request */
}
}
if (end_of_line)
{
/* Handle the end of the request line */
if (NULL != c->rq.method)
{
if (wsp_in_uri)
{
/* The end of the URI and the start of the HTTP version string
should be determined now. */
mhd_assert (NULL == c->rq.version);
mhd_assert (0 == c->rq.req_target_len);
if (0 != c->rq.hdrs.rq_line.last_ws_end)
{
/* Determine the end and the length of the URI */
if (NULL != c->rq.hdrs.rq_line.rq_tgt)
{
read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
c->rq.req_target_len =
c->rq.hdrs.rq_line.last_ws_start
- (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
}
else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
c->rq.hdrs.rq_line.last_ws_end) &&
(HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
{
/* Found only HTTP method and HTTP version and more than one
whitespace between them. Assume zero-length URI. */
mhd_assert (wsp_blocks);
c->rq.hdrs.rq_line.last_ws_start++;
read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
c->rq.hdrs.rq_line.rq_tgt =
read_buffer + c->rq.hdrs.rq_line.last_ws_start;
c->rq.req_target_len = 0;
c->rq.hdrs.rq_line.num_ws_in_uri = 0;
c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
}
/* Determine the start of the HTTP version string */
if (NULL != c->rq.hdrs.rq_line.rq_tgt)
{
c->rq.version = read_buffer + c->rq.hdrs.rq_line.last_ws_end;
}
}
}
else
{
/* The end of the URI and the start of the HTTP version string
should be already known. */
if ((NULL == c->rq.version)
&& (NULL != c->rq.hdrs.rq_line.rq_tgt)
&& (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
- read_buffer))
&& (0 != read_buffer[(size_t)
(c->rq.hdrs.rq_line.rq_tgt
- read_buffer) - 1]))
{
/* Found only HTTP method and HTTP version and more than one
whitespace between them. Assume zero-length URI. */
size_t uri_pos;
mhd_assert (wsp_blocks);
mhd_assert (0 == c->rq.req_target_len);
uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer) - 1;
mhd_assert (uri_pos < p);
c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
read_buffer[uri_pos] = 0; /* Zero terminate the URI */
c->rq.hdrs.rq_line.rq_tgt = read_buffer + uri_pos;
c->rq.req_target_len = 0;
c->rq.hdrs.rq_line.num_ws_in_uri = 0;
c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
}
}
if (NULL != c->rq.version)
{
mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
if (! parse_http_version (c, c->rq.version,
p
- (size_t) (c->rq.version
- read_buffer)))
{
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
return true; /* Unsupported / broken HTTP version */
}
read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
if ('\r' == chr)
{
p++; /* Consume CR */
mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
}
p++; /* Consume LF */
c->read_buffer += p;
c->read_buffer_size -= p;
c->read_buffer_offset -= p;
mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
c->rq.req_target_len);
mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
(0 != c->rq.req_target_len));
mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
- c->rq.hdrs.rq_line.rq_tgt) < \
c->rq.req_target_len));
mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
(c->rq.hdrs.rq_line.rq_tgt_qmark >= \
c->rq.hdrs.rq_line.rq_tgt));
return true; /* The request line is successfully parsed */
}
}
/* Error in the request line */
/* A quick simple check whether this line looks like an HTTP request */
if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
(mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
{
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_REQUEST_MALFORMED);
}
else
connection_close_error (c,
_ ("The request line is malformed.\n"));
return true;
}
/* Process possible end of the previously found whitespace delimiter */
if ((! wsp_blocks) &&
(p == c->rq.hdrs.rq_line.last_ws_end) &&
(0 != c->rq.hdrs.rq_line.last_ws_end))
{
/* Previous character was a whitespace char and whitespace blocks
are not allowed. */
/* The current position is the next character after
a whitespace delimiter */
if (NULL == c->rq.hdrs.rq_line.rq_tgt)
{
/* The current position is the start of the URI */
mhd_assert (0 == c->rq.req_target_len);
mhd_assert (NULL == c->rq.version);
c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
/* Reset the whitespace marker */
c->rq.hdrs.rq_line.last_ws_start = 0;
c->rq.hdrs.rq_line.last_ws_end = 0;
}
else
{
/* It was a whitespace after the start of the URI */
if (! wsp_in_uri)
{
mhd_assert ((0 != c->rq.req_target_len) || \
(c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
c->rq.version = read_buffer + p;
/* Reset the whitespace marker */
c->rq.hdrs.rq_line.last_ws_start = 0;
c->rq.hdrs.rq_line.last_ws_end = 0;
}
}
}
/* Process the current character.
Is it not the end of the line. */
if ((' ' == chr)
|| (('\t' == chr) && (tab_as_wsp))
|| ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
{
/* A whitespace character */
if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
(p != c->rq.hdrs.rq_line.last_ws_end) ||
(! wsp_blocks))
{
/* Found first whitespace char of the new whitespace block */
if (NULL == c->rq.method)
{
/* Found the end of the HTTP method string */
mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start);
mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end);
mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
mhd_assert (0 == c->rq.req_target_len);
mhd_assert (NULL == c->rq.version);
if (0 == p)
{
connection_close_error (c,
_ ("The request line starts with "
"a whitespace.\n"));
return true; /* Error in the request */
}
read_buffer[p] = 0; /* Zero-terminate the request method string */
c->rq.method = read_buffer;
parse_http_std_method (c, c->rq.method, p);
}
else
{
/* A whitespace after the start of the URI */
if (! wsp_in_uri)
{
/* Whitespace in URI is not allowed to be parsed */
if (NULL == c->rq.version)
{
mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
/* This is a delimiter between URI and HTTP version string */
read_buffer[p] = 0; /* Zero-terminate request URI string */
mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
- read_buffer)) <= p);
c->rq.req_target_len =
p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - read_buffer);
}
else
{
/* This is a delimiter AFTER version string */
/* A quick simple check whether this line looks like an HTTP request */
if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) &&
(mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd))
{
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_RQ_LINE_TOO_MANY_WSP);
}
else
connection_close_error (c,
_ ("The request line has more than "
"two whitespaces.\n"));
return true; /* Error in the request */
}
}
else
{
/* Whitespace in URI is allowed to be parsed */
if (0 != c->rq.hdrs.rq_line.last_ws_end)
{
/* The whitespace after the start of the URI has been found already */
c->rq.hdrs.rq_line.num_ws_in_uri +=
c->rq.hdrs.rq_line.last_ws_end
- c->rq.hdrs.rq_line.last_ws_start;
}
}
}
c->rq.hdrs.rq_line.last_ws_start = p;
c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
}
else
{
/* Continuation of the whitespace block */
mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end);
mhd_assert (0 != p);
c->rq.hdrs.rq_line.last_ws_end = p + 1;
}
}
else
{
/* Non-whitespace char, not the end of the line */
mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
(c->rq.hdrs.rq_line.last_ws_end == p) || \
wsp_in_uri);
if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
(0 != c->rq.hdrs.rq_line.last_ws_end) &&
(wsp_blocks))
{
/* The end of the whitespace block */
if (NULL == c->rq.hdrs.rq_line.rq_tgt)
{
/* This is the first character of the URI */
mhd_assert (0 == c->rq.req_target_len);
mhd_assert (NULL == c->rq.version);
c->rq.hdrs.rq_line.rq_tgt = read_buffer + p;
/* Reset the whitespace marker */
c->rq.hdrs.rq_line.last_ws_start = 0;
c->rq.hdrs.rq_line.last_ws_end = 0;
}
else
{
if (! wsp_in_uri)
{
/* This is the first character of the HTTP version */
mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
mhd_assert ((0 != c->rq.req_target_len) || \
(c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p));
mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
c->rq.version = read_buffer + p;
/* Reset the whitespace marker */
c->rq.hdrs.rq_line.last_ws_start = 0;
c->rq.hdrs.rq_line.last_ws_end = 0;
}
}
}
/* Handle other special characters */
if ('?' == chr)
{
if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
(NULL != c->rq.hdrs.rq_line.rq_tgt))
{
c->rq.hdrs.rq_line.rq_tgt_qmark = read_buffer + p;
}
}
else if ((0xb == chr) || (0xc == chr))
{
/* VT or LF characters */
mhd_assert (! other_wsp_as_wsp);
if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
(NULL == c->rq.version) &&
(wsp_in_uri))
{
c->rq.hdrs.rq_line.num_ws_in_uri++;
}
else
{
connection_close_error (c,
_ ("Invalid character is in the "
"request line.\n"));
return true; /* Error in the request */
}
}
else if (0 == chr)
{
/* NUL character */
connection_close_error (c,
_ ("The NUL character is in the "
"request line.\n"));
return true; /* Error in the request */
}
}
p++;
}
c->rq.hdrs.rq_line.proc_pos = p;
return false; /* Not enough data yet */
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_get_request_line (struct MHD_Connection *restrict c)
{
const int discp_lvl = c->daemon->req_cfg.strictnees;
/* Parse whitespace in URI, special parsing of the request line */
const bool wsp_in_uri = (0 >= discp_lvl);
/* Keep whitespace in URI, give app URI with whitespace instead of
automatic redirect to fixed URI */
const bool wsp_in_uri_keep = (-2 >= discp_lvl);
if (! get_request_line_inner (c))
{
/* End of the request line has not been found yet */
mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
if ((NULL != c->rq.version) &&
(HTTP_VER_LEN <
(c->rq.hdrs.rq_line.proc_pos
- (size_t) (c->rq.version - c->read_buffer))))
{
c->rq.http_ver = MHD_HTTP_VERSION_INVALID;
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_REQUEST_MALFORMED);
return true; /* Error in the request */
}
return false;
}
if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
return true; /* Error in the request */
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
mhd_assert (NULL == c->rq.url);
mhd_assert (0 == c->rq.url_len);
mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
{
if (! wsp_in_uri)
{
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_RQ_TARGET_INVALID_CHAR);
return true; /* Error in the request */
}
if (! wsp_in_uri_keep)
{
send_redirect_fixed_rq_target (c);
return true; /* Error in the request */
}
}
if (! process_request_target (c))
return true; /* Error in processing */
c->state = MHD_CONNECTION_REQ_LINE_RECEIVED;
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_switch_to_rq_headers_processing (struct MHD_Connection *restrict c)
{
c->rq.field_lines.start = c->read_buffer;
memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
c->state = MHD_CONNECTION_REQ_HEADERS_RECEIVING;
}
/**
* Send error reply when receive buffer space exhausted while receiving or
* storing the request headers
* @param c the connection to handle
* @param add_header the optional pointer to the current header string being
* processed or the header failed to be added.
* Could be not zero-terminated and can contain binary zeros.
* Can be NULL.
* @param add_header_size the size of the @a add_header
*/
static void
handle_req_headers_no_space (struct MHD_Connection *restrict c,
const char *restrict add_header,
size_t add_header_size)
{
unsigned int err_code;
err_code = get_no_space_err_status_code (c,
MHD_PROC_RECV_HEADERS,
add_header,
add_header_size);
transmit_error_response_static (c,
err_code,
ERR_RSP_REQUEST_HEADER_TOO_BIG);
}
/**
* Results of header line reading
*/
enum mhd_HdrLineReadRes
{
/**
* Not enough data yet
*/
MHD_HDR_LINE_READING_NEED_MORE_DATA = 0,
/**
* New header line has been read
*/
MHD_HDR_LINE_READING_GOT_HEADER,
/**
* Error in header data, error response has been queued
*/
MHD_HDR_LINE_READING_DATA_ERROR,
/**
* Found the end of the request header (end of field lines)
*/
MHD_HDR_LINE_READING_GOT_END_OF_HEADER
} _MHD_FIXED_ENUM;
/**
* Find the end of the request header line and make basic header parsing.
* Handle errors and header folding.
* @param c the connection to process
* @param process_footers if true then footers are processed,
* if false then headers are processed
* @param[out] hdr_name the name of the parsed header (field)
* @param[out] hdr_name the value of the parsed header (field)
* @return true if request header line completely processed,
* false if not enough data yet in the receive buffer
*/
static enum MHD_HdrLineReadRes_
get_req_header (struct MHD_Connection *restrict c,
bool process_footers,
struct MHD_String *restrict hdr_name,
struct MHD_String *restrict hdr_value)
{
const int discp_lvl = c->daemon->req_cfg.strictnees;
/* Treat bare LF as the end of the line.
RFC 9112, section 2.2-3
Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
Bare LF is processed as end of the line or rejected as broken request. */
const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
/* Keep bare CR character as is.
Violates RFC 9112, section 2.2-4 */
const bool bare_cr_keep = (-3 >= discp_lvl);
/* Treat bare CR as space; replace it with space before processing.
RFC 9112, section 2.2-4 */
const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
/* Treat NUL as space; replace it with space before processing.
RFC 9110, section 5.5-5 */
const bool nul_as_sp = (-1 >= discp_lvl);
/* Allow folded header lines.
RFC 9112, section 5.2-4 */
const bool allow_folded = (0 >= discp_lvl);
/* Do not reject headers with the whitespace at the start of the first line.
When allowed, the first line with whitespace character at the first
position is ignored (as well as all possible line foldings of the first
line).
RFC 9112, section 2.2-8 */
const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
/* Allow whitespace in header (field) name.
Violates RFC 9110, section 5.1-2 */
const bool allow_wsp_in_name = (-2 >= discp_lvl);
/* Allow zero-length header (field) name.
Violates RFC 9110, section 5.1-2 */
const bool allow_empty_name = (-2 >= discp_lvl);
/* Allow whitespace before colon.
Violates RFC 9112, section 5.1-2 */
const bool allow_wsp_before_colon = (-3 >= discp_lvl);
/* Do not abort the request when header line has no colon, just skip such
bad lines.
RFC 9112, section 5-1 */
const bool allow_line_without_colon = (-2 >= discp_lvl);
size_t p; /**< The position of the currently processed character */
(void) process_footers; /* Unused parameter in non-debug and no messages */
mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
c->state);
p = c->rq.hdrs.hdr.proc_pos;
mhd_assert (p <= c->read_buffer_offset);
while (p < c->read_buffer_offset)
{
char *const restrict read_buffer = c->read_buffer;
const char chr = read_buffer[p];
bool end_of_line;
mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
(c->rq.hdrs.hdr.name_len < p));
mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
(c->rq.hdrs.hdr.name_end_found));
mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
(c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
(0 != c->rq.hdrs.hdr.name_len));
mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
(0 == c->rq.hdrs.hdr.name_len) || \
(c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
(0 == c->rq.hdrs.hdr.value_start) || \
(c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
/* Check for the end of the line */
if ('\r' == chr)
{
if (0 != p)
{
/* Line is not empty, need to check for possible line folding */
if (p + 2 >= c->read_buffer_offset)
break; /* Not enough data yet to check for folded line */
}
else
{
/* Line is empty, no need to check for possible line folding */
if (p + 2 > c->read_buffer_offset)
break; /* Not enough data yet to check for the end of the line */
}
if ('\n' == read_buffer[p + 1])
end_of_line = true;
else
{
/* Bare CR alone */
/* Must be rejected or replaced with space char.
See RFC 9112, section 2.2-4 */
if (bare_cr_as_sp)
{
read_buffer[p] = ' ';
c->rq.num_cr_sp_replaced++;
continue; /* Re-start processing of the current character */
}
else if (! bare_cr_keep)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_CR_IN_HEADER);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_CR_IN_FOOTER);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
end_of_line = false;
}
}
else if ('\n' == chr)
{
/* Bare LF may be recognised as a line delimiter.
See RFC 9112, section 2.2-3 */
if (bare_lf_as_crlf)
{
if (0 != p)
{
/* Line is not empty, need to check for possible line folding */
if (p + 1 >= c->read_buffer_offset)
break; /* Not enough data yet to check for folded line */
}
end_of_line = true;
}
else
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_LF_IN_HEADER);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_BARE_LF_IN_FOOTER);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
}
else
end_of_line = false;
if (end_of_line)
{
/* Handle the end of the line */
/**
* The full length of the line, including CRLF (or bare LF).
*/
const size_t line_len = p + (('\r' == chr) ? 2 : 1);
char next_line_char;
mhd_assert (line_len <= c->read_buffer_offset);
if (0 == p)
{
/* Zero-length header line. This is the end of the request header
section.
RFC 9112, Section 2.1-1 */
mhd_assert (! c->rq.hdrs.hdr.starts_with_ws);
mhd_assert (! c->rq.hdrs.hdr.name_end_found);
mhd_assert (0 == c->rq.hdrs.hdr.name_len);
mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
mhd_assert (0 == c->rq.hdrs.hdr.value_start);
/* Consume the line with CRLF (or bare LF) */
c->read_buffer += line_len;
c->read_buffer_offset -= line_len;
c->read_buffer_size -= line_len;
return MHD_HDR_LINE_READING_GOT_END_OF_HEADER;
}
mhd_assert (line_len < c->read_buffer_offset);
mhd_assert (0 != line_len);
mhd_assert ('\n' == read_buffer[line_len - 1]);
next_line_char = read_buffer[line_len];
if ((' ' == next_line_char) ||
('\t' == next_line_char))
{
/* Folded line */
if (! allow_folded)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_OBS_FOLD);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_OBS_FOLD_FOOTER);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
/* Replace CRLF (or bare LF) character(s) with space characters.
See RFC 9112, Section 5.2-4 */
read_buffer[p] = ' ';
if ('\r' == chr)
read_buffer[p + 1] = ' ';
continue; /* Re-start processing of the current character */
}
else
{
/* It is not a folded line, it's the real end of the non-empty line */
bool skip_line = false;
mhd_assert (0 != p);
if (c->rq.hdrs.hdr.starts_with_ws)
{
/* This is the first line and it starts with whitespace. This line
must be discarded completely.
See RFC 9112, Section 2.2-8 */
mhd_assert (allow_wsp_at_start);
MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED,
"Whitespace-prefixed first header line " \
"has been skipped.");
skip_line = true;
}
else if (! c->rq.hdrs.hdr.name_end_found)
{
if (! allow_line_without_colon)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_HEADER_WITHOUT_COLON);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_FOOTER_WITHOUT_COLON);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
/* Skip broken line completely */
c->rq.skipped_broken_lines++;
skip_line = true;
}
if (skip_line)
{
/* Skip the entire line */
c->read_buffer += line_len;
c->read_buffer_offset -= line_len;
c->read_buffer_size -= line_len;
p = 0;
/* Reset processing state */
memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
/* Start processing of the next line */
continue;
}
else
{
/* This line should be valid header line */
size_t value_len;
mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */
hdr_name->len = c->rq.hdrs.hdr.name_len;
mhd_assert (0 == hdr_name->cstr[hdr_name->len]);
if (0 == c->rq.hdrs.hdr.value_start)
{
c->rq.hdrs.hdr.value_start = p;
read_buffer[p] = 0;
value_len = 0;
}
else if (0 != c->rq.hdrs.hdr.ws_start)
{
mhd_assert (p > c->rq.hdrs.hdr.ws_start);
mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start);
read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
}
else
{
mhd_assert (p > c->rq.hdrs.hdr.ws_start);
read_buffer[p] = 0;
value_len = p - c->rq.hdrs.hdr.value_start;
}
hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start;
hdr_value->len = value_len;
mhd_assert (0 == hdr_value->cstr[hdr_value->len]);
/* Consume the entire line */
c->read_buffer += line_len;
c->read_buffer_offset -= line_len;
c->read_buffer_size -= line_len;
return MHD_HDR_LINE_READING_GOT_HEADER;
}
}
}
else if ((' ' == chr) || ('\t' == chr))
{
if (0 == p)
{
if (! allow_wsp_at_start)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_BEFORE_HEADER);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_BEFORE_FOOTER);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
c->rq.hdrs.hdr.starts_with_ws = true;
}
else if ((! c->rq.hdrs.hdr.name_end_found) &&
(! c->rq.hdrs.hdr.starts_with_ws))
{
/* Whitespace in header name / between header name and colon */
if (allow_wsp_in_name || allow_wsp_before_colon)
{
if (0 == c->rq.hdrs.hdr.ws_start)
c->rq.hdrs.hdr.ws_start = p;
}
else
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_HEADER_NAME);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_FOOTER_NAME);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
}
else
{
/* Whitespace before/inside/after header (field) value */
if (0 == c->rq.hdrs.hdr.ws_start)
c->rq.hdrs.hdr.ws_start = p;
}
}
else if (0 == chr)
{
if (! nul_as_sp)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_INVALID_CHR_IN_HEADER);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_INVALID_CHR_IN_FOOTER);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
read_buffer[p] = ' ';
continue; /* Re-start processing of the current character */
}
else
{
/* Not a whitespace, not the end of the header line */
mhd_assert ('\r' != chr);
mhd_assert ('\n' != chr);
mhd_assert ('\0' != chr);
if ((! c->rq.hdrs.hdr.name_end_found) &&
(! c->rq.hdrs.hdr.starts_with_ws))
{
/* Processing the header (field) name */
if (':' == chr)
{
if (0 == c->rq.hdrs.hdr.ws_start)
c->rq.hdrs.hdr.name_len = p;
else
{
mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
if (! allow_wsp_before_colon)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_HEADER_NAME);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_FOOTER_NAME);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
#ifndef MHD_FAVOR_SMALL_CODE
c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_EMPTY_HEADER_NAME);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_EMPTY_FOOTER_NAME);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
c->rq.hdrs.hdr.name_end_found = true;
read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
}
else
{
if (0 != c->rq.hdrs.hdr.ws_start)
{
/* End of the whitespace in header (field) name */
mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
if (! allow_wsp_in_name)
{
if (! process_footers)
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_HEADER_NAME);
else
transmit_error_response_static (c,
MHD_HTTP_STATUS_BAD_REQUEST,
ERR_RSP_WSP_IN_FOOTER_NAME);
return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
}
#ifndef MHD_FAVOR_SMALL_CODE
c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
}
}
else
{
/* Processing the header (field) value */
if (0 == c->rq.hdrs.hdr.value_start)
c->rq.hdrs.hdr.value_start = p;
#ifndef MHD_FAVOR_SMALL_CODE
c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
#endif /* ! MHD_FAVOR_SMALL_CODE */
}
#ifdef MHD_FAVOR_SMALL_CODE
c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
#endif /* MHD_FAVOR_SMALL_CODE */
}
p++;
}
c->rq.hdrs.hdr.proc_pos = p;
return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_get_req_headers (struct MHD_Connection *restrict c, bool process_footers)
{
do
{
struct MHD_String hdr_name;
struct MHD_String hdr_value;
enum mhd_HdrLineReadRes res;
mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
c->state);
#ifndef NDEBUG
hdr_name.cstr = NULL;
hdr_value.cstr = NULL;
#endif /* ! NDEBUG */
res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
if (MHD_HDR_LINE_READING_GOT_HEADER == res)
{
mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
c->state);
mhd_assert (NULL != hdr_name.cstr);
mhd_assert (NULL != hdr_value.cstr);
/* Values must be zero-terminated and must not have binary zeros */
mhd_assert (strlen (hdr_name.cstr) == hdr_name.len);
mhd_assert (strlen (hdr_value.cstr) == hdr_value.len);
/* Values must not have whitespaces at the start or at the end */
mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' '));
mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t'));
mhd_assert ((hdr_name.len == 0) || \
(hdr_name.cstr[hdr_name.len - 1] != ' '));
mhd_assert ((hdr_name.len == 0) || \
(hdr_name.cstr[hdr_name.len - 1] != '\t'));
mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' '));
mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t'));
mhd_assert ((hdr_value.len == 0) || \
(hdr_value.cstr[hdr_value.len - 1] != ' '));
mhd_assert ((hdr_value.len == 0) || \
(hdr_value.cstr[hdr_value.len - 1] != '\t'));
if (! mhd_stream_add_field (c,
process_footers ?
MHD_VK_FOOTER : MHD_VK_HEADER,
&hdr_name,
&hdr_value))
{
size_t add_element_size;
mhd_assert (hdr_name.cstr < hdr_value.cstr);
if (!process_footers)
MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
"Failed to allocate memory in the connection memory " \
"pool to store header.");
else
MHD_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_MALLOC_FAILURE_REQ, \
"Failed to allocate memory in the connection memory " \
"pool to store footer.");
add_element_size = hdr_value.len
+ (size_t) (hdr_value.cstr - hdr_name.cstr);
if (! process_footers)
handle_req_headers_no_space (c, hdr_name.cstr, add_element_size);
else
handle_req_footers_no_space (c, hdr_name.cstr, add_element_size);
mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
return true;
}
/* Reset processing state */
reset_rq_header_processing_state (c);
mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
c->state);
/* Read the next header (field) line */
continue;
}
else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
{
mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
c->state);
return false;
}
else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
{
mhd_assert ((process_footers ? \
MHD_CONNECTION_FOOTERS_RECEIVING : \
MHD_CONNECTION_REQ_HEADERS_RECEIVING) < c->state);
mhd_assert (c->stop_with_error);
mhd_assert (c->discard_request);
return true;
}
mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res);
break;
} while (1);
if (1 == c->rq.num_cr_sp_replaced)
{
if (! process_footers)
MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
"One bare CR character has been replaced with space " \
"in the request line or in the request headers.");
else
MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \
"One bare CR character has been replaced with space " \
"in the request footers.");
}
else if (0 != c->rq.num_cr_sp_replaced)
{
if (! process_footers)
MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
MHD_LOG_FMT("%" PRIuFAST64 " bare CR characters have " \
"been replaced with spaces in the request " \
"line and/or in the request headers."), \
(uint_fast64_t) c->rq.num_cr_sp_replaced);
else
MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
MHD_LOG_FMT("%" PRIuFAST64 " bare CR characters have " \
"been replaced with spaces in the request " \
"footers."), \
(uint_fast64_t) c->rq.num_cr_sp_replaced);
}
if (1 == c->rq.skipped_broken_lines)
{
if (! process_footers)
MHD_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \
"One header line without colon has been skipped.");
else
MHD_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \
"One footer line without colon has been skipped.");
}
else if (0 != c->rq.skipped_broken_lines)
{
if (! process_footers)
MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
MHD_LOG_FMT("%" PRIu64 " header lines without colons "
"have been skipped."),
(uint_fast64_t) c->rq.skipped_broken_lines);
else
MHD_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \
MHD_LOG_FMT("%" PRIu64 " footer lines without colons "
"have been skipped."),
(uint_fast64_t) c->rq.skipped_broken_lines);
}
mhd_assert (c->rq.method < c->read_buffer);
if (! process_footers)
{
c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
mhd_assert (NULL != c->rq.field_lines.start);
c->rq.field_lines.size =
(size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
if ('\r' == *(c->read_buffer - 2))
c->rq.field_lines.size--;
c->state = MHD_CONNECTION_HEADERS_RECEIVED;
if (mhd_BUF_INC_SIZE > c->read_buffer_size)
{
/* Try to re-use some of the last bytes of the request header */
/* Do this only if space in the read buffer is limited AND
amount of read ahead data is small. */
/**
* The position of the terminating NUL after the last character of
* the last header element.
*/
const char *last_elmnt_end;
size_t shift_back_size;
struct mhd_RequestField *header;
header = mhd_DLINKEDL_GET_LAST(&(c->rq), fields);
if (NULL != header)
last_elmnt_end =
header->field.nv.value.cstr + header->field.nv.value.len;
else
last_elmnt_end = c->rq.version + HTTP_VER_LEN;
mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
if (0 != c->read_buffer_offset)
memmove (c->read_buffer - shift_back_size,
c->read_buffer,
c->read_buffer_offset);
c->read_buffer -= shift_back_size;
c->read_buffer_size += shift_back_size;
}
}
else
c->state = MHD_CONNECTION_FOOTERS_RECEIVED;
return true;
}
#ifdef COOKIE_SUPPORT
/**
* Cookie parsing result
*/
enum _MHD_ParseCookie
{
MHD_PARSE_COOKIE_OK = MHD_YES, /**< Success or no cookies in headers */
MHD_PARSE_COOKIE_OK_LAX = 2, /**< Cookies parsed, but workarounds used */
MHD_PARSE_COOKIE_MALFORMED = -1, /**< Invalid cookie header */
MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO /**< Not enough memory in the pool */
};
/**
* Parse the cookies string (see RFC 6265).
*
* Try to parse the cookies string even if it is not strictly formed
* as specified by RFC 6265.
*
* @param str the string to parse, without leading whitespaces
* @param str_len the size of the @a str, not including mandatory
* zero-termination
* @param connection the connection to add parsed cookies
* @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
*/
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_CSTR_(2)
MHD_FN_PAR_INOUT_SIZE_(2,1) enum _MHD_ParseCookie
parse_cookies_string (const size_t str_len,
char *restrict str,
struct MHD_Connection *restrict connection)
{
size_t i;
bool non_strict;
/* Skip extra whitespaces and empty cookies */
const bool allow_wsp_empty = (0 >= connection->daemon->req_cfg.strictnees);
/* Allow whitespaces around '=' character */
const bool wsp_around_eq = (-3 >= connection->daemon->req_cfg.strictnees);
/* Allow whitespaces in quoted cookie value */
const bool wsp_in_quoted = (-2 >= connection->daemon->req_cfg.strictnees);
/* Allow tab as space after semicolon between cookies */
const bool tab_as_sp = (0 >= connection->daemon->req_cfg.strictnees);
/* Allow no space after semicolon between cookies */
const bool allow_no_space = (0 >= connection->daemon->req_cfg.strictnees);
non_strict = false;
i = 0;
while (i < str_len)
{
size_t name_start;
size_t name_len;
size_t value_start;
size_t value_len;
bool val_quoted;
/* Skip any whitespaces and empty cookies */
while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
{
if (! allow_wsp_empty)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
i++;
if (i == str_len)
return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
}
/* 'i' must point to the first char of cookie-name */
name_start = i;
/* Find the end of the cookie-name */
do
{
const char l = str[i];
if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
(';' == l) || (0 == l))
break;
} while (str_len > ++i);
name_len = i - name_start;
/* Skip any whitespaces */
while (str_len > i && (' ' == str[i] || '\t' == str[i]))
{
if (! wsp_around_eq)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
i++;
}
if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
/* 'i' must point to the '=' char */
mhd_assert ('=' == str[i]);
i++;
/* Skip any whitespaces */
while (str_len > i && (' ' == str[i] || '\t' == str[i]))
{
if (! wsp_around_eq)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
i++;
}
/* 'i' must point to the first char of cookie-value */
if (str_len == i)
{
value_start = 0;
value_len = 0;
#ifndef NDEBUG
val_quoted = false; /* This assignment used in assert */
#endif
}
else
{
bool valid_cookie;
val_quoted = ('"' == str[i]);
if (val_quoted)
i++;
value_start = i;
/* Find the end of the cookie-value */
while (str_len > i)
{
const char l = str[i];
if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
('\\' == l) || (0 == l))
break;
if ((' ' == l) || ('\t' == l))
{
if (! val_quoted)
break;
if (! wsp_in_quoted)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
}
i++;
}
value_len = i - value_start;
if (val_quoted)
{
if ((str_len == i) || ('"' != str[i]))
return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
i++;
}
/* Skip any whitespaces */
if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
{
do
{
i++;
} while (str_len > i && (' ' == str[i] || '\t' == str[i]));
/* Whitespace at the end? */
if (str_len > i)
{
if (! allow_wsp_empty)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
}
}
if (str_len == i)
valid_cookie = true;
else if (';' == str[i])
valid_cookie = true;
else
valid_cookie = false;
if (! valid_cookie)
return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
}
mhd_assert (0 != name_len);
str[name_start + name_len] = 0; /* Zero-terminate the name */
if (0 != value_len)
{
struct MHD_String name;
struct MHD_String value;
mhd_assert (value_start + value_len <= str_len);
name.cstr = str + name_start;
name.len = name_len;
str[value_start + value_len] = 0; /* Zero-terminate the value */
value.cstr = str + value_start;
value.len = value_len;
if (! mhd_stream_add_field(connection,
MHD_VK_COOKIE,
&name,
&value))
return MHD_PARSE_COOKIE_NO_MEMORY;
}
else
{
struct MHD_String name;
struct MHD_String value;
name.cstr = str + name_start;
name.len = name_len;
value.cstr = ""
value.len = 0;
if (! mhd_stream_add_field(connection,
MHD_VK_COOKIE,
&name,
&value))
return MHD_PARSE_COOKIE_NO_MEMORY;
}
if (str_len > i)
{
mhd_assert (0 == str[i] || ';' == str[i]);
mhd_assert (! val_quoted || ';' == str[i]);
mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
i++;
if (str_len == i)
{ /* No next cookie after semicolon */
if (! allow_wsp_empty)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
}
else if (' ' != str[i])
{/* No space after semicolon */
if (('\t' == str[i]) && tab_as_sp)
i++;
else if (! allow_no_space)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
}
else
{
i++;
if (str_len == i)
{
if (! allow_wsp_empty)
return MHD_PARSE_COOKIE_MALFORMED;
non_strict = true;
}
}
}
}
return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
}
/**
* Parse the cookie header (see RFC 6265).
*
* @param connection connection to parse header of
* @return #MHD_PARSE_COOKIE_OK for success, error code otherwise
*/
static enum _MHD_ParseCookie
parse_cookie_header (struct MHD_Connection *connection)
{
struct MHD_String *hvalue;
char *cpy;
size_t i;
enum _MHD_ParseCookie parse_res;
struct mhd_RequestField *const saved_tail =
connection->rq.fields.last;
const bool allow_partially_correct_cookie =
(1 >= connection->daemon->req_cfg.strictnees);
hvalue = mhd_request_get_value_len(&(connection->rq),
MHD_VK_HEADER,
mhd_SSTR_LEN (MHD_HTTP_HEADER_COOKIE),
MHD_HTTP_HEADER_COOKIE);
if (NULL == hvalue)
return MHD_PARSE_COOKIE_OK;
if (0 == hvalue->len)
return MHD_PARSE_COOKIE_OK;
cpy = mhd_stream_alloc_memory (connection,
hvalue->len + 1);
if (NULL == cpy)
parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
else
{
memcpy (cpy,
hdr,
hdr_len);
cpy[hdr_len] = '\0';
i = 0;
/* Skip all initial whitespaces */
while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
i++;
parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
}
switch (parse_res)
{
case MHD_PARSE_COOKIE_OK:
break;
case MHD_PARSE_COOKIE_OK_LAX:
#ifdef HAVE_MESSAGES
if (saved_tail != connection->rq.headers_received_tail)
MHD_DLOG (connection->daemon,
_ ("The Cookie header has been parsed, but it is not fully "
"compliant with the standard.\n"));
#endif /* HAVE_MESSAGES */
break;
case MHD_PARSE_COOKIE_MALFORMED:
if (saved_tail != connection->rq.headers_received_tail)
{
if (! allow_partially_correct_cookie)
{
/* Remove extracted values from partially broken cookie */
/* Memory remains allocated until the end of the request processing */
connection->rq.headers_received_tail = saved_tail;
saved_tail->next = NULL;
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("The Cookie header has been ignored as it contains "
"malformed data.\n"));
#endif /* HAVE_MESSAGES */
}
#ifdef HAVE_MESSAGES
else
MHD_DLOG (connection->daemon,
_ ("The Cookie header has been only partially parsed as it "
"contains malformed data.\n"));
#endif /* HAVE_MESSAGES */
}
#ifdef HAVE_MESSAGES
else
MHD_DLOG (connection->daemon,
_ ("The Cookie header has malformed data.\n"));
#endif /* HAVE_MESSAGES */
break;
case MHD_PARSE_COOKIE_NO_MEMORY:
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("Not enough memory in the connection pool to "
"parse client cookies!\n"));
#endif /* HAVE_MESSAGES */
break;
default:
mhd_assert (0);
break;
}
#ifndef HAVE_MESSAGES
(void) saved_tail; /* Mute compiler warning */
#endif /* ! HAVE_MESSAGES */
return parse_res;
}
#endif /* COOKIE_SUPPORT */
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
mhd_parse_connection_headers (struct MHD_Connection *restrict c)
{
const char *clen;
const char *enc;
size_t val_len;
#ifdef COOKIE_SUPPORT
if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
{
handle_req_cookie_no_space (connection);
return;
}
#endif /* COOKIE_SUPPORT */
if ( (-3 < connection->daemon->client_discipline) &&
(MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
(MHD_NO ==
MHD_lookup_connection_value_n (connection,
MHD_VK_HEADER,
MHD_HTTP_HEADER_HOST,
mhd_SSTR_LEN (
MHD_HTTP_HEADER_HOST),
NULL,
NULL)) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("Received HTTP/1.1 request without `Host' header.\n"));
#endif
transmit_error_response_static (connection,
MHD_HTTP_STATUS_BAD_REQUEST,
REQUEST_LACKS_HOST);
return;
}
/* The presence of the request body is indicated by "Content-Length:" or
"Transfer-Encoding:" request headers.
Unless one of these two headers is used, the request has no request body.
See RFC9112, Section 6, paragraph 4. */
connection->rq.remaining_upload_size = 0;
if (MHD_NO !=
MHD_lookup_connection_value_n (connection,
MHD_VK_HEADER,
MHD_HTTP_HEADER_TRANSFER_ENCODING,
mhd_SSTR_LEN (
MHD_HTTP_HEADER_TRANSFER_ENCODING),
&enc,
NULL))
{
if (! MHD_str_equal_caseless_ (enc,
"chunked"))
{
transmit_error_response_static (connection,
MHD_HTTP_STATUS_BAD_REQUEST,
REQUEST_UNSUPPORTED_TR_ENCODING);
return;
}
else if (MHD_NO !=
MHD_lookup_connection_value_n (connection,
MHD_VK_HEADER,
MHD_HTTP_HEADER_CONTENT_LENGTH,
mhd_SSTR_LEN ( \
MHD_HTTP_HEADER_CONTENT_LENGTH),
NULL,
NULL))
{
/* TODO: add individual settings */
if (1 <= connection->daemon->client_discipline)
{
transmit_error_response_static (connection,
MHD_HTTP_STATUS_BAD_REQUEST,
REQUEST_LENGTH_WITH_TR_ENCODING);
return;
}
else
{
/* Must close connection after reply to prevent potential attack */
connection->keepalive = MHD_CONN_MUST_CLOSE;
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("The 'Content-Length' request header is ignored "
"as chunked Transfer-Encoding is used "
"for this request.\n"));
#endif /* HAVE_MESSAGES */
}
}
connection->rq.have_chunked_upload = true;
connection->rq.remaining_upload_size = MHD_SIZE_UNKNOWN;
}
else if (MHD_NO !=
MHD_lookup_connection_value_n (connection,
MHD_VK_HEADER,
MHD_HTTP_HEADER_CONTENT_LENGTH,
mhd_SSTR_LEN (
MHD_HTTP_HEADER_CONTENT_LENGTH),
&clen,
&val_len))
{
size_t num_digits;
num_digits = MHD_str_to_uint64_n_ (clen,
val_len,
&connection->rq.remaining_upload_size);
if (((0 == num_digits) &&
(0 != val_len) &&
('0' <= clen[0]) && ('9' >= clen[0]))
|| (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
{
connection->rq.remaining_upload_size = 0;
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("Too large value of 'Content-Length' header. " \
"Closing connection.\n"));
#endif
transmit_error_response_static (connection,
MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
REQUEST_CONTENTLENGTH_TOOLARGE);
}
else if ((val_len != num_digits) ||
(0 == num_digits))
{
connection->rq.remaining_upload_size = 0;
#ifdef HAVE_MESSAGES
MHD_DLOG (connection->daemon,
_ ("Failed to parse 'Content-Length' header. " \
"Closing connection.\n"));
#endif
transmit_error_response_static (connection,
MHD_HTTP_STATUS_BAD_REQUEST,
REQUEST_CONTENTLENGTH_MALFORMED);
}
}
}
+81
View File
@@ -0,0 +1,81 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_process_request.h
* @brief The declarations of internal functions for requests parsing
* and processing
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_STREAM_PROCESS_REQUEST_H
#define MHD_STREAM_PROCESS_STATES_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
struct MHD_Connection; /* forward declaration */
/**
* Find and parse the request line.
* @param c the connection to process
* @return true if request line completely processed (or unrecoverable error
* found) and state is changed,
* false if not enough data yet in the receive buffer
*/
MHD_INTERNAL bool
mhd_get_request_line (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Switch to request headers (field lines) processing state.
* @param c the connection to process
*/
MHD_INTERNAL void
mhd_switch_to_rq_headers_processing (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Find the end of the request headers and make basic header parsing.
* Advance to the next state when done, handle errors.
* @param c the connection to process
* @param process_footers if true then footers are processed,
* if false then headers are processed
* @return true if request headers reading finished (either successfully
* or with error),
* false if not enough data yet in the receive buffer
*/
MHD_INTERNAL bool
mhd_get_req_headers (struct MHD_Connection *restrict c, bool process_footers)
MHD_FN_PAR_NONNULL_ALL_;
/**
* Parse the various headers; figure out the size
* of the upload and make sure the headers follow
* the protocol. Advance to the appropriate state.
*
* @param c the connection to process
*/
MHD_INTERNAL void
mhd_parse_connection_headers (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_STREAM_PROCESS_STATES_H */
+359
View File
@@ -0,0 +1,359 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_process_states.h
* @brief The definitions of internal functions for processing
* stream states
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
#include "mhd_connection.h"
#include "stream_process_states.h"
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_conn_process_data (struct MHD_Connection *restrict c)
{
struct MHD_Daemon *const restrict d = c->daemon;
enum MHD_Result ret;
/* 'daemon' is not used if epoll is not available and asserts are disabled */
(void) d; /* Mute compiler warning */
if ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) ||
(0 != (c->sk_ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
{
mhd_assert ((mhd_SOCKET_ERR_NO_ERROR != c->sk_discnt_err) || \
mhd_SOCKET_ERR_IS_HARD(c->sk_discnt_err));
if ((mhd_SOCKET_ERR_NO_ERROR == c->sk_discnt_err) ||
(mhd_SOCKET_ERR_NOT_CHECKED == c->sk_discnt_err))
c->sk_discnt_err = mhd_socket_error_get_from_socket(c->socket_fd);
mhd_conn_pre_close_skt_err (c);
return false;
}
while (true) // TODO: support suspend
{
#ifdef HTTPS_SUPPORT
// TODO: support TLS, handshake
#endif /* HTTPS_SUPPORT */
switch (c->state)
{
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
if (mhd_get_request_line (c))
{
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
mhd_assert ((MHD_IS_HTTP_VER_SUPPORTED (c->rq.http_ver)) \
|| (c->discard_request));
continue;
}
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= c->state);
break;
case MHD_CONNECTION_REQ_LINE_RECEIVED:
mhd_switch_to_rq_headers_processing (c);
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED != c->state);
continue;
case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
if (mhd_get_req_headers (c, false))
{
mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < c->state);
mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == c->state) || \
(c->discard_request));
continue;
}
mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING == c->state);
break;
case MHD_CONNECTION_HEADERS_RECEIVED:
mhd_parse_connection_headers (c);
if (MHD_CONNECTION_HEADERS_RECEIVED != c->state)
continue;
c->state = MHD_CONNECTION_HEADERS_PROCESSED;
if (c->suspended)
break;
continue;
case MHD_CONNECTION_HEADERS_PROCESSED:
call_connection_handler (c);
if (MHD_CONNECTION_HEADERS_PROCESSED != c->state)
continue;
if (c->suspended)
continue;
if ( (NULL == c->rp.response) &&
(need_100_continue (c)) &&
/* If the client is already sending the payload (body)
there is no need to send "100 Continue" */
(0 == c->read_buffer_offset) )
{
c->state = MHD_CONNECTION_CONTINUE_SENDING;
break;
}
if ( (NULL != c->rp.response) &&
(0 != c->rq.remaining_upload_size) )
{
/* we refused (no upload allowed!) */
c->rq.remaining_upload_size = 0;
/* force close, in case client still tries to upload... */
c->discard_request = true;
}
c->state = (0 == c->rq.remaining_upload_size)
? MHD_CONNECTION_FULL_REQ_RECEIVED
: MHD_CONNECTION_BODY_RECEIVING;
if (c->suspended)
break;
continue;
case MHD_CONNECTION_CONTINUE_SENDING:
if (c->continue_message_write_offset ==
mhd_SSTR_LEN (HTTP_100_CONTINUE))
{
c->state = MHD_CONNECTION_BODY_RECEIVING;
continue;
}
break;
case MHD_CONNECTION_BODY_RECEIVING:
mhd_assert (0 != c->rq.remaining_upload_size);
mhd_assert (! c->discard_request);
mhd_assert (NULL == c->rp.response);
if (0 != c->read_buffer_offset)
{
process_request_body (c); /* loop call */
if (MHD_CONNECTION_BODY_RECEIVING != c->state)
continue;
}
/* Modify here when queueing of the response during data processing
will be supported */
mhd_assert (! c->discard_request);
mhd_assert (NULL == c->rp.response);
if (0 == c->rq.remaining_upload_size)
{
c->state = MHD_CONNECTION_BODY_RECEIVED;
continue;
}
break;
case MHD_CONNECTION_BODY_RECEIVED:
mhd_assert (! c->discard_request);
mhd_assert (NULL == c->rp.response);
if (0 == c->rq.remaining_upload_size)
{
if (c->rq.have_chunked_upload)
{
/* Reset counter variables reused for footers */
c->rq.num_cr_sp_replaced = 0;
c->rq.skipped_broken_lines = 0;
reset_rq_header_processing_state (c);
c->state = MHD_CONNECTION_FOOTERS_RECEIVING;
}
else
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
continue;
}
break;
case MHD_CONNECTION_FOOTERS_RECEIVING:
if (get_req_headers (c, true))
{
mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < c->state);
mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == c->state) || \
(c->discard_request));
continue;
}
mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == c->state);
break;
case MHD_CONNECTION_FOOTERS_RECEIVED:
/* The header, the body, and the footers of the request has been received,
* switch to the final processing of the request. */
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
continue;
case MHD_CONNECTION_FULL_REQ_RECEIVED:
call_connection_handler (c); /* "final" call */
if (c->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
continue;
if (NULL == c->rp.response)
break; /* try again next time */
/* Response is ready, start reply */
c->state = MHD_CONNECTION_START_REPLY;
continue;
case MHD_CONNECTION_START_REPLY:
mhd_assert (NULL != c->rp.response);
connection_switch_from_recv_to_send (c);
if (MHD_NO == build_header_response (c))
{
/* oops - close! */
CONNECTION_CLOSE_ERROR (c,
_ ("Closing connection (failed to create "
"response header).\n"));
continue;
}
c->state = MHD_CONNECTION_HEADERS_SENDING;
break;
case MHD_CONNECTION_HEADERS_SENDING:
/* no default action */
break;
case MHD_CONNECTION_HEADERS_SENT:
mhd_assert (0 && "Not implemented yet");
#if 0 //def UPGRADE_SUPPORT // TODO: upgrade support
if (NULL != c->rp.response->upgrade_handler)
{
c->state = MHD_CONNECTION_UPGRADE;
/* This connection is "upgraded". Pass socket to application. */
if (MHD_NO ==
MHD_response_execute_upgrade_ (c->rp.response,
connection))
{
/* upgrade failed, fail hard */
CONNECTION_CLOSE_ERROR (connection,
NULL);
continue;
}
/* Response is not required anymore for this connection. */
if (1)
{
struct MHD_Response *const resp = c->rp.response;
c->rp.response = NULL;
MHD_destroy_response (resp);
}
continue;
}
#endif /* UPGRADE_SUPPORT */
if (c->rp.props.send_reply_body)
{
if (c->rp.props.chunked)
c->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY;
else
c->state = MHD_CONNECTION_NORMAL_BODY_UNREADY;
}
else
c->state = MHD_CONNECTION_FULL_REPLY_SENT;
continue;
case MHD_CONNECTION_NORMAL_BODY_READY:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (! c->rp.props.chunked);
/* nothing to do here */
break;
case MHD_CONNECTION_NORMAL_BODY_UNREADY:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (! c->rp.props.chunked);
if (0 == c->rp.response->cntn_size)
{
if (c->rp.props.chunked)
c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
else
c->state = MHD_CONNECTION_FULL_REPLY_SENT;
continue;
}
if (MHD_NO != try_ready_normal_body (c))
{
c->state = MHD_CONNECTION_NORMAL_BODY_READY;
/* Buffering for flushable socket was already enabled*/
break;
}
/* mutex was already unlocked by "try_ready_normal_body */
/* not ready, no socket action */
break;
case MHD_CONNECTION_CHUNKED_BODY_READY:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (c->rp.props.chunked);
/* nothing to do here */
break;
case MHD_CONNECTION_CHUNKED_BODY_UNREADY:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (c->rp.props.chunked);
if ( (0 == c->rp.response->cntn_size) ||
(c->rp.rsp_cntn_read_pos ==
c->rp.response->cntn_size) )
{
c->state = MHD_CONNECTION_CHUNKED_BODY_SENT;
continue;
}
if (1)
{ /* pseudo-branch for local variables scope */
bool finished;
if (MHD_NO != try_ready_chunked_body (c, &finished))
{
c->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
MHD_CONNECTION_CHUNKED_BODY_READY;
continue;
}
/* mutex was already unlocked by try_ready_chunked_body */
}
break;
case MHD_CONNECTION_CHUNKED_BODY_SENT:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (c->rp.props.chunked);
mhd_assert (c->write_buffer_send_offset <= \
c->write_buffer_append_offset);
if (MHD_NO == build_connection_chunked_response_footer (c))
{
/* oops - close! */
CONNECTION_CLOSE_ERROR (c,
_ ("Closing connection (failed to create " \
"response footer)."));
continue;
}
mhd_assert (c->write_buffer_send_offset < \
c->write_buffer_append_offset);
c->state = MHD_CONNECTION_FOOTERS_SENDING;
continue;
case MHD_CONNECTION_FOOTERS_SENDING:
mhd_assert (c->rp.props.send_reply_body);
mhd_assert (c->rp.props.chunked);
/* no default action */
break;
case MHD_CONNECTION_FULL_REPLY_SENT:
// FIXME: support MHD_HTTP_STATUS_PROCESSING ?
/* Reset connection after complete reply */
connection_reset (c,
MHD_CONN_USE_KEEPALIVE == c->keepalive &&
! c->read_closed &&
! c->discard_request);
continue;
case MHD_CONNECTION_CLOSED:
cleanup_connection (c);
return MHD_NO;
#if 0 // def UPGRADE_SUPPORT
case MHD_CONNECTION_UPGRADE:
return MHD_YES; /* keep open */
#endif /* UPGRADE_SUPPORT */
default:
mhd_assert (0);
break;
}
break;
}
if (connection_check_timedout (c))
{
MHD_connection_close_ (c,
MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
return MHD_YES;
}
mhd_conn_update_active_state (c);
/* MHD_connection_update_event_loop_info (c);*/
return true;
}
+48
View File
@@ -0,0 +1,48 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
GNU libmicrohttpd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file src/mhd2/stream_process_states.h
* @brief The declarations of internal functions for processing
* stream states
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_STREAM_PROCESS_STATES_H
#define MHD_STREAM_PROCESS_STATES_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
struct MHD_Connection; /* forward declaration */
/**
* Process states and the data for the connection
* For HTTP/1.1 connection is equal stream
* @param c the connection to process
* @return true if states and data has been successfully processed,
* false if connection needs to be closed
*/
MHD_INTERNAL bool
mhd_conn_process_data (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
#endif /* ! MHD_STREAM_PROCESS_STATES_H */
+11
View File
@@ -48,6 +48,9 @@
#ifdef HAVE_CRTDEFS_H
# include <crtdefs.h> /* W32-specific header */
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifndef HAVE_SSIZE_T
# if defined(HAVE_PTRDIFF_T)
@@ -65,4 +68,12 @@ typedef intptr_t ssize_t;
# endif
#endif /* ! HAVE_SSIZE_T */
#ifndef PRIuFAST64
# ifdef PRIu64
# define PRIuFAST64 PRIu64
# else
# define PRIuFAST64 "llu"
# endif
#endif
#endif /* ! MHD_SYS_BASE_TYPES_H */
+59
View File
@@ -53,4 +53,63 @@
# include <ws2tcpip.h>
#endif
#ifdef IPPROTO_TCP
# if defined(TCP_CORK)
/**
* Value of TCP_CORK or TCP_NOPUSH
*/
# define mhd_TCP_CORK_NOPUSH TCP_CORK
# elif defined(TCP_NOPUSH)
/**
* Value of TCP_CORK or TCP_NOPUSH
*/
# define mhd_TCP_CORK_NOPUSH TCP_NOPUSH
# endif /* TCP_NOPUSH */
#endif /* IPPROTO_TCP */
#ifdef mhd_TCP_CORK_NOPUSH
# ifdef __linux__
/**
* Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
*/
# define mhd_CORK_RESET_PUSH_DATA 1
/**
* Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
* even if TCP_CORK/TCP_NOPUSH was in switched off state.
*/
# define mhd_CORK_RESET_PUSH_DATA_ALWAYS 1
#endif /* __linux__ */
#if (defined(__FreeBSD__) && \
((__FreeBSD__ + 0) >= 5 || (__FreeBSD_version + 0) >= 450000)) || \
(defined(__FreeBSD_kernel_version) && \
(__FreeBSD_kernel_version + 0) >= 450000)
/* FreeBSD pushes data to the network with reset of TCP_NOPUSH
* starting from version 4.5. */
/**
* Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
*/
#define mhd_CORK_RESET_PUSH_DATA 1
#endif /* __FreeBSD_version >= 450000 */
#ifdef __OpenBSD__
/* OpenBSD took implementation from FreeBSD */
/**
* Indicate that reset of TCP_CORK / TCP_NOPUSH push data to the network
*/
#define mhd_CORK_RESET_PUSH_DATA 1
#endif /* __OpenBSD__ */
#endif /* MHD_TCP_CORK_NOPUSH */
#ifdef __linux__
/**
* Indicate that set of TCP_NODELAY push data to the network
*/
# define mhd_NODELAY_SET_PUSH_DATA 1
/**
* Indicate that set of TCP_NODELAY push data to the network even
* if TCP_DELAY was already set and regardless of TCP_CORK / TCP_NOPUSH state
*/
# define mhd_NODELAY_SET_PUSH_DATA_ALWAYS 1
#endif /* __linux__ */
#endif /* ! MHD_SYS_IP_HEADERS_H */
+45 -11
View File
@@ -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 */
-156
View File
@@ -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 */