WIP POST parser

This commit is contained in:
Evgeny Grin (Karlson2k)
2024-08-13 11:32:48 +02:00
parent ae4e9e2818
commit c036146af5
29 changed files with 2932 additions and 286 deletions
+184 -28
View File
@@ -147,6 +147,8 @@
*/
#define MHD_VERSION 0x01990001
#ifndef MHD_BOOL_DEFINED
/**
* Representation of 'bool' in the public API as stdbool.h may not
* always be available and presence of 'bool' keyword may depend on
@@ -171,6 +173,10 @@ enum MHD_Bool
MHD_YES = 1
};
#define MHD_BOOL_DEFINED 1
#endif /* ! MHD_BOOL_DEFINED */
#ifndef MHD_STRINGS_DEFINED
@@ -638,16 +644,46 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REQ_COOKIE_INVALID = 40163
,
/**
* Parsing of the POST data is incomplete because client used incorrect
* format of POST encoding.
* Some POST data is available or has been provided via callback.
*/
MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT = 40202
,
/**
* The request does not have "Content-Type:" header and POST data cannot
* be parsed
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40180
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40280
,
/**
* The request has unknown POST encoding specified by "Content-Type:" header
*/
MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE = 40281
,
/**
* The request has "Content-Type: multipart/form-data" header without
* "boundary" parameter
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY = 40282
,
/**
* The request has "Content-Type: multipart/form-data" header with misformed
* data
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED = 40283
,
/**
* The POST data cannot be parsed because client used incorrect format
* of POST encoding.
*/
MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
,
/**
* The request cannot be processed. Sending error reply.
*/
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 40200
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
,
/* 50000-level errors are because of an error internal
@@ -1109,6 +1145,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
,
/**
* The request POST data cannot be parsed because stream has not enough
* pool memory free.
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM = 50260
,
/**
* The POST data cannot be parsed completely because no "large shared buffer"
* space is available.
* Some POST data may be parsed.
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM = 50261
,
/**
* The application set POST encoding to "multipart/form-data", but the request
* has no "Content-Type: multipart/form-data" header which is required
* to find "boundary" used in this encoding
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART = 50284
,
/**
* The feature is not supported by this MHD build (either
* disabled by configure parameters or build platform
@@ -4912,7 +4968,8 @@ struct MHD_NameValueKind
* is queued. If the data is needed beyond this point, it should be copied.
*
* @param cls closure
* @param nvt the name, the value and the kind of the element
* @param nv the name and the value of the element
* @param kind the type (kind) of the element
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
@@ -4920,7 +4977,8 @@ struct MHD_NameValueKind
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_NameValueIterator)(void *cls,
const struct MHD_NameValueKind *nvt);
enum MHD_ValueKind kind,
const struct MHD_NameAndValue *nv);
/**
@@ -6173,6 +6231,81 @@ MHD_FN_PAR_NONNULL_ (1);
#define MHD_action_process_upload_inc(request,uc,uc_cls) \
MHD_action_process_upload (request, 0, NULL, NULL, uc, uc_cls)
#ifndef MHD_POST_PARSE_RESULT_DEFINED
enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
{
/**
* The POST data parse successfully and completely.
*/
MHD_POST_PARSE_RES_OK = 0
,
/**
* The POST request has no content or zero-length content.
*/
MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
,
/**
* Parsing of the POST data is incomplete because client used incorrect
* format of POST encoding.
* Some POST data is available or has been provided via callback.
*/
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 2
,
/**
* The POST data cannot be parsed completely because the stream has
* no free pool memory.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
,
/**
* The POST data cannot be parsed completely because no "large shared buffer"
* space is available.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
,
/**
* The POST data cannot be parsed because 'Content-Type:' is unknown.
*/
MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
,
/**
* The POST data cannot be parsed because 'Content-Type:' header is not set.
*/
MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
,
/**
* The POST data cannot be parsed because "Content-Type:" request header has
* no "boundary" parameter for "multipart/form-data"
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
,
/**
* The POST data cannot be parsed because "Content-Type: multipart/form-data"
* request header is misformed
*/
MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
,
/**
* The application set POST encoding to "multipart/form-data", but the request
* has no "Content-Type: multipart/form-data" header which is required
* to find "boundary" used in this encoding
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
,
/**
* The POST data cannot be parsed because client used incorrect format
* of POST encoding.
*/
MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
};
#define MHD_POST_PARSE_RESULT_DEFINED 1
#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
#ifndef MHD_POST_DATA_READER_DEFINED
/**
@@ -6182,15 +6315,24 @@ MHD_FN_PAR_NONNULL_ (1);
*
* @param req the request
* @param cls user-specified closure
* @param name 0-terminated key for the value
* @param filename name of the uploaded file, NULL if not known
* @param content_type mime-type of the data, NULL if not known
* @param encoding the encoding of the data
* @param data pointer to @a size bytes of data at the
* specified @a off offset,
* NOT zero-terminated
* @param off offset of data in the overall value
* @param size number of bytes in @a data available
* @param name the name of the POST field
* @param filename the name of the uploaded file, @a cstr member is NULL if not
* known / not provided
* @param content_type the mime-type of the data, cstr member is NULL if not
* known / not provided
* @param encoding the encoding of the data, cstr member is NULL if not known /
* not provided
* @param size the number of bytes in @a data available, may be zero if
* the @a final_data is #MHD_YES
* @param data the pointer to @a size bytes of data at the specified
* @a off offset, NOT zero-terminated
* @param off the offset of @a data in the overall value, always equal to
* the sum of sizes of previous calls for the same field / file;
* client may provide more than one field with the same name and
* the same filename, the new filed (or file) is indicated by zero
* value of @a off (and the end is indicated by @a final_data)
* @param final_data if set to #MHD_YES then full field data is provided,
* if set to #MHD_NO then more field data may be provided
* @return action specifying how to proceed:
* #MHD_upload_action_continue() if all is well,
* #MHD_upload_action_suspend() to stop reading the upload until
@@ -6209,9 +6351,10 @@ typedef const struct MHD_UploadAction *
const struct MHD_StringNullable *filename,
const struct MHD_StringNullable *content_type,
const struct MHD_StringNullable *encoding,
size_t size,
const void *data,
uint_fast64_t off,
size_t size);
enum MHD_Bool final_data);
/**
@@ -6219,15 +6362,14 @@ typedef const struct MHD_UploadAction *
* of the postprocessor upload data.
* @param req the request
* @param cls the closure
* @param parsing_result the result of POST data parsing: MHD_SC_OK,
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE or ...
* @param parsing_result the result of POST data parsing
* @return the action to proceed
*/
typedef const struct MHD_UploadAction *
(MHD_FN_PAR_NONNULL_ (1)
*MHD_PostDataFinished) (struct MHD_Request *req,
void *cls,
enum MHD_StatusCode parsing_result);
enum MHD_PostParseResult parsing_result);
#define MHD_POST_DATA_READER_DEFINED 1
#endif /* ! MHD_POST_DATA_READER_DEFINED */
@@ -6250,8 +6392,12 @@ typedef const struct MHD_UploadAction *
* request POST data. Within the set limit the buffer is
* allocated automatically from the "large" shared memory
* pool if necessary.
* @param auto_stream_size the size above which values are not buffered and
* processed by the @a steam_reader automatically,
* @param auto_stream_size the size of the field (in encoded form) above which
* values are not buffered and processed by
* the @a steam_reader automatically;
* useful to have large data (like file uploads)
* processed incrementally, while keeping buffer space
* for small fields only;
* ignored if @a stream_reader is NULL
* @param enc the data encoding to use,
* use #MHD_HTTP_POST_ENCODING_OTHER to detect automatically
@@ -6283,44 +6429,54 @@ MHD_action_parse_post (struct MHD_Request *request,
MHD_FN_PAR_NONNULL_ (1);
#ifndef MHD_POSTFILED_DEFINED
/**
* Post data element.
* If any member is not provided/set then pointer to C string is NULL.
* If any member is set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_PostData
struct MHD_PostField
{
/**
* The name of the field
*/
struct MHD_String name;
/**
* The field data
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable value;
/**
* The filename if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable filename;
/**
* The Content-Type if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable content_type;
/**
* The Transfer-Encoding if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable transfer_encoding;
/**
* The field data
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
*/
struct MHD_StringNullable value;
};
#define MHD_POSTFILED_DEFINED 1
#endif /* ! MHD_POSTFILED_DEFINED */
/**
* Iterator over POST data.
*
@@ -6336,7 +6492,7 @@ struct MHD_PostData
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_PostDataIterator)(void *cls,
const struct MHD_PostData *data);
const struct MHD_PostField *data);
/**
* Get all of the post data from the request via request.
@@ -6372,7 +6528,7 @@ MHD_EXTERN_ size_t
MHD_request_get_post_data_list (
struct MHD_Request *request,
size_t num_elements,
struct MHD_PostData elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
struct MHD_PostField elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2);
+126 -26
View File
@@ -611,7 +611,8 @@ struct MHD_NameValueKind
* is queued. If the data is needed beyond this point, it should be copied.
*
* @param cls closure
* @param nvt the name, the value and the kind of the element
* @param nv the name and the value of the element
* @param kind the type (kind) of the element
* @return #MHD_YES to continue iterating,
* #MHD_NO to abort the iteration
* @ingroup request
@@ -619,7 +620,8 @@ struct MHD_NameValueKind
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_NameValueIterator)(void *cls,
const struct MHD_NameValueKind *nvt);
enum MHD_ValueKind kind,
const struct MHD_NameAndValue *nv);
/**
@@ -1872,6 +1874,81 @@ MHD_FN_PAR_NONNULL_ (1);
#define MHD_action_process_upload_inc(request,uc,uc_cls) \
MHD_action_process_upload (request, 0, NULL, NULL, uc, uc_cls)
#ifndef MHD_POST_PARSE_RESULT_DEFINED
enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
{
/**
* The POST data parse successfully and completely.
*/
MHD_POST_PARSE_RES_OK = 0
,
/**
* The POST request has no content or zero-length content.
*/
MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
,
/**
* Parsing of the POST data is incomplete because client used incorrect
* format of POST encoding.
* Some POST data is available or has been provided via callback.
*/
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 2
,
/**
* The POST data cannot be parsed completely because the stream has
* no free pool memory.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
,
/**
* The POST data cannot be parsed completely because no "large shared buffer"
* space is available.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
,
/**
* The POST data cannot be parsed because 'Content-Type:' is unknown.
*/
MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
,
/**
* The POST data cannot be parsed because 'Content-Type:' header is not set.
*/
MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
,
/**
* The POST data cannot be parsed because "Content-Type:" request header has
* no "boundary" parameter for "multipart/form-data"
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
,
/**
* The POST data cannot be parsed because "Content-Type: multipart/form-data"
* request header is misformed
*/
MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
,
/**
* The application set POST encoding to "multipart/form-data", but the request
* has no "Content-Type: multipart/form-data" header which is required
* to find "boundary" used in this encoding
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
,
/**
* The POST data cannot be parsed because client used incorrect format
* of POST encoding.
*/
MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
};
#define MHD_POST_PARSE_RESULT_DEFINED 1
#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
#ifndef MHD_POST_DATA_READER_DEFINED
/**
@@ -1881,15 +1958,24 @@ MHD_FN_PAR_NONNULL_ (1);
*
* @param req the request
* @param cls user-specified closure
* @param name 0-terminated key for the value
* @param filename name of the uploaded file, NULL if not known
* @param content_type mime-type of the data, NULL if not known
* @param encoding the encoding of the data
* @param data pointer to @a size bytes of data at the
* specified @a off offset,
* NOT zero-terminated
* @param off offset of data in the overall value
* @param size number of bytes in @a data available
* @param name the name of the POST field
* @param filename the name of the uploaded file, @a cstr member is NULL if not
* known / not provided
* @param content_type the mime-type of the data, cstr member is NULL if not
* known / not provided
* @param encoding the encoding of the data, cstr member is NULL if not known /
* not provided
* @param size the number of bytes in @a data available, may be zero if
* the @a final_data is #MHD_YES
* @param data the pointer to @a size bytes of data at the specified
* @a off offset, NOT zero-terminated
* @param off the offset of @a data in the overall value, always equal to
* the sum of sizes of previous calls for the same field / file;
* client may provide more than one field with the same name and
* the same filename, the new filed (or file) is indicated by zero
* value of @a off (and the end is indicated by @a final_data)
* @param final_data if set to #MHD_YES then full field data is provided,
* if set to #MHD_NO then more field data may be provided
* @return action specifying how to proceed:
* #MHD_upload_action_continue() if all is well,
* #MHD_upload_action_suspend() to stop reading the upload until
@@ -1908,9 +1994,10 @@ typedef const struct MHD_UploadAction *
const struct MHD_StringNullable *filename,
const struct MHD_StringNullable *content_type,
const struct MHD_StringNullable *encoding,
size_t size,
const void *data,
uint_fast64_t off,
size_t size);
enum MHD_Bool final_data);
/**
@@ -1918,15 +2005,14 @@ typedef const struct MHD_UploadAction *
* of the postprocessor upload data.
* @param req the request
* @param cls the closure
* @param parsing_result the result of POST data parsing: MHD_SC_OK,
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE or ...
* @param parsing_result the result of POST data parsing
* @return the action to proceed
*/
typedef const struct MHD_UploadAction *
(MHD_FN_PAR_NONNULL_ (1)
*MHD_PostDataFinished) (struct MHD_Request *req,
void *cls,
enum MHD_StatusCode parsing_result);
enum MHD_PostParseResult parsing_result);
#define MHD_POST_DATA_READER_DEFINED 1
#endif /* ! MHD_POST_DATA_READER_DEFINED */
@@ -1949,8 +2035,12 @@ typedef const struct MHD_UploadAction *
* request POST data. Within the set limit the buffer is
* allocated automatically from the "large" shared memory
* pool if necessary.
* @param auto_stream_size the size above which values are not buffered and
* processed by the @a steam_reader automatically,
* @param auto_stream_size the size of the field (in encoded form) above which
* values are not buffered and processed by
* the @a steam_reader automatically;
* useful to have large data (like file uploads)
* processed incrementally, while keeping buffer space
* for small fields only;
* ignored if @a stream_reader is NULL
* @param enc the data encoding to use,
* use #MHD_HTTP_POST_ENCODING_OTHER to detect automatically
@@ -1982,44 +2072,54 @@ MHD_action_parse_post (struct MHD_Request *request,
MHD_FN_PAR_NONNULL_ (1);
#ifndef MHD_POSTFILED_DEFINED
/**
* Post data element.
* If any member is not provided/set then pointer to C string is NULL.
* If any member is set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_PostData
struct MHD_PostField
{
/**
* The name of the field
*/
struct MHD_String name;
/**
* The field data
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable value;
/**
* The filename if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable filename;
/**
* The Content-Type if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable content_type;
/**
* The Transfer-Encoding if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable transfer_encoding;
/**
* The field data
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
*/
struct MHD_StringNullable value;
};
#define MHD_POSTFILED_DEFINED 1
#endif /* ! MHD_POSTFILED_DEFINED */
/**
* Iterator over POST data.
*
@@ -2035,7 +2135,7 @@ struct MHD_PostData
typedef enum MHD_Bool
(MHD_FN_PAR_NONNULL_ (2)
*MHD_PostDataIterator)(void *cls,
const struct MHD_PostData *data);
const struct MHD_PostField *data);
/**
* Get all of the post data from the request via request.
@@ -2071,7 +2171,7 @@ MHD_EXTERN_ size_t
MHD_request_get_post_data_list (
struct MHD_Request *request,
size_t num_elements,
struct MHD_PostData elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
struct MHD_PostField elements[MHD_FN_PAR_DYN_ARR_SIZE_ (num_elements)])
MHD_FN_PAR_NONNULL_ (1)
MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2);
+58 -2
View File
@@ -147,6 +147,8 @@
*/
#define MHD_VERSION 0x01990001
#ifndef MHD_BOOL_DEFINED
/**
* Representation of 'bool' in the public API as stdbool.h may not
* always be available and presence of 'bool' keyword may depend on
@@ -171,6 +173,10 @@ enum MHD_Bool
MHD_YES = 1
};
#define MHD_BOOL_DEFINED 1
#endif /* ! MHD_BOOL_DEFINED */
#ifndef MHD_STRINGS_DEFINED
@@ -638,16 +644,46 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_REQ_COOKIE_INVALID = 40163
,
/**
* Parsing of the POST data is incomplete because client used incorrect
* format of POST encoding.
* Some POST data is available or has been provided via callback.
*/
MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT = 40202
,
/**
* The request does not have "Content-Type:" header and POST data cannot
* be parsed
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40180
MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE = 40280
,
/**
* The request has unknown POST encoding specified by "Content-Type:" header
*/
MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE = 40281
,
/**
* The request has "Content-Type: multipart/form-data" header without
* "boundary" parameter
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY = 40282
,
/**
* The request has "Content-Type: multipart/form-data" header with misformed
* data
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED = 40283
,
/**
* The POST data cannot be parsed because client used incorrect format
* of POST encoding.
*/
MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT = 40290
,
/**
* The request cannot be processed. Sending error reply.
*/
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 40200
MHD_SC_REQ_PROCCESSING_ERR_REPLY = 41000
,
/* 50000-level errors are because of an error internal
@@ -1109,6 +1145,26 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode
*/
MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE = 50250
,
/**
* The request POST data cannot be parsed because stream has not enough
* pool memory free.
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM = 50260
,
/**
* The POST data cannot be parsed completely because no "large shared buffer"
* space is available.
* Some POST data may be parsed.
*/
MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM = 50261
,
/**
* The application set POST encoding to "multipart/form-data", but the request
* has no "Content-Type: multipart/form-data" header which is required
* to find "boundary" used in this encoding
*/
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART = 50284
,
/**
* The feature is not supported by this MHD build (either
* disabled by configure parameters or build platform
+10
View File
@@ -36,6 +36,7 @@ libmicrohttpd2_la_SOURCES = \
mhd_sockets_funcs.c mhd_sockets_funcs.h \
mhd_socket_error.c mhd_socket_error.h \
mhd_atomic_counter.c mhd_atomic_counter.h \
mhd_bool.h \
mhd_str.c mhd_str.h \
mhd_str_macros.h mhd_str_types.h \
mhd_buffer.h \
@@ -90,6 +91,15 @@ libmicrohttpd2_la_SOURCES += \
compat_calloc.c
endif
post_parser_files = \
http_post_enc.h \
mhd_post_parser.h mhd_post_result.h mhd_postfield.h \
post_parser_funcs.c post_parser_funcs.h
if HAVE_POST_PARSER
libmicrohttpd2_la_SOURCES += $(post_parser_files)
endif
libmicrohttpd2_la_CPPFLAGS = \
$(AM_CPPFLAGS) $(MHD_LIB_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) \
+11 -11
View File
@@ -120,11 +120,11 @@ mhd_daemon_get_lbuf (struct MHD_Daemon *restrict d,
if (! mhd_daemon_claim_lbuf (d, requested_size))
{
buf->size = 0;
buf->buf = NULL;
buf->data = NULL;
return false;
}
buf->buf = (char *) malloc (requested_size);
if (NULL == buf->buf)
buf->data = (char *) malloc (requested_size);
if (NULL == buf->data)
{
buf->size = 0;
mhd_daemon_reclaim_lbuf (d, requested_size);
@@ -142,23 +142,23 @@ mhd_daemon_grow_lbuf (struct MHD_Daemon *restrict d,
struct mhd_Buffer *restrict buf)
{
void *new_alloc;
mhd_assert (NULL != buf->buf || 0 == buf->size);
mhd_assert (0 != buf->size || NULL == buf->buf);
mhd_assert (NULL != buf->data || 0 == buf->size);
mhd_assert (0 != buf->size || NULL == buf->data);
if (! mhd_daemon_claim_lbuf (d, grow_size))
return false;
if (NULL == buf->buf)
if (NULL == buf->data)
new_alloc = malloc (grow_size);
else
new_alloc = realloc (buf->buf, buf->size + grow_size);
new_alloc = realloc (buf->data, buf->size + grow_size);
if (NULL == new_alloc)
{
mhd_daemon_reclaim_lbuf (d, grow_size);
return false;
}
buf->buf = (char *) new_alloc;
buf->data = (char *) new_alloc;
buf->size += grow_size;
return true;
@@ -172,11 +172,11 @@ mhd_daemon_free_lbuf (struct MHD_Daemon *restrict d,
{
if (0 == buf->size)
{
mhd_assert (NULL == buf->buf);
mhd_assert (NULL == buf->data);
return;
}
free (buf->buf);
buf->buf = NULL;
free (buf->data);
buf->data = NULL;
mhd_daemon_reclaim_lbuf (d, buf->size);
buf->size = 0;
}
+1
View File
@@ -264,6 +264,7 @@ close_all_daemon_conns (struct MHD_Daemon *d)
c = mhd_DLINKEDL_GET_LAST (&(d->conns),all_conn))
{
mhd_conn_pre_close_d_shutdown (c);
mhd_conn_pre_clean (c);
mhd_conn_close_final (c);
}
}
+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/http_post_enc.h
* @brief The definition of the enum for POST encoding types
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_HTTP_POST_ENC_H
#define MHD_HTTP_POST_ENC_H 1
#include "mhd_sys_options.h"
#ifndef MHD_HTTP_POSTENCODING_DEFINED
/**
* @brief Possible encodings for HTML forms submitted as HTTP POST requests
*
* @defgroup postenc HTTP POST encodings
* See also: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-2
* @{
*/
enum MHD_FIXED_ENUM_MHD_APP_SET_ MHD_HTTP_PostEncoding
{
/**
* No post encoding / broken data / unknown encoding
*/
MHD_HTTP_POST_ENCODING_OTHER = 0
,
/**
* "application/x-www-form-urlencoded"
* See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#url-encoded-form-data
* See https://url.spec.whatwg.org/#application/x-www-form-urlencoded
* See https://datatracker.ietf.org/doc/html/rfc3986#section-2
*/
MHD_HTTP_POST_ENCODING_FORM_URLENCODED = 1
,
/**
* "multipart/form-data"
* See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data
* See https://www.rfc-editor.org/rfc/rfc7578.html
*/
MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA = 2
,
/**
* "text/plain"
* Introduced by HTML5
* See https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
* @warning Format is ambiguous. Do not use unless there is a very strong reason.
*/
MHD_HTTP_POST_ENCODING_TEXT_PLAIN = 3
};
/** @} */ /* end of group postenc */
# define MHD_HTTP_POSTENCODING_DEFINED 1
#endif /* ! MHD_HTTP_POSTENCODING_DEFINED */
#endif /* ! MHD_HTTP_POST_ENC_H */
+5 -4
View File
@@ -36,7 +36,8 @@
#include "http_post_enc.h"
#ifdef HAVE_POST_PARSER
# include "mhd_public_api.h"
# include "mhd_bool.h"
# include "mhd_post_result.h"
#endif
@@ -146,16 +147,16 @@ typedef const struct MHD_UploadAction *
const struct MHD_StringNullable *filename,
const struct MHD_StringNullable *content_type,
const struct MHD_StringNullable *encoding,
size_t size,
const void *data,
uint_fast64_t off,
size_t size);
enum MHD_Bool final_data);
typedef const struct MHD_UploadAction *
(MHD_FN_PAR_NONNULL_ (1)
*MHD_PostDataFinished) (struct MHD_Request *req,
void *cls,
enum MHD_StatusCode parsing_result);
enum MHD_PostParseResult parsing_result);
#define MHD_POST_DATA_READER_DEFINED 1
#endif /* ! MHD_POST_DATA_READER_DEFINED */
+54
View File
@@ -0,0 +1,54 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Christian Grothoff
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_bool.h
* @brief The definition of the enum MHD_Bool, which is used in public API
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_BOOL_H
#define MHD_BOOL_H 1
#include "mhd_sys_options.h"
#ifndef MHD_BOOL_DEFINED
enum MHD_Bool
{
/**
* MHD-internal return code for "NO".
*/
MHD_NO = 0
,
/**
* MHD-internal return code for "YES". All non-zero values
* will be interpreted as "YES", but MHD will only ever
* return #MHD_YES or #MHD_NO.
*/
MHD_YES = 1
};
#define MHD_BOOL_DEFINED 1
#endif /* ! MHD_BOOL_DEFINED */
#endif /* ! MHD_BOOL_H */
+20 -4
View File
@@ -31,19 +31,35 @@
#include "sys_base_types.h"
/**
* The buffer with size
* The buffer with the size
*/
struct mhd_Buffer
{
/**
* The size of the allocated @a buf buffer
* The size of the data or the allocation pointed by @a buf buffer
*/
size_t size;
/**
* The pointer to the allocation
* The pointer to the data or the allocation
*/
char *buf;
char *data;
};
/**
* The data with the size
*/
struct mhd_BufferConst
{
/**
* The size of the data pointed by @a buf buffer
*/
size_t size;
/**
* The pointer to the data
*/
const char *data;
};
#endif /* ! MHD_BUFFER_H */
+52 -46
View File
@@ -201,133 +201,139 @@ enum MHD_FIXED_ENUM_ MHD_CONNECTION_STATE
* Connection just started (no headers received).
* Waiting for the line with the request type, URL and version.
*/
MHD_CONNECTION_INIT = 0,
MHD_CONNECTION_INIT = 0
,
/**
* Part of the request line was received.
* Wait for complete line.
*/
MHD_CONNECTION_REQ_LINE_RECEIVING,
MHD_CONNECTION_REQ_LINE_RECEIVING
,
/**
* We got the URL (and request type and version). Wait for a header line.
*
* A milestone state. No received data is processed in this state.
*/
MHD_CONNECTION_REQ_LINE_RECEIVED,
MHD_CONNECTION_REQ_LINE_RECEIVED
,
/**
* Receiving request headers. Wait for the rest of the headers.
*/
MHD_CONNECTION_REQ_HEADERS_RECEIVING,
MHD_CONNECTION_REQ_HEADERS_RECEIVING
,
/**
* We got the request headers. Process them.
*/
MHD_CONNECTION_HEADERS_RECEIVED,
MHD_CONNECTION_HEADERS_RECEIVED
,
/**
* We have processed the request headers. Call application callback.
*/
MHD_CONNECTION_HEADERS_PROCESSED,
MHD_CONNECTION_HEADERS_PROCESSED
,
/**
* We have processed the headers and need to send 100 CONTINUE.
*/
MHD_CONNECTION_CONTINUE_SENDING,
MHD_CONNECTION_CONTINUE_SENDING
,
/**
* We have sent 100 CONTINUE (or do not need to). Read the message body.
*/
MHD_CONNECTION_BODY_RECEIVING,
MHD_CONNECTION_BODY_RECEIVING
,
/**
* We got the request body.
*
* A milestone state. No received data is processed in this state.
*/
MHD_CONNECTION_BODY_RECEIVED,
MHD_CONNECTION_BODY_RECEIVED
,
/**
* We are reading the request footers.
*/
MHD_CONNECTION_FOOTERS_RECEIVING,
MHD_CONNECTION_FOOTERS_RECEIVING
,
/**
* We received the entire footer.
*
* A milestone state. No data is receiving in this state.
*/
MHD_CONNECTION_FOOTERS_RECEIVED,
MHD_CONNECTION_FOOTERS_RECEIVED
,
/**
* We received the entire request.
*
* A milestone state. No data is receiving in this state.
*/
MHD_CONNECTION_FULL_REQ_RECEIVED,
MHD_CONNECTION_FULL_REQ_RECEIVED
,
/**
* Finished receiving request data: either complete request received or
* MHD is going to send reply early, without getting full request.
*/
MHD_CONNECTION_REQ_RECV_FINISHED,
MHD_CONNECTION_REQ_RECV_FINISHED
,
/**
* Finished reading of the request and the response is ready.
* Switch internal logic from receiving to sending, prepare connection
* sending the reply and build the reply header.
*/
MHD_CONNECTION_START_REPLY,
MHD_CONNECTION_START_REPLY
,
/**
* We have prepared the response headers in the write buffer.
* Send the response headers.
*/
MHD_CONNECTION_HEADERS_SENDING,
MHD_CONNECTION_HEADERS_SENDING
,
/**
* We have sent the response headers. Get ready to send the body.
*/
MHD_CONNECTION_HEADERS_SENT,
MHD_CONNECTION_HEADERS_SENT
,
/**
* We are waiting for the client to provide more
* data of a non-chunked body.
*/
MHD_CONNECTION_UNCHUNKED_BODY_UNREADY,
MHD_CONNECTION_UNCHUNKED_BODY_UNREADY
,
/**
* We are ready to send a part of a non-chunked body. Send it.
*/
MHD_CONNECTION_UNCHUNKED_BODY_READY,
MHD_CONNECTION_UNCHUNKED_BODY_READY
,
/**
* We are waiting for the client to provide a chunk of the body.
*/
MHD_CONNECTION_CHUNKED_BODY_UNREADY,
MHD_CONNECTION_CHUNKED_BODY_UNREADY
,
/**
* We are ready to send a chunk.
*/
MHD_CONNECTION_CHUNKED_BODY_READY,
MHD_CONNECTION_CHUNKED_BODY_READY
,
/**
* We have sent the chunked response body. Prepare the footers.
*/
MHD_CONNECTION_CHUNKED_BODY_SENT,
MHD_CONNECTION_CHUNKED_BODY_SENT
,
/**
* We have prepared the response footer. Send it.
*/
MHD_CONNECTION_FOOTERS_SENDING,
MHD_CONNECTION_FOOTERS_SENDING
,
/**
* We have sent the entire reply.
* Shutdown connection or restart processing to get a new request.
*/
MHD_CONNECTION_FULL_REPLY_SENT,
MHD_CONNECTION_FULL_REPLY_SENT
,
/**
* Finished regular connection processing.
* Initial buffers cleanup and freeing.
*/
MHD_CONNECTION_PRE_CLOSING
,
/**
* This connection is to be closed.
*/
+9
View File
@@ -831,6 +831,15 @@ struct mhd_DaemonRequestProcessingSettings
};
/**
* 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)
#ifndef NDEBUG
/**
* Various debugging data
+281
View File
@@ -0,0 +1,281 @@
/*
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_post_parser.h
* @brief The definition of the post parsers data structures
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_POST_PARSER_H
#define MHD_POST_PARSER_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
#include "sys_bool_type.h"
#include "http_post_enc.h"
#include "mhd_post_result.h"
#include "mhd_buffer.h"
/**
* The "application/x-www-form-urlencoded" parsing data
*/
struct mhd_PostParserUrlEncData
{
};
/**
* The "multipart/form-data" parsing data
*/
struct mhd_PostParserMPartFormData
{
/**
* The boundary marker.
* Allocated in the stream pool
*/
struct mhd_BufferConst bound;
};
// TODO: describe
enum MHD_FIXED_ENUM_ mhd_PostTextState
{
/**
* The line processing has not been started yet
*/
mhd_POST_TEXT_ST_NOT_STARTED = 0
,
/**
* Processing name of the field
*/
mhd_POST_TEXT_ST_NAME
,
/**
* At the '=' character after the name.
* This is an intermediate state, should be processed and switched to the next
* state immediately.
* Should not be used outside processing loop.
*/
mhd_POST_TEXT_ST_AT_EQ
,
/**
* The '=' character after the name has been found.
* Looking for the first value character.
*/
mhd_POST_TEXT_ST_EQ_FOUND
,
/**
* Processing the value of the field.
*/
mhd_POST_TEXT_ST_VALUE
,
/**
* At the CR character.
* This is an intermediate state, should be processed and switched to the next
* state immediately.
* Should not be used outside processing loop.
*/
mhd_POST_TEXT_ST_AT_CR
,
/**
* Looking for LF character after CR character.
*/
mhd_POST_TEXT_ST_CR_FOUND
,
/**
* At the LF character without preceding CR character.
* This is an intermediate state, should be processed and switched to the next
* state immediately.
* Should not be used outside processing loop.
*/
mhd_POST_TEXT_ST_AT_LF_BARE
,
/**
* End of the line found.
* This is an intermediate state, should be processed and switched to the next
* state immediately.
* Should not be used outside processing loop.
*/
mhd_POST_TEXT_ST_FULL_LINE_FOUND
};
/**
* The "text/plain" parsing data
*/
struct mhd_PostParserTextData
{
/**
* The parsing state
*/
enum mhd_PostTextState st;
/**
* The index of the start of the name.
*/
size_t name_idx;
/**
* The length of the name of the current field, not including
* the terminating zero.
* Zero until the length is found.
*/
size_t name_len;
/**
* The index of the start of the value.
* Zero until the value is found.
* Cannot be zero if any (including zero-length) value available.
*/
size_t value_idx;
/**
* The length of the value of the current field, not including
* the terminating zero.
* Zero until the length is found.
*/
size_t value_len;
};
/**
* The encoding-specific parsing data
*/
union mhd_PostParserDetailedData
{
/**
* The "application/x-www-form-urlencoded" parsing data
*/
struct mhd_PostParserUrlEncData u_enc;
/**
* The "multipart/form-data" parsing data
*/
struct mhd_PostParserMPartFormData m_form;
/**
* The "text/plain" parsing data
*/
struct mhd_PostParserTextData text;
};
// TODO: remove?
/**
* The type of partially processed data in the buffer
*/
enum MHD_FIXED_ENUM_ mhd_PostParserPartProcType
{
/**
* No data in the buffer
*/
mhd_POST_PARSER_PART_PROC_TYPE_NONE = 0
,
/**
* The data is partially processed name
*/
mhd_POST_PARSER_PART_PROC_TYPE_NAME
,
/**
* The data is partially processed value
*/
mhd_POST_PARSER_PART_PROC_TYPE_VALUE
};
// TODO: remove?
/**
* Buffered partially processed data
*/
struct mhd_PostParserPartProcessedData
{
/**
* Partially processed data, left from previous upload data portion
*/
struct mhd_Buffer data;
/**
* The type of partially processed data in the @a data buffer
*/
enum mhd_PostParserPartProcType d_type;
};
/**
* The POST parsing data
*/
struct mhd_PostParserData
{
/**
* The result of parsing POST data
*/
enum MHD_PostParseResult parse_result;
/**
* The type of POSE encoding is used.
* Active member of @a e_d depends on this type.
*/
enum MHD_HTTP_PostEncoding enc;
/**
* The encoding-specific parsing data
*/
union mhd_PostParserDetailedData e_d;
/**
* The "large" buffer for POST parsing and POST upload values
*/
struct mhd_Buffer lbuf;
/**
* The size of the data currently in the @a lbuf
*/
size_t lbuf_used;
/**
* The remaining size of "large shared buffer" allowed to allocate for this
* POST parsing
*/
size_t lbuf_left; // TODO: Remove? Rename to 'lbuf_limit'?
/**
* True if any POST data was parsed successfully.
*/
bool some_data_provided;
/**
* The start index of the current field.
* If the field is processed by incremental callback, buffer can be freed or
* reused up to this position (inclusive).
*/
size_t field_start;
/**
* The offset in the value data.
* Used when value is processed incrementally otherwise it is zero.
*/
size_t value_off;
/**
* The position of the next character to be parsed
*/
size_t next_parse_pos;
};
#endif /* ! MHD_POST_PARSER_H */
+110
View File
@@ -0,0 +1,110 @@
/*
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_post_result.h
* @brief The definition of enum for POST parsing result
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_POST_RESULT_H
#define MHD_POST_RESULT_H 1
#include "mhd_sys_options.h"
// TODO: describe and copy to the main header
#ifndef MHD_POST_PARSE_RESULT_DEFINED
enum MHD_FIXED_ENUM_MHD_SET_ MHD_PostParseResult
{
/**
* The POST data parse successfully and completely.
*/
MHD_POST_PARSE_RES_OK = 0
,
/**
* The POST request has no content or zero-length content.
*/
MHD_POST_PARSE_RES_REQUEST_EMPTY = 1
,
/**
* Parsing of the POST data is incomplete because client used incorrect
* format of POST encoding.
* Some POST data is available or has been provided via callback.
*/
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT = 2
,
/**
* The POST data cannot be parsed completely because the stream has
* no free pool memory.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM = 60
,
/**
* The POST data cannot be parsed completely because no "large shared buffer"
* space is available.
* Some POST data may be parsed.
*/
MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM = 61
,
/**
* The POST data cannot be parsed because 'Content-Type:' is unknown.
*/
MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE = 80
,
/**
* The POST data cannot be parsed because 'Content-Type:' header is not set.
*/
MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE = 81
,
/**
* The POST data cannot be parsed because "Content-Type:" request header has
* no "boundary" parameter for "multipart/form-data"
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY = 82
,
/**
* The POST data cannot be parsed because "Content-Type: multipart/form-data"
* request header is misformed
*/
MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED = 83
,
/**
* The application set POST encoding to "multipart/form-data", but the request
* has no "Content-Type: multipart/form-data" header which is required
* to find "boundary" used in this encoding
*/
MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART = 84
,
/**
* The POST data cannot be parsed because client used incorrect format
* of POST encoding.
*/
MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT = 90
};
#define MHD_POST_PARSE_RESULT_DEFINED 1
#endif /* ! MHD_POST_PARSE_RESULT_DEFINED */
#endif /* ! MHD_POST_RESULT_H */
+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_postfield.h
* @brief The definition of the struct MHD_PostField
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_POSTFIELD_H
#define MHD_POSTFIELD_H 1
#include "mhd_sys_options.h"
#ifndef MHD_POSTFILED_DEFINED
struct MHD_PostField
{
/**
* The name of the field
*/
struct MHD_String name;
/**
* The field data
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable value;
/**
* The filename if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable filename;
/**
* The Content-Type if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable content_type;
/**
* The Transfer-Encoding if provided (only for "multipart/form-data")
* If not set or defined then to C string is NULL.
* If set to empty string then pointer to C string not NULL,
* but the length is zero.
*/
struct MHD_StringNullable transfer_encoding;
};
#define MHD_POSTFILED_DEFINED 1
#endif /* ! MHD_POSTFILED_DEFINED */
#endif /* ! MHD_POSTFIELD_H */
+59 -2
View File
@@ -41,6 +41,11 @@
#include "mhd_action.h"
#include "mhd_buffer.h"
#ifdef HAVE_POST_PARSER
# include "mhd_postfield.h"
# include "mhd_post_parser.h"
#endif
/**
* The action set by the application
@@ -200,6 +205,33 @@ struct mhd_RequestField
mhd_DLINKEDL_LIST_DEF (mhd_RequestField);
#ifdef HAVE_POST_PARSER
struct mhd_RequestPostField; /* forward declarations */
mhd_DLINKEDL_LINKS_DEF (mhd_RequestPostField);
/**
* The data for POST request fields
*/
struct mhd_RequestPostField
{
/**
* The field data
*/
struct MHD_PostField field;
/**
* Headers are kept in a double-linked list.
*/
mhd_DLNKDL_LINKS (mhd_RequestPostField,post_fields);
};
mhd_DLINKEDL_LIST_DEF (mhd_RequestPostField);
#endif /* HAVE_POST_PARSER */
/**
* The request content data
@@ -219,17 +251,30 @@ struct mhd_ReqContentData
uint_fast64_t cntn_size;
/**
* The size of the received content
* The size of the received content.
* Excluding chunked encoding framing.
*/
uint_fast64_t recv_size;
/**
* The size of the processed content
* The size of the processed content.
* Excluding chunked encoding framing.
*/
uint_fast64_t proc_size;
};
union mhd_ReqContentParsingData
{
#ifdef HAVE_POST_PARSER
/**
* The POST parsing data
*/
struct mhd_PostParserData post;
#endif /* HAVE_POST_PARSER */
// TODO: move "raw" upload processing data here
};
/**
* Request-specific values.
*
@@ -242,6 +287,13 @@ struct MHD_Request
*/
mhd_DLNKDL_LIST (mhd_RequestField,fields);
#ifdef HAVE_POST_PARSER
/**
* Linked list of parsed POST fields.
*/
mhd_DLNKDL_LIST (mhd_RequestPostField,post_fields);
#endif /* HAVE_POST_PARSER */
/**
* The action set by the application
*/
@@ -257,6 +309,11 @@ struct MHD_Request
*/
bool too_large;
/**
* Upload processing data
*/
union mhd_ReqContentParsingData u_proc;
/**
* HTTP version string (i.e. http/1.1). Allocated
* in pool.
+216 -2
View File
@@ -2003,6 +2003,10 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
}
#endif /* DAUTH_SUPPORT */
#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
MHD_INTERNAL size_t
mhd_str_unquote (const char *quoted,
size_t quoted_len,
@@ -2027,8 +2031,7 @@ mhd_str_unquote (const char *quoted,
return w;
}
#endif /* DAUTH_SUPPORT */
#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
@@ -2326,3 +2329,214 @@ MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
#undef mhd_base64_map_type
#endif /* BAUTH_SUPPORT */
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_str_starts_with_token_opt_param (const struct MHD_String *restrict str,
const struct MHD_String *restrict token)
{
size_t i;
mhd_assert (0 != token->len);
mhd_assert (NULL == memchr(token->cstr, '=', token->len));
mhd_assert (NULL == memchr(token->cstr, ' ', token->len));
mhd_assert (NULL == memchr(token->cstr, '\t', token->len));
if (str->len < token->len)
return false; /* The string is too short to match */
if (! mhd_str_equal_caseless_bin_n (str->cstr,
token->cstr,
token->len))
return false; /* The string does not start with the token */
for (i = token->len; i < str->len; ++i)
{
const char c = str->cstr[i];
if ((' ' == c) || ('\t' == c))
continue;
if (';' == c)
return true; /* Found the start of the token parameters */
return false; /* The initial part of the string does not fully match the token */
}
mhd_assert (0 && "The string should not have whitespace at the end");
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_(4)
MHD_FN_PAR_OUT_(5) enum mhd_StingStartsWithTokenResult
mhd_str_starts_with_token_req_param (
const struct MHD_String *restrict str,
const struct MHD_String *restrict token,
const struct MHD_String *restrict par,
struct mhd_BufferConst *restrict par_value,
bool *restrict par_value_needs_unquote)
{
size_t i;
const char *const restrict cstr = str->cstr;
bool token_found;
bool param_found;
mhd_assert (0 != token->len);
mhd_assert (NULL == memchr(token->cstr, '=', token->len));
mhd_assert (NULL == memchr(token->cstr, ' ', token->len));
mhd_assert (NULL == memchr(token->cstr, '\t', token->len));
mhd_assert (NULL == memchr(par->cstr, '=', par->len));
mhd_assert (NULL == memchr(par->cstr, ' ', par->len));
mhd_assert (NULL == memchr(par->cstr, '\t', par->len));
par_value->data = NULL;
par_value->size = 0;
if (str->len < token->len)
return false; /* The string is too short to match */
if (! mhd_str_equal_caseless_bin_n (cstr,
token->cstr,
token->len))
return mhd_STR_STARTS_W_TOKEN_NO_TOKEN; /* The string does not start with the token */
token_found = false;
param_found = false;
i = token->len;
do
{
/* Find start of the next parameter */
for ((void) 0; i < str->len; ++i)
{
const char c = cstr[i];
if ((' ' == c) || ('\t' == c))
continue;
if (';' == c)
{
/* Found the start of the next token parameter */
if (param_found)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
break;
}
if (',' == c)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* Found the start of the next token */
if (! token_found)
{
if (i == token->len)
{
/* The initial part of the string does not fully match the token or
formatting is not correct */
return mhd_STR_STARTS_W_TOKEN_NO_TOKEN;
}
/* The string has garbage after the token and whitespace */
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
}
/* The garbage after the parameter */
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
}
token_found = true;
if (i == str->len)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
/* 'i' is at the start of the parameter name */
if (par->len > str->len - i - 1)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* the token is found, but the parameter is not */
else
{ /* Check the parameter */
bool val_needs_unquote;
size_t j;
const char *const prm_str = cstr + i;
for (j = 0; j < par->len; ++j)
if (! charsequalcaseless (prm_str[j],
par->cstr[j]))
break;
i += j;
mhd_assert (str->len > i);
if ((j == par->len) &&
('=' == cstr[i]))
{
/* The parameter name matches required parameter */
param_found = true;
par_value->data = cstr + i + 1;
}
else
{
/* i points to the char in the parameter name */
while ('=' != cstr[i])
{
if ((';' == cstr[i]) /* end of the parameter */
|| (',' == cstr[i]) /* end of the token */
|| (str->len == ++i)) /* end of the field string */
{
/* parameter without the value */
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT;
}
}
}
mhd_assert (str->len > i);
mhd_assert ('=' == cstr[i]);
/* 'i' points to '=' between parameter name and parameter value */
++i; /* Advance to the first char in the parameter value */
if (str->len == i)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN; /* Zero-length parameter value */
val_needs_unquote = false;
/* 'i' points to the char after '=' */
if ('"' == cstr[i])
{
/* The value is quoted */
++(par_value->data); /* Point to the first quoted char */
do
{
++i; /* Advance to the next char */
if (str->len == i)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT; /* No closing quote */
if ('\\' == cstr[i])
{
val_needs_unquote = true;
++i; /* Skip quoted char */
if (str->len == i)
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT; /* No closing quote */
}
} while ('"' != cstr[i]);
if (param_found)
{
par_value->size = (size_t) ((cstr + i) - par_value->data);
*par_value_needs_unquote = val_needs_unquote;
}
/* Complete value found */
/* Check for the garbage data at the end */
++i; /* Advance to the next char */
}
else
{
/* The value is not quoted */
while ((' ' != cstr[i]) &&
('\t' != cstr[i]))
{
if ((';' == cstr[i]) /* end of the parameter */
|| (',' == cstr[i]) /* end of the token */
|| (str->len == ++i)) /* end of the field string */
break;
}
/* The end parameter value */
if (param_found)
{
par_value->size = (size_t) ((cstr + i) - par_value->data);
*par_value_needs_unquote = false;
}
/* Check for the garbage data at the end */
}
/* 'i' points to the next char after end of the parameter value */
}
} while (i < str->len);
mhd_assert (token_found);
return mhd_STR_STARTS_W_TOKEN_HAS_TOKEN;
}
+86 -1
View File
@@ -27,12 +27,19 @@
#define MHD_STR_H 1
#include "mhd_sys_options.h"
#include "sys_base_types.h"
#include "sys_bool_type.h"
#include "mhd_str_types.h"
#include "mhd_buffer.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.
@@ -682,6 +689,10 @@ mhd_str_equal_caseless_quoted_bin_n (const char *quoted,
#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))
#endif /* DAUTH_SUPPORT */
#if defined(DAUTH_SUPPORT) || defined(HAVE_POST_PARSER)
/**
* Convert string from quoted to unquoted form as specified by
* RFC7230#section-3.2.6 and RFC7694#quoted.strings.
@@ -702,7 +713,7 @@ mhd_str_unquote (const char *quoted,
size_t quoted_len,
char *result);
#endif /* DAUTH_SUPPORT */
#endif /* DAUTH_SUPPORT HAVE_POST_PARSER */
#if defined(DAUTH_SUPPORT) || defined(BAUTH_SUPPORT)
@@ -766,4 +777,78 @@ mhd_base64_to_bin_n (const char *base64,
#endif /* BAUTH_SUPPORT */
/**
* Check whether the given field value string starts with given token.
* Token matched case-insensitive.
*
* Matched considered successful if string has given token at the first
* position. The token may be followed by optional whitespaces and the semicolon
* symbol. The string is not checked after the semicolon (if any).
*
* @param str the string to check
* @param token the token to find, must not be empty
* @return 'true' if match is successful,
* 'false' otherwise
*/
MHD_INTERNAL bool
mhd_str_starts_with_token_opt_param (const struct MHD_String *restrict str,
const struct MHD_String *restrict token)
MHD_FN_PAR_NONNULL_ALL_;
/**
* The result of check for token with parameter
*/
enum MHD_FIXED_ENUM_ mhd_StingStartsWithTokenResult
{
/**
* The string does not start with the specified token
*/
mhd_STR_STARTS_W_TOKEN_NO_TOKEN = 0
,
/**
* The string has specified token at the initial position
* @note While formatting problems are not detected, it does not guarantee
* that string has a perfect format.
*/
mhd_STR_STARTS_W_TOKEN_HAS_TOKEN = 1
,
/**
* The string has specified token at the initial position, but broken
* formatting is detected.
*/
mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT = -1
};
/**
* Check whether the given field value string starts with given token, find
* required parameter.
* Token and parameter matched case-insensitive.
*
* Matched considered successful if string has given token at the first
* position. The token should be followed by optional whitespaces and
* one or more parameters, delimited by the semicolon symbol.
*
* @param str the string to check
* @param token the token to find, must not be empty
* @param par the name of the parameter, must not be empty
* @param[out] par_value set to the found parameter value or @a data member
* set to NULL if parameter is not found
* @param par_value_needs_unquote set to 'true' if @a par_value
* needs to be "unquoted"
* @return result of token detection and string parsing; if token is found
* the presence of the required parameter is indicated only
* by @a par_value
*
*/
MHD_INTERNAL enum mhd_StingStartsWithTokenResult
mhd_str_starts_with_token_req_param (
const struct MHD_String *restrict str,
const struct MHD_String *restrict token,
const struct MHD_String *restrict par,
struct mhd_BufferConst *restrict par_value,
bool *restrict par_value_needs_unquote)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_(4) MHD_FN_PAR_OUT_(5);
#endif /* MHD_STR_H */
+1165
View File
@@ -0,0 +1,1165 @@
/*
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/post_parser_funcs.c
* @brief The implementation of internal POST parser functions
* @author Karlson2k (Evgeny Grin)
*/
#include "mhd_sys_options.h"
#include "post_parser_funcs.h"
#include "mhd_post_parser.h"
#include <string.h>
#include "mhd_action.h"
#include "mhd_request.h"
#include "mhd_connection.h"
#include "mhd_str_macros.h"
#include "mhd_str.h"
#include "daemon_logger.h"
#include "stream_funcs.h"
#include "stream_process_request.h"
#include "daemon_funcs.h"
#include "request_get_value.h"
/**
* The result of 'multipart/form-data' processing
*/
enum MHD_FIXED_ENUM_ mhd_ProcessMPartResult
{
/**
* Sting processed successfully, boundary detected
*/
mhd_PROC_MPART_OK = 0
,
/**
* Error processing string, the error result is set
*/
mhd_PROC_MPART_ERROR_SET
,
/**
* The string is not 'multipart/form-data' header
*/
mhd_PROC_MPART_NO_MPART
};
/**
* Process 'Content-Type:' header value as 'multipart/form-data' data to
* prepare POST parsing data, including setting correct 'boundary' value
* @param c the stream to use
* @param h_cnt_tp the 'Content-Type:' header value string
* @return 'mhd_PROC_MPART_OK' if processed successfully and boundary has been
* detected and set,
* 'mhd_PROC_MPART_ERROR_SET' is has some error in processing which
* resulted in specific error set in
* the stream,
* 'mhd_PROC_MPART_NO_MPART' is string is not 'multipart/form-data' data
*/
static MHD_FN_PAR_NONNULL_ALL_ enum mhd_MPartDetectResult
process_mpart_header (struct MHD_Connection *restrict c,
const struct MHD_String *restrict h_cnt_tp)
{
static const struct MHD_String mpart_token =
mhd_MSTR_INIT ("multipart/form-data");
static const struct MHD_String mpart_bound_par =
mhd_MSTR_INIT ("boundary");
struct mhd_BufferConst mpart_bound;
bool mpart_bound_quoted;
enum mhd_StingStartsWithTokenResult res;
mhd_assert (NULL != h_cnt_tp->cstr);
res = mhd_str_starts_with_token_req_param (h_cnt_tp,
&mpart_token,
&mpart_bound_par,
&mpart_bound,
&mpart_bound_quoted);
if (mhd_STR_STARTS_W_TOKEN_NO_TOKEN == res)
return mhd_PROC_MPART_NO_MPART;
if (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN_BAD_FORMAT == res)
{
mhd_LOG_PRINT (c->daemon, \
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_MISFORMED, \
mhd_LOG_FMT ("The request POST data cannot be parsed " \
"because 'Content-Type: " \
"multipart/form-data' header is " \
"misformed: %.*s%s"), \
(int) ((h_cnt_tp->len <= 127) ? h_cnt_tp->len : 127),
h_cnt_tp->cstr,
(h_cnt_tp->len <= 127) ? "" : "...");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_FAILED_HEADER_MISFORMED;
return mhd_PROC_MPART_ERROR_SET;
}
mhd_assert (mhd_STR_STARTS_W_TOKEN_HAS_TOKEN == res);
if (0 != mpart_bound.size)
{
mhd_LOG_MSG (c->daemon, \
MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NO_BOUNDARY, \
"The request POST data cannot be parsed because " \
"'Content-Type: multipart/form-data' header has " \
"no 'boundary' parameter value.");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_FAILED_HEADER_NO_BOUNDARY;
return mhd_PROC_MPART_ERROR_SET;
}
mhd_assert (NULL != mpart_bound.data);
if (! mpart_bound_quoted)
{
c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
c->rq.u_proc.post.e_d.m_form.bound = mpart_bound;
}
else
{
char *buf;
mhd_assert (2 <= mpart_bound.size); /* At least one char and at least one '\' */
buf = mhd_stream_alloc_memory (c, mpart_bound.size);
if (NULL == buf)
{
/* It is very low probability that pool would not have memory just
* to held the small boundary string. While it could be possible
* to allocate memory from "large buffer", it would over-complicate
* code here and at freeing part. */
mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
"The request POST data cannot be parsed because " \
"there is not enough pool memory.");
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
return mhd_PROC_MPART_ERROR_SET;
}
c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
c->rq.u_proc.post.e_d.m_form.bound.size =
mhd_str_unquote (mpart_bound.data,
mpart_bound.size,
buf);
mhd_assert (0 != c->rq.u_proc.post.e_d.m_form.bound.size);
}
return mhd_PROC_MPART_OK;
}
/**
* Detect used POST encoding and 'boundary' for 'multipart/form-data'.
* @param c the stream to use
* @return 'true' if detected successfully,
* 'false' if POST encoding cannot be detected
*/
static MHD_FN_PAR_NONNULL_(1) bool
detect_post_enc (struct MHD_Connection *restrict c)
{
const struct MHD_StringNullable *h_cnt_tp;
mhd_assert (MHD_CONNECTION_BODY_RECEIVING > c->state);
h_cnt_tp = mhd_request_get_value_st (&(c->rq),
MHD_VK_HEADER,
MHD_HTTP_HEADER_CONTENT_TYPE);
if (NULL == h_cnt_tp)
{
mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
"The request POST data cannot be parsed because " \
"the request has no 'Content-Type:' header and no " \
"explicit POST encoding is set.");
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
return false; /* The "Content-Type:" is not defined by the client */
}
mhd_assert (NULL != h_cnt_tp->cstr);
if (mhd_str_equal_caseless_n_st ("application/x-www-form-urlencoded",
h_cnt_tp->cstr,
h_cnt_tp->len))
{
c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
return true;
}
if (1)
{
enum mhd_MPartDetectResult res;
res = process_mpart_header (c,
(const struct MHD_String *) (const void *)
h_cnt_tp);
if (mhd_PROC_MPART_OK == res)
return true;
if (mhd_PROC_MPART_ERROR_SET == res)
return false;
mhd_assert (mhd_PROC_MPART_NO_MPART == res);
}
if (1)
{
static const struct MHD_String txt_tkn = mhd_MSTR_INIT("text/plain");
if (mhd_str_starts_with_token_opt_param (h_cnt_tp,
&txt_tkn))
{
c->rq.u_proc.post.enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN;
return true;
}
}
mhd_LOG_MSG (c->daemon, \
MHD_SC_REQ_POST_PARSE_FAILED_UNKNOWN_CNTN_TYPE, \
"The request POST data cannot be parsed because " \
"'Content-Type' header value is unknown or unsupported.");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_FAILED_UNKNOWN_CNTN_TYPE;
return false;
}
/**
* Detect 'boundary' for 'multipart/form-data' POST encoding.
* @param c the stream to use
* @return 'true' if succeed,
* 'false' if failed and error result is set
*/
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;
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)
{
mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_CNTN_TYPE, \
"The request POST data cannot be parsed because " \
"'multipart/form-data' requires 'boundary' parameter, but " \
"the request has no 'Content-Type:' header.");
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_CNTN_TYPE;
return false;
}
mhd_assert (NULL != h_cnt_tp->cstr);
enum mhd_MPartDetectResult res;
res = process_mpart_header (c,
(const struct MHD_String *) (const void *)
h_cnt_tp);
if (mhd_PROC_MPART_OK == res)
return true;
if (mhd_PROC_MPART_NO_MPART == res)
{
mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_HEADER_NOT_MPART, \
"The request POST data cannot be parsed because " \
"'multipart/form-data' requires 'boundary' parameter, but " \
"the request has no 'Content-Type: multipart/form-data' " \
"header.");
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_HEADER_NOT_MPART;
return false;
}
mhd_assert (mhd_PROC_MPART_ERROR_SET == res);
return false;
}
static MHD_FN_PAR_NONNULL_(1) void
reset_text_parse_field_data (struct mhd_PostParserData *pdata)
{
mhd_assert (MHD_HTTP_POST_ENCODING_TEXT_PLAIN == pdata->enc);
memset (&(pdata->e_d.text), 0, sizeof(pdata->e_d.text));
#ifndef HAVE_NULL_PTR_ALL_ZEROS
pdata->e_d.text.name_ptr = NULL;
pdata->e_d.text.value_ptr = NULL;
#endif
pdata->field_start = 0;
}
/**
* Finish initialisation of data for POST parsing
* @param c the stream to use
*/
static MHD_FN_PAR_NONNULL_(1) void
init_post_parse_data (struct MHD_Connection *restrict c)
{
struct mhd_PostParserData *const pdata =
&(c->rq.u_proc.post);
mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
c->rq.u_proc.post.enc);
pdata->lbuf_left = c->rq.app_act.head_act.data.post_parse.buffer_size;
switch (pdata->enc)
{
case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
// TODO:
break;
case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
// TODO
break;
case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
reset_text_parse_field_data (pdata);
break;
case MHD_HTTP_POST_ENCODING_OTHER:
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
}
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_(1) bool
mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
{
mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
if (MHD_HTTP_POST_ENCODING_OTHER ==
c->rq.app_act.head_act.data.post_parse.enc)
{
if (! detect_post_enc (c))
{
mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return false;
}
}
else if (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA ==
c->rq.app_act.head_act.data.post_parse.enc)
{
if (! detect_mpart_boundary_from_the_header (c))
{
mhd_assert (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result);
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return false;
}
}
mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != \
c->rq.u_proc.post.enc);
mhd_assert ((MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
c->rq.u_proc.post.enc) || \
(0 != c->rq.u_proc.post.e_d.m_form.bound.size));
init_post_parse_data (c);
return true;
}
/**
* Allocate memory from "large shared buffer" for POST parsing
* @param c the stream to use
* @param alloc_size the size to allocate
* @param[out] buf the buffer to allocate
* @return 'true' if succeed,
* 'false' otherwise
*/
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_(3) bool
get_lbuf_fixed_size (struct MHD_Connection *restrict c,
size_t alloc_size,
struct mhd_Buffer *restrict buf)
{
mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
if (alloc_size > c->rq.u_proc.post.lbuf_left)
return NULL;
if (! mhd_daemon_get_lbuf (c->daemon,
alloc_size,
buf))
return false;
c->rq.u_proc.post.lbuf_left -= alloc_size;
return true;
}
/**
* Grow the allocated memory from "large shared buffer" for POST parsing
* @param c the stream to use
* @param grow_size the size to grow
* @param[in,out] buf the buffer to grow
* @return 'true' if succeed,
* 'false' otherwise
*/
static MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_(3) bool
grow_lbuf_fixed_size (struct MHD_Connection *restrict c,
size_t grow_size,
struct mhd_Buffer *restrict buf)
{
mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
if (grow_size > c->rq.u_proc.post.lbuf_left)
return NULL;
if (! mhd_daemon_grow_lbuf (c->daemon,
grow_size,
buf))
return false;
c->rq.u_proc.post.lbuf_left -= grow_size;
return true;
}
/**
* Add parsed POST field to the list of request's fields
* @param c the stream to use
* @param name the name of the field
* @param filename the filename of the filed
* @param content_type the "Content-Type:" of the field
* @param transfer_encoding the "Transfer-Encoding:" of the field
* @param value the value of the field
* @return 'true' if succeed,
* 'false' if memory allocation failed (no pool memory in the stream)
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
add_parsed_post_field (struct MHD_Connection *restrict c,
struct MHD_String *restrict name,
struct MHD_StringNullable *restrict filename,
struct MHD_StringNullable *restrict content_type,
struct MHD_StringNullable *restrict transfer_encoding,
struct MHD_StringNullable *restrict value)
{
struct mhd_RequestPostField *pfield;
mhd_assert (NULL != name->cstr);
mhd_assert ((NULL != filename->cstr) || (0 == filename->len));
mhd_assert ((NULL != content_type->cstr) || (0 == content_type->len));
mhd_assert ((NULL != transfer_encoding->cstr) || \
(0 == transfer_encoding->len));
mhd_assert ((NULL != value->cstr) || (0 == value->len));
pfield = (struct mhd_RequestPostField *)
mhd_stream_alloc_memory (c,
sizeof (struct mhd_RequestPostField));
if (NULL == pfield)
return false;
pfield->field.name = *name;
pfield->field.value = *value;
pfield->field.filename = *filename;
pfield->field.content_type = *content_type;
pfield->field.transfer_encoding = *transfer_encoding;
mhd_DLINKEDL_INIT_LINKS (pfield, post_fields);
mhd_DLINKEDL_INS_FIRST (&(c->rq), pfield, post_fields);
return true;
}
MHD_static_inline_ MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_(1)
MHD_FN_PAR_OUT_(10) MHD_FN_PAR_OUT_(11)
MHD_FN_PAR_OUT_(12) MHD_FN_PAR_OUT_(13) void
make_post_strings_from_buf_and_indices (const char *restrict buf,
size_t name_start,
size_t name_len,
size_t filename_start,
size_t filename_len,
size_t cntn_type_start,
size_t cntn_type_len,
size_t enc_start,
size_t enc_len,
struct MHD_String *name,
struct MHD_StringNullable *filename,
struct MHD_StringNullable *content_type,
struct MHD_StringNullable *encoding)
{
name->len = name_len;
name->cstr = buf + name_start;
if (0 != filename_start)
{
filename->len = filename_len;
filename->cstr = buf + filename_start;
}
else
{
filename->len = 0;
filename->cstr = NULL;
}
if (0 != cntn_type_start)
{
content_type->len = cntn_type_len;
content_type->cstr = buf + cntn_type_start;
}
else
{
content_type->len = 0;
content_type->cstr = NULL;
}
if (0 != enc_start)
{
encoding->len = enc_len;
encoding->cstr = buf + enc_start;
}
else
{
encoding->len = 0;
encoding->cstr = NULL;
}
}
/**
* Process new full parsed POST filed
* @param c the stream to use
* @param buf the buffer, where the data is
* @param pnext_pos the pointer to variable holding index on the next position
* to be parsed
* @param pdata_size the pointer to variable holding the size of the data
* @param field_start the start of the current field in the @a buf
* @param name_start the start of the name of the filed in the @a buf
* @param name_len the length of the name, not including mandatory terminating
* zero
* @param filename_start the start of the filename of the field in the @a buf,
* zero if no filename is provided
* @param filename_len the length of the filename, not including mandatory
* terminating zero
* @param cntn_type_start the start of the Content-Type value of the field in
* the @a buf, zero if no Content-Type is provided
* @param cntn_type_len the length of the Content-Type value, not including
* mandatory terminating zero
* @param enc_start the start of the Content-Encoding value of the field in
* the @a buf, zero if no Content-Encoding is provided
* @param enc_len the length of the Content-Encoding value, not including
* mandatory terminating zero
* @param value_start the start of the field value in the @a buf, zero if
* no value is provided
* @param value_len the length of the field value, not including mandatory
* terminating zero
* @return 'true' if stream state has been changed,
* 'false' to continue parsing
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
process_complete_field_all (struct MHD_Connection *restrict c,
const char *restrict buf,
size_t *restrict pnext_pos,
size_t *restrict pdata_size,
size_t field_start,
size_t name_start,
size_t name_len,
size_t filename_start,
size_t filename_len,
size_t cntn_type_start,
size_t cntn_type_len,
size_t enc_start,
size_t enc_len,
size_t value_start,
size_t value_len)
{
struct mhd_PostParseActionData *const p_par =
&(c->rq.app_act.head_act.data.post_parse);
struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
struct MHD_String name;
struct MHD_StringNullable filename;
struct MHD_StringNullable content_type;
struct MHD_StringNullable encoding;
struct MHD_StringNullable value;
mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act);
mhd_assert ((0 == filename_start) || \
(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
mhd_assert ((0 == cntn_type_start) || \
(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
mhd_assert ((0 == enc_start) || \
(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA == p_data->enc));
mhd_assert (*pnext_pos < *pdata_size);
mhd_assert (value_start + value_len < *pnext_pos);
mhd_assert ((name_start + name_len < value_start) || \
(0 == value_start));
mhd_assert (name_start + name_len < *pnext_pos);
mhd_assert ((filename_start + filename_len < value_start) || \
(0 == value_start));
mhd_assert (filename_start + filename_len < *pnext_pos);
mhd_assert ((cntn_type_start + cntn_type_len < value_start) || \
(0 == value_start));
mhd_assert (cntn_type_start + cntn_type_len < *pnext_pos);
mhd_assert ((enc_start + enc_len < value_start) || \
(0 == value_start));
mhd_assert (enc_start + enc_len < *pnext_pos);
mhd_assert (field_start <= name_start);
mhd_assert ((field_start <= filename_start) || (0 == filename_start));
mhd_assert ((field_start <= cntn_type_start) || (0 == cntn_type_start));
mhd_assert ((field_start <= enc_start) || (0 == enc_start));
mhd_assert ((field_start <= value_start) || (0 == value_start));
mhd_assert ((0 != filename_start) || (0 == filename_len));
mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
mhd_assert ((0 != enc_start) || (0 == enc_len));
mhd_assert ((0 != value_start) || (0 == value_len));
p_data->some_data_provided = true;
make_post_strings_from_buf_and_indices (buf,
name_start,
name_len,
filename_start,
filename_len,
cntn_type_start,
cntn_type_len,
enc_start,
enc_len,
&name,
&filename,
&content_type,
&encoding);
if (NULL != p_par->stream_reader)
{
if ((p_par->auto_stream_size < (*pnext_pos - field_start))
|| (0 != p_data->value_off))
{
bool res;
struct mhd_PostParseActionData *act;
const size_t field_size = *pnext_pos - field_start;
act = p_par->stream_reader (&(c->rq),
p_par->reader_cls,
&name,
&filename,
&content_type,
&encoding,
value_len,
buf + value_start,
p_data->value_off,
MHD_YES);
res = mhd_stream_process_upload_action (c, act, false);
if (c->suspended)
return true;
p_data->value_off = 0;
if (*pdata_size > *pnext_pos)
{
memmove (buf + field_start,
buf + *pnext_pos,
*pdata_size - *pnext_pos);
}
*pnext_pos -= field_size;
*pdata_size -= field_size;
return res;
}
}
if (0 != value_start)
{
value.len = value_len;
value.cstr = buf + value_start;
}
if (! add_parsed_post_field (c,
name,
filename,
content_type,
encoding,
value))
{
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
mhd_LOG_MSG (c->daemon, MHD_SC_REQ_POST_PARSE_FAILED_NO_POOL_MEM, \
"The request POST data cannot be parsed completely " \
"because there is not enough pool memory.");
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_FAILED_NO_POOL_MEM;
return true;
}
return false; /* Continue parsing */
}
/**
* Process new full parsed POST filed
* @param c the stream to use
* @param buf the buffer, where the data is
* @param pnext_pos the pointer to variable holding index on the next position
* to be parsed
* @param pdata_size the pointer to variable holding the size of the data
* @param field_start the start of the current field in the @a buf
* @param name_start the start of the name of the filed in the @a buf
* @param name_len the length of the name, not including mandatory terminating
* zero
* @param value_start the start of the field value in the @a buf, zero if
* no value is provided
* @param value_len the length of the field value, not including mandatory
* terminating zero
* @return 'true' if stream state has been changed,
* 'false' to continue parsing
*/
static MHD_FN_PAR_NONNULL_ALL_ bool
process_complete_field (struct MHD_Connection *restrict c,
const char *restrict buf,
size_t *restrict *pnext_pos,
size_t *restrict *pdata_size,
size_t field_start,
size_t name_start,
size_t name_len,
size_t value_start,
size_t value_len)
{
mhd_assert (value_start + value_len < *pnext_pos);
mhd_assert ((name_start + name_len < value_start) || \
(0 == value_start));
mhd_assert (name_start + name_len < *pnext_pos);
mhd_assert (field_start <= name_start);
mhd_assert ((field_start <= value_start) || (0 == value_start));
mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
c->rq.u_proc.post.enc);
return process_complete_field_all (c,
buf,
pnext_pos,
pdata_size,
field_start,
name_start,
name_len,
0, 0, 0, 0, 0, 0,
value_start,
value_len);
}
static MHD_FN_PAR_NONNULL_ALL_ bool
process_partial_value_all (struct MHD_Connection *restrict c,
const char *restrict buf,
size_t *restrict pnext_pos,
size_t *restrict pdata_size,
size_t field_start,
size_t name_start,
size_t name_len,
size_t filename_start,
size_t filename_len,
size_t cntn_type_start,
size_t cntn_type_len,
size_t enc_start,
size_t enc_len,
size_t part_value_start,
size_t part_value_len)
{
struct mhd_PostParseActionData *const p_par =
&(c->rq.app_act.head_act.data.post_parse);
struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
mhd_assert (*pnext_pos < *pdata_size);
mhd_assert (part_value_start + part_value_len == *pnext_pos);
mhd_assert (0 != part_value_start);
mhd_assert (name_start + name_len < *pnext_pos);
mhd_assert (filename_start + filename_len < part_value_start);
mhd_assert (filename_start + filename_len < *pnext_pos);
mhd_assert (cntn_type_start + cntn_type_len < part_value_start);
mhd_assert (cntn_type_start + cntn_type_len < *pnext_pos);
mhd_assert (enc_start + enc_len < part_value_start);
mhd_assert (enc_start + enc_len < *pnext_pos);
mhd_assert ((0 != filename_start) || (0 == filename_len));
mhd_assert ((0 != cntn_type_start) || (0 == cntn_type_len));
mhd_assert ((0 != enc_start) || (0 == enc_len));
p_data->some_data_provided = true;
if (NULL == p_par->stream_reader)
return false; /* Continue parsing */
if ((p_par->auto_stream_size < (*pnext_pos - field_start))
|| (0 != p_data->value_off))
{
struct MHD_String name;
struct MHD_StringNullable filename;
struct MHD_StringNullable content_type;
struct MHD_StringNullable encoding;
struct MHD_StringNullable value;
struct mhd_PostParseActionData *act;
bool res;
make_post_strings_from_buf_and_indices (buf,
name_start,
name_len,
filename_start,
filename_len,
cntn_type_start,
cntn_type_len,
enc_start,
enc_len,
&name,
&filename,
&content_type,
&encoding);
act = p_par->stream_reader (&(c->rq),
p_par->reader_cls,
&name,
&filename,
&content_type,
&encoding,
part_value_len,
buf + part_value_start,
p_data->value_off,
MHD_NO);
res = mhd_stream_process_upload_action(c, act, false);
if (c->suspended)
return true;
p_data->value_off += part_value_len;
if (*pdata_size > *pnext_pos)
{
memmove (buf + part_value_start,
buf + part_value_start + part_value_len,
part_value_len);
}
*pnext_pos -= part_value_len;
*pdata_size -= part_value_len;
return res;
}
return false; /* Continue parsing */
static MHD_FN_PAR_NONNULL_ALL_ bool
process_partial_value (struct MHD_Connection *restrict c,
const char *restrict buf,
size_t *restrict *pnext_pos,
size_t *restrict *pdata_size,
size_t field_start,
size_t name_start,
size_t name_len,
size_t part_value_start,
size_t part_value_len)
{
mhd_assert (part_value_start + part_value_len < *pnext_pos);
mhd_assert (name_start + name_len < part_value_start);
mhd_assert (0 != part_value_start);
mhd_assert (name_start + name_len < *pnext_pos);
mhd_assert (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA != \
c->rq.u_proc.post.enc);
return process_partial_value_all (c,
buf,
pnext_pos,
pdata_size,
field_start,
name_start,
name_len,
0, 0, 0, 0, 0, 0,
part_value_start,
part_value_len);
}
static MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_INOUT_(2) MHD_FN_PAR_INOUT_(3) bool
parse_post_text (struct MHD_Connection *restrict c,
size_t *restrict pdata_size,
char *restrict buf)
{
const int discp_lvl = c->daemon->req_cfg.strictnees;
/* Treat bare LF as the end of the line.
The same logic used here as for parsing HTTP headers.
Bare LF is processed as the end of the line or rejected as broken
request. */
const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
struct mhd_PostParserTextData *const tf = &(p_data->e_d.text); /**< the current "text" field */
size_t i;
bool enc_broken;
mhd_assert (MHD_POST_PARSE_RES_OK == c->rq.u_proc.post.parse_result);
mhd_assert (! c->discard_request);
mhd_assert (p_data->next_parse_pos < *pdata_size);
enc_broken = false;
i = p_data->next_parse_pos;
while (*pdata_size > i)
{
switch (tf->st)
{
case mhd_POST_TEXT_ST_NOT_STARTED:
mhd_assert (0 == p_data->field_start);
mhd_assert (0 == p_data->value_off);
p_data->field_start = i;
tf->name_idx = i;
tf->st = mhd_POST_TEXT_ST_NAME;
/* Intentional fall-through */
case mhd_POST_TEXT_ST_NAME:
do /* Fast local loop */
{
if ('=' == buf[i])
{
tf->st = mhd_POST_TEXT_ST_AT_EQ;
break;
}
else if ('\r' == buf[i])
{
tf->st = mhd_POST_TEXT_ST_AT_CR;
break;
}
else if ('\n' == buf[i])
{
tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
break;
}
} while (*pdata_size > ++i);
mhd_assert ((*pdata_size == i) || \
(mhd_POST_TEXT_ST_NAME != tf->st));
continue;
case mhd_POST_TEXT_ST_AT_EQ:
mhd_assert (i > tf->name_idx);
mhd_assert (0 == tf->name_len);
mhd_assert (0 == tf->value_len);
buf[i] = 0; /* Zero-terminate the name */
tf->name_len = i - tf->name_idx;
tf->st = mhd_POST_TEXT_ST_EQ_FOUND;
++i; /* Process the next char */
continue;
case mhd_POST_TEXT_ST_EQ_FOUND:
mhd_assert (0 == p_data->value_off);
mhd_assert (0 == tf->value_idx);
mhd_assert (0 == tf->value_len);
tf->value_idx = i;
tf->st = mhd_POST_TEXT_ST_VALUE;
/* Intentional fall-through */
case mhd_POST_TEXT_ST_VALUE:
do /* Fast local loop */
{
if ('\r' == buf[i])
{
tf->st = mhd_POST_TEXT_ST_AT_CR;
break;
}
else if ('\n' == buf[i])
{
tf->st = mhd_POST_TEXT_ST_AT_LF_BARE;
break;
}
} while (*pdata_size > ++i);
mhd_assert ((*pdata_size == i) || \
(mhd_POST_TEXT_ST_VALUE != tf->st));
continue;
case mhd_POST_TEXT_ST_AT_LF_BARE:
if (! bare_lf_as_crlf)
{
enc_broken = true;
break;
}
/* Intentional fall-through */
case mhd_POST_TEXT_ST_AT_CR:
mhd_assert (0 == tf->value_len);
buf[i] = 0; /* Zero-terminate the value (or the name) */
if (0 != tf->value_idx)
tf->value_len = i - tf->value_idx;
else
tf->name_len = i - tf->name_idx;
if (mhd_POST_TEXT_ST_AT_LF_BARE == tf->st)
tf->st = mhd_POST_TEXT_ST_FULL_LINE_FOUND;
else
{
tf->st = mhd_POST_TEXT_ST_CR_FOUND;
++i; /* Process the next char */
}
continue;
case mhd_POST_TEXT_ST_CR_FOUND:
if ('\n' != buf[i])
{
enc_broken = true;
break;
}
/* Intentional fall-through */
case mhd_POST_TEXT_ST_FULL_LINE_FOUND:
++i; /* Advance to the next char to be checked */
if (process_complete_field (c,
buf,
&i,
pdata_size,
p_data->field_start,
tf->name_idx,
tf->name_len,
tf->value_idx,
tf->value_len))
{
if (c->suspended)
--i; /* Go back to the same position */
return true;
}
mhd_assert (*pdata_size >= i);
reset_text_parse_field_data (&(c->rq.u_proc.post));
continue; /* Process the next char */
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
enc_broken = true;
break;
}
mhd_assert (enc_broken);
break;
}
mhd_assert ((*pdata_size == i) || enc_broken);
if (enc_broken)
{
if (p_data->some_data_provided)
{
mhd_LOG_MSG (c->daemon, \
MHD_SC_REQ_POST_PARSE_PARTIAL_INVALID_POST_FORMAT, \
"The request POST has broken encoding or format and " \
"was parsed only partially.");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
}
else
{
mhd_LOG_MSG (c->daemon, \
MHD_SC_REQ_POST_PARSE_FAILED_INVALID_POST_FORMAT, \
"The request POST has broken encoding or format and " \
"cannot be parsed.");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_FAILED_INVALID_POST_FORMAT;
}
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
}
mhd_assert (mhd_POST_TEXT_ST_AT_EQ != tf->st);
mhd_assert (mhd_POST_TEXT_ST_AT_CR != tf->st);
mhd_assert (mhd_POST_TEXT_ST_AT_LF_BARE != tf->st);
mhd_assert (mhd_POST_TEXT_ST_FULL_LINE_FOUND != tf->st);
mhd_assert (*pdata_size == i);
if ((mhd_POST_TEXT_ST_VALUE == tf->st) &&
(i != tf->value_idx))
{
if (process_partial_value(c,
buf,
&i,
pdata_size,
p_data->field_start,
tf->name_idx,
tf->name_len,
tf->value_idx,
i - tf->value_idx))
return true;
}
return false; /* Continue parsing */
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_INOUT_(3) bool
mhd_stream_post_parse (struct MHD_Connection *restrict c,
size_t *restrict data_size,
char *restrict buf)
{
struct mhd_PostParserData *const p_data = &(c->rq.u_proc.post);
bool alloc_failed;
mhd_assert (MHD_HTTP_POST_ENCODING_OTHER != p_data->enc);
mhd_assert (p_data->lbuf.size >= p_data->lbuf_used);
// TODO: support process in the connection buffer
if (NULL == p_data->lbuf.data)
{
alloc_failed = ! get_lbuf_fixed_size (c,
*data_size,
&(p_data->lbuf));
}
else
{
if (*data_size > (p_data->lbuf.size - p_data->lbuf_used))
alloc_failed =
! grow_lbuf_fixed_size (c,
*data_size -
(p_data->lbuf.size - p_data->lbuf_used),
&(p_data->lbuf));
else
alloc_failed = false;
}
if (alloc_failed)
{
mhd_LOG_MSG (c->daemon, \
MHD_SC_REQ_POST_PARSE_FAILED_NO_LARGE_BUF_MEM, \
"Not enough large shared buffer memory to " \
"parse POST request.");
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_FAILED_NO_LARGE_BUF_MEM;
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
}
memcpy (p_data->lbuf.data + p_data->lbuf_used,
buf,
*data_size);
p_data->lbuf_used += *data_size;
*data_size = 0; /* All data has been moved */
switch (p_data->enc)
{
case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
// TODO: finish
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
// TODO: finish
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
return parse_post_text (c,
&(p_data->lbuf_used),
p_data->lbuf.data);
case MHD_HTTP_POST_ENCODING_OTHER:
default:
mhd_assert (0 && "Impossible value");
MHD_UNREACHABLE_;
c->rq.u_proc.post.parse_result =
MHD_POST_PARSE_RES_PARTIAL_INVALID_POST_FORMAT;
c->discard_request = true;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
break;
}
return true;
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
MHD_FN_PAR_INOUT_(3) void
mhd_stream_post_final_data (struct MHD_Connection *restrict c,
size_t *restrict data_size,
char *restrict buf)
{
// TODO: implement check for unprocessed broken partial data
return;
}
+65
View File
@@ -0,0 +1,65 @@
/*
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/post_parser_funcs.h
* @brief The declarations of internal POST parser functions
* @author Karlson2k (Evgeny Grin)
*/
#ifndef MHD_POST_PARSER_FUNCS_H
#define MHD_POST_PARSER_FUNCS_H 1
#include "mhd_sys_options.h"
#include "sys_bool_type.h"
struct MHD_Connection; /* forward declaration */
/**
* Make preparation necessary for parsing POST data.
* @param c the stream to use
* @return 'true' if succeed,
* 'false' if failed and error result is set in the stream
*/
MHD_INTERNAL bool
mhd_stream_prepare_for_post_parse (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_(1);
/**
* Parse POST data.
* @param c the stream to use
* @return // FIXME
*/
MHD_INTERNAL bool
mhd_stream_post_parse (struct MHD_Connection *restrict c,
size_t *restrict data_size,
char *restrict buf)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_SIZE_(3, 2);
// TODO: describe
MHD_INTERNAL void
mhd_stream_post_final_data (struct MHD_Connection *restrict c,
size_t *restrict data_size,
char *restrict buf)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_INOUT_(3);
#endif /* ! MHD_POST_PARSER_FUNCS_H */
+27 -7
View File
@@ -1,5 +1,6 @@
/*
This file is part of GNU libmicrohttpd
Copyright (C) 2024 Christian Grothoff
Copyright (C) 2024 Evgeny Grin (Karlson2k)
GNU libmicrohttpd is free software; you can redistribute it and/or
@@ -48,18 +49,37 @@ mhd_request_get_value_n (struct MHD_Request *restrict request,
size_t key_len,
const char *restrict key)
{
struct mhd_RequestField *f;
mhd_assert (strlen (key) == key_len);
for (f = mhd_DLINKEDL_GET_FIRST (request, fields); NULL != f;
f = mhd_DLINKEDL_GET_NEXT (f, fields))
if (MHD_VK_POSTDATA != kind)
{
if ((key_len == f->field.nv.name.len) &&
(kind == f->field.kind) &&
(0 == memcmp (key, f->field.nv.name.cstr, key_len)))
return &(f->field.nv.value);
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) &&
(0 != (kind & f->field.kind)) &&
(0 == memcmp (key, f->field.nv.name.cstr, key_len)))
return &(f->field.nv.value);
}
}
#if HAVE_POST_PARSER
if (0 != (MHD_VK_POSTDATA & kind))
{
struct mhd_RequestPostField *f;
for (f = mhd_DLINKEDL_GET_FIRST (request, post_fields); NULL != f;
f = mhd_DLINKEDL_GET_NEXT (f, post_fields))
{
if ((key_len == f->field.name.len) &&
(0 == memcmp (key, f->field.name.cstr, key_len)))
return &(f->field.value);
}
}
#endif /* HAVE_POST_PARSER */
return NULL;
}
+2 -2
View File
@@ -62,9 +62,9 @@ respond_with_error_len (struct MHD_Connection *c,
/* Discard most of the request data */
if (NULL != c->rq.cntn.lbuf.buf)
if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.buf = NULL;
c->rq.cntn.lbuf.data = NULL;
c->write_buffer = NULL;
c->write_buffer_size = 0;
+10
View File
@@ -41,6 +41,16 @@ MHD_INTERNAL void
mhd_response_deinit_content_data (struct MHD_Response *restrict r)
MHD_FN_PAR_NONNULL_ (1);
/**
* Create special internal-only response for sending automatic error messages
* @param sc the HTTP status code (enum MHD_HTTP_StatusCode)
* @param cntn_len the length of the @a cntn
* @param cntn the response content
* @param spec_hdr_len the length of the @a spec_hdr
* @param spec_hdr the special string to be used as a header string
* @return the response object if succeed,
* NULL if failed (out of memory)
*/
MHD_INTERNAL struct MHD_Response *
mhd_response_special_for_error (unsigned int sc,
size_t cntn_len,
+17 -17
View File
@@ -496,8 +496,10 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
{
mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \
(c->rp.response->cfg.int_err_resp));
/* Next function will destroy response, notify client,
* destroy memory pool and set connection state to "CLOSED" */
/* Next function will notify client and set connection
* state to "PRE-CLOSING" */
/* Later response and memory pool will be destroyed */
mhd_conn_pre_close (c,
c->stop_with_error ?
mhd_CONN_CLOSE_ERR_REPLY_SENT :
@@ -652,7 +654,6 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
const char *log_msg)
{
bool close_hard;
bool use_local_lingering;
enum MHD_RequestEndedCode end_code;
enum MHD_StatusCode sc;
@@ -774,7 +775,6 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
use_local_lingering = false;
/* Make changes on the socket early to let the kernel and the remote
* to process the changes in parallel. */
if (close_hard)
@@ -789,13 +789,12 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
if (mhd_socket_shut_wr (c->socket_fd) && (! c->sk_rmt_shut_wr))
{
(void) 0; // TODO: start local lingering phase
c->state = MHD_CONNECTION_CLOSED; // TODO: start local lingering phase
c->state = MHD_CONNECTION_PRE_CLOSING; // TODO: start local lingering phase
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase
// use_local_lingering = true;
}
else
{ /* No need / not possible to linger */
c->state = MHD_CONNECTION_CLOSED;
c->state = MHD_CONNECTION_PRE_CLOSING;
c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
}
}
@@ -809,13 +808,6 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
(void) log_msg;
#endif /* ! HAVE_LOG_FUNCTIONALITY */
mhd_stream_call_dcc_cleanup_if_needed (c);
if (NULL != c->rp.resp_iov.iov)
{
free (c->rp.resp_iov.iov);
c->rp.resp_iov.iov = NULL;
}
#if 0 // TODO: notification callback
mhd_assert ((MHD_CONNECTION_INIT != c->state) || (! c->rq.app_aware));
if ( (NULL != d->notify_completed) &&
@@ -842,9 +834,6 @@ mhd_conn_pre_close (struct MHD_Connection *restrict c,
#ifndef NDEBUG
c->dbg.pre_closed = true;
#endif
if (! use_local_lingering)
mhd_conn_pre_clean (c);
}
@@ -854,8 +843,18 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c)
{
// TODO: support suspended connections
mhd_assert (c->dbg.pre_closed);
mhd_assert (! c->dbg.pre_cleaned);
mhd_conn_mark_unready (c, c->daemon);
mhd_stream_call_dcc_cleanup_if_needed (c);
if (NULL != c->rp.resp_iov.iov)
{
free (c->rp.resp_iov.iov);
c->rp.resp_iov.iov = NULL;
}
if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.data = NULL;
@@ -893,6 +892,7 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c)
}
#endif /* MHD_USE_EPOLL */
c->state = MHD_CONNECTION_CLOSED;
#ifndef NDEBUG
c->dbg.pre_cleaned = true;
#endif
+8 -6
View File
@@ -1009,7 +1009,7 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
size_to_fill);
c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
if (! preprocess_dcc_action (c, act))
return false;
return true;
if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
{
mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
@@ -1029,7 +1029,7 @@ mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
mhd_CONN_CLOSE_APP_ERROR,
"Closing connection (application returned more data "
"than requested).");
return false;
return true;
}
c->rp.rsp_cntn_read_pos += filled;
c->write_buffer_append_offset += filled;
@@ -1122,6 +1122,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
"No memory in the pool for the reply chunked content.");
return true;
}
mhd_assert (max_chunk_overhead < \
(c->write_buffer_size));
@@ -1178,7 +1179,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
size_to_fill);
c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
if (! preprocess_dcc_action (c, act))
return false;
return true;
if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
{
mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
@@ -1196,7 +1197,7 @@ mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
mhd_CONN_CLOSE_APP_ERROR,
"Closing connection (application returned more data "
"than requested).");
return false;
return true;
}
c->rp.rsp_cntn_read_pos += filled;
c->write_buffer_append_offset += filled;
@@ -1300,7 +1301,7 @@ prep_chunked_footer_inn (struct MHD_Connection *restrict c)
}
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
{
if (! prep_chunked_footer_inn (c))
@@ -1308,7 +1309,8 @@ mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
mhd_STREAM_ABORT (c,
mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
"No memory in the pool for the reply chunked footer.");
return;
return true;
}
c->state = MHD_CONNECTION_FOOTERS_SENDING;
return false;
}
+6 -1
View File
@@ -75,6 +75,8 @@ MHD_FN_PAR_NONNULL_ALL_;
/**
* Prepare the chunked response content of this connection for sending.
*
* @param c the connection
*
* @return 'true' if connection new state could be processed now,
* 'false' if no new state processing is needed.
*/
@@ -88,8 +90,11 @@ MHD_FN_PAR_NONNULL_ALL_;
* with response footers.
*
* @param c the connection
*
* @return 'true' if connection new state could be processed now,
* 'false' if no new state processing is needed.
*/
MHD_INTERNAL void
MHD_INTERNAL bool
mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
+163 -123
View File
@@ -422,14 +422,6 @@
*/
#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
/**
* 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
*/
@@ -586,7 +578,7 @@ get_request_line_inner (struct MHD_Connection *restrict c)
(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);
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);
@@ -1561,7 +1553,7 @@ get_req_header (struct MHD_Connection *restrict c,
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);
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);
@@ -2949,15 +2941,36 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
}
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
#ifdef HAVE_POST_PARSER
case mhd_ACTION_POST_PARSE:
mhd_assert (0 && "Not implemented yet");
if (0 == c->rq.cntn.cntn_size)
{
c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY;
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
return true;
}
if (! mhd_stream_prepare_for_post_parse (c))
{
mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVED < c->state);
return true;
}
if (need_100_continue (c))
{
c->state = MHD_CONNECTION_CONTINUE_SENDING;
return true;
}
c->state = MHD_CONNECTION_BODY_RECEIVING;
return true;
#endif /* HAVE_POST_PARSER */
case mhd_ACTION_SUSPEND:
c->suspended = true;
return false;
case mhd_ACTION_ABORT:
mhd_conn_pre_close_app_abort (c);
return false;
return true;
#ifndef HAVE_POST_PARSER
case mhd_ACTION_POST_PARSE:
#endif /* ! HAVE_POST_PARSER */
case mhd_ACTION_NO_ACTION:
default:
mhd_assert (0 && "Impossible value");
@@ -2976,10 +2989,10 @@ mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
* @return true if connection state has been changed,
* false otherwise
*/
static MHD_FN_PAR_NONNULL_ (1) bool
process_upload_action (struct MHD_Connection *restrict c,
const struct MHD_UploadAction *act,
bool final)
MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) bool
mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
const struct MHD_UploadAction *act,
bool final)
{
if (NULL != act)
{
@@ -3011,7 +3024,7 @@ process_upload_action (struct MHD_Connection *restrict c,
return false;
case mhd_UPLOAD_ACTION_ABORT:
mhd_conn_pre_close_app_abort (c);
return false;
return true;
case mhd_UPLOAD_ACTION_NO_ACTION:
default:
mhd_assert (0 && "Impossible value");
@@ -3034,7 +3047,7 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
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);
const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl);
/* Allow "Bad WhiteSpace" in chunk extension.
RFC 9112, Section 7.1.1, Paragraph 2 */
const bool allow_bws = (2 < discp_lvl);
@@ -3229,79 +3242,94 @@ process_request_chunked_body (struct MHD_Connection *restrict c)
}
mhd_assert (c->rq.app_aware);
#ifdef HAVE_POST_PARSER
if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
{
mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
return false;
}
if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
{
need_inc_proc = false;
mhd_assert (0 == c->rq.cntn.proc_size);
if ((uint_fast64_t) c->rq.cntn.lbuf.size <
c->rq.cntn.recv_size + cntn_data_ready)
{
size_t grow_size;
grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
- c->rq.cntn.lbuf.size);
if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
cntn_data_ready) || (! mhd_daemon_grow_lbuf (d,
grow_size,
&(c->rq.cntn.lbuf))))
{
/* Failed to grow the buffer, no space to put the new data */
const struct MHD_UploadAction *act;
if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
{
mhd_RESPOND_WITH_ERROR_STATIC (
c,
MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
ERR_RSP_MSG_REQUEST_TOO_BIG);
return true;
}
c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
/* Process previously buffered data */
mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
c->rq.cntn.recv_size,
c->rq.cntn.lbuf.buf);
c->rq.cntn.proc_size = c->rq.cntn.recv_size;
mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
if (process_upload_action (c, act, false))
return true;
need_inc_proc = true;
}
}
if (! need_inc_proc)
{
memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
buffer_head, cntn_data_ready);
c->rq.cntn.recv_size += cntn_data_ready;
}
}
else
need_inc_proc = true;
if (need_inc_proc)
{
const struct MHD_UploadAction *act;
mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
size_t size_provided;
c->rq.cntn.recv_size += cntn_data_ready;
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
cntn_data_ready,
buffer_head);
c->rq.cntn.proc_size += cntn_data_ready;
state_updated = process_upload_action (c, act, false);
}
size_provided = cntn_data_ready;
state_updated = mhd_stream_post_parse (c,
&cntn_data_ready,
buffer_head);
mhd_assert (0 == cntn_data_ready); // TODO: support one chunk in-place processing?
c->rq.cntn.recv_size += size_provided;
}
else
#endif /* HAVE_POST_PARSER */
if (1)
{
mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
{
need_inc_proc = false;
mhd_assert (0 == c->rq.cntn.proc_size);
if ((uint_fast64_t) c->rq.cntn.lbuf.size <
c->rq.cntn.recv_size + cntn_data_ready)
{
size_t grow_size;
grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready
- c->rq.cntn.lbuf.size);
if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) <
cntn_data_ready) ||
(! mhd_daemon_grow_lbuf (d,
grow_size,
&(c->rq.cntn.lbuf))))
{
/* Failed to grow the buffer, no space to put the new data */
const struct MHD_UploadAction *act;
if (NULL != c->rq.app_act.head_act.data.upload.inc.cb)
{
mhd_RESPOND_WITH_ERROR_STATIC (
c,
MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
ERR_RSP_MSG_REQUEST_TOO_BIG);
return true;
}
c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */
/* Process previously buffered data */
mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
c->rq.cntn.recv_size,
c->rq.cntn.lbuf.data);
c->rq.cntn.proc_size = c->rq.cntn.recv_size;
mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf));
if (process_upload_action (c, act, false))
return true;
need_inc_proc = true;
}
}
if (! need_inc_proc)
{
memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
buffer_head, cntn_data_ready);
c->rq.cntn.recv_size += cntn_data_ready;
}
}
else
need_inc_proc = true;
if (need_inc_proc)
{
const struct MHD_UploadAction *act;
mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
c->rq.cntn.recv_size += cntn_data_ready;
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
cntn_data_ready,
buffer_head);
c->rq.cntn.proc_size += cntn_data_ready;
state_updated = process_upload_action (c, act, false);
}
}
/* dh left "processed" bytes in buffer for next time... */
buffer_head += cntn_data_ready;
available -= cntn_data_ready;
@@ -3341,54 +3369,66 @@ process_request_nonchunked_body (struct MHD_Connection *restrict c)
else
cntn_data_ready = c->read_buffer_offset;
if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
{
mhd_assert (0 && "Not implemented yet"); // TODO: implement POST
return false;
}
mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
read_buf_reuse = false;
state_updated = false;
if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)
{
// TODO: implement processing in pool memory if buffer is large enough
mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
(uint_fast64_t) c->rq.cntn.lbuf.size);
memcpy (c->rq.cntn.lbuf.buf + c->rq.cntn.recv_size,
c->read_buffer, cntn_data_ready);
size_t size_provided;
c->rq.cntn.recv_size += cntn_data_ready;
size_provided = cntn_data_ready;
state_updated = mhd_stream_post_parse (c,
&size_provided,
c->read_buffer);
mhd_assert (0 == size_provided); // TODO: support one chunk in-place processing?
read_buf_reuse = true; // TODO: support one chunk in-place processing?
c->rq.cntn.recv_size += cntn_data_ready;
read_buf_reuse = true;
if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
{
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
state_updated = true;
}
}
else
{
const struct MHD_UploadAction *act;
mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act);
if (NULL != c->rq.app_act.head_act.data.upload.full.cb)
{
// TODO: implement processing in pool memory if buffer is large enough
mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <=
(uint_fast64_t) c->rq.cntn.lbuf.size);
memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size,
c->read_buffer, cntn_data_ready);
c->rq.cntn.recv_size += cntn_data_ready;
read_buf_reuse = true;
if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size)
{
c->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
state_updated = true;
}
}
else
{
const struct MHD_UploadAction *act;
mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb);
c->rq.cntn.recv_size += cntn_data_ready;
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
cntn_data_ready,
c->read_buffer);
c->rq.cntn.proc_size += cntn_data_ready;
read_buf_reuse = true;
state_updated = process_upload_action (c, act, false);
c->rq.cntn.recv_size += cntn_data_ready;
act = c->rq.app_act.head_act.data.upload.inc.cb (
c->rq.app_act.head_act.data.upload.inc.cls,
&(c->rq),
cntn_data_ready,
c->read_buffer);
c->rq.cntn.proc_size += cntn_data_ready;
read_buf_reuse = true;
state_updated = process_upload_action (c, act, false);
}
}
if (read_buf_reuse)
{
size_t data_left_size;
mhd_assert (c->read_buffer_offset >= cntn_data_ready);
data_left_size = c->read_buffer_offset - cntn_data_ready;
if (0 != data_left_size)
memmove (c->read_buffer, c->read_buffer + cntn_data_ready, data_left_size)
;
memmove (c->read_buffer,
c->read_buffer + cntn_data_ready,
data_left_size);
c->read_buffer_offset = data_left_size;
}
@@ -3423,14 +3463,14 @@ mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
{
mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size);
mhd_assert (0 == c->rq.cntn.proc_size);
mhd_assert (NULL != c->rq.cntn.lbuf.buf);
mhd_assert (NULL != c->rq.cntn.lbuf.data);
mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size);
// TODO: implement processing in pool memory if it is large enough
act = c->rq.app_act.head_act.data.upload.full.cb (
c->rq.app_act.head_act.data.upload.full.cls,
&(c->rq),
c->rq.cntn.recv_size,
c->rq.cntn.lbuf.buf);
c->rq.cntn.lbuf.data);
c->rq.cntn.proc_size = c->rq.cntn.recv_size;
}
else
@@ -3450,9 +3490,9 @@ mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c)
MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c)
{
if (NULL != c->rq.cntn.lbuf.buf)
if (NULL != c->rq.cntn.lbuf.data)
mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
c->rq.cntn.lbuf.buf = NULL;
c->rq.cntn.lbuf.data = NULL;
if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size)
c->discard_request = true;
mhd_assert (NULL != c->rp.response);
+16
View File
@@ -145,6 +145,22 @@ MHD_INTERNAL bool
mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c)
MHD_FN_PAR_NONNULL_ALL_;
/**
* React on provided action for upload
* @param c the stream to use
* @param act the action provided by application
* @param final set to 'true' if this is final upload callback
* @return true if connection state has been changed,
* false otherwise
*/
MHD_INTERNAL bool
mhd_stream_process_upload_action (struct MHD_Connection *restrict c,
const struct MHD_UploadAction *act,
bool final)
MHD_FN_PAR_NONNULL_ (1);
/**
* Process non-chunked request body or upload chunking encoding.
* Call the upload handler of the application.
+17 -4
View File
@@ -252,7 +252,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
return false;
}
while (true) // TODO: support suspend
while (! c->suspended)
{
#ifdef HTTPS_SUPPORT
// TODO: support TLS, handshake
@@ -357,7 +357,7 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_assert (NULL != c->rp.response);
mhd_stream_switch_from_recv_to_send (c);
if (! mhd_stream_build_header_response (c))
break;
continue;
mhd_assert (MHD_CONNECTION_START_REPLY != c->state);
break;
case MHD_CONNECTION_HEADERS_SENDING:
@@ -441,7 +441,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_assert (c->write_buffer_send_offset <= \
c->write_buffer_append_offset);
mhd_stream_call_dcc_cleanup_if_needed (c);
mhd_stream_prep_chunked_footer (c);
if (mhd_stream_prep_chunked_footer (c))
continue;
break;
case MHD_CONNECTION_FOOTERS_SENDING:
mhd_assert (c->rp.props.send_reply_body);
@@ -457,6 +458,9 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
&& ! c->discard_request
&& ! c->sk_rmt_shut_wr);
continue;
case MHD_CONNECTION_PRE_CLOSING:
mhd_conn_pre_clean (c);
break;
case MHD_CONNECTION_CLOSED:
break;
#if 0 // def UPGRADE_SUPPORT
@@ -471,6 +475,8 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
break;
}
mhd_assert (MHD_CONNECTION_PRE_CLOSING != c->state);
if (MHD_CONNECTION_CLOSED == c->state)
return false;
@@ -488,15 +494,22 @@ mhd_conn_process_data (struct MHD_Connection *restrict c)
mhd_CONN_CLOSE_HTTP_COMPLETED :
mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
NULL);
mhd_conn_pre_clean (c);
return false;
}
if (mhd_stream_check_timedout (c)) // TODO: centralise timeout checks
{
mhd_conn_pre_close_timedout (c);
mhd_conn_pre_clean (c);
return false;
}
if (! update_active_state (c))
{
mhd_conn_pre_clean (c);
return false;
}
update_active_state (c);
/* MHD_connection_update_event_loop_info (c);*/
return true;