diff --git a/src/tests/basic/Makefile.am b/src/tests/basic/Makefile.am index 6b2d0e4d..b756ee26 100644 --- a/src/tests/basic/Makefile.am +++ b/src/tests/basic/Makefile.am @@ -22,7 +22,9 @@ noinst_LTLIBRARIES = \ libmhdt_la_SOURCES = \ libtest.c libtest.h \ - libtest_convenience.c + libtest_convenience.c \ + libtest_convenience_client_request.c \ + libtest_convenience_server_reply.c libmhdt_la_LIBADD = \ -lpthread \ -lcurl @@ -84,7 +86,7 @@ check_PROGRAMS += \ test_create_start_destroy_thread_pool \ test_create_start_destroy_thread_pool_ipv4 \ $(EMPTY_ITEM) - + if MHD_USE_SELECT check_PROGRAMS += \ test_create_start_destroy_select_int_thread_ipv4 \ @@ -195,4 +197,3 @@ test_create_start_destroy_poll_thread_per_conn_ipv4_SOURCES = $(basic_test_sourc test_create_start_destroy_epoll_int_thread_ipv4_SOURCES = $(basic_test_sources) test_create_start_destroy_epoll_thread_per_conn_ipv4_SOURCES = $(basic_test_sources) - diff --git a/src/tests/basic/libtest_convenience.c b/src/tests/basic/libtest_convenience.c index 7034c154..50f5416e 100644 --- a/src/tests/basic/libtest_convenience.c +++ b/src/tests/basic/libtest_convenience.c @@ -143,563 +143,3 @@ MHDT_server_run_blocking (void *cls, #endif - - -const struct MHD_Action * -MHDT_server_reply_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; - - return MHD_action_from_response ( - request, - MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK, - strlen (text), - text)); -} - - -const struct MHD_Action * -MHDT_server_reply_with_header ( - 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 *header = cls; - size_t hlen = strlen (header) + 1; - char name[hlen]; - const char *colon = strchr (header, ':'); - const char *value; - struct MHD_Response *resp; - - memcpy (name, - header, - hlen); - name[colon - header] = '\0'; - value = &name[colon - header + 1]; - - resp = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); - if (MHD_SC_OK != - MHD_response_add_header (resp, - name, - value)) - return NULL; - return MHD_action_from_response ( - request, - resp); -} - - -const struct MHD_Action * -MHDT_server_reply_check_query ( - 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 *equery = cls; - size_t qlen = strlen (equery) + 1; - char qc[qlen]; - - memcpy (qc, - equery, - qlen); - for (const char *tok = strtok (qc, "&"); - NULL != tok; - tok = strtok (NULL, "&")) - { - const char *end; - const struct MHD_StringNullable *sn; - const char *val; - - end = strchr (tok, '='); - if (NULL == end) - { - end = &tok[strlen (tok)]; - val = NULL; - } - else - { - val = end + 1; - } - { - size_t alen = end - tok; - char arg[alen + 1]; - - memcpy (arg, - tok, - alen); - arg[alen] = '\0'; - sn = MHD_request_get_value (request, - MHD_VK_GET_ARGUMENT, - arg); - if (NULL == val) - { - if (NULL != sn->cstr) - { - fprintf (stderr, - "NULL expected for query key %s, got %s\n", - arg, - sn->cstr); - return NULL; - } - } - else - { - if (NULL == sn->cstr) - { - fprintf (stderr, - "%s expected for query key %s, got NULL\n", - val, - arg); - return NULL; - } - if (0 != strcmp (val, - sn->cstr)) - { - fprintf (stderr, - "%s expected for query key %s, got %s\n", - val, - arg, - sn->cstr); - return NULL; - } - } - } - } - - return MHD_action_from_response ( - request, - MHD_response_from_empty ( - MHD_HTTP_STATUS_NO_CONTENT)); -} - - -const struct MHD_Action * -MHDT_server_reply_check_header ( - 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 *want = cls; - size_t wlen = strlen (want) + 1; - char key[wlen]; - const char *colon = strchr (want, ':'); - const struct MHD_StringNullable *have; - const char *value; - - memcpy (key, - want, - wlen); - if (NULL != colon) - { - key[colon - want] = '\0'; - value = &key[colon - want + 1]; - } - else - { - value = NULL; - } - have = MHD_request_get_value (request, - MHD_VK_HEADER, - key); - if (NULL == value) - { - if (NULL != have->cstr) - { - fprintf (stderr, - "Have unexpected client header `%s': `%s'\n", - key, - have->cstr); - return NULL; - } - } - else - { - if (NULL == have->cstr) - { - fprintf (stderr, - "Missing expected client header `%s'\n", - want); - return NULL; - } - if (0 != strcmp (have->cstr, - value)) - { - fprintf (stderr, - "Client HTTP header `%s' was expected to be `%s' but is `%s'\n", - key, - value, - have->cstr); - return NULL; - } - } - return MHD_action_from_response ( - request, - MHD_response_from_empty ( - MHD_HTTP_STATUS_NO_CONTENT)); -} - - -/** - * Closure for the write_cb(). - */ -struct WriteBuffer -{ - /** - * Where to store the response. - */ - char *buf; - - /** - * Number of bytes in @e buf. - */ - size_t len; - - /** - * Current write offset in @e buf. - */ - size_t pos; - - /** - * Set to non-zero on errors (buffer full). - */ - int err; -}; - - -/** - * Callback for CURLOPT_WRITEFUNCTION processing - * data downloaded from the HTTP server. - * - * @param ptr data uploaded - * @param size size of a member - * @param nmemb number of members - * @param stream must be a `struct WriteBuffer` - * @return bytes processed (size*nmemb) or error - */ -static size_t -write_cb (void *ptr, - size_t size, - size_t nmemb, - void *stream) -{ - struct WriteBuffer *wb = stream; - size_t prod = size * nmemb; - - if ( (prod / size != nmemb) || - (wb->pos + prod < wb->pos) || - (wb->pos + prod > wb->len) ) - { - wb->err = 1; - return CURLE_WRITE_ERROR; - } - memcpy (wb->buf + wb->pos, - ptr, - prod); - wb->pos += prod; - return prod; -} - - -/** - * Declare variables needed to check a download. - * - * @param text text data we expect to receive - */ -#define DECLARE_WB(text) \ - size_t wb_tlen = strlen (text); \ - char wb_buf[wb_tlen]; \ - struct WriteBuffer wb = { \ - .buf = wb_buf, \ - .len = wb_tlen \ - } - - -/** - * Set CURL options to the write_cb() and wb buffer - * to check a download. - * - * @param c CURL handle - */ -#define SETUP_WB(c) do { \ - if (CURLE_OK != \ - curl_easy_setopt (c, \ - CURLOPT_WRITEFUNCTION, \ - &write_cb)) \ - { \ - curl_easy_cleanup (c); \ - return "Failed to set write callback for curl request"; \ - } \ - if (CURLE_OK != \ - curl_easy_setopt (c, \ - CURLOPT_WRITEDATA, \ - &wb)) \ - { \ - curl_easy_cleanup (c); \ - return "Failed to set write buffer for curl request"; \ - } \ -} while (0) - -/** - * Check that we received the expected text. - * - * @param text text we expect to have downloaded - */ -#define CHECK_WB(text) do { \ - if ( (wb_tlen != wb.pos) || \ - (0 != wb.err) || \ - (0 != memcmp (text, \ - wb_buf, \ - wb_tlen)) ) \ - return "Downloaded data does not match expectations"; \ -} while (0) - - -/** - * Perform the curl request @a c and cleanup and - * return an error if the request failed. - * - * @param c request to perform - */ -#define PERFORM_REQUEST(c) do { \ - CURLcode res; \ - res = curl_easy_perform (c); \ - if (CURLE_OK != res) \ - { \ - curl_easy_cleanup (c); \ - return "Failed to fetch URL"; \ - } \ -} while (0) - -/** - * Check that the curl request @a c completed - * with the @a want status code. - * Return an error if the status does not match. - * - * @param c request to check - * @param want desired HTTP status code - */ -#define CHECK_STATUS(c,want) do { \ - if (! check_status (c, want)) \ - { \ - curl_easy_cleanup (c); \ - return "Unexpected HTTP status"; \ - } \ -} while (0) - -/** - * Chec that the HTTP status of @a c matches @a expected_status - * - * @param a completed CURL request - * @param expected_status the expected HTTP response code - * @return true if the status matches - */ -static bool -check_status (CURL *c, - unsigned int expected_status) -{ - long status; - - if (CURLE_OK != - curl_easy_getinfo (c, - CURLINFO_RESPONSE_CODE, - &status)) - { - fprintf (stderr, - "Failed to get HTTP status"); - return false; - } - if (status != expected_status) - { - fprintf (stderr, - "Expected HTTP status %u, got %ld\n", - expected_status, - status); - return false; - } - return true; -} - - -const char * -MHDT_client_get_root ( - void *cls, - const struct MHDT_PhaseContext *pc) -{ - const char *text = cls; - CURL *c; - DECLARE_WB (text); - - c = curl_easy_init (); - if (NULL == c) - return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } - SETUP_WB (c); - PERFORM_REQUEST (c); - CHECK_STATUS (c, MHD_HTTP_STATUS_OK); - curl_easy_cleanup (c); - CHECK_WB (text); - return NULL; -} - - -const char * -MHDT_client_get_with_query ( - void *cls, - const struct MHDT_PhaseContext *pc) -{ - const char *args = cls; - size_t alen = strlen (args); - CURL *c; - size_t blen = strlen (pc->base_url); - char u[alen + blen + 1]; - const char *text = "ok"; - DECLARE_WB (text); - - memcpy (u, - pc->base_url, - blen); - memcpy (u + blen, - args, - alen); - u[alen + blen] = '\0'; - c = curl_easy_init (); - if (NULL == c) - return "Failed to initialize Curl handle"; - - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - u)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } - SETUP_WB (c); - PERFORM_REQUEST (c); - CHECK_STATUS (c, MHD_HTTP_STATUS_OK); - curl_easy_cleanup (c); - CHECK_WB (text); - return NULL; -} - - -const char * -MHDT_client_set_header (void *cls, - const struct MHDT_PhaseContext *pc) -{ - const char *hdr = cls; - CURL *c; - CURLcode res; - struct curl_slist *slist; - - c = curl_easy_init (); - if (NULL == c) - return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } - slist = curl_slist_append (NULL, - hdr); - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_HTTPHEADER, - slist)) - { - curl_easy_cleanup (c); - curl_slist_free_all (slist); - return "Failed to set custom header for curl request"; - } - res = curl_easy_perform (c); - curl_slist_free_all (slist); - if (CURLE_OK != res) - { - curl_easy_cleanup (c); - return "Failed to fetch URL"; - } - CHECK_STATUS (c, - MHD_HTTP_STATUS_NO_CONTENT); - curl_easy_cleanup (c); - return NULL; -} - - -const char * -MHDT_client_expect_header (void *cls, - const struct MHDT_PhaseContext *pc) -{ - const char *hdr = cls; - size_t hlen = strlen (hdr) + 1; - char key[hlen]; - const char *colon = strchr (hdr, ':'); - const char *value; - CURL *c; - bool found = false; - - if (NULL == colon) - return "Invalid expected header passed"; - memcpy (key, - hdr, - hlen); - key[colon - hdr] = '\0'; - value = &key[colon - hdr + 1]; - c = curl_easy_init (); - if (NULL == c) - return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } - PERFORM_REQUEST (c); - CHECK_STATUS (c, - MHD_HTTP_STATUS_NO_CONTENT); - for (size_t index = 0; ! found; index++) - { - CURLHcode rval; - struct curl_header *hout; - - rval = curl_easy_header (c, - key, - index, - CURLH_HEADER, - -1 /* last request */, - &hout); - if (CURLHE_BADINDEX == rval) - break; - found = (0 == strcmp (value, - hout->value)); - } - if (! found) - { - curl_easy_cleanup (c); - return "Expected HTTP response header not found"; - } - curl_easy_cleanup (c); - return NULL; -} diff --git a/src/tests/basic/libtest_convenience_client_request.c b/src/tests/basic/libtest_convenience_client_request.c new file mode 100644 index 00000000..3917676a --- /dev/null +++ b/src/tests/basic/libtest_convenience_client_request.c @@ -0,0 +1,387 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Christian Grothoff + + 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 libtest_convenience.c + * @brief convenience functions implementing clients making requests for libtest users + * @author Christian Grothoff + */ +#include +#include +#include +#include +#include +#include "microhttpd2.h" +#include "libtest.h" +#include + +/** + * Closure for the write_cb(). + */ +struct WriteBuffer +{ + /** + * Where to store the response. + */ + char *buf; + + /** + * Number of bytes in @e buf. + */ + size_t len; + + /** + * Current write offset in @e buf. + */ + size_t pos; + + /** + * Set to non-zero on errors (buffer full). + */ + int err; +}; + + +/** + * Callback for CURLOPT_WRITEFUNCTION processing + * data downloaded from the HTTP server. + * + * @param ptr data uploaded + * @param size size of a member + * @param nmemb number of members + * @param stream must be a `struct WriteBuffer` + * @return bytes processed (size*nmemb) or error + */ +static size_t +write_cb (void *ptr, + size_t size, + size_t nmemb, + void *stream) +{ + struct WriteBuffer *wb = stream; + size_t prod = size * nmemb; + + if ( (prod / size != nmemb) || + (wb->pos + prod < wb->pos) || + (wb->pos + prod > wb->len) ) + { + wb->err = 1; + return CURLE_WRITE_ERROR; + } + memcpy (wb->buf + wb->pos, + ptr, + prod); + wb->pos += prod; + return prod; +} + + +/** + * Declare variables needed to check a download. + * + * @param text text data we expect to receive + */ +#define DECLARE_WB(text) \ + size_t wb_tlen = strlen (text); \ + char wb_buf[wb_tlen]; \ + struct WriteBuffer wb = { \ + .buf = wb_buf, \ + .len = wb_tlen \ + } + + +/** + * Set CURL options to the write_cb() and wb buffer + * to check a download. + * + * @param c CURL handle + */ +#define SETUP_WB(c) do { \ + if (CURLE_OK != \ + curl_easy_setopt (c, \ + CURLOPT_WRITEFUNCTION, \ + &write_cb)) \ + { \ + curl_easy_cleanup (c); \ + return "Failed to set write callback for curl request"; \ + } \ + if (CURLE_OK != \ + curl_easy_setopt (c, \ + CURLOPT_WRITEDATA, \ + &wb)) \ + { \ + curl_easy_cleanup (c); \ + return "Failed to set write buffer for curl request"; \ + } \ +} while (0) + +/** + * Check that we received the expected text. + * + * @param text text we expect to have downloaded + */ +#define CHECK_WB(text) do { \ + if ( (wb_tlen != wb.pos) || \ + (0 != wb.err) || \ + (0 != memcmp (text, \ + wb_buf, \ + wb_tlen)) ) \ + return "Downloaded data does not match expectations"; \ +} while (0) + + +/** + * Perform the curl request @a c and cleanup and + * return an error if the request failed. + * + * @param c request to perform + */ +#define PERFORM_REQUEST(c) do { \ + CURLcode res; \ + res = curl_easy_perform (c); \ + if (CURLE_OK != res) \ + { \ + curl_easy_cleanup (c); \ + return "Failed to fetch URL"; \ + } \ +} while (0) + +/** + * Check that the curl request @a c completed + * with the @a want status code. + * Return an error if the status does not match. + * + * @param c request to check + * @param want desired HTTP status code + */ +#define CHECK_STATUS(c,want) do { \ + if (! check_status (c, want)) \ + { \ + curl_easy_cleanup (c); \ + return "Unexpected HTTP status"; \ + } \ +} while (0) + +/** + * Chec that the HTTP status of @a c matches @a expected_status + * + * @param a completed CURL request + * @param expected_status the expected HTTP response code + * @return true if the status matches + */ +static bool +check_status (CURL *c, + unsigned int expected_status) +{ + long status; + + if (CURLE_OK != + curl_easy_getinfo (c, + CURLINFO_RESPONSE_CODE, + &status)) + { + fprintf (stderr, + "Failed to get HTTP status"); + return false; + } + if (status != expected_status) + { + fprintf (stderr, + "Expected HTTP status %u, got %ld\n", + expected_status, + status); + return false; + } + return true; +} + + +const char * +MHDT_client_get_root ( + void *cls, + const struct MHDT_PhaseContext *pc) +{ + const char *text = cls; + CURL *c; + DECLARE_WB (text); + + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + pc->base_url)) + { + curl_easy_cleanup (c); + return "Failed to set URL for curl request"; + } + SETUP_WB (c); + PERFORM_REQUEST (c); + CHECK_STATUS (c, MHD_HTTP_STATUS_OK); + curl_easy_cleanup (c); + CHECK_WB (text); + return NULL; +} + + +const char * +MHDT_client_get_with_query ( + void *cls, + const struct MHDT_PhaseContext *pc) +{ + const char *args = cls; + size_t alen = strlen (args); + CURL *c; + size_t blen = strlen (pc->base_url); + char u[alen + blen + 1]; + const char *text = "ok"; + DECLARE_WB (text); + + memcpy (u, + pc->base_url, + blen); + memcpy (u + blen, + args, + alen); + u[alen + blen] = '\0'; + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + u)) + { + curl_easy_cleanup (c); + return "Failed to set URL for curl request"; + } + SETUP_WB (c); + PERFORM_REQUEST (c); + CHECK_STATUS (c, MHD_HTTP_STATUS_OK); + curl_easy_cleanup (c); + CHECK_WB (text); + return NULL; +} + + +const char * +MHDT_client_set_header (void *cls, + const struct MHDT_PhaseContext *pc) +{ + const char *hdr = cls; + CURL *c; + CURLcode res; + struct curl_slist *slist; + + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + pc->base_url)) + { + curl_easy_cleanup (c); + return "Failed to set URL for curl request"; + } + slist = curl_slist_append (NULL, + hdr); + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_HTTPHEADER, + slist)) + { + curl_easy_cleanup (c); + curl_slist_free_all (slist); + return "Failed to set custom header for curl request"; + } + res = curl_easy_perform (c); + curl_slist_free_all (slist); + if (CURLE_OK != res) + { + curl_easy_cleanup (c); + return "Failed to fetch URL"; + } + CHECK_STATUS (c, + MHD_HTTP_STATUS_NO_CONTENT); + curl_easy_cleanup (c); + return NULL; +} + + +const char * +MHDT_client_expect_header (void *cls, + const struct MHDT_PhaseContext *pc) +{ + const char *hdr = cls; + size_t hlen = strlen (hdr) + 1; + char key[hlen]; + const char *colon = strchr (hdr, ':'); + const char *value; + CURL *c; + bool found = false; + + if (NULL == colon) + return "Invalid expected header passed"; + memcpy (key, + hdr, + hlen); + key[colon - hdr] = '\0'; + value = &key[colon - hdr + 1]; + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + pc->base_url)) + { + curl_easy_cleanup (c); + return "Failed to set URL for curl request"; + } + PERFORM_REQUEST (c); + CHECK_STATUS (c, + MHD_HTTP_STATUS_NO_CONTENT); + for (size_t index = 0; ! found; index++) + { + CURLHcode rval; + struct curl_header *hout; + + rval = curl_easy_header (c, + key, + index, + CURLH_HEADER, + -1 /* last request */, + &hout); + if (CURLHE_BADINDEX == rval) + break; + found = (0 == strcmp (value, + hout->value)); + } + if (! found) + { + curl_easy_cleanup (c); + return "Expected HTTP response header not found"; + } + curl_easy_cleanup (c); + return NULL; +} diff --git a/src/tests/basic/libtest_convenience_server_reply.c b/src/tests/basic/libtest_convenience_server_reply.c new file mode 100644 index 00000000..191f0939 --- /dev/null +++ b/src/tests/basic/libtest_convenience_server_reply.c @@ -0,0 +1,239 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Christian Grothoff + + 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 libtest_convenience_server_reply.c + * @brief convenience functions that generate + * replies from the server for libtest users + * @author Christian Grothoff + */ +#include +#include +#include +#include +#include +#include "microhttpd2.h" +#include "libtest.h" +#include + + +const struct MHD_Action * +MHDT_server_reply_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; + + return MHD_action_from_response ( + request, + MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK, + strlen (text), + text)); +} + + +const struct MHD_Action * +MHDT_server_reply_with_header ( + 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 *header = cls; + size_t hlen = strlen (header) + 1; + char name[hlen]; + const char *colon = strchr (header, ':'); + const char *value; + struct MHD_Response *resp; + + memcpy (name, + header, + hlen); + name[colon - header] = '\0'; + value = &name[colon - header + 1]; + + resp = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); + if (MHD_SC_OK != + MHD_response_add_header (resp, + name, + value)) + return NULL; + return MHD_action_from_response ( + request, + resp); +} + + +const struct MHD_Action * +MHDT_server_reply_check_query ( + 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 *equery = cls; + size_t qlen = strlen (equery) + 1; + char qc[qlen]; + + memcpy (qc, + equery, + qlen); + for (const char *tok = strtok (qc, "&"); + NULL != tok; + tok = strtok (NULL, "&")) + { + const char *end; + const struct MHD_StringNullable *sn; + const char *val; + + end = strchr (tok, '='); + if (NULL == end) + { + end = &tok[strlen (tok)]; + val = NULL; + } + else + { + val = end + 1; + } + { + size_t alen = end - tok; + char arg[alen + 1]; + + memcpy (arg, + tok, + alen); + arg[alen] = '\0'; + sn = MHD_request_get_value (request, + MHD_VK_GET_ARGUMENT, + arg); + if (NULL == val) + { + if (NULL != sn->cstr) + { + fprintf (stderr, + "NULL expected for query key %s, got %s\n", + arg, + sn->cstr); + return NULL; + } + } + else + { + if (NULL == sn->cstr) + { + fprintf (stderr, + "%s expected for query key %s, got NULL\n", + val, + arg); + return NULL; + } + if (0 != strcmp (val, + sn->cstr)) + { + fprintf (stderr, + "%s expected for query key %s, got %s\n", + val, + arg, + sn->cstr); + return NULL; + } + } + } + } + + return MHD_action_from_response ( + request, + MHD_response_from_empty ( + MHD_HTTP_STATUS_NO_CONTENT)); +} + + +const struct MHD_Action * +MHDT_server_reply_check_header ( + 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 *want = cls; + size_t wlen = strlen (want) + 1; + char key[wlen]; + const char *colon = strchr (want, ':'); + const struct MHD_StringNullable *have; + const char *value; + + memcpy (key, + want, + wlen); + if (NULL != colon) + { + key[colon - want] = '\0'; + value = &key[colon - want + 1]; + } + else + { + value = NULL; + } + have = MHD_request_get_value (request, + MHD_VK_HEADER, + key); + if (NULL == value) + { + if (NULL != have->cstr) + { + fprintf (stderr, + "Have unexpected client header `%s': `%s'\n", + key, + have->cstr); + return NULL; + } + } + else + { + if (NULL == have->cstr) + { + fprintf (stderr, + "Missing expected client header `%s'\n", + want); + return NULL; + } + if (0 != strcmp (have->cstr, + value)) + { + fprintf (stderr, + "Client HTTP header `%s' was expected to be `%s' but is `%s'\n", + key, + value, + have->cstr); + return NULL; + } + } + return MHD_action_from_response ( + request, + MHD_response_from_empty ( + MHD_HTTP_STATUS_NO_CONTENT)); +}