libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 40b675518b14fef4d327323f63c9c70f1b2bc44e
parent 87d40361d30012eced13e0d0a7e0544754bc777b
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Fri, 28 Oct 2022 11:59:21 +0300

testzzuf: re-worked zzuf testing

* merged test_get, test_get_chunked, test_put
* fixed use of header instead of footer in chunked data callback
* added checks for results of all used MHD functions
* added reading of all data in the input buffer provided by MHD for
  callback
* re-used libcurl handles to re-use connections in all modes
* added testing of 'poll' and 'epoll' for all tests
* removed the need for socat
* removed forking with potential forked process zombies
* fixed processing only of the first request (as soon  as socat
  connection become broken, all following requests went to nowhere)
* implemented correct response for unsupported methods
* used proper timeout for 'external select()' (taken into account
  libcurl timeout too)
* added check for extra reply data
* added debug print of libcurl sent and received data
* merged all tests into single source file
* fixed a lot of compiler warnings
* added check for incorrect replies produced by MHD

Diffstat:
Mconfigure.ac | 86+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/testzzuf/Makefile.am | 121+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/testzzuf/mhd_debug_funcs.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Msrc/testzzuf/mhd_debug_funcs.h | 10++++++++++
Dsrc/testzzuf/socat.c | 117-------------------------------------------------------------------------------
Msrc/testzzuf/test_get.c | 1726+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Dsrc/testzzuf/test_get_chunked.c | 364-------------------------------------------------------------------------------
Dsrc/testzzuf/test_long_header.c | 265-------------------------------------------------------------------------------
Dsrc/testzzuf/test_post.c | 416-------------------------------------------------------------------------------
Dsrc/testzzuf/test_post_form.c | 432-------------------------------------------------------------------------------
Dsrc/testzzuf/test_put.c | 380-------------------------------------------------------------------------------
Dsrc/testzzuf/test_put_chunked.c | 395-------------------------------------------------------------------------------
Dsrc/testzzuf/test_put_large.c | 403-------------------------------------------------------------------------------
Asrc/testzzuf/zzuf_test_runner.sh | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 files changed, 1746 insertions(+), 3069 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -3052,47 +3052,6 @@ AS_IF([[test "x$enable_postprocessor" != "xno"]], AM_CONDITIONAL([HAVE_POSTPROCESSOR], [test "x$enable_postprocessor" != "xno"]) AC_MSG_RESULT([[$enable_postprocessor]]) - -# optional: have zzuf, socat? -run_zzuf_tests="no" -AS_VAR_IF([use_heavy_tests],["yes"], - [ - AS_VAR_IF([enable_curl],["yes"], - [ - AC_CHECK_PROG([have_zzuf],[zzuf],[yes],[no]) - AS_VAR_IF([have_zzuf],["yes"], - [ - AC_CHECK_PROG([have_socat],[socat],[yes],[no]) - AS_VAR_IF([have_socat],["yes"], - [ - run_zzuf_tests="yes" - run_zzuf_tests_MSG="yes" - ], - [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, socat tool not found" - ] - ) - ], - [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, zzuf tool not found" - ] - ) - ], - [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, tests with libcurl are not enabled" - ] - ) - ], - [ - run_zzuf_tests="no" - run_zzuf_tests_MSG="no, heavy tests are not enabled" - ] -) -AM_CONDITIONAL([RUN_ZZUF_TESTS],[test "x$run_zzuf_tests" = "xyes"]) - have_gnutls=no have_gnutls_sni=no have_gcrypt=no @@ -4883,6 +4842,50 @@ int main(void) ] ) +# fuzzing tests +run_zzuf_tests="no" +AS_VAR_IF([use_heavy_tests],["yes"], + [ + AS_VAR_IF([enable_curl],["yes"], + [ + AC_PATH_PROG([ZZUF],[zzuf],[no]) + AS_VAR_IF([ZZUF],["no"], + [ + run_zzuf_tests="no" + run_zzuf_tests_MSG="no, zzuf tool not found" + ], + [ + AS_IF([test "x${ac_cv_func_accept4}" = "xyes" && test "x${enable_asserts}" = "xno"], + [ + run_zzuf_tests="no" + run_zzuf_tests_MSG="no, fuzzing tests require asserts enabled on this platform" + ], + [test -n "${enabled_sanitizers}"], + [ + run_zzuf_tests="no" + run_zzuf_tests_MSG="no, fuzzing tests cannot be used with sanitizers" + ], + [ + run_zzuf_tests="yes" + run_zzuf_tests_MSG="yes" + ] + ) + ] + ) + ], + [ + run_zzuf_tests="no" + run_zzuf_tests_MSG="no, tests with libcurl are not enabled" + ] + ) + ], + [ + run_zzuf_tests="no" + run_zzuf_tests_MSG="no, heavy tests are not enabled" + ] +) +AM_CONDITIONAL([RUN_ZZUF_TESTS],[test "x$run_zzuf_tests" = "xyes"]) + # Final flags that may interfere with autoconf detections AS_CASE([${enable_build_type}],[debug|debugger], [ # Debug build or build for walking with debugger @@ -4955,6 +4958,7 @@ AC_CONFIG_COMMANDS([po-directories], # not contain the actual installation. AC_DEFINE_DIR([MHD_PLUGIN_INSTALL_PREFIX], [libdir/libmicrohttpd], [tls plugins]) +AC_SUBST([ZZUF]) # should experimental code be compiled (code that may not yet compile)? AC_MSG_CHECKING(whether to compile experimental code) diff --git a/src/testzzuf/Makefile.am b/src/testzzuf/Makefile.am @@ -1,17 +1,29 @@ # This Makefile.am is in the public domain + SUBDIRS = . +# ZZUF_SEED can be redefined to use other initial seeds +# for extended testing. E.g., +# make ZZUF_SEED=1234 check +ZZUF_SEED = 0 + +# Additional flags for zzuf +ZZUF_FLAGS = + AM_CPPFLAGS = \ -I$(top_srcdir)/src/include \ -I$(top_srcdir)/src/microhttpd \ -DMHD_CPU_COUNT=$(CPU_COUNT) \ $(CPPFLAGS_ac) $(LIBCURL_CPPFLAGS) -AM_CFLAGS = $(CFLAGS_ac) @LIBGCRYPT_CFLAGS@ +AM_CFLAGS = $(CFLAGS_ac) AM_LDFLAGS = $(LDFLAGS_ac) -AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) +AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) \ + ZZUF="$(ZZUF)" ; export ZZUF ; \ + ZZUF_SEED="$(ZZUF_SEED)" ; export ZZUF_SEED ; \ + ZZUF_FLAGS="$(ZZUF_FLAGS)" ; export ZZUF_FLAGS ; if USE_COVERAGE AM_CFLAGS += -fprofile-arcs -ftest-coverage @@ -25,11 +37,6 @@ $(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/ @echo ' cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la'; \ $(am__cd) $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la -EXTRA_DIST = README socat.c - -THREAD_ONLY_TESTS = \ - test_long_header - check_PROGRAMS = \ test_get \ test_get_chunked \ @@ -38,64 +45,86 @@ check_PROGRAMS = \ test_put \ test_put_chunked \ test_put_large \ - test_get11 \ - test_post11 \ - test_post_form11 \ - test_put11 \ - test_put_large11 + test_get_long_uri \ + test_get_long_header \ + test_get_close \ + test_get_chunked_close \ + test_post_close \ + test_post_form_close \ + test_put_close \ + test_put_chunked_close \ + test_put_large_close \ + test_get_long_uri_close \ + test_get_long_header_close \ + test_get10 \ + test_get_chunked10 \ + test_post10 \ + test_post_form10 \ + test_put10 \ + test_put_large10 \ + test_get_long_uri10 \ + test_get_long_header10 .NOTPARALLEL: -if USE_POSIX_THREADS -check_PROGRAMS += \ - $(THREAD_ONLY_TESTS) -endif -if USE_W32_THREADS -check_PROGRAMS += \ - $(THREAD_ONLY_TESTS) -endif +TESTS = $(check_PROGRAMS) +dist_check_SCRIPTS = zzuf_test_runner.sh -TESTS = $(check_PROGRAMS) +LOG_COMPILER = @SHELL@ $(srcdir)/zzuf_test_runner.sh + +tests_common_sources = mhd_debug_funcs.h mhd_debug_funcs.c test_get_SOURCES = \ - test_get.c + test_get.c $(tests_common_sources) + +test_get_chunked_SOURCES = $(test_get_SOURCES) + +test_post_SOURCES = $(test_get_SOURCES) + +test_post_form_SOURCES = $(test_get_SOURCES) + +test_put_SOURCES = $(test_get_SOURCES) + +test_put_chunked_SOURCES = $(test_get_SOURCES) + +test_put_large_SOURCES = $(test_get_SOURCES) + +test_get_long_uri_SOURCES = $(test_get_SOURCES) + +test_get_long_header_SOURCES = $(test_get_SOURCES) + +test_get_close_SOURCES = $(test_get_SOURCES) + +test_get_chunked_close_SOURCES = $(test_get_chunked_SOURCES) + +test_post_close_SOURCES = $(test_post_SOURCES) -test_get_chunked_SOURCES = \ - test_get_chunked.c +test_post_form_close_SOURCES = $(test_post_form_SOURCES) -test_post_SOURCES = \ - test_post.c +test_put_close_SOURCES = $(test_put_SOURCES) -test_post_form_SOURCES = \ - test_post_form.c +test_put_chunked_close_SOURCES = $(test_put_chunked_SOURCES) -test_put_SOURCES = \ - test_put.c +test_put_large_close_SOURCES = $(test_put_large_SOURCES) -test_put_chunked_SOURCES = \ - test_put_chunked.c +test_get_long_uri_close_SOURCES = $(test_get_long_uri_SOURCES) -test_put_large_SOURCES = \ - test_put_large.c +test_get_long_header_close_SOURCES = $(test_get_long_header_SOURCES) +test_get10_SOURCES = $(test_get_SOURCES) +test_get_chunked10_SOURCES = $(test_get_chunked_SOURCES) -test_get11_SOURCES = \ - test_get.c +test_post10_SOURCES = $(test_post_SOURCES) -test_post11_SOURCES = \ - test_post.c +test_post_form10_SOURCES = $(test_post_form_SOURCES) -test_post_form11_SOURCES = \ - test_post_form.c +test_put10_SOURCES = $(test_put_SOURCES) -test_put11_SOURCES = \ - test_put.c +test_put_large10_SOURCES = $(test_put_large_SOURCES) -test_put_large11_SOURCES = \ - test_put_large.c +test_get_long_uri10_SOURCES = $(test_get_long_uri_SOURCES) -test_long_header_SOURCES = \ - test_long_header.c +test_get_long_header10_SOURCES = $(test_get_long_header_SOURCES) diff --git a/src/testzzuf/mhd_debug_funcs.c b/src/testzzuf/mhd_debug_funcs.c @@ -61,3 +61,48 @@ MHD_avoid_accept4_ (struct MHD_Daemon *daemon) #endif /* ! _DEBUG */ #endif /* USE_ACCEPT4 */ } + +#ifdef MHD_ASAN_ACTIVE +#define MHD_ASAN_ENABLED_ 1 +#else /* ! MHD_ASAN_ACTIVE */ +#ifdef __SANITIZE_ADDRESS__ +#define MHD_ASAN_ENABLED_ 1 +#else /* ! __SANITIZE_ADDRESS__ */ +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define MHD_ASAN_ENABLED_ 1 +#endif /* __has_feature(address_sanitizer) */ +#endif /* __has_feature */ +#endif /* ! __SANITIZE_ADDRESS__ */ +#endif /* ! MHD_ASAN_ACTIVE */ +/** + * Checks whether any know sanitizer is enabled for this build. + * zzuf does not work together with sanitizers as both are intercepting + * standard library calls. + * @return non-zero if any sanitizer is enabled, + * zero otherwise + */ +int +MHD_are_sanitizers_enabled_ (void) +{ + int ret = 0; +#ifdef MHD_ASAN_ENABLED_ + ++ret; +#endif /* ! MHD_ASAN_ENABLED_ */ +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) + ++ret; +#endif +#if __has_feature(memory_sanitizer) + ++ret; +#endif +#if __has_feature(dataflow_sanitizer) + ++ret; +#endif +#else /* ! defined(__has_feature) */ +#ifdef __SANITIZE_THREAD__ + ++ret; +#endif +#endif /* ! defined(__has_feature) */ + return ret; +} diff --git a/src/testzzuf/mhd_debug_funcs.h b/src/testzzuf/mhd_debug_funcs.h @@ -44,4 +44,14 @@ MHD_is_avoid_accept4_possible_ (void); void MHD_avoid_accept4_ (struct MHD_Daemon *daemon); +/** + * Checks whether any know sanitizer is enabled for this build. + * zzuf does not work together with sanitizers as both are intercepting + * standard library calls. + * @return non-zero if any sanitizer is enabled, + * zero otherwise + */ +int +MHD_are_sanitizers_enabled_ (void); + #endif /* MHD_DEBUG_FUNCS_H */ diff --git a/src/testzzuf/socat.c b/src/testzzuf/socat.c @@ -1,117 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2008,2016 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file socat.c - * @brief Code to fork-exec zzuf and start the socat process - * @author Christian Grothoff - */ - -#include <errno.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <signal.h> - -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN 1 -#endif /* !WIN32_LEAN_AND_MEAN */ -#include <windows.h> -#endif - - -/** - * A larger loop count will run more random tests -- - * which would be good, except that it may take too - * long for most user's patience. So this small - * value is the default. - */ -#ifndef _MHD_VHEAVY_TESTS -#define LOOP_COUNT 10 -#else /* ! _MHD_VHEAVY_TESTS */ -#define LOOP_COUNT 200 -#endif /* ! _MHD_VHEAVY_TESTS */ - -#define CURL_TIMEOUT 50L - -static pid_t zzuf_pid; - -static void -zzuf_socat_start () -{ - int status; - char *const args[] = { - "zzuf", - "--ratio=0.0:0.75", - "-n", - "-A", - "socat", - "-lf", - "/dev/null", - "TCP4-LISTEN:11081,reuseaddr,fork", - "TCP4:127.0.0.1:11080", - NULL, - }; - zzuf_pid = fork (); - if (zzuf_pid == -1) - { - fprintf (stderr, "fork failed: %s\n", strerror (errno)); - exit (1); - } - if (zzuf_pid != 0) - { - (void) sleep (1); /* allow zzuf and socat to start */ - status = 0; - if (0 < waitpid (zzuf_pid, &status, WNOHANG)) - { - if (WIFEXITED (status)) - fprintf (stderr, - "zzuf died with status code %d!\n", - WEXITSTATUS (status)); - if (WIFSIGNALED (status)) - fprintf (stderr, - "zzuf died from signal %d!\n", WTERMSIG (status)); - exit (1); - } - return; - } - setpgid (0, 0); - execvp ("zzuf", args); - fprintf (stderr, "execution of `zzuf' failed: %s\n", strerror (errno)); - exit (1); -} - - -static void -zzuf_socat_stop () -{ - int status; - if (zzuf_pid != 0) - { - if (0 != killpg (zzuf_pid, SIGINT)) - fprintf (stderr, "Failed to killpg: %s\n", strerror (errno)); - kill (zzuf_pid, SIGINT); - waitpid (zzuf_pid, &status, 0); - (void) sleep (1); /* allow socat to also die in peace */ - } -} - - -/* end of socat.c */ diff --git a/src/testzzuf/test_get.c b/src/testzzuf/test_get.c @@ -1,6 +1,7 @@ /* This file is part of libmicrohttpd Copyright (C) 2007, 2008 Christian Grothoff + Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -19,12 +20,12 @@ */ /** - * @file test_get.c - * @brief Testcase for libmicrohttpd GET operations + * @file testzzuf/test_get.c + * @brief Several testcases for libmicrohttpd with input fuzzing * @author Christian Grothoff + * @author Karlson2k (Evgeny Grin) */ -#include "MHD_config.h" #include "platform.h" #include <curl/curl.h> #include <microhttpd.h> @@ -36,298 +37,1603 @@ #include <unistd.h> #endif -#include "socat.c" +#include "mhd_debug_funcs.h" +#include "test_helpers.h" +#ifndef MHD_STATICSTR_LEN_ +/** + * Determine length of static string / macro strings at compile time. + */ +#define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) +#endif /* ! MHD_STATICSTR_LEN_ */ + +#ifndef CURL_VERSION_BITS +#define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) +#endif /* ! CURL_VERSION_BITS */ +#ifndef CURL_AT_LEAST_VERSION +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) +#endif /* ! CURL_AT_LEAST_VERSION */ + +/** + * A larger loop count will run more random tests -- + * which would be good, except that it may take too + * long for most user's patience. So this small + * value is the default. + * Can be redefined by CPPFLAGS=-DLOOP_COUNT=123 + */ +#ifndef LOOP_COUNT +#ifndef _MHD_VHEAVY_TESTS +#define LOOP_COUNT 10 +#else /* ! _MHD_HEAVY_TESTS */ +#define LOOP_COUNT 200 +#endif /* ! _MHD_HEAVY_TESTS */ +#endif /* LOOP_COUNT */ + +#ifdef _DEBUG +/* Uncomment the next line (or use CPPFLAGS) to see all request and response bodies in log */ +/* #define TEST_PRINT_BODY */ +/* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are sent by libcurl */ +/* #define TEST_PRINT_BODY_RQ 1 */ +/* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are received by libcurl */ +/* #define TEST_PRINT_BODY_RP 1 */ +#endif /* _DEBUG */ + +#define MHD_TIMEOUT 2 + +#define CURL_TIMEOUT 5 + +/* Global test parameters */ static int oneone; +static int dry_run; +static int use_get; +static int use_get_chunked; +static int use_put; +static int use_put_large; +static int use_put_chunked; +static int use_post; +static int use_post_form; +static int use_long_header; +static int use_long_uri; +static int use_close; -struct CBC +#define TEST_BASE_URI "http:/" "/127.0.0.1/test_uri" + +#define EMPTY_PAGE "Empty page." +#define EMPTY_PAGE_ALT "Alternative empty page." +#define METHOD_NOT_SUPPORTED "HTTP method is not supported." +#define POST_DATA_BROKEN "The POST request is ill-formed." + +#define POST_KEY1 "test" +#define POST_VALUE1 "test_post" +#define POST_KEY2 "library" +#define POST_VALUE2 "GNU libmicrohttpd" +#define POST_URLENC_DATA \ + POST_KEY1 "=" POST_VALUE1 "&" POST_KEY2 "=" "GNU%20libmicrohttpd" + +#define PUT_NORMAL_SIZE 11 +/* Does not need to be very large as MHD buffer will be made smaller anyway */ +#define PUT_LARGE_SIZE (4 * 1024) +/* The length of "very long" URI and header strings. MHD uses smaller buffer. */ +#define TEST_STRING_VLONG_LEN 8 * 1024 + + +#if ! CURL_AT_LEAST_VERSION (7,56,0) +#define TEST_USE_STATIC_POST_DATA 1 +static struct curl_httppost *post_first; +static struct curl_httppost *post_last; +#endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */ + +static struct curl_slist *libcurl_long_header; + +/** + * Initialise long header for libcurl + * + * @return non-zero if succeed, + * zero if failed + */ +static int +long_header_init (void) { char *buf; - size_t pos; - size_t size; + + buf = malloc (TEST_STRING_VLONG_LEN + 1); + if (NULL == buf) + { + fprintf (stderr, "malloc() failed " + "at line %d.\n", (int) __LINE__); + return 0; + } + buf[TEST_STRING_VLONG_LEN] = 0; + buf[0] = 'A'; + memset (buf + 1, 'a', TEST_STRING_VLONG_LEN / 2 - 2); + buf[TEST_STRING_VLONG_LEN / 2 - 1] = ':'; + buf[TEST_STRING_VLONG_LEN / 2] = ' '; + memset (buf + TEST_STRING_VLONG_LEN / 2 + 1, 'c', + TEST_STRING_VLONG_LEN / 2 - 1); + libcurl_long_header = curl_slist_append (NULL, buf); + free (buf); + if (NULL != libcurl_long_header) + return ! 0; /* Success exit point */ + + fprintf (stderr, "curl_slist_append() failed " + "at line %d.\n", (int) __LINE__); + return 0; /* Failure exit point */ +} + + +/** + * Globally initialise test environment + * @return non-zero if succeed, + * zero if failed + */ +static int +test_global_init (void) +{ + libcurl_long_header = NULL; + if (CURLE_OK != curl_global_init (CURL_GLOBAL_WIN32)) + { + fprintf (stderr, "curl_global_init() failed " + "at line %d.\n", (int) __LINE__); + return 0; + } + + if (long_header_init ()) + { +#ifndef TEST_USE_STATIC_POST_DATA + return 1; /* Success exit point */ +#else /* ! TEST_USE_STATIC_POST_DATA */ + post_first = NULL; + post_last = NULL; + if ((CURL_FORMADD_OK != + curl_formadd (&post_first, &post_last, + CURLFORM_PTRNAME, POST_KEY1, + CURLFORM_NAMELENGTH, + (long) MHD_STATICSTR_LEN_ (POST_KEY1), + CURLFORM_PTRCONTENTS, POST_VALUE1, +#if CURL_AT_LEAST_VERSION (7,46,0) + CURLFORM_CONTENTLEN, + (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE1), +#else /* ! CURL_AT_LEAST_VERSION(7,46,0) */ + CURLFORM_CONTENTSLENGTH, + (long) MHD_STATICSTR_LEN_ (POST_VALUE1), +#endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */ + CURLFORM_END)) || + (CURL_FORMADD_OK != + curl_formadd (&post_first, &post_last, + CURLFORM_PTRNAME, POST_KEY2, + CURLFORM_NAMELENGTH, + (long) MHD_STATICSTR_LEN_ (POST_KEY2), + CURLFORM_PTRCONTENTS, POST_VALUE2, +#if CURL_AT_LEAST_VERSION (7,46,0) + CURLFORM_CONTENTLEN, + (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE2), +#else /* ! CURL_AT_LEAST_VERSION(7,46,0) */ + CURLFORM_CONTENTSLENGTH, + (long) MHD_STATICSTR_LEN_ (POST_VALUE2), +#endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */ + CURLFORM_END))) + fprintf (stderr, "curl_formadd() failed " + "at line %d.\n", (int) __LINE__); + else + return 1; /* Success exit point */ + + if (NULL != post_first) + curl_formfree (post_first); + curl_slist_free_all (libcurl_long_header); +#endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */ + } + curl_global_cleanup (); + return 0; /* Failure exit point */ +} + + +/** + * Globally de-initialise test environment + */ +static void +test_global_deinit (void) +{ +#ifdef TEST_USE_STATIC_POST_DATA + curl_formfree (post_first); +#endif /* TEST_USE_STATIC_POST_DATA */ + curl_global_cleanup (); + if (NULL != libcurl_long_header) + curl_slist_free_all (libcurl_long_header); +} + + +/** + * libcurl callback parameters for uploads, downloads and debug callbacks + */ +struct CBC +{ + /* Upload members */ + size_t up_pos; + size_t up_size; + /* Download members */ + char *dn_buf; + size_t dn_pos; + size_t dn_buf_size; + /* Debug callback members */ + unsigned int excess_found; }; +static void +initCBC (struct CBC *libcurlcbc, char *dn_buf, size_t dn_buf_size) +{ + libcurlcbc->up_pos = 0; + if (use_put_large) + libcurlcbc->up_size = PUT_LARGE_SIZE; + else if (use_put) + libcurlcbc->up_size = PUT_NORMAL_SIZE; + else + libcurlcbc->up_size = 0; + libcurlcbc->dn_buf = dn_buf; + libcurlcbc->dn_pos = 0; + libcurlcbc->dn_buf_size = dn_buf_size; + libcurlcbc->excess_found = 0; +} + + +static void +resetCBC (struct CBC *libcurlcbc) +{ + libcurlcbc->up_pos = 0; + libcurlcbc->dn_pos = 0; +} + + +static size_t +putBuffer (void *stream, size_t item_size, size_t nitems, void *ctx) +{ + size_t to_fill; + size_t i; + struct CBC *cbc = ctx; + + to_fill = cbc->up_size - cbc->up_pos; + /* Skip overflow check as the return value is valid anyway */ + if (use_put_chunked) + { + /* Send data as several chunks */ + if (to_fill > cbc->up_size / 3) + to_fill = cbc->up_size / 3; + } + if (to_fill > item_size * nitems) + to_fill = item_size * nitems; + + /* Avoid libcurl magic numbers */ +#ifdef CURL_READFUNC_PAUSE + if (CURL_READFUNC_ABORT == to_fill) + to_fill -= 2; +#endif /* CURL_READFUNC_PAUSE */ +#ifdef CURL_READFUNC_ABORT + if (CURL_READFUNC_ABORT == to_fill) + --to_fill; +#endif /* CURL_READFUNC_ABORT */ + for (i = 0; i < to_fill; ++i) + ((char *) stream)[i] = 'a' + (char) ((cbc->up_pos + i) + % (unsigned char) ('z' - 'a' + 1)); + + cbc->up_pos += to_fill; + return to_fill; +} + + static size_t copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) { struct CBC *cbc = ctx; - if (cbc->pos + size * nmemb > cbc->size) + if (cbc->dn_pos + size * nmemb > cbc->dn_buf_size) return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; + memcpy (&cbc->dn_buf[cbc->dn_pos], ptr, size * nmemb); + cbc->dn_pos += size * nmemb; return size * nmemb; } +#define TEST_MAGIC_MARKER0 0xFEE1C0DE +#define TEST_MAGIC_MARKER1 (TEST_MAGIC_MARKER0 + 1) +#define TEST_MAGIC_MARKER2 (TEST_MAGIC_MARKER0 + 2) + +struct content_cb_param_strct +{ + unsigned int magic0; /**< Must have TEST_MAGIC_MARKER0 value */ + struct MHD_Response *response; /**< The pointer to the response structure */ +}; + +/** + * MHD content reader callback that returns + * data in chunks. + */ +static ssize_t +content_cb (void *cls, uint64_t pos, char *buf, size_t max) +{ + struct content_cb_param_strct *param = (struct content_cb_param_strct *) cls; + size_t fill_size; + + if ((unsigned int) TEST_MAGIC_MARKER0 != param->magic0) + { + fprintf (stderr, "Wrong cls pointer " + "at line %d.\n", (int) __LINE__); + fflush (stderr); + abort (); + } + + if (pos >= 128 * 10) + { + if (MHD_YES != + MHD_add_response_footer (param->response, "Footer", "working")) + { + fprintf (stderr, "MHD_add_response_footer() failed " + "at line %d.\n", (int) __LINE__); + fflush (stderr); + abort (); + } + return MHD_CONTENT_READER_END_OF_STREAM; + } + + if (128 > max) + fill_size = 128; + else + fill_size = max; + memset (buf, 'A' + (char) (unsigned int) (pos / 128), fill_size); + + return (ssize_t) fill_size; +} + + +/** + * Deallocate memory for callback cls. + */ +static void +crcf (void *ptr) +{ + free (ptr); +} + + +struct req_process_strct +{ + unsigned int magic2; /**< Must have TEST_MAGIC_MARKER2 value */ + int is_static; /**< Non-zero if statically allocated, zero if malloc()'ed */ + struct MHD_PostProcessor *postprocsr; + unsigned int post_data_sum; +}; + static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) +post_iterator (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *value, uint64_t off, size_t size) +{ + struct req_process_strct *param = (struct req_process_strct *) cls; + size_t i; + + (void) filename; (void) content_type; (void) transfer_encoding; + (void) off; /* Unused. Mute compiler warnings. */ + + if (TEST_MAGIC_MARKER2 != param->magic2) + { + fprintf (stderr, "The 'param->magic2' has wrong value " + "at line %d.\n", (int) __LINE__); + abort (); + } + + if (MHD_POSTDATA_KIND != kind) + { + fprintf (stderr, "The 'kind' parameter has wrong value " + "at line %d.\n", (int) __LINE__); + abort (); + } + + if (NULL != key) + param->post_data_sum += (unsigned int) strlen (key); + + for (i = 0; size > i; ++i) + param->post_data_sum += (unsigned int) (unsigned char) value[i]; + + return MHD_YES; +} + + +static void +free_req_pr_data (struct req_process_strct *pr_data) +{ + if (NULL == pr_data) + return; + if (TEST_MAGIC_MARKER2 != pr_data->magic2) + { + fprintf (stderr, "The 'pr_data->magic2' has wrong value " + "at line %d.\n", (int) __LINE__); + abort (); + } + if (pr_data->is_static) + { + if (NULL != pr_data->postprocsr) + { + fprintf (stderr, "The 'pr_data->postprocsr' has wrong value " + "at line %d.\n", (int) __LINE__); + abort (); + } + return; + } + if (NULL != pr_data->postprocsr) + MHD_destroy_post_processor (pr_data->postprocsr); + pr_data->postprocsr = NULL; + free (pr_data); +} + + +struct ahc_param_strct +{ + unsigned int magic1; /**< Must have TEST_MAGIC_MARKER1 value */ + unsigned int err_flag; /**< Non-zero if any error is encountered */ + unsigned int num_replies; /**< The number of replies sent for the current request */ +}; + +static enum MHD_Result +send_error_response (struct MHD_Connection *connection, + struct ahc_param_strct *param, + unsigned int status_code, + const char *static_text, + const size_t static_text_len) { - static int ptr; - const char *me = cls; + struct MHD_Response *response; + response = + MHD_create_response_from_buffer_static (static_text_len, + static_text); + if (NULL != response) + { + if (MHD_YES == MHD_add_response_header (response, + MHD_HTTP_HEADER_CONNECTION, + "close")) + { + if (MHD_YES == MHD_queue_response (connection, status_code, response)) + { + MHD_destroy_response (response); + return MHD_YES; /* Success exit point */ + } + else + fprintf (stderr, "MHD_queue_response() failed " + "at line %d.\n", (int) __LINE__); + } + else + fprintf (stderr, "MHD_add_response_header() failed " + "at line %d.\n", (int) __LINE__); + MHD_destroy_response (response); + } + else + fprintf (stderr, "MHD_create_response_from_callback() failed " + "at line %d.\n", (int) __LINE__); + + param->err_flag = 1; + return MHD_NO; /* Failure exit point */ +} + + +static enum MHD_Result +ahc_check (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, size_t *upload_data_size, + void **req_cls) +{ + static struct req_process_strct static_req_pr_data = { + TEST_MAGIC_MARKER2, ! 0, NULL, 0 + }; + struct req_process_strct *req_pr_data; + struct ahc_param_strct *param = (struct ahc_param_strct *) cls; struct MHD_Response *response; enum MHD_Result ret; - (void) version; (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ + unsigned char data_sum; + int is_post_req; + if (NULL == cls) + { + fprintf (stderr, "The 'cls' parameter is NULL " + "at line %d.\n", (int) __LINE__); + fflush (stderr); + abort (); + } + if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1) + { + fprintf (stderr, "The 'param->magic1' has wrong value " + "at line %d.\n", (int) __LINE__); + fflush (stderr); + abort (); + } + if (NULL == connection) + { + fprintf (stderr, "The 'connection' parameter is NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } + if (1) + { /* Simple check for 'connection' parameter validity */ + const union MHD_ConnectionInfo *conn_info; + conn_info = + MHD_get_connection_info (connection, + MHD_CONNECTION_INFO_CONNECTION_TIMEOUT); + if (NULL == conn_info) + { + fprintf (stderr, "The 'MHD_get_connection_info' has returned NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + } + else if (MHD_TIMEOUT != conn_info->connection_timeout) + { + fprintf (stderr, "The 'MHD_get_connection_info' has returned " + "unexpected timeout value " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + } + } if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); + { + fprintf (stderr, "The 'url' parameter is NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + } if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); + { + fprintf (stderr, "The 'method' parameter is NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); + { + fprintf (stderr, "The 'version' parameter is NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); + { + fprintf (stderr, "The 'upload_data_size' parameter is NULL " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp (me, method)) - return MHD_NO; /* unexpected method */ - if (&ptr != *req_cls) { - *req_cls = &ptr; + fprintf (stderr, "The 'upload_data' parameter is NULL " + "while '*upload_data_size' is not zero " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } + if ((NULL != upload_data) && (0 == *upload_data_size)) + { + fprintf (stderr, "The 'upload_data' parameter is NOT NULL " + "while '*upload_data_size' is zero " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + return MHD_NO; /* Should not reply */ + } + + if (0 != param->num_replies) + { + /* Phantom "second" request due to the fuzzing of the input. Refuse. */ + return MHD_NO; + } + + is_post_req = (0 == strcmp (method, MHD_HTTP_METHOD_POST)); + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) + && (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) + && (0 != strcmp (method, MHD_HTTP_METHOD_PUT)) + && (! is_post_req)) + { + /* Unsupported method for this callback */ + return send_error_response (connection, param, MHD_HTTP_NOT_IMPLEMENTED, + METHOD_NOT_SUPPORTED, + MHD_STATICSTR_LEN_ (METHOD_NOT_SUPPORTED)); + } + + if (NULL == *req_cls) + { + if (! is_post_req) + { /* Use static memory */ + *req_cls = &static_req_pr_data; + } + else + { /* POST request, use PostProcessor */ + req_pr_data = + (struct req_process_strct *) malloc (sizeof (struct req_process_strct)); + if (NULL == req_pr_data) + { + fprintf (stderr, "malloc() failed " + "at line %d.\n", (int) __LINE__); + return MHD_NO; + } + req_pr_data->magic2 = TEST_MAGIC_MARKER2; + req_pr_data->is_static = 0; + req_pr_data->post_data_sum = 0; + req_pr_data->postprocsr = MHD_create_post_processor (connection, 1024, + &post_iterator, + req_pr_data); + if (NULL == req_pr_data->postprocsr) + { + free (req_pr_data); + if (NULL == upload_data) + return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST, + POST_DATA_BROKEN, + MHD_STATICSTR_LEN_ (POST_DATA_BROKEN)); + else + return MHD_NO; /* Cannot handle request, broken POST */ + } + *req_cls = req_pr_data; + } + if (NULL == upload_data) + return MHD_YES; + } + req_pr_data = (struct req_process_strct *) *req_cls; + + data_sum = 0; + if (NULL != upload_data) + { + if (is_post_req) + { + if (MHD_YES != MHD_post_process (req_pr_data->postprocsr, + upload_data, *upload_data_size)) + { + free_req_pr_data (req_pr_data); + *req_cls = NULL; + /* Processing upload body (context), error reply cannot be queued here */ + return MHD_NO; + } + *upload_data_size = 0; /* All data have been processed */ + } + else + { + /* Check that all 'upload_data' is addressable */ + size_t pos; + for (pos = 0; pos < *upload_data_size; ++pos) + data_sum += (unsigned char) upload_data[pos]; + if (0 != *upload_data_size) + { + if (3 >= *upload_data_size) + *upload_data_size = 0; /* Consume all incoming data */ + else + *upload_data_size = data_sum % *upload_data_size; /* Pseudo-random */ + } + } return MHD_YES; } + if (is_post_req) + { + if (MHD_YES != MHD_destroy_post_processor (req_pr_data->postprocsr)) + { + free (req_pr_data); + *req_cls = NULL; + return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST, + POST_DATA_BROKEN, + MHD_STATICSTR_LEN_ (POST_DATA_BROKEN)); + } + req_pr_data->postprocsr = NULL; + } + data_sum += (unsigned char) req_pr_data->post_data_sum; + free_req_pr_data (req_pr_data); *req_cls = NULL; - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - if (ret == MHD_NO) + + ret = MHD_YES; + if (use_get_chunked) + { + struct content_cb_param_strct *cnt_cb_param; + cnt_cb_param = malloc (sizeof (struct content_cb_param_strct)); + if (NULL == cnt_cb_param) + { + fprintf (stderr, "malloc() failed " + "at line %d.\n", (int) __LINE__); + /* External error, do not rise the error flag */ + return MHD_NO; + } + cnt_cb_param->magic0 = (unsigned int) TEST_MAGIC_MARKER0; + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, + 1024, + &content_cb, cnt_cb_param, + &crcf); + if (NULL == response) + { + fprintf (stderr, "MHD_create_response_from_callback() failed " + "at line %d.\n", (int) __LINE__); + free (cnt_cb_param); + param->err_flag = 1; + ret = MHD_NO; + } + else + cnt_cb_param->response = response; + } + else if (use_get || use_put || use_post) + { + /* Randomly choose the response page for the POST requests */ + if (0 == data_sum % 2) + response = + MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (EMPTY_PAGE), + EMPTY_PAGE); + else + response = + MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ ( \ + EMPTY_PAGE_ALT), + EMPTY_PAGE_ALT); + + if (NULL == response) + { + fprintf (stderr, "MHD_create_response_from_buffer_static() failed " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + ret = MHD_NO; + } + } + else + { + fprintf (stderr, "Response is not implemented for this test. " + "Internal logic is broken. " + "At line %d.\n", (int) __LINE__); abort (); + } + + if (NULL != response) + { + if ((MHD_YES == ret) && + (use_close || (! oneone && (0 != strcmp (version, + MHD_HTTP_VERSION_1_0))))) + { + ret = MHD_add_response_header (response, + MHD_HTTP_HEADER_CONNECTION, + "close"); + if (MHD_YES != ret) + { + fprintf (stderr, "MHD_add_response_header() failed " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + } + } + if (MHD_YES == ret) + { + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + if (MHD_YES != ret) + { + fprintf (stderr, "MHD_queue_response() failed " + "at line %d.\n", (int) __LINE__); + param->err_flag = 1; + } + } + else + param->num_replies++; + + MHD_destroy_response (response); + } + else + { + fprintf (stderr, "MHD_create_response_from_buffer_static() failed " + "at line %d.\n", (int) __LINE__); + ret = MHD_NO; + } return ret; } -static unsigned int -testInternalGet () +static void +req_completed_cleanup (void *cls, + struct MHD_Connection *connection, + void **req_cls, + enum MHD_RequestTerminationCode toe) +{ + struct ahc_param_strct *param = (struct ahc_param_strct *) cls; + struct req_process_strct *req_pr_data = (struct req_process_strct *) *req_cls; + (void) connection; /* Unused. Mute compiler warning. */ + + if (NULL == param) + { + fprintf (stderr, "The 'cls' parameter is NULL at line %d.\n", + (int) __LINE__); + fflush (stderr); + abort (); + } + if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1) + { + fprintf (stderr, "The 'param->magic1' has wrong value at line %d.\n", + (int) __LINE__); + fflush (stderr); + abort (); + } + if (NULL == req_pr_data) + return; /* The data have been freed */ + if ((unsigned int) TEST_MAGIC_MARKER2 != req_pr_data->magic2) + { + fprintf (stderr, "The 'req_pr_data->magic2' has wrong value at line %d.\n", + (int) __LINE__); + fflush (stderr); + abort (); + } + if (MHD_REQUEST_TERMINATED_COMPLETED_OK == toe) + { + fprintf (stderr, "The request completed successful, but request cls has" + "not been cleared. " + "At line %d.\n", (int) __LINE__); + param->err_flag = 1; + } + if (req_pr_data->is_static) + return; + if (NULL != req_pr_data->postprocsr) + MHD_destroy_post_processor (req_pr_data->postprocsr); + req_pr_data->postprocsr = NULL; + free (req_pr_data); + *req_cls = NULL; +} + + +/* Un-comment the next line (or use CPPFLAGS) to avoid + logging of the traffic with debug builds */ +/* #define TEST_NO_PRINT_TRAFFIC 1 */ + +#ifdef _DEBUG +#ifdef TEST_PRINT_BODY +#ifndef TEST_PRINT_BODY_RQ +#define TEST_PRINT_BODY_RQ 1 +#endif /* TEST_PRINT_BODY_RQ */ +#ifndef TEST_PRINT_BODY_RP +#define TEST_PRINT_BODY_RP 1 +#endif /* TEST_PRINT_BODY_RP */ +#endif /* TEST_PRINT_BODY */ +#endif /* _DEBUG */ + +static int +libcurl_debug_cb (CURL *handle, + curl_infotype type, + char *data, + size_t size, + void *ctx) +{ + static const char excess_mark[] = "Excess found"; + static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark); + struct CBC *cbc = ctx; + (void) handle; + +#if defined(_DEBUG) && ! defined(TEST_NO_PRINT_TRAFFIC) + switch (type) + { + case CURLINFO_TEXT: + fprintf (stderr, "* %.*s", (int) size, data); + break; + case CURLINFO_HEADER_IN: + fprintf (stderr, "< %.*s", (int) size, data); + break; + case CURLINFO_HEADER_OUT: + fprintf (stderr, "> %.*s", (int) size, data); + break; + case CURLINFO_DATA_IN: +#ifdef TEST_PRINT_BODY_RP + fprintf (stderr, "<| %.*s\n", (int) size, data); +#endif /* TEST_PRINT_BODY_RP */ + break; + case CURLINFO_DATA_OUT: +#ifdef TEST_PRINT_BODY_RQ + fprintf (stderr, ">| %.*s\n", (int) size, data); +#endif /* TEST_PRINT_BODY_RQ */ + break; + case CURLINFO_SSL_DATA_IN: + case CURLINFO_SSL_DATA_OUT: + case CURLINFO_END: + default: + break; + } +#endif /* _DEBUG && ! TEST_NO_PRINT_TRAFFIC */ + if (use_close || ! oneone) + { + /* Check for extra data only if every connection is terminated by MHD + after one request, otherwise MHD may react on garbage after request + data. */ + if (CURLINFO_TEXT == type) + { + if ((size >= excess_mark_len) && + (0 == memcmp (data, excess_mark, excess_mark_len))) + { + fprintf (stderr, "Extra data has been detected in MHD reply " + "at line %d.\n", (int) __LINE__); + cbc->excess_found++; + } + } + } + return 0; +} + + +static CURL * +setupCURL (struct CBC *cbc, uint16_t port +#ifndef TEST_USE_STATIC_POST_DATA + , curl_mime **mime +#endif /* ! TEST_USE_STATIC_POST_DATA */ + ) { - struct MHD_Daemon *d; CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + CURLcode e; + char *buf; + const char *uri_to_use; + +#ifndef TEST_USE_STATIC_POST_DATA + *mime = NULL; +#endif /* ! TEST_USE_STATIC_POST_DATA */ + + if (! use_long_uri) + { + uri_to_use = TEST_BASE_URI; + buf = NULL; + } + else + { + size_t pos; + buf = malloc (TEST_STRING_VLONG_LEN + 1); + if (NULL == buf) + { + fprintf (stderr, "malloc() failed " + "at line %d.\n", (int) __LINE__); + return NULL; + } + memcpy (buf, TEST_BASE_URI, MHD_STATICSTR_LEN_ (TEST_BASE_URI)); + for (pos = MHD_STATICSTR_LEN_ (TEST_BASE_URI); + pos < TEST_STRING_VLONG_LEN; + ++pos) + { + if (0 == pos % 9) + buf[pos] = '/'; + else + buf[pos] = 'a' + (char) (unsigned char) (pos % ((unsigned char) + ('z' - 'a' + 1))); + } + buf[TEST_STRING_VLONG_LEN] = 0; + uri_to_use = buf; + } + + c = curl_easy_init (); + if (NULL == c) + { + fprintf (stderr, "curl_easy_init() failed " + "at line %d.\n", (int) __LINE__); + return NULL; + } + + if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_URL, + uri_to_use))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, + &copyBuffer))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, + ((long) CURL_TIMEOUT)))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION, + oneone ? + CURL_HTTP_VERSION_1_1 : + CURL_HTTP_VERSION_1_0))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_TIMEOUT, + ((long) CURL_TIMEOUT)))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L))) && +#ifdef _DEBUG + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_VERBOSE, 1L))) && +#endif /* _DEBUG */ + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, + &libcurl_debug_cb))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGDATA, + cbc))) && +#if CURL_AT_LEAST_VERSION (7, 19, 4) + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS, + CURLPROTO_HTTP))) && +#endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ +#if CURL_AT_LEAST_VERSION (7, 45, 0) + (CURLE_OK == (e = curl_easy_setopt (c, + CURLOPT_DEFAULT_PROTOCOL, "http"))) && +#endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ +#if CURL_AT_LEAST_VERSION (7, 24, 0) + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE, + "host!127.0.0.101"))) && +#else /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */ + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE, + "127.0.0.101"))) && +#endif /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */ + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTPHEADER, + use_long_header ? + libcurl_long_header : NULL))) + ) + { + if (NULL != buf) + { + free (buf); + buf = NULL; + } + if (use_put) + { + if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READFUNCTION, + &putBuffer))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READDATA, cbc))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_UPLOAD, (long) 1))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, + use_put_chunked ? + ((curl_off_t) -1) : + ((curl_off_t) cbc->up_size))))) + { + return c; /* Success exit point for 'use_put' */ + } + else + fprintf (stderr, "PUT-related curl_easy_setopt() failed at line %d, " + "error: %s\n", (int) __LINE__, + curl_easy_strerror (e)); + } + else if (use_post) + { + if (! use_post_form) + { + if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POST, (long) 1))) && + (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POSTFIELDS, + POST_URLENC_DATA))) && + (CURLE_OK == + (e = curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, + MHD_STATICSTR_LEN_ (POST_URLENC_DATA))))) + { + return c; /* Success exit point for 'use_post' */ + } + else + fprintf (stderr, + "POST-related curl_easy_setopt() failed at line %d, " + "error: %s\n", (int) __LINE__, + curl_easy_strerror (e)); + } + else + { +#ifndef TEST_USE_STATIC_POST_DATA + *mime = curl_mime_init (c); + if (NULL != *mime) + { + curl_mimepart *part; + if ((NULL != (part = curl_mime_addpart (*mime))) && + (CURLE_OK == curl_mime_name (part, POST_KEY1)) && + (CURLE_OK == curl_mime_data (part, POST_VALUE1, + MHD_STATICSTR_LEN_ (POST_VALUE1))) && + (NULL != (part = curl_mime_addpart (*mime))) && + (CURLE_OK == curl_mime_name (part, POST_KEY2)) && + (CURLE_OK == curl_mime_data (part, POST_VALUE2, + MHD_STATICSTR_LEN_ (POST_VALUE2)))) + { + if (CURLE_OK == + (e = curl_easy_setopt (c, CURLOPT_MIMEPOST, *mime))) + return c; /* Success exit point for 'use_post' */ + else + fprintf (stderr, "curl_easy_setopt(c, CURLOPT_MIMEPOST, mime) " + "failed at line %d, error: %s\n", + (int) __LINE__, curl_easy_strerror (e)); + } + else + fprintf (stderr, "curl_mime_addpart(), curl_mime_name() or " + "curl_mime_data() failed.\n"); + } + else + fprintf (stderr, "curl_mime_init() failed.\n"); + +#else /* TEST_USE_STATIC_POST_DATA */ + if (CURLE_OK == (e = curl_easy_setopt (c, + CURLOPT_HTTPPOST, post_first))) + { + return c; /* Success exit point for 'use_post' */ + } + else + fprintf (stderr, "POST form-related curl_easy_setopt() failed, " + "error: %s\n", curl_easy_strerror (e)); +#endif /* TEST_USE_STATIC_POST_DATA */ + } + } else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); + return c; /* Success exit point */ } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; + else + fprintf (stderr, "curl_easy_setopt() failed at line %d, " + "error: %s\n", (int) __LINE__, + curl_easy_strerror (e)); + + curl_easy_cleanup (c); +#ifndef TEST_USE_STATIC_POST_DATA + if (NULL != *mime) + curl_mime_free (*mime); +#endif /* ! TEST_USE_STATIC_POST_DATA */ + + if (NULL != buf) + free (buf); + + return NULL; /* Failure exit point */ +} + + +static struct MHD_Daemon * +start_daemon_for_test (unsigned int daemon_flags, uint16_t *pport, + struct ahc_param_strct *callback_param) +{ + struct MHD_Daemon *d; + struct MHD_OptionItem ops[] = { + { MHD_OPTION_END, 0, NULL }, + { MHD_OPTION_END, 0, NULL } + }; + callback_param->magic1 = (unsigned int) TEST_MAGIC_MARKER1; + callback_param->err_flag = 0; + callback_param->num_replies = 0; + + if (use_put_large) + { + ops[0].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT; + ops[0].value = (intptr_t) (PUT_LARGE_SIZE / 4); + } + else if (use_long_header || use_long_uri) + { + ops[0].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT; + ops[0].value = (intptr_t) (TEST_STRING_VLONG_LEN / 2); + } + d = MHD_start_daemon (daemon_flags /* | MHD_USE_ERROR_LOG */, + *pport, NULL, NULL, + &ahc_check, callback_param, + MHD_OPTION_CONNECTION_TIMEOUT, + (unsigned int) MHD_TIMEOUT, + MHD_OPTION_NOTIFY_COMPLETED, + &req_completed_cleanup, callback_param, + MHD_OPTION_ARRAY, ops, + MHD_OPTION_END); + if (NULL == d) + { + fprintf (stderr, "MHD_start_daemon() failed " + "at line %d.\n", (int) __LINE__); + return NULL; + } + /* Do not use accept4() as only accept() is intercepted by zzuf */ + MHD_avoid_accept4_ (d); + + if (0 == *pport) + { + const union MHD_DaemonInfo *dinfo; + + dinfo = MHD_get_daemon_info (d, + MHD_DAEMON_INFO_BIND_PORT); + if ( (NULL == dinfo) || + (0 == dinfo->port) ) + { + fprintf (stderr, "MHD_get_daemon_info() failed " + "at line %d.\n", (int) __LINE__); + MHD_stop_daemon (d); + return NULL; + } + *pport = dinfo->port; + } + return d; +} + + +static void +print_test_starting (unsigned int daemon_flags) +{ + fflush (stderr); + if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags)) + { + if (0 != (MHD_USE_THREAD_PER_CONNECTION & daemon_flags)) + { + if (0 != (MHD_USE_POLL & daemon_flags)) + printf ("\nStarting test with internal polling by poll() and " + "thread-per-connection.\n"); + else + printf ("\nStarting test with internal polling by select() and " + "thread-per-connection.\n"); + } + else + { + if (0 != (MHD_USE_POLL & daemon_flags)) + printf ("\nStarting test with internal polling by poll().\n"); + else if (0 != (MHD_USE_EPOLL & daemon_flags)) + printf ("\nStarting test with internal polling by 'epoll'.\n"); + else + printf ("\nStarting test with internal polling by select().\n"); + } + } + else + { + if (0 != (MHD_USE_EPOLL & daemon_flags)) + printf ("\nStarting test with external polling and internal 'epoll'.\n"); + else + printf ("\nStarting test with external polling.\n"); + } + fflush (stdout); } static unsigned int -testMultithreadedGet () +testInternalPolling (uint16_t *pport, unsigned int daemon_flags) { struct MHD_Daemon *d; CURL *c; char buf[2048]; struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + struct ahc_param_strct callback_param; + unsigned int ret; +#ifndef TEST_USE_STATIC_POST_DATA + curl_mime *mime; +#endif /* ! TEST_USE_STATIC_POST_DATA */ + + if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags)) + { + fprintf (stderr, "Wrong internal flags, the test is broken. " + "At line %d.\n", (int) __LINE__); + abort (); /* Wrong flags, error in code */ + } + + print_test_starting (daemon_flags); + initCBC (&cbc, buf, sizeof(buf)); + d = start_daemon_for_test (daemon_flags, pport, &callback_param); if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); + return 1; + + ret = 0; + c = setupCURL (&cbc, *pport +#ifndef TEST_USE_STATIC_POST_DATA + , &mime +#endif /* ! TEST_USE_STATIC_POST_DATA */ + ); + if (NULL != c) + { + int i; + + for (i = dry_run ? LOOP_COUNT : 0; i < LOOP_COUNT; i++) + { + fprintf (stderr, "."); + callback_param.num_replies = 0; + resetCBC (&cbc); + /* Run libcurl without checking the result */ + curl_easy_perform (c); + fflush (stderr); + } curl_easy_cleanup (c); +#ifndef TEST_USE_STATIC_POST_DATA + if (NULL != mime) + curl_mime_free (mime); +#endif /* ! TEST_USE_STATIC_POST_DATA */ + } + else + ret = 99; /* Not an MHD error */ + + if ((0 == ret) && callback_param.err_flag) + { + fprintf (stderr, "One or more errors have been detected by " + "access handler callback function. " + "At line %d.\n", (int) __LINE__); + ret = 1; + } + else if ((0 == ret) && cbc.excess_found) + { + fprintf (stderr, "The extra reply data have been detected one " + "or more times. " + "At line %d.\n", (int) __LINE__); + ret = 1; } + fprintf (stderr, "\n"); - zzuf_socat_stop (); MHD_stop_daemon (d); - return 0; + fflush (stderr); + return ret; } static unsigned int -testExternalGet () +testExternalPolling (uint16_t *pport, unsigned int daemon_flags) { struct MHD_Daemon *d; - CURL *c; + CURLM *multi; char buf[2048]; struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); + struct ahc_param_strct callback_param; + unsigned int ret; +#ifndef TEST_USE_STATIC_POST_DATA + curl_mime *mime; +#endif /* ! TEST_USE_STATIC_POST_DATA */ + + if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags)) + { + fprintf (stderr, "Wrong internal flags, the test is broken. " + "At line %d.\n", (int) __LINE__); + abort (); /* Wrong flags, error in code */ + } + + print_test_starting (daemon_flags); + initCBC (&cbc, buf, sizeof(buf)); + d = start_daemon_for_test (daemon_flags, pport, &callback_param); if (d == NULL) - return 256; + return 1; + + ret = 0; multi = curl_multi_init (); if (multi == NULL) { - MHD_stop_daemon (d); - return 512; + fprintf (stderr, "curl_multi_init() failed " + "at line %d.\n", (int) __LINE__); + ret = 99; /* Not an MHD error */ } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) + else { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + CURL *c; + c = setupCURL (&cbc, *pport +#ifndef TEST_USE_STATIC_POST_DATA + , &mime +#endif /* ! TEST_USE_STATIC_POST_DATA */ + ); + + if (NULL == c) + ret = 99; /* Not an MHD error */ else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) + int i; + + for (i = dry_run ? LOOP_COUNT : 0; + (i < LOOP_COUNT) && (0 == ret); i++) { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; + CURLMcode mret; + + /* The same 'multi' handle will be used in transfers so + connection will be reused. + The same 'easy' handle is added (and removed later) to (re-)start + the same transfer. */ + mret = curl_multi_add_handle (multi, c); + if (CURLM_OK != mret) + { + fprintf (stderr, "curl_multi_add_handle() failed at %d, " + "error: %s\n", (int) __LINE__, + curl_multi_strerror (mret)); + ret = 99; /* Not an MHD error */ + } + else + { + time_t start; + + fprintf (stderr, "."); + callback_param.num_replies = 0; + resetCBC (&cbc); + start = time (NULL); + do + { + fd_set rs; + fd_set ws; + fd_set es; + int maxfd_curl; + MHD_socket maxfd_mhd; + int maxfd; + int running; + struct timeval tv; + + maxfd_curl = 0; + maxfd_mhd = MHD_INVALID_SOCKET; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + curl_multi_perform (multi, &running); + if (0 == running) + { + int msgs_left; + do + { + (void) curl_multi_info_read (multi, &msgs_left); + } while (0 != msgs_left); + break; /* The transfer has been finished */ + } + mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxfd_curl); + if (CURLM_OK != mret) + { + fprintf (stderr, "curl_multi_fdset() failed at line %d, " + "error: %s\n", (int) __LINE__, + curl_multi_strerror (mret)); + ret = 99; /* Not an MHD error */ + break; + } + if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxfd_mhd)) + { + fprintf (stderr, "MHD_get_fdset() failed " + "at line %d.\n", (int) __LINE__); + ret = 1; + break; + } +#ifndef MHD_WINSOCK_SOCKETS + if ((int) maxfd_mhd > maxfd_curl) + maxfd = (int) maxfd_mhd; + else +#endif /* ! MHD_WINSOCK_SOCKETS */ + maxfd = maxfd_curl; + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + if (0 == MHD_get_timeout64s (d)) + tv.tv_usec = 0; + else + { + long curl_to = -1; + curl_multi_timeout (multi, &curl_to); + if (0 == curl_to) + tv.tv_usec = 0; + } + if (-1 == select (maxfd + 1, &rs, &ws, &es, &tv)) + { +#ifdef MHD_POSIX_SOCKETS + if (EINTR != errno) + fprintf (stderr, "Unexpected select() error " + "at line %d.\n", (int) __LINE__); +#else /* ! MHD_POSIX_SOCKETS */ + if ((WSAEINVAL != WSAGetLastError ()) || + (0 != rs.fd_count) || (0 != ws.fd_count) || + (0 != es.fd_count)) + fprintf (stderr, "Unexpected select() error " + "at line %d.\n", (int) __LINE__); + Sleep ((unsigned long) tv.tv_usec / 1000); +#endif /* ! MHD_POSIX_SOCKETS */ + } + MHD_run (d); + } while (time (NULL) - start <= MHD_TIMEOUT); + /* Remove 'easy' handle from 'multi' handle to + * restart the transfer or to finish. */ + curl_multi_remove_handle (multi, c); + } } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); curl_easy_cleanup (c); } + curl_multi_cleanup (multi); +#ifndef TEST_USE_STATIC_POST_DATA + if (NULL != mime) + curl_mime_free (mime); +#endif /* ! TEST_USE_STATIC_POST_DATA */ } + + if ((0 == ret) && callback_param.err_flag) + { + fprintf (stderr, "One or more errors have been detected by " + "access handler callback function. " + "At line %d.\n", (int) __LINE__); + ret = 1; + } + else if ((0 == ret) && cbc.excess_found) + { + fprintf (stderr, "The extra reply data have been detected one " + "or more times. " + "At line %d.\n", (int) __LINE__); + ret = 1; + } + fprintf (stderr, "\n"); - curl_multi_cleanup (multi); - zzuf_socat_stop (); MHD_stop_daemon (d); return 0; } +static unsigned int +run_all_checks (void) +{ + uint16_t port; + unsigned int testRes; + unsigned int ret = 0; + + if (MHD_are_sanitizers_enabled_ ()) + { + fprintf (stderr, "The test does not work with sanitizers. " + "At line %d.\n", (int) __LINE__); + return 77; + } + if (! MHD_is_avoid_accept4_possible_ ()) + { + fprintf (stderr, + "Non-debug build of MHD on this platform use accept4() function. " + "Test with zzuf is not possible. " + "At line %d.\n", (int) __LINE__); + return 77; + } + if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) + port = 0; /* Use system automatic assignment */ + else + { + port = 4010; /* Use predefined port, may break parallel testing of another MHD build */ + if (oneone) + port += 100; + if (use_long_uri) + port += 30; + else if (use_long_header) + port += 35; + else if (use_get_chunked) + port += 0; + else if (use_get) + port += 5; + else if (use_post_form) + port += 10; + else if (use_post) + port += 15; + else if (use_put_large) + port += 20; + else if (use_put_chunked) + port += 25; + } + + if (! dry_run && (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))) + { + testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY); + if ((77 == testRes) || (99 == testRes)) + return testRes; + ret += testRes; + testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY + | MHD_USE_THREAD_PER_CONNECTION); + if ((77 == testRes) || (99 == testRes)) + return testRes; + ret += testRes; + + if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) + { + testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY); + if ((77 == testRes) || (99 == testRes)) + return testRes; + ret += testRes; + testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY + | MHD_USE_THREAD_PER_CONNECTION); + if ((77 == testRes) || (99 == testRes)) + return testRes; + ret += testRes; + } + + if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) + { + testRes = testInternalPolling (&port, MHD_USE_EPOLL_INTERNALLY); + if ((77 == testRes) || (99 == testRes)) + return testRes; + } + } + testRes = testExternalPolling (&port, MHD_NO_FLAG); + if ((77 == testRes) || (99 == testRes)) + return testRes; + ret += testRes; + + return ret; +} + + int main (int argc, char *const *argv) { - unsigned int errorCount = 0; - (void) argc; /* Unused. Silent compiler warning. */ + unsigned int res; + + oneone = ! has_in_name (argv[0], "10"); + use_get = has_in_name (argv[0], "_get"); + use_get_chunked = has_in_name (argv[0], "_get_chunked"); + use_put = has_in_name (argv[0], "_put"); + use_put_large = has_in_name (argv[0], "_put_large"); + use_put_chunked = has_in_name (argv[0], "_put_chunked"); + use_post = has_in_name (argv[0], "_post"); + use_post_form = has_in_name (argv[0], "_post_form"); + use_long_header = has_in_name (argv[0], "_long_header"); + use_long_uri = has_in_name (argv[0], "_long_uri"); + use_close = has_in_name (argv[0], "_close"); - oneone = (NULL != strrchr (argv[0], (int) '/')) ? - (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) + dry_run = has_param (argc, argv, "--dry-run") || + has_param (argc, argv, "-n"); + + if (1 != + ((use_get ? 1 : 0) + (use_put ? 1 : 0) + (use_post ? 1 : 0))) { - errorCount += testInternalGet (); - errorCount += testMultithreadedGet (); + fprintf (stderr, "Wrong test name '%s': no or multiple indications " + "for the test type.\n", argv[0] ? argv[0] : "(NULL)"); + return 99; } - errorCount += testExternalGet (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ + + /* zzuf cannot bypass exit values. + Unless 'dry run' is used, do not return errors for external error + conditions (like out-of-memory) as they will be reported as test failures. */ + if (! test_global_init ()) + return dry_run ? 99 : 0; + res = run_all_checks (); + test_global_deinit (); + if (99 == res) + return dry_run ? 99 : 0; + if (77 == res) + return dry_run ? 77 : 0; + return (0 == res) ? 0 : 1; /* 0 == pass */ } diff --git a/src/testzzuf/test_get_chunked.c b/src/testzzuf/test_get_chunked.c @@ -1,364 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file daemontest_get_chunked.c - * @brief Testcase for libmicrohttpd GET operations with chunked content encoding - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - -#include "socat.c" - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -/** - * MHD content reader callback that returns - * data in chunks. - */ -static ssize_t -crc (void *cls, uint64_t pos, char *buf, size_t max) -{ - struct MHD_Response **responseptr = cls; - - if (pos == 128 * 10) - { - MHD_add_response_header (*responseptr, "Footer", "working"); - return MHD_CONTENT_READER_END_OF_STREAM; - } - if (max < 128) - abort (); /* should not happen in this testcase... */ - memset (buf, 'A' + (pos / 128), 128); - return 128; -} - - -/** - * Dummy function that does nothing. - */ -static void -crcf (void *ptr) -{ - free (ptr); -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, void **req_cls) -{ - static int aptr; - const char *me = cls; - struct MHD_Response *response; - struct MHD_Response **responseptr; - enum MHD_Result ret; - - (void) url; - (void) version; /* Unused. Silent compiler warning. */ - (void) upload_data; - (void) upload_data_size; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp (me, method)) - return MHD_NO; /* unexpected method */ - if (&aptr != *req_cls) - { - /* do never respond on first call */ - *req_cls = &aptr; - return MHD_YES; - } - responseptr = malloc (sizeof (struct MHD_Response *)); - if (NULL == responseptr) - return MHD_NO; - response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, - 1024, - &crc, responseptr, &crcf); - if (NULL == response) - { - free (responseptr); - return MHD_NO; - } - *responseptr = response; - ret = MHD_queue_response (connection, - MHD_HTTP_OK, - response); - MHD_destroy_response (response); - return ret; -} - - -static unsigned int -testInternalGet () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testMultithreadedGet () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); - if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testExternalGet () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); - if (d == NULL) - return 256; - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - } - fprintf (stderr, "\n"); - curl_multi_cleanup (multi); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; (void) argv; /* Unused. Silent compiler warning. */ - - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalGet (); - errorCount += testMultithreadedGet (); - } - errorCount += testExternalGet (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_long_header.c b/src/testzzuf/test_long_header.c @@ -1,265 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file test_long_header.c - * @brief Testcase for libmicrohttpd handling of very long headers - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - -#include "socat.c" - -/** - * We will set the memory available per connection to - * half of this value, so the actual value does not have - * to be big at all... - */ -#define VERY_LONG (1024 * 10) - -static int oneone; - -static enum MHD_Result -apc_all (void *cls, const struct sockaddr *addr, socklen_t addrlen) -{ - (void) cls; (void) addr; (void) addrlen; /* Unused. Silent compiler warning. */ - return MHD_YES; -} - - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - (void) ptr; (void) ctx; /* Unused. Silent compiler warning. */ - return size * nmemb; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - const char *me = cls; - struct MHD_Response *response; - enum MHD_Result ret; - (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ - (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp (me, method)) - return MHD_NO; /* unexpected method */ - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return ret; -} - - -static unsigned int -testLongUrlGet () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - char *url; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - &apc_all, - NULL, - &ahc_echo, - "GET", - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (VERY_LONG / 2), MHD_OPTION_END); - - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - url = malloc (VERY_LONG); - if (NULL == url) - { - zzuf_socat_stop (); - return 1; - } - memset (url, 'a', VERY_LONG); - url[VERY_LONG - 1] = '\0'; - memcpy (url, "http://127.0.0.1:11081/", - strlen ("http://127.0.0.1:11081/")); - curl_easy_setopt (c, CURLOPT_URL, url); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - free (url); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testLongHeaderGet () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - char *url; - struct curl_slist *header = NULL; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - &apc_all, - NULL, - &ahc_echo, - "GET", - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (VERY_LONG / 2), MHD_OPTION_END); - if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - url = malloc (VERY_LONG); - if (NULL == url) - { - zzuf_socat_stop (); - curl_easy_cleanup (c); - return 16; - } - memset (url, 'a', VERY_LONG); - url[VERY_LONG - 1] = '\0'; - url[VERY_LONG / 2] = ':'; - url[VERY_LONG / 2 + 1] = ' '; - header = curl_slist_append (header, url); - - curl_easy_setopt (c, CURLOPT_HTTPHEADER, header); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_slist_free_all (header); - header = NULL; - curl_easy_cleanup (c); - free (url); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - const char *sl; - (void) argc; /* Unused. Silent compiler warning. */ - - sl = strrchr (argv[0], (int) '/'); - oneone = (NULL != sl) ? (NULL != strstr (sl, "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - errorCount += testLongUrlGet (); - errorCount += testLongHeaderGet (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_post.c b/src/testzzuf/test_post.c @@ -1,416 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file test_post.c - * @brief Testcase for libmicrohttpd POST operations using URL-encoding - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - - -#include "socat.c" - -#define POST_DATA "name=daniel&project=curl" - -static int oneone; - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - - -static void -completed_cb (void *cls, - struct MHD_Connection *connection, - void **req_cls, - enum MHD_RequestTerminationCode toe) -{ - struct MHD_PostProcessor *pp = *req_cls; - (void) cls; (void) connection; (void) toe; /* Unused. Silent compiler warning. */ - - if (NULL != pp) - MHD_destroy_post_processor (pp); - *req_cls = NULL; -} - - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -/** - * Note that this post_iterator is not perfect - * in that it fails to support incremental processing. - * (to be fixed in the future) - */ -static enum MHD_Result -post_iterator (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *value, uint64_t off, size_t size) -{ - int *eok = cls; - (void) kind; (void) filename; (void) content_type; /* Unused. Silent compiler warning. */ - (void) transfer_encoding; (void) off; /* Unused. Silent compiler warning. */ - - if ((0 == strcmp (key, "name")) && - (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) - (*eok) |= 1; - if ((0 == strcmp (key, "project")) && - (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) - (*eok) |= 2; - return MHD_YES; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - static int eok; - struct MHD_Response *response; - struct MHD_PostProcessor *pp; - enum MHD_Result ret; - (void) cls; (void) version; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp ("POST", method)) - { - return MHD_NO; /* unexpected method */ - } - pp = *req_cls; - if (pp == NULL) - { - eok = 0; - pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); - *req_cls = pp; - } - MHD_post_process (pp, upload_data, *upload_data_size); - if ((eok == 3) && (0 == *upload_data_size)) - { - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - MHD_destroy_post_processor (pp); - *req_cls = NULL; - return ret; - } - *upload_data_size = 0; - return MHD_YES; -} - - -static unsigned int -testInternalPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); - curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); - curl_easy_setopt (c, CURLOPT_POST, 1L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - - return 0; -} - - -static unsigned int -testMultithreadedPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 16; - - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); - curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); - curl_easy_setopt (c, CURLOPT_POST, 1L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testExternalPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 1082, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 256; - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); - curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); - curl_easy_setopt (c, CURLOPT_POST, 1L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - - - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - - } - fprintf (stderr, "\n"); - curl_multi_cleanup (multi); - zzuf_socat_stop (); - - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; /* Unused. Silent compiler warning. */ - - oneone = (NULL != strrchr (argv[0], (int) '/')) ? - (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalPost (); - errorCount += testMultithreadedPost (); - } - errorCount += testExternalPost (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_post_form.c b/src/testzzuf/test_post_form.c @@ -1,432 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file test_post_form.c - * @brief Testcase for libmicrohttpd POST operations using multipart/postform data - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - - -#include "socat.c" - -static int oneone; - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - - -static void -completed_cb (void *cls, - struct MHD_Connection *connection, - void **req_cls, - enum MHD_RequestTerminationCode toe) -{ - struct MHD_PostProcessor *pp = *req_cls; - (void) cls; (void) connection; (void) toe; /* Unused. Silent compiler warning. */ - - if (NULL != pp) - MHD_destroy_post_processor (pp); - *req_cls = NULL; -} - - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -/** - * Note that this post_iterator is not perfect - * in that it fails to support incremental processing. - * (to be fixed in the future) - */ -static enum MHD_Result -post_iterator (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *value, uint64_t off, size_t size) -{ - int *eok = cls; - (void) kind; (void) filename; (void) content_type; /* Unused. Silent compiler warning. */ - (void) transfer_encoding; (void) off; /* Unused. Silent compiler warning. */ - - if (key == NULL) - return MHD_YES; -#if 0 - fprintf (stderr, "PI sees %s-%.*s\n", key, size, value); -#endif - if ((0 == strcmp (key, "name")) && - (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) - (*eok) |= 1; - if ((0 == strcmp (key, "project")) && - (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) - (*eok) |= 2; - return MHD_YES; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - static int eok; - struct MHD_Response *response; - struct MHD_PostProcessor *pp; - enum MHD_Result ret; - (void) cls; (void) version; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp ("POST", method)) - { - return MHD_NO; /* unexpected method */ - } - pp = *req_cls; - if (pp == NULL) - { - eok = 0; - pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); - if (pp == NULL) - return MHD_NO; - *req_cls = pp; - } - MHD_post_process (pp, upload_data, *upload_data_size); - if ((eok == 3) && (0 == *upload_data_size)) - { - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - MHD_destroy_post_processor (pp); - *req_cls = NULL; - return ret; - } - *upload_data_size = 0; - return MHD_YES; -} - - -static struct curl_httppost * -make_form () -{ - struct curl_httppost *post = NULL; - struct curl_httppost *last = NULL; - - curl_formadd (&post, &last, CURLFORM_COPYNAME, "name", - CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); - curl_formadd (&post, &last, CURLFORM_COPYNAME, "project", - CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); - return post; -} - - -static unsigned int -testInternalPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - struct curl_httppost *pd; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - pd = make_form (); - curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - curl_formfree (pd); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testMultithreadedPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - int i; - struct curl_httppost *pd; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - pd = make_form (); - curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - curl_formfree (pd); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testExternalPost () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - struct curl_httppost *pd; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 1082, NULL, NULL, &ahc_echo, NULL, - MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, - MHD_OPTION_END); - if (d == NULL) - return 256; - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - pd = make_form (); - curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_formfree (pd); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - curl_formfree (pd); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - curl_formfree (pd); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - curl_formfree (pd); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - curl_multi_cleanup (multi); - - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; /* Unused. Silent compiler warning. */ - - oneone = (NULL != strrchr (argv[0], (int) '/')) ? - (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalPost (); - errorCount += testMultithreadedPost (); - } - errorCount += testExternalPost (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_put.c b/src/testzzuf/test_put.c @@ -1,380 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file test_put.c - * @brief Testcase for libmicrohttpd PUT operations - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - - -#include "socat.c" - -static int oneone; - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - -static size_t -putBuffer (void *stream, size_t size, size_t nmemb, void *ptr) -{ - unsigned int *pos = ptr; - unsigned int wrt; - - wrt = size * nmemb; - if (wrt > 8 - (*pos)) - wrt = 8 - (*pos); - memcpy (stream, &("Hello123"[*pos]), wrt); - (*pos) += wrt; - return wrt; -} - - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - int *done = cls; - struct MHD_Response *response; - enum MHD_Result ret; - (void) version; (void) req_cls; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp ("PUT", method)) - return MHD_NO; /* unexpected method */ - if ((*done) == 0) - { - if (*upload_data_size != 8) - return MHD_YES; /* not yet ready */ - if (0 == memcmp (upload_data, "Hello123", 8)) - { - *upload_data_size = 0; - } - else - { - printf ("Invalid upload data `%8s'!\n", upload_data); - return MHD_NO; - } - *done = 1; - return MHD_YES; - } - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return ret; -} - - -static unsigned int -testInternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testMultithreadedPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testExternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - unsigned int pos = 0; - int done_flag = 0; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 256; - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - - - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - } - fprintf (stderr, "\n"); - curl_multi_cleanup (multi); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; /* Unused. Silent compiler warning. */ - - oneone = (NULL != strrchr (argv[0], (int) '/')) ? - (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalPut (); - errorCount += testMultithreadedPut (); - } - errorCount += testExternalPut (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_put_chunked.c b/src/testzzuf/test_put_chunked.c @@ -1,395 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file daemontest_put_chunked.c - * @brief Testcase for libmicrohttpd PUT operations with chunked encoding - * for the upload data - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - -#include "socat.c" - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - -static size_t -putBuffer (void *stream, size_t size, size_t nmemb, void *ptr) -{ - unsigned int *pos = ptr; - unsigned int wrt; - - wrt = size * nmemb; - if (wrt > 8 - (*pos)) - wrt = 8 - (*pos); - if (wrt > 4) - wrt = 4; /* only send half at first => force multiple chunks! */ - memcpy (stream, &("Hello123"[*pos]), wrt); - (*pos) += wrt; - return wrt; -} - - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - int *done = cls; - struct MHD_Response *response; - enum MHD_Result ret; - int have; - (void) version; (void) req_cls; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp ("PUT", method)) - return MHD_NO; /* unexpected method */ - if ((*done) < 8) - { - have = *upload_data_size; - if (have + *done > 8) - { - return MHD_NO; - } - if (0 == have) - { - (void) 0; /* Do nothing - no data yet */ - } - else if (0 == memcmp (upload_data, &"Hello123"[*done], have)) - { - *done += have; - *upload_data_size = 0; - } - else - { - return MHD_NO; - } -#if 0 - fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8); -#endif - return MHD_YES; - } - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return ret; -} - - -static unsigned int -testInternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - done_flag = 0; - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - /* by not giving the file size, we force chunking! */ - /* - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - */ - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testMultithreadedPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - CURLcode errornum; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, - 11081, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 16; - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - /* by not giving the file size, we force chunking! */ - /* - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - */ - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - if (CURLE_OK != (errornum = curl_easy_perform (c))) - { - fprintf (stderr, - "curl_easy_perform failed: `%s'\n", - curl_easy_strerror (errornum)); - curl_easy_cleanup (c); - MHD_stop_daemon (d); - return 32; - } - curl_easy_cleanup (c); - MHD_stop_daemon (d); - if (cbc.pos != strlen ("/hello_world")) - return 64; - if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) - return 128; - - return 0; -} - - -static unsigned int -testExternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - char buf[2048]; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - unsigned int pos = 0; - int done_flag = 0; - int i; - - multi = NULL; - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_ERROR_LOG, - 11082, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 256; - - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - done_flag = 0; - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11082/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - /* by not giving the file size, we force chunking! */ - /* - curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); - */ - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - - - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - } - fprintf (stderr, "\n"); - curl_multi_cleanup (multi); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; (void) argv; /* Unused. Silent compiler warning. */ - - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalPut (); - errorCount += testMultithreadedPut (); - } - errorCount += testExternalPut (); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/test_put_large.c b/src/testzzuf/test_put_large.c @@ -1,403 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2007, 2008 Christian Grothoff - - libmicrohttpd is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your - option) any later version. - - 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with libmicrohttpd; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file test_put_large.c - * @brief Testcase for libmicrohttpd PUT operations - * @author Christian Grothoff - */ - -#include "MHD_config.h" -#include "platform.h" -#include <curl/curl.h> -#include <microhttpd.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#ifndef WINDOWS -#include <unistd.h> -#endif - -#include "socat.c" - -static int oneone; - -/** - * Do not make this much larger since we will hit the - * MHD default buffer limit and the test code is not - * written for incremental upload processing... - */ -#define PUT_SIZE (256 * 1024) - -static char *put_buffer; - -struct CBC -{ - char *buf; - size_t pos; - size_t size; -}; - -static size_t -putBuffer (void *stream, size_t size, size_t nmemb, void *ptr) -{ - unsigned int *pos = ptr; - unsigned int wrt; - - wrt = size * nmemb; - if (wrt > PUT_SIZE - (*pos)) - wrt = PUT_SIZE - (*pos); - memcpy (stream, &put_buffer[*pos], wrt); - (*pos) += wrt; - return wrt; -} - - -static size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) -{ - struct CBC *cbc = ctx; - - if (cbc->pos + size * nmemb > cbc->size) - return 0; /* overflow */ - memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); - cbc->pos += size * nmemb; - return size * nmemb; -} - - -static enum MHD_Result -ahc_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, size_t *upload_data_size, - void **req_cls) -{ - int *done = cls; - struct MHD_Response *response; - enum MHD_Result ret; - (void) version; (void) req_cls; /* Unused. Silent compiler warning. */ - - if (NULL == url) - fprintf (stderr, "The \"url\" parameter is NULL.\n"); - if (NULL == method) - fprintf (stderr, "The \"method\" parameter is NULL.\n"); - if (NULL == version) - fprintf (stderr, "The \"version\" parameter is NULL.\n"); - if (NULL == upload_data_size) - fprintf (stderr, "The \"upload_data_size\" parameter is NULL.\n"); - if ((0 != *upload_data_size) && (NULL == upload_data)) - fprintf (stderr, "Upload data is NULL with non-zero size.\n"); - if (0 != strcmp ("PUT", method)) - return MHD_NO; /* unexpected method */ - if ((*done) == 0) - { - if (*upload_data_size != PUT_SIZE) - { -#if 0 - fprintf (stderr, - "Waiting for more data (%u/%u)...\n", - *upload_data_size, PUT_SIZE); -#endif - return MHD_YES; /* not yet ready */ - } - if (0 == memcmp (upload_data, put_buffer, PUT_SIZE)) - { - *upload_data_size = 0; - } - else - { - return MHD_NO; - } - *done = 1; - return MHD_YES; - } - response = MHD_create_response_from_buffer (strlen (url), - (void *) url, - MHD_RESPMEM_MUST_COPY); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return ret; -} - - -static unsigned int -testInternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - char buf[2048]; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = - MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 1; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testMultithreadedPut () -{ - struct MHD_Daemon *d; - CURL *c; - struct CBC cbc; - unsigned int pos = 0; - int done_flag = 0; - char buf[2048]; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_INTERNAL_POLLING_THREAD /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); - if (d == NULL) - return 16; - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - curl_easy_perform (c); - curl_easy_cleanup (c); - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 0; -} - - -static unsigned int -testExternalPut () -{ - struct MHD_Daemon *d; - CURL *c; - struct CBC cbc; - CURLM *multi; - CURLMcode mret; - fd_set rs; - fd_set ws; - fd_set es; - int max; - int running; - time_t start; - struct timeval tv; - unsigned int pos = 0; - int done_flag = 0; - char buf[2048]; - int i; - - cbc.buf = buf; - cbc.size = 2048; - cbc.pos = 0; - multi = NULL; - d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_ERROR_LOG */, - 11080, - NULL, NULL, &ahc_echo, &done_flag, - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (PUT_SIZE * 4), MHD_OPTION_END); - if (d == NULL) - return 256; - multi = curl_multi_init (); - if (multi == NULL) - { - MHD_stop_daemon (d); - return 512; - } - zzuf_socat_start (); - for (i = 0; i < LOOP_COUNT; i++) - { - fprintf (stderr, "."); - - c = curl_easy_init (); - curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world"); - curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); - curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); - curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); - curl_easy_setopt (c, CURLOPT_READDATA, &pos); - curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); - curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE); - curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT); - if (oneone) - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - else - curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT); - /* NOTE: use of CONNECTTIMEOUT without also - * setting NOSIGNAL results in really weird - * crashes on my system! */ - curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); - - - mret = curl_multi_add_handle (multi, c); - if (mret != CURLM_OK) - { - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 1024; - } - start = time (NULL); - while ((time (NULL) - start < 5) && (c != NULL)) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - curl_multi_perform (multi, &running); - mret = curl_multi_fdset (multi, &rs, &ws, &es, &max); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 2048; - } - if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max)) - { - curl_multi_remove_handle (multi, c); - curl_multi_cleanup (multi); - curl_easy_cleanup (c); - zzuf_socat_stop (); - MHD_stop_daemon (d); - return 4096; - } - tv.tv_sec = 0; - tv.tv_usec = 1000; - select (max + 1, &rs, &ws, &es, &tv); - curl_multi_perform (multi, &running); - if (running == 0) - { - curl_multi_info_read (multi, &running); - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - c = NULL; - } - MHD_run (d); - } - if (c != NULL) - { - curl_multi_remove_handle (multi, c); - curl_easy_cleanup (c); - } - } - fprintf (stderr, "\n"); - zzuf_socat_stop (); - curl_multi_cleanup (multi); - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, char *const *argv) -{ - unsigned int errorCount = 0; - (void) argc; /* Unused. Silent compiler warning. */ - - oneone = (NULL != strrchr (argv[0], (int) '/')) ? - (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0; - if (0 != curl_global_init (CURL_GLOBAL_WIN32)) - return 2; - put_buffer = malloc (PUT_SIZE); - if (0 == put_buffer) - return 77; - memset (put_buffer, 1, PUT_SIZE); - if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) - { - errorCount += testInternalPut (); - errorCount += testMultithreadedPut (); - } - errorCount += testExternalPut (); - free (put_buffer); - if (errorCount != 0) - fprintf (stderr, "Error (code: %u)\n", errorCount); - curl_global_cleanup (); - return (0 == errorCount) ? 0 : 1; /* 0 == pass */ -} diff --git a/src/testzzuf/zzuf_test_runner.sh b/src/testzzuf/zzuf_test_runner.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +mhd_listen_ip='127.0.0.1' +max_runtime_sec='300' + +if test "x${ZZUF}" = "xno" ; then + echo "zzuf command missing" 1>&2 + exit 77 +fi + +if command -v "${ZZUF}" > /dev/null 2>&1 ; then : ; else + echo "zzuf command missing" 1>&2 + exit 77 +fi + +# zzuf cannot pass-through the return value of checked program +# so try the direct dry-run first to get possibe 77 or 99 codes +echo "## Dry-run of the $@..." +if "$@" --dry-run ; then + echo "# Dry-run succeded." +else + res_code=$? + echo "Dry-run failed with exit code $res_code. $@ will not be run with zzuf." 1>&2 + exit $res_code +fi + +# fuzz the input only for IP ${mhd_listen_ip}. libcurl uses another IP +# in this test therefore libcurl input is not fuzzed. +zzuf_all_params="--ratio=0.001:0.4 --autoinc --verbose --signal \ + --max-usertime=${max_runtime_sec} --check-exit --network \ + --allow=${mhd_listen_ip} --exclude=." + +if test -n "${ZZUF_SEED}" ; then + zzuf_all_params="${zzuf_all_params} --seed=${ZZUF_SEED}" +fi + +if test -n "${ZZUF_FLAGS}" ; then + zzuf_all_params="${zzuf_all_params} ${ZZUF_FLAGS}" +fi + +# Uncomment the next line to see more data in logs +#zzuf_all_params="${zzuf_all_params} -dd" + +echo "## Dry-run of the $@ with zzuf..." +if "$ZZUF" ${zzuf_all_params} "$@" --dry-run ; then + echo "# Dry-run with zzuf succeded." +else + res_code=$? + echo "$@ cannot be run with zzuf. The test is skipped." 1>&2 + exit 77 +fi + +echo "## Real test of $@ with zzuf..." +"$ZZUF" ${zzuf_all_params} "$@" +exit $?