socat-ing get'

'
This commit is contained in:
Christian Grothoff
2008-04-13 02:28:23 +00:00
parent ec8aedc77b
commit 2369ddff27
4 changed files with 258 additions and 154 deletions
+2
View File
@@ -2,6 +2,8 @@ SUBDIRS = .
INCLUDES = -I$(top_srcdir)/src/include
EXTRA_DIST = README socat.c
check_PROGRAMS = \
daemontest_get \
daemontest_post \
+13
View File
@@ -0,0 +1,13 @@
Testcases in this directory require zzuf and socat.
zzuf is used to randomly mess with the TCP connection between the CURL
clients and the MHD server. The goal is to expose problems in MHD's
error handling (by introducing random syntax errors). socat is
used to listen on port 11081 and forward the randomzied stream to
port 11080 where MHD is waiting.
As a result, the testcases in this directory do NOT check that
whatever CURL returns is what was expected -- random modifications to
the TCP stream can have random effects ;-). Testcases "fail" if the
code crashes or hangs indefinitely.
+144 -154
View File
@@ -1,6 +1,6 @@
/*
This file is part of libmicrohttpd
(C) 2007 Christian Grothoff
(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
@@ -21,7 +21,6 @@
/**
* @file daemontest_get.c
* @brief Testcase for libmicrohttpd GET operations
* TODO: test parsing of query
* @author Christian Grothoff
*/
@@ -36,6 +35,18 @@
#include <unistd.h>
#endif
#include "socat.c"
/**
* 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.
*/
#define LOOP_COUNT 10
#define CURL_TIMEOUT 50L
static int oneone;
struct CBC
@@ -96,45 +107,39 @@ testInternalGet ()
CURL *c;
char buf[2048];
struct CBC cbc;
CURLcode errornum;
int i;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */,
11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
if (d == NULL)
return 1;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
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, 1);
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 2;
}
curl_easy_cleanup (c);
zzuf_socat_start();
for (i=0;i<LOOP_COUNT;i++) {
fprintf(stderr, ".");
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
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, 1);
curl_easy_perform (c);
curl_easy_cleanup (c);
}
fprintf(stderr, "\n");
zzuf_socat_stop();
MHD_stop_daemon (d);
if (cbc.pos != strlen ("/hello_world"))
return 4;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 8;
return 0;
}
@@ -145,45 +150,39 @@ testMultithreadedGet ()
CURL *c;
char buf[2048];
struct CBC cbc;
CURLcode errornum;
int i;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */,
11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
if (d == NULL)
return 16;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
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, 1);
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);
zzuf_socat_start();
for (i=0;i<LOOP_COUNT;i++) {
fprintf(stderr, ".");
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
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, 1);
curl_easy_perform (c);
curl_easy_cleanup (c);
}
fprintf(stderr, "\n");
zzuf_socat_stop();
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;
}
@@ -202,35 +201,18 @@ testExternalGet ()
fd_set es;
int max;
int running;
struct CURLMsg *msg;
time_t start;
struct timeval tv;
int i;
multi = NULL;
cbc.buf = buf;
cbc.size = 2048;
cbc.pos = 0;
d = MHD_start_daemon (MHD_USE_DEBUG,
1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG*/,
11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
if (d == NULL)
return 256;
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
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_TIMEOUT, 150L);
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, 1);
multi = curl_multi_init ();
if (multi == NULL)
{
@@ -238,75 +220,83 @@ testExternalGet ()
MHD_stop_daemon (d);
return 512;
}
mret = curl_multi_add_handle (multi, c);
if (mret != CURLM_OK)
{
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
MHD_stop_daemon (d);
return 1024;
}
start = time (NULL);
while ((time (NULL) - start < 5) && (multi != 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);
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);
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)
{
msg = curl_multi_info_read (multi, &running);
if (msg == NULL)
break;
if (msg->msg == CURLMSG_DONE)
{
if (msg->data.result != CURLE_OK)
printf ("%s failed at %s:%d: `%s'\n",
"curl_multi_perform",
__FILE__,
__LINE__, curl_easy_strerror (msg->data.result));
curl_multi_remove_handle (multi, c);
curl_multi_cleanup (multi);
curl_easy_cleanup (c);
c = NULL;
multi = NULL;
}
}
MHD_run (d);
}
if (multi != NULL)
{
curl_multi_remove_handle (multi, c);
curl_easy_cleanup (c);
curl_multi_cleanup (multi);
}
zzuf_socat_start();
for (i=0;i<LOOP_COUNT;i++) {
fprintf(stderr, ".");
c = curl_easy_init ();
curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
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_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, 1);
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);
if (cbc.pos != strlen ("/hello_world"))
return 8192;
if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
return 16384;
return 0;
}
@@ -322,7 +312,7 @@ main (int argc, char *const *argv)
return 2;
errorCount += testInternalGet ();
errorCount += testMultithreadedGet ();
errorCount += testExternalGet ();
errorCount += testExternalGet ();
if (errorCount != 0)
fprintf (stderr, "Error (code: %u)\n", errorCount);
curl_global_cleanup ();
+99
View File
@@ -0,0 +1,99 @@
/*
This file is part of libmicrohttpd
(C) 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., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, 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>
static pid_t zzuf_pid;
static void
zzuf_socat_start() {
int status;
char * const args[] = {
"zzuf",
"--ratio=0.0:0.75",
"-n",
"-A",
"socat",
"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)
{
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;
}
setpgrp();
execvp("zzuf",
args);
fprintf(stderr,
"execution of `zzuf' failed: %s\n",
strerror(errno));
zzuf_pid = 0; /* fork failed */
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);
sleep(1); /* allow socat to also die in peace */
}
}
/* end of socat.c */