libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 8d8cb5885a8cee79c51dbcfc1ee8ea3f73846903
parent ab052bb0724cf5fb4caa4ad6652ed5945e0ad55e
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Tue, 11 Nov 2025 02:42:20 +0100

Public API change: MHD_request_get_value() returns MHD_Bool instead of pointer

This simplifies both the lib and an app code.

Diffstat:
Mdoc/examples/host-example.c | 18+++++++++---------
Msrc/examples2/demo.c | 32++++++++++++++++----------------
Msrc/include/microhttpd2.h | 34+++++++++++++++++++++-------------
Msrc/include/microhttpd2_main.h.in | 34+++++++++++++++++++++-------------
Msrc/mhd2/mhd_request.h | 8--------
Msrc/mhd2/post_parser_funcs.c | 38+++++++++++++++++++-------------------
Msrc/mhd2/request_get_value.c | 78++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/mhd2/request_get_value.h | 24++++++++++++++++--------
Msrc/mhd2/stream_funcs.c | 15++++++++-------
9 files changed, 152 insertions(+), 129 deletions(-)

diff --git a/doc/examples/host-example.c b/doc/examples/host-example.c @@ -12,25 +12,25 @@ handle_request (void *cls, enum MHD_HTTP_Method method, uint_fast64_t upload_size) { - struct MHD_String *host; + struct MHD_StringNullable host; /* We passed NULL in main() for the closure */ assert (NULL == cls); /* MHD_HTTP_HEADER_HOST is literally just "Host" */ - host = MHD_request_get_value (request, - MHD_VK_HEADER, - MHD_HTTP_HEADER_HOST); - if (NULL == host) + if (MHD_request_get_value (request, + MHD_VK_HEADER, + MHD_HTTP_HEADER_HOST, + &host)) { - /* In HTTP/1.0, the 'Host' header was optional! */ fprintf (stderr, - "No 'Host:' header provided\n"); + "'Host:' is %s\n", + host->cstr); } else { + /* In HTTP/1.0, the 'Host' header was optional! */ fprintf (stderr, - "'Host:' is %s\n", - host->cstr); + "No 'Host:' header provided\n"); } /* This simply closes the connection after receiving the HTTP header, never return actually returning diff --git a/src/examples2/demo.c b/src/examples2/demo.c @@ -897,8 +897,8 @@ done_cb (struct MHD_Request *req, { struct UploadContext *uc = (struct UploadContext *) cls; const struct MHD_UploadAction *ret; - const struct MHD_StringNullable *cat; - const struct MHD_StringNullable *lang; + struct MHD_StringNullable cat; + struct MHD_StringNullable lang; char fn[PATH_MAX + 1]; int res; @@ -927,16 +927,16 @@ done_cb (struct MHD_Request *req, internal_error_response); goto cleanup; } - cat = MHD_request_get_value (req, + if (! MHD_request_get_value (req, MHD_VK_POSTDATA, - "category"); - lang = MHD_request_get_value (req, - MHD_VK_POSTDATA, - "language"); - if ( (NULL == lang) || - (NULL == lang->cstr) || - (NULL == cat) || - (NULL == cat->cstr) ) + "category", + &cat) || + ! MHD_request_get_value (req, + MHD_VK_POSTDATA, + "language", + &lang) || + (NULL == cat.cstr) || + (NULL == lang.cstr) ) { fprintf (stderr, "Required argument missing\n"); @@ -972,13 +972,13 @@ done_cb (struct MHD_Request *req, (void) mkdir (lang->cstr, S_IRWXU); #else - (void) mkdir (lang->cstr); + (void) mkdir (lang.cstr); #endif res = snprintf (fn, sizeof (fn), "%s/%s", - lang->cstr, - cat->cstr); + lang.cstr, + cat.cstr); if ( (0 >= res) || (sizeof (fn) <= (size_t) res) ) { @@ -998,8 +998,8 @@ done_cb (struct MHD_Request *req, res = snprintf (fn, sizeof (fn), "%s/%s/%s", - lang->cstr, - cat->cstr, + lang.cstr, + cat.cstr, uc->filename); if ( (0 >= res) || (sizeof (fn) <= (size_t) res) ) diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -5342,17 +5342,20 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind /** * HTTP header. + * The 'value' for this kind is mandatory. */ MHD_VK_HEADER = (1u << 0) , /** * Cookies. Note that the original HTTP header containing * the cookie(s) will still be available and intact. + * The 'value' for this kind is optional. */ MHD_VK_COOKIE = (1u << 1) , /** * URI query parameter. + * The 'value' for this kind is optional. */ MHD_VK_URI_QUERY_PARAM = (1u << 2) , @@ -5372,6 +5375,7 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind , /** * HTTP trailer (only for HTTP 1.1 chunked encodings, "footer"). + * The 'value' for this kind is mandatory. */ MHD_VK_TRAILER = (1u << 4) , @@ -5427,10 +5431,8 @@ struct MHD_NameValueKind * * The @a nv pointer is valid only until return from this function. * - * For @a kind other then #MHD_VK_POSTDATA the pointers to the strings in @a nv - * are valid until the response is queued. - * For the #MHD_VK_POSTDATA @a kind the pointers to the strings in @a nv - * are valid until any MHD_UploadAction is provided. + * The strings in @a nv are valid until any MHD_Action or MHD_UploadAction + * is provided. * If the data is needed beyond this point, it should be copied. * * @param cls closure @@ -5499,24 +5501,30 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3); * 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 any MHD_Action or MHD_UploadAction is - * provided. If the data is needed beyond this point, it should be copied. + * The data in the @a value_out is valid until any MHD_Action or + * MHD_UploadAction is provided. 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 name of the value looking for (used for case-insensetive - match), empty to lookup 'trailing' value without a key - * @return NULL if no such item was found, - * non-NULL if item found (the inner pointer to string can be NULL - * if item found, but has no value) + * match), empty to lookup 'trailing' value without a key + * @param[out] value_out set to the value of the header if succeed, + * the @a cstr pointer could be NULL even if succeed + * if the requested item found, but has no value + * @return #MHD_YES if succeed, the @a value_out is set; + * #MHD_NO if no such item was found, the @a value_out string pointer + * set to NULL * @ingroup request */ -MHD_EXTERN_ const struct MHD_StringNullable * +MHD_EXTERN_ enum MHD_Bool MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request, enum MHD_ValueKind kind, - const char *MHD_RESTRICT key) + const char *MHD_RESTRICT key, + struct MHD_StringNullable *MHD_RESTRICT value_out) MHD_FN_PAR_NONNULL_ (1) -MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3); +MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) +MHD_FN_PAR_OUT_ (4); /** diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in @@ -525,17 +525,20 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind /** * HTTP header. + * The 'value' for this kind is mandatory. */ MHD_VK_HEADER = (1u << 0) , /** * Cookies. Note that the original HTTP header containing * the cookie(s) will still be available and intact. + * The 'value' for this kind is optional. */ MHD_VK_COOKIE = (1u << 1) , /** * URI query parameter. + * The 'value' for this kind is optional. */ MHD_VK_URI_QUERY_PARAM = (1u << 2) , @@ -555,6 +558,7 @@ enum MHD_FLAGS_ENUM_ MHD_ValueKind , /** * HTTP trailer (only for HTTP 1.1 chunked encodings, "footer"). + * The 'value' for this kind is mandatory. */ MHD_VK_TRAILER = (1u << 4) , @@ -610,10 +614,8 @@ struct MHD_NameValueKind * * The @a nv pointer is valid only until return from this function. * - * For @a kind other then #MHD_VK_POSTDATA the pointers to the strings in @a nv - * are valid until the response is queued. - * For the #MHD_VK_POSTDATA @a kind the pointers to the strings in @a nv - * are valid until any MHD_UploadAction is provided. + * The strings in @a nv are valid until any MHD_Action or MHD_UploadAction + * is provided. * If the data is needed beyond this point, it should be copied. * * @param cls closure @@ -682,24 +684,30 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_OUT_SIZE_ (4,3); * 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 any MHD_Action or MHD_UploadAction is - * provided. If the data is needed beyond this point, it should be copied. + * The data in the @a value_out is valid until any MHD_Action or + * MHD_UploadAction is provided. 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 name of the value looking for (used for case-insensetive - match), empty to lookup 'trailing' value without a key - * @return NULL if no such item was found, - * non-NULL if item found (the inner pointer to string can be NULL - * if item found, but has no value) + * match), empty to lookup 'trailing' value without a key + * @param[out] value_out set to the value of the header if succeed, + * the @a cstr pointer could be NULL even if succeed + * if the requested item found, but has no value + * @return #MHD_YES if succeed, the @a value_out is set; + * #MHD_NO if no such item was found, the @a value_out string pointer + * set to NULL * @ingroup request */ -MHD_EXTERN_ const struct MHD_StringNullable * +MHD_EXTERN_ enum MHD_Bool MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request, enum MHD_ValueKind kind, - const char *MHD_RESTRICT key) + const char *MHD_RESTRICT key, + struct MHD_StringNullable *MHD_RESTRICT value_out) MHD_FN_PAR_NONNULL_ (1) -MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3); +MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) +MHD_FN_PAR_OUT_ (4); /** diff --git a/src/mhd2/mhd_request.h b/src/mhd2/mhd_request.h @@ -240,14 +240,6 @@ struct mhd_RequestPostField struct mhd_PostFieldInt field; /** - * Temporal representation of the @a field for application. - * - * Filled/updated only when application required short form of POST - * data. - */ - struct MHD_NameAndValue field_for_app; - - /** * Headers are kept in a double-linked list. */ mhd_DLNKDL_LINKS (mhd_RequestPostField,post_fields); diff --git a/src/mhd2/post_parser_funcs.c b/src/mhd2/post_parser_funcs.c @@ -215,14 +215,14 @@ process_mpart_header (struct MHD_Connection *restrict c, static MHD_FN_PAR_NONNULL_ (1) bool detect_post_enc (struct MHD_Connection *restrict c) { - const struct MHD_StringNullable *h_cnt_tp; + struct MHD_StringNullable hdr_cnt_tp; mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); - h_cnt_tp = mhd_request_get_value_st (&(c->rq), - MHD_VK_HEADER, - MHD_HTTP_HEADER_CONTENT_TYPE); - if (NULL == h_cnt_tp) + if (! mhd_request_get_value_st (&(c->rq), + MHD_VK_HEADER, + MHD_HTTP_HEADER_CONTENT_TYPE, + &hdr_cnt_tp)) { mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \ "The request POST data cannot be parsed because " \ @@ -232,11 +232,11 @@ detect_post_enc (struct MHD_Connection *restrict c) return false; /* The "Content-Type:" is not defined by the client */ } - mhd_assert (NULL != h_cnt_tp->cstr); + mhd_assert (NULL != hdr_cnt_tp.cstr); if (mhd_str_equal_caseless_n_st ("application/x-www-form-urlencoded", - h_cnt_tp->cstr, - h_cnt_tp->len)) + hdr_cnt_tp.cstr, + hdr_cnt_tp.len)) { c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; return true; @@ -248,7 +248,7 @@ detect_post_enc (struct MHD_Connection *restrict c) res = process_mpart_header (c, (const struct MHD_String *) (const void *) - h_cnt_tp); + &hdr_cnt_tp); if (mhd_MPART_DET_OK == res) return true; @@ -263,9 +263,9 @@ detect_post_enc (struct MHD_Connection *restrict c) { static const struct MHD_String txt_tkn = mhd_MSTR_INIT ("text/plain"); struct MHD_String h_cnt_tp_copy; - mhd_assert (NULL != h_cnt_tp->cstr); - h_cnt_tp_copy.len = h_cnt_tp->len; - h_cnt_tp_copy.cstr = h_cnt_tp->cstr; + mhd_assert (NULL != hdr_cnt_tp.cstr); + h_cnt_tp_copy.len = hdr_cnt_tp.len; + h_cnt_tp_copy.cstr = hdr_cnt_tp.cstr; if (mhd_str_starts_with_token_opt_param (&h_cnt_tp_copy, &txt_tkn)) @@ -293,16 +293,16 @@ detect_post_enc (struct MHD_Connection *restrict c) static MHD_FN_PAR_NONNULL_ALL_ bool detect_mpart_boundary_from_the_header (struct MHD_Connection *restrict c) { - const struct MHD_StringNullable *h_cnt_tp; + struct MHD_StringNullable hdr_cnt_tp; enum mhd_MPartDetectResult res; mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == \ c->rq.app_act.head_act.data.post_parse.enc); - h_cnt_tp = mhd_request_get_value_st (&(c->rq), - MHD_VK_HEADER, - MHD_HTTP_HEADER_CONTENT_TYPE); - if (NULL == h_cnt_tp) + if (! mhd_request_get_value_st (&(c->rq), + MHD_VK_HEADER, + MHD_HTTP_HEADER_CONTENT_TYPE, + &hdr_cnt_tp)) { mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \ "The request POST data cannot be parsed because " \ @@ -312,11 +312,11 @@ detect_mpart_boundary_from_the_header (struct MHD_Connection *restrict c) return false; } - mhd_assert (NULL != h_cnt_tp->cstr); + mhd_assert (NULL != hdr_cnt_tp.cstr); res = process_mpart_header (c, (const struct MHD_String *) (const void *) - h_cnt_tp); + &hdr_cnt_tp); if (mhd_MPART_DET_OK == res) return true; diff --git a/src/mhd2/request_get_value.c b/src/mhd2/request_get_value.c @@ -60,13 +60,18 @@ MHD_INTERNAL -MHD_FN_PAR_NONNULL_ (1) -MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4) const struct MHD_StringNullable * +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3) +MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4) +MHD_FN_PAR_OUT_ (5) bool mhd_request_get_value_n (struct MHD_Request *restrict request, enum MHD_ValueKind kind, size_t key_len, - const char *restrict key) + const char *restrict key, + struct MHD_StringNullable *restrict value_out) { + mhd_assert (strlen (key) == key_len); + value_out->len = 0u; + value_out->cstr = NULL; mhd_assert (strlen (key) == key_len); @@ -82,7 +87,10 @@ mhd_request_get_value_n (struct MHD_Request *restrict request, mhd_str_equal_caseless_bin_n (key, f->field.nv.name.cstr, key_len)) - return &(f->field.nv.value); + { + *value_out = f->field.nv.value; + return true; + } } } @@ -99,34 +107,40 @@ mhd_request_get_value_n (struct MHD_Request *restrict request, buf + f->field.name.pos, key_len)) { - f->field_for_app.value.cstr = + value_out->cstr = (0 == f->field.value.pos) ? NULL : (buf + f->field.value.pos); - f->field_for_app.value.len = f->field.value.len; + value_out->len = f->field.value.len; - mhd_assert ((NULL != f->field_for_app.value.cstr) || \ - (0 == f->field_for_app.value.len)); + mhd_assert ((NULL != value_out->cstr) || \ + (0 == value_out->len)); - return &(f->field_for_app.value); + return true; } } } #endif /* MHD_SUPPORT_POST_PARSER */ - return NULL; + return false; } MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1) -MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) const struct MHD_StringNullable * +MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) +MHD_FN_PAR_OUT_ (4) enum MHD_Bool MHD_request_get_value (struct MHD_Request *MHD_RESTRICT request, enum MHD_ValueKind kind, - const char *MHD_RESTRICT key) + const char *MHD_RESTRICT key, + struct MHD_StringNullable *MHD_RESTRICT value_out) { size_t len; len = strlen (key); - return mhd_request_get_value_n (request, kind, len, key); + return mhd_request_get_value_n (request, + kind, + len, + key, + value_out) ? MHD_YES : MHD_NO; } @@ -205,20 +219,18 @@ MHD_request_get_values_cb (struct MHD_Request *request, ++count; if (NULL != iterator) { - if (f->field_for_app.name.cstr != buf + f->field.name.pos) - { - f->field_for_app.name.cstr = buf + f->field.name.pos; - f->field_for_app.name.len = f->field.name.len; - f->field_for_app.value.cstr = - (0 == f->field.value.pos) ? - NULL : (buf + f->field.value.pos); - f->field_for_app.value.len = f->field.value.len; - } + struct MHD_NameAndValue field; + + field.name.cstr = buf + f->field.name.pos; + field.name.len = f->field.name.len; + field.value.cstr = + (0 == f->field.value.pos) ? NULL : (buf + f->field.value.pos); + field.value.len = f->field.value.len; if (MHD_NO == iterator (iterator_cls, MHD_VK_POSTDATA, - &(f->field_for_app))) + &field)) return count; } } @@ -250,19 +262,13 @@ MHD_request_get_post_data_cb (struct MHD_Request *request, { struct MHD_PostField field; - if (f->field_for_app.name.cstr != buf + f->field.name.pos) - { - f->field_for_app.name.cstr = buf + f->field.name.pos; - f->field_for_app.name.len = f->field.name.len; - if (0 == f->field.value.pos) - f->field_for_app.value.cstr = NULL; - else - f->field_for_app.value.cstr = buf + f->field.value.pos; - f->field_for_app.value.len = f->field.value.len; - } - - field.name = f->field_for_app.name; - field.value = f->field_for_app.value; + field.name.cstr = buf + f->field.name.pos; + field.name.len = f->field.name.len; + if (0 == f->field.value.pos) + field.value.cstr = NULL; + else + field.value.cstr = buf + f->field.value.pos; + field.value.len = f->field.value.len; if (0 == f->field.filename.pos) field.filename.cstr = NULL; diff --git a/src/mhd2/request_get_value.h b/src/mhd2/request_get_value.h @@ -56,7 +56,7 @@ * Get specified field value from request * If multiple values match the kind, return first found. * - * The returned pointer is valid until the response is queued. + * The returned pointer is valid until any action is set. * If the data is needed beyond this point, it should be copied. * * @param request request to get values from @@ -64,16 +64,24 @@ * @param key_len the length of the @a key string * @param key the header to look for, empty to lookup 'trailing' value * without a key - * @return NULL if no such item was found + * @param[out] value_out set to the value of the filed if succeed, + * the @a cstr pointer could be NULL even if succeed + * if the requested filed found, but has no value + * @return 'true' if succeed, the @a value_out is set; + * 'false' if no such field was found, the @a value_out string pointer + * set to NULL * @ingroup request */ -MHD_INTERNAL const struct MHD_StringNullable * +MHD_INTERNAL +bool mhd_request_get_value_n (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); + const char *restrict key, + struct MHD_StringNullable *restrict value_out) +MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3) +MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4) +MHD_FN_PAR_OUT_ (5); /** * Get specified field value from request @@ -89,8 +97,8 @@ MHD_FN_PAR_NONNULL_ (4) MHD_FN_PAR_CSTR_ (4); * @return NULL if no such item was found * @ingroup request */ -#define mhd_request_get_value_st(r,k,str) \ - mhd_request_get_value_n ((r),(k),mhd_SSTR_LEN (str),(str)) +#define mhd_request_get_value_st(r,k,str,v_out) \ + mhd_request_get_value_n ((r),(k),mhd_SSTR_LEN (str),(str),(v_out)) #endif /* ! MHD_REQUEST_GET_VALUE_H */ diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c @@ -397,17 +397,18 @@ mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c, { static const size_t host_field_name_len = mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST); - const struct MHD_StringNullable *host_value; - host_value = mhd_request_get_value_n (&(c->rq), - MHD_VK_HEADER, - host_field_name_len, - MHD_HTTP_HEADER_HOST); - if (NULL != host_value) + struct MHD_StringNullable host_value; + + if (mhd_request_get_value_n (&(c->rq), + MHD_VK_HEADER, + host_field_name_len, + MHD_HTTP_HEADER_HOST, + &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 */ + 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)