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:
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)