libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 41f35bbcb5d2771ea37ed920c34d8092a58dc432
parent 6981fc2791391a6f32c21bc8346c63571ae6005d
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Thu, 17 Mar 2022 20:50:32 +0300

Added MHD_RF_HEAD_ONLY_RESPONSE response flag

Useful if application needs to send reply for HEAD request, but creation
of full reply body is costly.

Diffstat:
Msrc/include/microhttpd.h | 28++++++++++++++++++++++++----
Msrc/microhttpd/connection.c | 36++++++++++++++++++++++++++++++++++--
Msrc/microhttpd/response.c | 55+++++++++++++++++++++++++++++++++++++------------------
3 files changed, 95 insertions(+), 24 deletions(-)

diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -1,7 +1,7 @@ /* This file is part of libmicrohttpd Copyright (C) 2006-2021 Christian Grothoff (and other contributing authors) - Copyright (C) 2014-2021 Evgeny Grin (Karlson2k) + Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -96,7 +96,7 @@ extern "C" * they are parsed as decimal numbers. * Example: 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00097501 +#define MHD_VERSION 0x00097502 /* If generic headers don't work on your platform, include headers which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', @@ -3234,7 +3234,10 @@ MHD_lookup_connection_value_n (struct MHD_Connection *connection, * header is added automatically based the size of the body in the response. * If body size it set to #MHD_SIZE_UNKNOWN or chunked encoding is enforced * then "Transfer-Encoding: chunked" header (for HTTP/1.1 only) is added instead - * of "Content-Length" header. + * of "Content-Length" header. For example, if response with zero-size body is + * used for HEAD request, then "Content-Length: 0" is added automatically to + * reply headers. + * @sa #MHD_RF_HEAD_ONLY_RESPONSE * * In situations, where reply body is required, like answer for the GET request * with @a status_code #MHD_HTTP_OK, headers "Content-Length" (for known body @@ -3364,6 +3367,8 @@ enum MHD_ResponseFlags /** * Disable sanity check preventing clients from manually * setting the HTTP content length option. + * Allow to set several "Content-Length" headers. These headers will + * be used even with replies without body. * @note Available since #MHD_VERSION 0x00096702 */ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH = 1 << 2, @@ -3374,7 +3379,22 @@ enum MHD_ResponseFlags * Disabled by default for HTTP/1.1 clients as per RFC. * @note Available since #MHD_VERSION 0x00097310 */ - MHD_RF_SEND_KEEP_ALIVE_HEADER = 1 << 3 + MHD_RF_SEND_KEEP_ALIVE_HEADER = 1 << 3, + + /** + * Enable special processing of the response as body-less (with undefined + * body size). No automatic "Content-Length" or "Transfer-Encoding: chunked" + * headers are added when the response is used with #MHD_HTTP_NOT_MODIFIED + * code or to respond to HEAD request. + * The flag also allow to set arbitrary "Content-Length" by + * MHD_add_response_header() function. + * This flag value can be used only with responses created without body + * (zero-size body). + * Responses with this flag enabled cannot be used in situations where + * reply body must be sent to the client. + * @note Available since #MHD_VERSION 0x00097502 + */ + MHD_RF_HEAD_ONLY_RESPONSE = 1 << 4 } _MHD_FIXED_FLAGS_ENUM; diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1913,6 +1913,7 @@ buffer_append (char *buf, * @param buf_size the size of the @a buf * @param response the response * @param filter_transf_enc skip "Transfer-Encoding" header if any + * @param filter_content_len skip "Content-Length" header if any * @param add_close add "close" token to the * "Connection:" header (if any), ignored if no "Connection:" * header was added by user or if "close" token is already @@ -1928,6 +1929,7 @@ add_user_headers (char *buf, size_t buf_size, struct MHD_Response *response, bool filter_transf_enc, + bool filter_content_len, bool add_close, bool add_keep_alive) { @@ -1938,7 +1940,9 @@ add_user_headers (char *buf, mhd_assert (! add_close || ! add_keep_alive); if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED)) - filter_transf_enc = false; /* No such header */ + filter_transf_enc = false; /* No such header */ + if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH)) + filter_content_len = false; /* No such header */ if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR)) { add_close = false; /* No such header */ @@ -1963,6 +1967,19 @@ add_user_headers (char *buf, continue; /* Skip "Transfer-Encoding" header */ } } + if (filter_content_len) + { /* Need to filter-out "Content-Length" */ + if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH) == + hdr->header_size) && + (MHD_str_equal_caseless_bin_n_ (MHD_HTTP_HEADER_CONTENT_LENGTH, + hdr->header, hdr->header_size)) ) + { + /* Reset filter flag if only one header is allowed */ + filter_transf_enc = + (0 == (r->flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH)); + continue; /* Skip "Content-Length" header */ + } + } /* Add user header */ el_size = hdr->header_size + 2 + hdr->value_size + 2; @@ -2184,13 +2201,17 @@ build_header_response (struct MHD_Connection *connection) if (! add_user_headers (buf, &pos, buf_size, r, ! c->rp_props.chunked, + (! c->rp_props.use_reply_body_headers) && + (0 == + (r->flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH)), use_conn_close, use_conn_k_alive)) return MHD_NO; /* Other automatic headers */ - if (c->rp_props.use_reply_body_headers) + if ( (c->rp_props.use_reply_body_headers) && + (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) ) { /* Body-specific headers */ @@ -5320,6 +5341,17 @@ MHD_queue_response (struct MHD_Connection *connection, return MHD_NO; } + if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) && + (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) ) + { +#ifdef HAVE_MESSAGES + MHD_DLOG (daemon, + _ ("HEAD-only response cannot be used when the request requires " + "reply body to be sent!\n")); +#endif + return MHD_NO; + } + #ifdef HAVE_MESSAGES if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) && (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) ) diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -501,9 +501,12 @@ MHD_add_response_header (struct MHD_Response *response, MHD_HTTP_HEADER_TRANSFER_ENCODING)) { if (! MHD_str_equal_caseless_ (content, "chunked")) - return MHD_NO; + return MHD_NO; /* Only "chunked" encoding is allowed */ if (0 != (response->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED)) - return MHD_YES; + return MHD_YES; /* Already has "chunked" encoding header */ + if ( (0 != (response->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH)) && + (0 == (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) ) + return MHD_NO; /* Has "Content-Length" header and no "Insanity" flag */ if (MHD_NO != add_response_entry (response, MHD_HEADER_KIND, header, @@ -545,19 +548,22 @@ MHD_add_response_header (struct MHD_Response *response, if (MHD_str_equal_caseless_ (header, MHD_HTTP_HEADER_CONTENT_LENGTH)) { - if (0 == (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) - { - /* MHD sets automatically correct Content-Length always when needed, - reject attempt to manually override it */ - return MHD_NO; - } - if (MHD_NO != add_response_entry (response, - MHD_HEADER_KIND, - header, - content)) + /* Generally MHD sets automatically correct "Content-Length" always when + * needed. + * Custom "Content-Length" header is allowed only in special cases. */ + if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) || + ((0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) && + (0 == (response->flags_auto & (MHD_RAF_HAS_TRANS_ENC_CHUNKED + | MHD_RAF_HAS_CONTENT_LENGTH)))) ) { - response->flags_auto |= MHD_RAF_HAS_CONTENT_LENGTH; - return MHD_YES; + if (MHD_NO != add_response_entry (response, + MHD_HEADER_KIND, + header, + content)) + { + response->flags_auto |= MHD_RAF_HAS_CONTENT_LENGTH; + return MHD_YES; + } } return MHD_NO; } @@ -895,12 +901,25 @@ MHD_set_response_options (struct MHD_Response *response, enum MHD_Result ret; enum MHD_ResponseOptions ro; - if ( (0 != (response->flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH)) && - (0 == (flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH))) - { /* Request to remove MHD_RF_INSANITY_HEADER_CONTENT_LENGTH flag */ - if (0 != (response->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH)) + if (0 != (response->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH)) + { /* Response has custom "Content-Lengh" header */ + if ( (0 != (response->flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH)) && + (0 == (flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH))) + { /* Request to remove MHD_RF_INSANITY_HEADER_CONTENT_LENGTH flag */ return MHD_NO; + } + if ( (0 != (response->flags & MHD_RF_HEAD_ONLY_RESPONSE)) && + (0 == (flags & MHD_RF_HEAD_ONLY_RESPONSE))) + { /* Request to remove MHD_RF_HEAD_ONLY_RESPONSE flag */ + if (0 == (flags & MHD_RF_INSANITY_HEADER_CONTENT_LENGTH)) + return MHD_NO; + } } + + if ( (0 != (flags & MHD_RF_HEAD_ONLY_RESPONSE)) && + (0 != response->total_size) ) + return MHD_NO; + ret = MHD_YES; response->flags = flags;