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
This commit is contained in:
Evgeny Grin (Karlson2k)
2023-06-20 23:22:54 +03:00
parent 87d40361d3
commit 40b675518b
14 changed files with 1771 additions and 3094 deletions
+45 -41
View File
@@ -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)
+76 -47
View File
@@ -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
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_chunked.c
test_get_chunked_SOURCES = $(test_get_SOURCES)
test_post_SOURCES = \
test_post.c
test_post_SOURCES = $(test_get_SOURCES)
test_post_form_SOURCES = \
test_post_form.c
test_post_form_SOURCES = $(test_get_SOURCES)
test_put_SOURCES = \
test_put.c
test_put_SOURCES = $(test_get_SOURCES)
test_put_chunked_SOURCES = \
test_put_chunked.c
test_put_chunked_SOURCES = $(test_get_SOURCES)
test_put_large_SOURCES = \
test_put_large.c
test_put_large_SOURCES = $(test_get_SOURCES)
test_get_long_uri_SOURCES = $(test_get_SOURCES)
test_get_long_header_SOURCES = $(test_get_SOURCES)
test_get11_SOURCES = \
test_get.c
test_get_close_SOURCES = $(test_get_SOURCES)
test_post11_SOURCES = \
test_post.c
test_get_chunked_close_SOURCES = $(test_get_chunked_SOURCES)
test_post_form11_SOURCES = \
test_post_form.c
test_post_close_SOURCES = $(test_post_SOURCES)
test_put11_SOURCES = \
test_put.c
test_post_form_close_SOURCES = $(test_post_form_SOURCES)
test_put_large11_SOURCES = \
test_put_large.c
test_put_close_SOURCES = $(test_put_SOURCES)
test_long_header_SOURCES = \
test_long_header.c
test_put_chunked_close_SOURCES = $(test_put_chunked_SOURCES)
test_put_large_close_SOURCES = $(test_put_large_SOURCES)
test_get_long_uri_close_SOURCES = $(test_get_long_uri_SOURCES)
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_post10_SOURCES = $(test_post_SOURCES)
test_post_form10_SOURCES = $(test_post_form_SOURCES)
test_put10_SOURCES = $(test_put_SOURCES)
test_put_large10_SOURCES = $(test_put_large_SOURCES)
test_get_long_uri10_SOURCES = $(test_get_long_uri_SOURCES)
test_get_long_header10_SOURCES = $(test_get_long_header_SOURCES)
+45
View File
@@ -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;
}
+10
View File
@@ -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 */
-117
View File
@@ -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 */
+1540 -234
View File
@@ -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;
}
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)
#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
{
static int ptr;
const char *me = cls;
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
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)
{
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 == 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 (&ptr != *req_cls)
if (NULL == cls)
{
*req_cls = &ptr;
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 "
"at line %d.\n", (int) __LINE__);
param->err_flag = 1;
}
if (NULL == method)
{
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 "
"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 "
"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, "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 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 */
)
{
CURL *c;
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
return c; /* Success exit point */
}
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
testInternalPolling (uint16_t *pport, unsigned int daemon_flags)
{
struct MHD_Daemon *d;
CURL *c;
char buf[2048];
struct CBC cbc;
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 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");
MHD_stop_daemon (d);
fflush (stderr);
return ret;
}
static unsigned int
testInternalGet ()
testExternalPolling (uint16_t *pport, unsigned int daemon_flags)
{
struct MHD_Daemon *d;
CURL *c;
CURLM *multi;
char buf[2048];
struct CBC cbc;
int i;
struct ahc_param_strct callback_param;
unsigned int ret;
#ifndef TEST_USE_STATIC_POST_DATA
curl_mime *mime;
#endif /* ! TEST_USE_STATIC_POST_DATA */
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 (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 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);
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);
}
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);
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
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;
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)
int i;
for (i = dry_run ? LOOP_COUNT : 0;
(i < LOOP_COUNT) && (0 == ret); i++)
{
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
zzuf_socat_stop ();
MHD_stop_daemon (d);
return 2048;
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);
}
}
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);
}
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 = (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))
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");
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 */
}
-364
View File
@@ -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 */
}
-265
View File
@@ -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 */
}
-416
View File
@@ -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 */
}
-432
View File
@@ -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 */
}
-380
View File
@@ -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 */
}
-395
View File
@@ -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 */
}
-403
View File
@@ -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 */
}
+55
View File
@@ -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 $?