Merge branch 'fetcher-response-code'

Extends the fetcher API to retrieve the response status code for a request.
This commit is contained in:
Tobias Brunner
2014-05-19 14:32:53 +02:00
9 changed files with 158 additions and 25 deletions
+6
View File
@@ -81,6 +81,12 @@ enum fetcher_option_t {
*/ */
FETCH_TIMEOUT, FETCH_TIMEOUT,
/**
* Sets a pointer to a variable that receives the request's response code.
* Additional argument is a u_int*.
*/
FETCH_RESPONSE_CODE,
/** /**
* Callback to invoke with each chunk of data. * Callback to invoke with each chunk of data.
* Additional argument fetch_callback_t. * Additional argument fetch_callback_t.
@@ -113,6 +113,10 @@ METHOD(fetcher_manager_t, fetch, status_t,
good = fetcher->set_option(fetcher, opt, good = fetcher->set_option(fetcher, opt,
va_arg(args, fetcher_callback_t)); va_arg(args, fetcher_callback_t));
continue; continue;
case FETCH_RESPONSE_CODE:
good = fetcher->set_option(fetcher, opt,
va_arg(args, u_int*));
continue;
case FETCH_SOURCEIP: case FETCH_SOURCEIP:
host = va_arg(args, host_t*); host = va_arg(args, host_t*);
if (host && !host->is_anyaddr(host)) if (host && !host->is_anyaddr(host))
+27 -1
View File
@@ -49,6 +49,11 @@ struct private_curl_fetcher_t {
*/ */
fetcher_callback_t cb; fetcher_callback_t cb;
/**
* Variable that receives the response code
*/
u_int *result;
/** /**
* Timeout for a transfer * Timeout for a transfer
*/ */
@@ -82,6 +87,7 @@ METHOD(fetcher_t, fetch, status_t,
{ {
char error[CURL_ERROR_SIZE], *enc_uri; char error[CURL_ERROR_SIZE], *enc_uri;
status_t status; status_t status;
long result = 0;
cb_data_t data = { cb_data_t data = {
.cb = this->cb, .cb = this->cb,
.user = userdata, .user = userdata,
@@ -123,10 +129,25 @@ METHOD(fetcher_t, fetch, status_t,
status = NOT_SUPPORTED; status = NOT_SUPPORTED;
break; break;
case CURLE_OK: case CURLE_OK:
if (this->result)
{
curl_easy_getinfo(this->curl, CURLINFO_RESPONSE_CODE,
&result);
*this->result = result;
}
status = SUCCESS; status = SUCCESS;
break; break;
default: default:
DBG1(DBG_LIB, "libcurl http request failed: %s", error); if (this->result)
{ /* don't log an error in this case */
curl_easy_getinfo(this->curl, CURLINFO_RESPONSE_CODE,
&result);
*this->result = result;
}
else
{
DBG1(DBG_LIB, "libcurl http request failed: %s", error);
}
status = FAILED; status = FAILED;
break; break;
} }
@@ -188,6 +209,11 @@ METHOD(fetcher_t, set_option, bool,
this->cb = va_arg(args, fetcher_callback_t); this->cb = va_arg(args, fetcher_callback_t);
break; break;
} }
case FETCH_RESPONSE_CODE:
{
this->result = va_arg(args, u_int*);
break;
}
case FETCH_SOURCEIP: case FETCH_SOURCEIP:
{ {
char buf[64]; char buf[64];
+14 -2
View File
@@ -63,6 +63,11 @@ struct private_soup_fetcher_t {
* Fetcher callback function * Fetcher callback function
*/ */
fetcher_callback_t cb; fetcher_callback_t cb;
/**
* Response status
*/
u_int *result;
}; };
/** /**
@@ -119,12 +124,16 @@ METHOD(fetcher_t, fetch, status_t,
DBG2(DBG_LIB, "sending http request to '%s'...", uri); DBG2(DBG_LIB, "sending http request to '%s'...", uri);
soup_session_send_message(data.session, message); soup_session_send_message(data.session, message);
if (this->result)
{
*this->result = message->status_code;
}
if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code)) if (SOUP_STATUS_IS_SUCCESSFUL(message->status_code))
{ {
status = SUCCESS; status = SUCCESS;
} }
else else if (!this->result)
{ { /* only log an error if the code is not returned */
DBG1(DBG_LIB, "HTTP request failed: %s", message->reason_phrase); DBG1(DBG_LIB, "HTTP request failed: %s", message->reason_phrase);
} }
g_object_unref(G_OBJECT(message)); g_object_unref(G_OBJECT(message));
@@ -157,6 +166,9 @@ METHOD(fetcher_t, set_option, bool,
case FETCH_CALLBACK: case FETCH_CALLBACK:
this->cb = va_arg(args, fetcher_callback_t); this->cb = va_arg(args, fetcher_callback_t);
break; break;
case FETCH_RESPONSE_CODE:
this->result = va_arg(args, u_int*);
break;
default: default:
supported = FALSE; supported = FALSE;
break; break;
+1 -1
View File
@@ -52,7 +52,7 @@ tests_SOURCES = tests.h tests.c \
tests_CFLAGS = \ tests_CFLAGS = \
-I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libstrongswan/tests \ -I$(top_srcdir)/src/libstrongswan/tests \
-DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \ -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \
-DPLUGINS=\""${s_plugins}\"" \ -DPLUGINS=\""${s_plugins}\"" \
@COVERAGE_CFLAGS@ @COVERAGE_CFLAGS@
@@ -18,6 +18,8 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#define HTTP_SUCCESS(status) ((status) >= 200 && (status) < 300)
/** /**
* HTTP test definition * HTTP test definition
*/ */
@@ -42,6 +44,8 @@ typedef struct {
void *res; void *res;
/* length of response data */ /* length of response data */
int res_len; int res_len;
/* status code, defaults to 200 */
u_int code;
} test_service_t; } test_service_t;
static char large[] = { static char large[] = {
@@ -147,40 +151,50 @@ static bool servicing(void *data, stream_t *stream)
ck_assert(memeq(body, test->req, test->req_len)); ck_assert(memeq(body, test->req, test->req_len));
} }
if (!test->code)
{
test->code = 200;
}
/* response headers */ /* response headers */
snprintf(buf, sizeof(buf), "HTTP/1.%u 200 OK\r\n", test->minor); snprintf(buf, sizeof(buf), "HTTP/1.%u %u OK\r\n", test->minor, test->code);
ck_assert(stream->write_all(stream, buf, strlen(buf))); ck_assert(stream->write_all(stream, buf, strlen(buf)));
/* if the response code indicates an error the following write operations
* might fail because the client already terminated the TCP connection */
#define may_fail(test, op) ck_assert(op || !HTTP_SUCCESS(test->code))
t = time(NULL); t = time(NULL);
gmtime_r(&t, &tm); gmtime_r(&t, &tm);
strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &tm); strftime(buf, sizeof(buf), "%a, %d %b %Y %T %z", &tm);
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
snprintf(buf, sizeof(buf), "Server: strongSwan unit test\r\n"); snprintf(buf, sizeof(buf), "Server: strongSwan unit test\r\n");
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
/* rest of response headers */ /* rest of response headers */
snprintf(buf, sizeof(buf), "Content-Type: text/plain\r\n"); snprintf(buf, sizeof(buf), "Content-Type: text/plain\r\n");
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
snprintf(buf, sizeof(buf), "Content-Length: %u\r\n", test->res_len); snprintf(buf, sizeof(buf), "Content-Length: %u\r\n", test->res_len);
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
snprintf(buf, sizeof(buf), "Connection: close\r\n"); snprintf(buf, sizeof(buf), "Connection: close\r\n");
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
snprintf(buf, sizeof(buf), "\r\n"); snprintf(buf, sizeof(buf), "\r\n");
ck_assert(stream->write_all(stream, buf, strlen(buf))); may_fail(test, stream->write_all(stream, buf, strlen(buf)));
/* response body */ /* response body */
ck_assert(stream->write_all(stream, test->res, test->res_len)); may_fail(test, stream->write_all(stream, test->res, test->res_len));
return FALSE; return FALSE;
} }
static test_service_t gtests[] = { static test_service_t gtests[] = {
{ "GET", 1, "127.0.0.1", 6543, "/a/test/?b=c", NULL, { "GET", 1, "127.0.0.1", 6543, "/a/test/?b=c", NULL,
NULL, 0, "\x12\x34", 2 }, NULL, 0, "\x12\x34", 2, 0 },
{ "GET", 0, "localhost", 6543, "/", NULL, { "GET", 0, "localhost", 6543, "/", NULL,
NULL, 0, NULL, 0 }, NULL, 0, NULL, 0, 0 },
{ "GET", 0, "127.0.0.1", 6543, "/largefile", NULL, { "GET", 0, "127.0.0.1", 6543, "/largefile", NULL,
NULL, 0, large, sizeof(large) }, NULL, 0, large, sizeof(large), 0 },
{ "GET", 1, "[::1]", 6543, "/ipv6-url", NULL, { "GET", 1, "[::1]", 6543, "/ipv6-url", NULL,
NULL, 0, "\x00\r\n\r\x00testdatablabla", 20 }, NULL, 0, "\x00\r\n\r\x00testdatablabla", 20, 0 },
}; };
START_TEST(test_get) START_TEST(test_get)
@@ -215,11 +229,11 @@ END_TEST
static test_service_t ptests[] = { static test_service_t ptests[] = {
{ "POST", 1, "127.0.0.1", 6543, "/a/test/?b=c", "application/binary", { "POST", 1, "127.0.0.1", 6543, "/a/test/?b=c", "application/binary",
"\x23\x45", 2, "\x12\x34", 2 }, "\x23\x45", 2, "\x12\x34", 2, 0 },
{ "POST", 0, "localhost", 6543, "/largefile", "application/x-large", { "POST", 0, "localhost", 6543, "/largefile", "application/x-large",
large, sizeof(large), large, sizeof(large) }, large, sizeof(large), large, sizeof(large), 0 },
{ "POST", 1, "[::1]", 6543, "/ipv6-url", "text/plain", { "POST", 1, "[::1]", 6543, "/ipv6-url", "text/plain",
"\x00\r\n\r\x00testdatablabla", 20, "\x00\r\n\r\x00testdatablabla", 20 }, "\x00\r\n\r\x00testdatablabla", 20, "\x00\r\n\r\x00testdatablabla", 20, 0 },
}; };
START_TEST(test_post) START_TEST(test_post)
@@ -254,6 +268,42 @@ START_TEST(test_post)
} }
END_TEST END_TEST
static test_service_t rtests[] = {
{ "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 200 },
{ "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 204 },
{ "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 400 },
{ "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 404 },
{ "GET", 1, "localhost", 6544, "/", NULL, NULL, 0, NULL, 0, 500 },
};
START_TEST(test_response_code)
{
stream_service_t *service;
status_t status;
chunk_t data;
char uri[256];
u_int code;
lib->processor->set_threads(lib->processor, 8);
snprintf(uri, sizeof(uri), "tcp://%s:%u", rtests[_i].host, rtests[_i].port);
service = lib->streams->create_service(lib->streams, uri, 1);
ck_assert(service != NULL);
service->on_accept(service, servicing, &rtests[_i], JOB_PRIO_HIGH, 0);
snprintf(uri, sizeof(uri), "http://%s:%u%s",
rtests[_i].host, rtests[_i].port, rtests[_i].path);
status = lib->fetcher->fetch(lib->fetcher, uri, &data,
FETCH_RESPONSE_CODE, &code, FETCH_END);
ck_assert_int_eq(status, HTTP_SUCCESS(rtests[_i].code) ? SUCCESS : FAILED);
ck_assert_int_eq(code, rtests[_i].code);
free(data.ptr);
service->destroy(service);
}
END_TEST
Suite *fetch_http_suite_create() Suite *fetch_http_suite_create()
{ {
Suite *s; Suite *s;
@@ -269,5 +319,9 @@ Suite *fetch_http_suite_create()
tcase_add_loop_test(tc, test_post, 0, countof(ptests)); tcase_add_loop_test(tc, test_post, 0, countof(ptests));
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("response code");
tcase_add_loop_test(tc, test_response_code, 0, countof(rtests));
suite_add_tcase(s, tc);
return s; return s;
} }
+21 -2
View File
@@ -40,6 +40,11 @@ static int failure_line;
*/ */
static backtrace_t *failure_backtrace; static backtrace_t *failure_backtrace;
/**
* Flag to indicate if a worker thread failed
*/
static bool worker_failed;
/** /**
* Longjump restore point when failing * Longjump restore point when failing
*/ */
@@ -169,6 +174,17 @@ void test_fail_msg(const char *file, int line, char *fmt, ...)
test_failure(); test_failure();
} }
/**
* See header.
*/
void test_fail_if_worker_failed()
{
if (pthread_self() == main_thread && worker_failed)
{
test_failure();
}
}
/** /**
* Signal handler catching critical and alarm signals * Signal handler catching critical and alarm signals
*/ */
@@ -180,8 +196,9 @@ static void test_sighandler(int signal)
switch (signal) switch (signal)
{ {
case SIGUSR1: case SIGUSR1:
/* a different thread failed, abort test */ /* a different thread failed, abort test at the next opportunity */
return test_failure(); worker_failed = TRUE;
return;
case SIGSEGV: case SIGSEGV:
signame = "SIGSEGV"; signame = "SIGSEGV";
break; break;
@@ -251,6 +268,8 @@ void test_setup_timeout(int s)
sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR1, &action, NULL);
alarm(s); alarm(s);
worker_failed = FALSE;
} }
/** /**
+15 -3
View File
@@ -236,6 +236,12 @@ void test_fail_vmsg(const char *file, int line, char *fmt, va_list args);
*/ */
void test_fail_msg(const char *file, int line, char *fmt, ...); void test_fail_msg(const char *file, int line, char *fmt, ...);
/**
* Let a test fail if one of the worker threads has failed (only if called from
* the main thread).
*/
void test_fail_if_worker_failed();
/** /**
* Check if two integers equal, fail test if not * Check if two integers equal, fail test if not
* *
@@ -246,6 +252,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
({ \ ({ \
typeof(a) _a = a; \ typeof(a) _a = a; \
typeof(b) _b = b; \ typeof(b) _b = b; \
test_fail_if_worker_failed(); \
if (_a != _b) \ if (_a != _b) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, #a " != " #b " (%d != %d)", _a, _b); \ test_fail_msg(__FILE__, __LINE__, #a " != " #b " (%d != %d)", _a, _b); \
@@ -262,6 +269,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
({ \ ({ \
char* _a = (char*)a; \ char* _a = (char*)a; \
char* _b = (char*)b; \ char* _b = (char*)b; \
test_fail_if_worker_failed(); \
if (!_a || !_b || !streq(_a, _b)) \ if (!_a || !_b || !streq(_a, _b)) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, \ test_fail_msg(__FILE__, __LINE__, \
@@ -279,6 +287,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
({ \ ({ \
chunk_t _a = (chunk_t)a; \ chunk_t _a = (chunk_t)a; \
chunk_t _b = (chunk_t)b; \ chunk_t _b = (chunk_t)b; \
test_fail_if_worker_failed(); \
if (_a.len != _b.len || !memeq(a.ptr, b.ptr, a.len)) \ if (_a.len != _b.len || !memeq(a.ptr, b.ptr, a.len)) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, \ test_fail_msg(__FILE__, __LINE__, \
@@ -293,6 +302,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
*/ */
#define test_assert(x) \ #define test_assert(x) \
({ \ ({ \
test_fail_if_worker_failed(); \
if (!(x)) \ if (!(x)) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, #x); \ test_fail_msg(__FILE__, __LINE__, #x); \
@@ -308,6 +318,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
*/ */
#define test_assert_msg(x, fmt, ...) \ #define test_assert_msg(x, fmt, ...) \
({ \ ({ \
test_fail_if_worker_failed(); \
if (!(x)) \ if (!(x)) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \ test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \
@@ -327,6 +338,7 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
#define fail(fmt, ...) test_fail_msg(__FILE__, __LINE__, fmt, ##__VA_ARGS__) #define fail(fmt, ...) test_fail_msg(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fail_if(x, fmt, ...) \ #define fail_if(x, fmt, ...) \
({ \ ({ \
test_fail_if_worker_failed(); \
if (x) \ if (x) \
{ \ { \
test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \ test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \
@@ -341,10 +353,10 @@ void test_fail_msg(const char *file, int line, char *fmt, ...);
#define tcase_set_timeout test_case_set_timeout #define tcase_set_timeout test_case_set_timeout
#define suite_add_tcase test_suite_add_case #define suite_add_tcase test_suite_add_case
#define START_TEST(name) static void name (int _i) { #define START_TEST(name) static void name (int _i) {
#define END_TEST } #define END_TEST test_fail_if_worker_failed(); }
#define START_SETUP(name) static void name() { #define START_SETUP(name) static void name() {
#define END_SETUP } #define END_SETUP test_fail_if_worker_failed(); }
#define START_TEARDOWN(name) static void name() { #define START_TEARDOWN(name) static void name() {
#define END_TEARDOWN } #define END_TEARDOWN test_fail_if_worker_failed(); }
#endif /** TEST_SUITE_H_ @}*/ #endif /** TEST_SUITE_H_ @}*/
+1 -1
View File
@@ -11,7 +11,7 @@ tls_tests_CFLAGS = \
-I$(top_srcdir)/src/libtls \ -I$(top_srcdir)/src/libtls \
-I$(top_srcdir)/src/libstrongswan \ -I$(top_srcdir)/src/libstrongswan \
-I$(top_srcdir)/src/libstrongswan/tests \ -I$(top_srcdir)/src/libstrongswan/tests \
-DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \ -DPLUGINDIR=\""$(abs_top_builddir)/src/libstrongswan/plugins\"" \
-DPLUGINS=\""${s_plugins}\"" \ -DPLUGINS=\""${s_plugins}\"" \
@COVERAGE_CFLAGS@ @COVERAGE_CFLAGS@