diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in index 65750f2e..de7704ae 100644 --- a/src/include/microhttpd2_main.h.in +++ b/src/include/microhttpd2_main.h.in @@ -1129,16 +1129,19 @@ struct MHD_DynContentZCIoVec * The number of elements in @a iov */ unsigned int iov_count; + /** * The pointer to the array with @a iov_count elements. */ const struct MHD_IoVec *iov; + /** * The callback to free resources. * It is called once the full array of iov elements is sent. * No callback is called if NULL. */ MHD_FreeCallback iov_fcb; + /** * The parameter for @a iov_fcb */ @@ -1149,6 +1152,7 @@ struct MHD_DynContentZCIoVec * The action type returned by Dynamic Content Creator callback */ struct MHD_DynamicContentCreatorAction; + /** * The context used for Dynamic Content Creator callback */ @@ -1163,7 +1167,7 @@ struct MHD_DynamicContentCreatorContext; * The total size of the data in the buffer and in @a iov_data must * be non-zero. * @param[in,out] ctx the pointer the context as provided to the callback - * @param data_size the amount of the data placed to the provided buffer, + * @param data_size the amount of the data placed to the provided buffer (not @a iov_data), * cannot be larger than provided buffer size, * must be non-zero if @a iov_data is NULL or has no data, * @param iov_data the optional pointer to the iov data, @@ -1184,13 +1188,52 @@ MHD_DCC_action_continue_zc ( MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (4); -#define MHD_DCC_action_continue(ctx,data_size,chunk_ext) \ + +/** + * Set action to "continue processing", the data is provided in the + * buffer and/or in the zero-copy @a iov_data. + * If data is provided both in the buffer and @a ivo_data then + * data in the buffer sent first, following the iov data. + * The total size of the data in the buffer and in @a iov_data must + * be non-zero. + * @param[in,out] ctx the pointer the context as provided to the callback + * @param data_size the amount of the data placed to the provided buffer (not @a iov_data), + * cannot be larger than provided buffer size, + * must be non-zero. + * @param chunk_ext the optional pointer to chunk extension string, + * can be NULL to not use chunk extension, + * ignored if chunked encoding is not used + * @return the pointer to the action if succeed, + * NULL (equivalent of MHD_DCC_action_abort())in case of any error + */ +#define MHD_DCC_action_continue_ce(ctx,data_size,chunk_ext) \ MHD_DCC_action_continue_zc (ctx, data_size, NULL, chunk_ext) + +/** + * Set action to "continue processing", the data is provided in the + * buffer and/or in the zero-copy @a iov_data. + * If data is provided both in the buffer and @a ivo_data then + * data in the buffer sent first, following the iov data. + * The total size of the data in the buffer and in @a iov_data must + * be non-zero. + * @param[in,out] ctx the pointer the context as provided to the callback + * @param data_size the amount of the data placed to the provided buffer; + * cannot be larger than provided buffer size, + * must be non-zero. + * + * @return the pointer to the action if succeed, + * NULL (equivalent of MHD_DCC_action_abort())in case of any error + */ +#define MHD_DCC_action_continue(ctx,data_size) \ + MHD_DCC_action_continue_ce (ctx, data_size, NULL) + + /** * Set action to "finished". * If function failed for any reason, the action is automatically * set to "stop with error". + * * @param[in,out] ctx the pointer the context as provided to the callback * @param num_footers number of elements in the @a footers array, * must be zero if @a footers is NULL @@ -1209,8 +1252,17 @@ MHD_DCC_action_finished_with_footer ( MHD_FN_PAR_NONNULL_ (1); -#define MHD_DCC_action_finished(action) \ - MHD_DCC_set_action_finished_with_footer (action, 0, NULL) +/** + * Set action to "finished". + * If function failed for any reason, the action is automatically + * set to "stop with error". + * + * @param[in,out] ctx the pointer the context as provided to the callback + * @return the pointer to the action if succeed, + * NULL (equivalent of MHD_DCC_action_abort())in case of any error + */ +#define MHD_DCC_action_finished(ctx) \ + MHD_DCC_set_action_finished_with_footer (ctx, 0, NULL) /** diff --git a/src/tests/client_server/libtest.h b/src/tests/client_server/libtest.h index f315a321..82abe857 100644 --- a/src/tests/client_server/libtest.h +++ b/src/tests/client_server/libtest.h @@ -218,6 +218,35 @@ MHDT_server_reply_text ( uint_fast64_t upload_size); +/** + * Returns the text from @a cls as the response to any + * request, but using chunks by returning @a cls + * word-wise (breaking into chunks at spaces). + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param request the request object + * @param path the requested uri (without arguments after "?") + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param upload_size the size of the message upload content payload, + * #MHD_SIZE_UNKNOWN for chunked uploads (if the + * final chunk has not been processed yet) + * @return action how to proceed, NULL + * if the request must be aborted due to a serious + * error while handling the request (implies closure + * of underling data stream, for HTTP/1.1 it means + * socket closure). + */ +const struct MHD_Action * +MHDT_server_reply_chunked_text ( + void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size); + + /** * Returns writes text from @a cls to a temporary file * and then uses the file descriptor to serve the diff --git a/src/tests/client_server/libtest_convenience_server_reply.c b/src/tests/client_server/libtest_convenience_server_reply.c index d4d41e5d..475da432 100644 --- a/src/tests/client_server/libtest_convenience_server_reply.c +++ b/src/tests/client_server/libtest_convenience_server_reply.c @@ -366,3 +366,85 @@ MHDT_server_reply_check_upload ( &check_upload_cb, (void *) want); } + + +/** + * Closure for #chunk_return. + */ +struct ChunkContext +{ + /** + * Where we are in the buffer. + */ + const char *pos; +}; + + +/** + * Function that returns a string in chunks. + * + * @param dyn_cont_cls must be a `struct ChunkContext` + * @param ctx the context to produce the action to return, + * the pointer is only valid until the callback returns + * @param pos position in the datastream to access; + * note that if a `struct MHD_Response` object is re-used, + * it is possible for the same content reader to + * be queried multiple times for the same data; + * however, if a `struct MHD_Response` is not re-used, + * libmicrohttpd guarantees that "pos" will be + * the sum of all data sizes provided by this callback + * @param[out] buf where to copy the data + * @param max maximum number of bytes to copy to @a buf (size of @a buf) + * @return action to use, + * NULL in case of any error (the response will be aborted) + */ +static const struct MHD_DynamicContentCreatorAction * +chunk_return (void *cls, + struct MHD_DynamicContentCreatorContext *ctx, + uint_fast64_t pos, + void *buf, + size_t max) +{ + struct ChunkContext *cc = cls; + size_t imax = strlen (cc->pos); + const char *space = strchr (cc->pos, ' '); + + if (0 == imax) + return MHD_DCC_action_finished (ctx); + if (imax > space - cc->pos + 1) + imax = space - cc->pos; + if (imax > max) + imax = max; + memcpy (buf, + cc->pos, + imax); + cc->pos += imax; + return MHD_DCC_action_continue (ctx, + imax); +} + + +const struct MHD_Action * +MHDT_server_reply_chunked_text ( + void *cls, + struct MHD_Request *MHD_RESTRICT request, + const struct MHD_String *MHD_RESTRICT path, + enum MHD_HTTP_Method method, + uint_fast64_t upload_size) +{ + const char *text = cls; + struct ChunkContext *cc; + + cc = malloc (sizeof (struct ChunkContext)); + if (NULL == cc) + return NULL; + cc->pos = text; + + return MHD_action_from_response ( + request, + MHD_response_from_callback (MHD_HTTP_STATUS_OK, + MHD_SIZE_UNKNOWN, + &chunk_return, + cc, + &free)); +} diff --git a/src/tests/client_server/test_client_server.c b/src/tests/client_server/test_client_server.c index 26f6df02..add2b474 100644 --- a/src/tests/client_server/test_client_server.c +++ b/src/tests/client_server/test_client_server.c @@ -238,6 +238,14 @@ main (int argc, char *argv[]) .timeout_ms = 5000, .num_clients = 1 }, + { + .label = "chunked response get", + .server_cb = &MHDT_server_reply_chunked_text, + .server_cb_cls = "Hello world", + .client_cb = &MHDT_client_get_root, + .client_cb_cls = "Hello world", + .timeout_ms = 2500, + }, // TODO: chunked download { .label = NULL,