[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libmicrohttpd] 252/335: expand test suite
From: |
gnunet |
Subject: |
[libmicrohttpd] 252/335: expand test suite |
Date: |
Sat, 27 Jul 2024 22:02:28 +0200 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to tag stf-m2
in repository libmicrohttpd.
commit fcb47a6a1c88e9fef2a7a167b4c469edb67da50a
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Jul 21 16:20:06 2024 +0200
expand test suite
---
src/tests/basic/libtest.h | 127 +++++++++
src/tests/basic/libtest_convenience.c | 467 +++++++++++++++++++++++++++++++++-
src/tests/basic/test_client_server.c | 22 +-
3 files changed, 610 insertions(+), 6 deletions(-)
diff --git a/src/tests/basic/libtest.h b/src/tests/basic/libtest.h
index 4f0187a1..213590fe 100644
--- a/src/tests/basic/libtest.h
+++ b/src/tests/basic/libtest.h
@@ -74,6 +74,50 @@ MHDT_client_get_root (void *cls,
const struct MHDT_PhaseContext *pc);
+/**
+ * Run request against the base URL with the
+ * query arguments from @a cls appended to it.
+ * Expect the server to return a 200 OK response.
+ *
+ * @param cls closure with query parameters to append
+ * to the base URL of the server
+ * @param pc context for the client
+ * @return error message, NULL on success
+ */
+const char *
+MHDT_client_get_with_query (void *cls,
+ const struct MHDT_PhaseContext *pc);
+
+
+/**
+ * Run request against the base URL with the
+ * custom header from @a cls set.
+ * Expect the server to return a 204 No content response.
+ *
+ * @param cls closure with custom header to set
+ * @param pc context for the client
+ * @return error message, NULL on success
+ */
+const char *
+MHDT_client_set_header (void *cls,
+ const struct MHDT_PhaseContext *pc);
+
+
+/**
+ * Run request against the base URL and expect the
+ * header from @a cls to be set in the 200 OK response.
+ *
+ * @param cls closure with custom header to set,
+ * must be of the format "$KEY:$VALUE"
+ * without space before the "$VALUE".
+ * @param pc context for the client
+ * @return error message, NULL on success
+ */
+const char *
+MHDT_client_expect_header (void *cls,
+ const struct MHDT_PhaseContext *pc);
+
+
/**
* A phase defines some server and client-side
* behaviors to execute.
@@ -148,6 +192,89 @@ MHDT_server_reply_text (
uint_fast64_t upload_size);
+/**
+ * Returns an emtpy response with a custom header
+ * set from @a cls and the #MHD_HTTP_STATUS_NO_CONTENT.
+ *
+ * @param cls header in the format "$NAME:$VALUE"
+ * without a space before "$VALUE".
+ * @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_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);
+
+
+/**
+ * Checks that the request query arguments match the
+ * arguments given in @a cls.
+ * request.
+ *
+ * @param cls string with expected arguments separated by '&' and '='. URI
encoding is NOT supported.
+ * @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_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);
+
+
+/**
+ * Checks that the client request includes the given
+ * custom header. If so, returns #MHD_SC_OK with "ok".
+ *
+ * @param cls expected header with "$NAME:$VALUE" format.
+ * @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_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);
+
+
/**
* Initialize options for an MHD daemon for a test.
*
diff --git a/src/tests/basic/libtest_convenience.c
b/src/tests/basic/libtest_convenience.c
index a0181901..85b751cb 100644
--- a/src/tests/basic/libtest_convenience.c
+++ b/src/tests/basic/libtest_convenience.c
@@ -95,6 +95,324 @@ MHDT_server_reply_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 key %s, got %s\n",
+ arg,
+ sn->cstr);
+ return NULL;
+ }
+ }
+ else
+ {
+ if (NULL == sn->cstr)
+ {
+ fprintf (stderr,
+ "%s expected for key %s, got NULL\n",
+ val,
+ arg);
+ return NULL;
+ }
+ if (0 != strcmp (val,
+ sn->cstr))
+ {
+ fprintf (stderr,
+ "%s expected for key %s, got %s\n",
+ val,
+ arg,
+ sn->cstr);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return MHD_action_from_response (
+ request,
+ MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK,
+ strlen ("ok"),
+ "ok"));
+}
+
+
+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)
+{
+ // FIXME: actual check logic missing...
+ return MHD_action_from_response (
+ request,
+ MHD_response_from_buffer_static (MHD_HTTP_STATUS_OK,
+ strlen ("ok"),
+ "ok"));
+}
+
+
+/**
+ * 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 %u, got %ld\n",
+ expected_status,
+ status);
+ return false;
+ }
+ return true;
+}
+
+
const char *
MHDT_client_get_root (
void *cls,
@@ -102,7 +420,77 @@ MHDT_client_get_root (
{
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)
@@ -115,10 +503,85 @@ MHDT_client_get_root (
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_easy_cleanup (c);
+ curl_slist_free_all (slist);
if (CURLE_OK != res)
+ {
+ curl_easy_cleanup (c);
return "Failed to fetch URL";
- (void) text; // FIXME: check data returned was 'text'
+ }
+ 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/test_client_server.c
b/src/tests/basic/test_client_server.c
index b4f0ee93..349cf94b 100644
--- a/src/tests/basic/test_client_server.c
+++ b/src/tests/basic/test_client_server.c
@@ -1,6 +1,6 @@
/*
This file is part of GNU libmicrohttpd
- Copyright (C) 2016, 2024 Evgeny Grin (Karlson2k)
+ Copyright (C) 2016, 2024 Christian Grothoff & 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
@@ -42,19 +42,33 @@ main (int argc, char *argv[])
},
// Basic upload
// HTTP client header
+ {
+ .label = "server response with custom header",
+ .server_cb = &MHDT_server_reply_check_header,
+ .server_cb_cls = "C-Header:testvalue",
+ .client_cb = &MHDT_client_set_header,
+ .client_cb_cls = "C-Header:testvalue",
+ .timeout_ms = 5,
+ },
// Response with custom header
-#if 0
+ {
+ .label = "server response with custom header",
+ .server_cb = &MHDT_server_reply_with_header,
+ .server_cb_cls = "X-Header:testvalue",
+ .client_cb = &MHDT_client_expect_header,
+ .client_cb_cls = "X-Header:testvalue",
+ .timeout_ms = 5,
+ },
// URL with query parameters
{
.label = "URL with query parameters",
.server_cb = &MHDT_server_reply_check_query,
- .server_cb_cls = "TODO",
+ .server_cb_cls = "a=b&c",
.client_cb = &MHDT_client_get_with_query,
.client_cb_cls = "a=b&c",
.timeout_ms = 5,
.num_clients = 10
},
-#endif
// chunked upload
// chunked download
{
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libmicrohttpd] 251/335: Implemented MHD_daemon_get_info_fixed_sz(), (continued)
- [libmicrohttpd] 251/335: Implemented MHD_daemon_get_info_fixed_sz(), gnunet, 2024/07/27
- [libmicrohttpd] 246/335: WIP-5, gnunet, 2024/07/27
- [libmicrohttpd] 224/335: Minor update, gnunet, 2024/07/27
- [libmicrohttpd] 253/335: fix issues in test logic, gnunet, 2024/07/27
- [libmicrohttpd] 240/335: more example test code, gnunet, 2024/07/27
- [libmicrohttpd] 254/335: Fixed GET parameters parsing, gnunet, 2024/07/27
- [libmicrohttpd] 239/335: first test against test framework, gnunet, 2024/07/27
- [libmicrohttpd] 241/335: WIP-3, gnunet, 2024/07/27
- [libmicrohttpd] 248/335: WIP-5 fixes-2, gnunet, 2024/07/27
- [libmicrohttpd] 244/335: microhttpd2.h: improved URI and termination callbacks and related data, gnunet, 2024/07/27
- [libmicrohttpd] 252/335: expand test suite,
gnunet <=
- [libmicrohttpd] 260/335: Copy-paste & merge errors clean-up, gnunet, 2024/07/27
- [libmicrohttpd] 247/335: WIP-5 fixes, gnunet, 2024/07/27
- [libmicrohttpd] 261/335: split up convenience API, gnunet, 2024/07/27
- [libmicrohttpd] 250/335: fix shadowing, gnunet, 2024/07/27
- [libmicrohttpd] 264/335: expand test suite to cover upload and sendfile(), gnunet, 2024/07/27
- [libmicrohttpd] 233/335: -wrong variable, gnunet, 2024/07/27
- [libmicrohttpd] 263/335: -remove redundant comments, gnunet, 2024/07/27
- [libmicrohttpd] 262/335: expand test suite to cover more threading modes, gnunet, 2024/07/27
- [libmicrohttpd] 266/335: ignore, gnunet, 2024/07/27
- [libmicrohttpd] 255/335: clean up example, gnunet, 2024/07/27