static size_t read_server_partial = 0;
static size_t read_client_partial = 0;
static unsigned int test_max_pending = 200;
+static unsigned int client_ioloop_nesting = 0;
static struct ip_addr bind_ip;
static in_port_t bind_port = 0;
static int fd_listen = -1;
static pid_t server_pid = (pid_t)-1;
+static struct ioloop *ioloop_nested = NULL;
+static unsigned ioloop_nested_first = 0;
+static unsigned ioloop_nested_last = 0;
/*
* Test files
*/
struct test_client_request {
+ struct test_client_request *prev, *next;
+ struct http_client_request *hreq;
+
struct io *io;
struct istream *payload;
struct istream *file;
};
static struct http_client *http_client;
+static struct test_client_request *client_requests;
static unsigned int client_files_first, client_files_last;
+static struct test_client_request *
+test_client_request_new(void)
+{
+ struct test_client_request *tcreq;
+
+ tcreq = i_new(struct test_client_request, 1);
+ DLLIST_PREPEND(&client_requests, tcreq);
+
+ return tcreq;
+}
+
static void
test_client_request_destroy(void *context)
{
i_stream_unref(&tcreq->payload);
if (tcreq->file != NULL)
i_stream_unref(&tcreq->file);
+
+ DLLIST_REMOVE(&client_requests, tcreq);
i_free(tcreq);
}
+static void
+test_client_requests_switch_ioloop(void)
+{
+ struct test_client_request *tcreq;
+
+ for (tcreq = client_requests; tcreq != NULL;
+ tcreq = tcreq->next) {
+ if (tcreq->io != NULL)
+ tcreq->io = io_loop_move_io(&tcreq->io);
+ if (tcreq->payload != NULL)
+ i_stream_switch_ioloop(tcreq->payload);
+ }
+}
+
/* download */
static void test_client_download_continue(void);
static void
-test_client_download_finished(struct test_client_request *tcreq)
+test_client_download_finished(unsigned int files_idx)
{
const char **paths;
unsigned int count;
paths = array_get_modifiable(&files, &count);
- i_assert(tcreq->files_idx < count);
+ i_assert(files_idx < count);
i_assert(client_files_first < count);
- i_assert(paths[tcreq->files_idx] != NULL);
+ i_assert(paths[files_idx] != NULL);
- paths[tcreq->files_idx] = NULL;
+ paths[files_idx] = NULL;
test_client_download_continue();
}
struct istream *payload = tcreq->payload;
const unsigned char *pdata, *fdata;
size_t psize, fsize, pleft;
+ unsigned int files_idx = tcreq->files_idx;
off_t ret;
/* read payload */
tcreq->files_idx);
}
- /* finished */
- test_client_download_finished(tcreq);
-
/* dereference payload stream; finishes the request */
tcreq->payload = NULL;
io_remove(&tcreq->io); /* holds a reference too */
i_stream_unref(&payload);
+
+ /* finished */
+ test_client_download_finished(files_idx);
}
}
path, resp->status, resp->reason);
}
i_stream_unref(&fstream);
- test_client_download_finished(tcreq);
+ test_client_download_finished(tcreq->files_idx);
return;
}
path, tcreq->files_idx);
}
i_stream_unref(&fstream);
- test_client_download_finished(tcreq);
+ test_client_download_finished(tcreq->files_idx);
return;
}
client_files_last++) {
const char *path = paths[client_files_last];
- tcreq = i_new(struct test_client_request, 1);
+ tcreq = test_client_request_new();
tcreq->files_idx = client_files_last;
if (debug) {
"retrieving %s [%u]",
path, tcreq->files_idx);
}
- hreq = http_client_request(http_client,
+ hreq = tcreq->hreq = http_client_request(http_client,
"GET", net_ip2addr(&bind_ip),
t_strconcat("/download/", path, NULL),
test_client_download_response, tcreq);
static void test_client_echo_continue(void);
static void
-test_client_echo_finished(struct test_client_request *tcreq)
+test_client_echo_finished(unsigned int files_idx)
{
const char **paths;
unsigned int count;
paths = array_get_modifiable(&files, &count);
- i_assert(tcreq->files_idx < count);
+ i_assert(files_idx < count);
i_assert(client_files_first < count);
- i_assert(paths[tcreq->files_idx] != NULL);
+ i_assert(paths[files_idx] != NULL);
- paths[tcreq->files_idx] = NULL;
+ paths[files_idx] = NULL;
test_client_echo_continue();
}
struct istream *payload = tcreq->payload;
const unsigned char *pdata, *fdata;
size_t psize, fsize, pleft;
+ unsigned int files_idx = tcreq->files_idx;
off_t ret;
/* read payload */
tcreq->files_idx);
}
- /* finished */
- test_client_echo_finished(tcreq);
-
/* dereference payload stream; finishes the request */
tcreq->payload = NULL;
io_remove(&tcreq->io); /* holds a reference too */
i_stream_unref(&payload);
+
+ /* finished */
+ test_client_echo_finished(files_idx);
}
}
path, tcreq->files_idx);
}
i_stream_unref(&fstream);
- test_client_echo_finished(tcreq);
+ test_client_echo_finished(tcreq->files_idx);
return;
}
struct test_client_request *tcreq;
struct http_client_request *hreq;
const char **paths;
- unsigned int count;
+ unsigned int count, first_submitted;
paths = array_get_modifiable(&files, &count);
i_assert(client_files_first <= client_files_last);
for (; client_files_first < client_files_last &&
- paths[client_files_first] == NULL; client_files_first++)
+ paths[client_files_first] == NULL; client_files_first++);
if (debug) {
i_debug("test client: echo: "
if (debug && client_files_first < count) {
const char *path = paths[client_files_first];
i_debug("test client: echo: "
- "next blocking: %s", (path == NULL ? "none" : path));
+ "next blocking: %s [%d]",
+ (path == NULL ? "none" : path),
+ client_files_first);
}
if (client_files_first >= count) {
return;
}
+ first_submitted = client_files_last;
for (; client_files_last < count &&
(client_files_last - client_files_first) < test_max_pending;
client_files_last++) {
path, client_files_last);
}
- tcreq = i_new(struct test_client_request, 1);
+ tcreq = test_client_request_new();
tcreq->files_idx = client_files_last;
- hreq = http_client_request(http_client,
+ hreq = tcreq->hreq = http_client_request(http_client,
"PUT", net_ip2addr(&bind_ip),
t_strconcat("/echo/", path, NULL),
test_client_echo_response, tcreq);
i_stream_unref(&fstream);
}
+
+ /* run nested ioloop (if requested) if new requests cross a nesting
+ boundary */
+ if (ioloop_nested != NULL) {
+ unsigned int i;
+
+ i_assert(ioloop_nested_first <= count);
+ i_assert(ioloop_nested_last <= count);
+ for (i = ioloop_nested_first; i < ioloop_nested_last; i++) {
+ if (paths[i] != NULL) {
+ if (debug) {
+ i_debug("test client: "
+ "not leaving ioloop [%u]", i);
+ }
+ break;
+ }
+ }
+
+ if (i == ioloop_nested_last)
+ io_loop_stop(ioloop_nested);
+ } else if (client_ioloop_nesting > 0 &&
+ ((client_files_last / client_ioloop_nesting) !=
+ (first_submitted / client_ioloop_nesting)) ) {
+ struct ioloop *prev_ioloop = current_ioloop;
+
+ ioloop_nested_first = first_submitted;
+ ioloop_nested_last = first_submitted + client_ioloop_nesting;
+ if (ioloop_nested_last > client_files_last)
+ ioloop_nested_last = client_files_last;
+
+ if (debug) {
+ i_debug("test client: echo: entering ioloop for %u...%u",
+ ioloop_nested_first, ioloop_nested_last);
+ }
+
+ ioloop_nested = io_loop_create();
+ http_client_switch_ioloop(http_client);
+ test_client_requests_switch_ioloop();
+
+ io_loop_run(ioloop_nested);
+
+ io_loop_set_current(prev_ioloop);
+ http_client_switch_ioloop(http_client);
+ test_client_requests_switch_ioloop();
+ io_loop_set_current(ioloop_nested);
+ io_loop_destroy(&ioloop_nested);
+ ioloop_nested = NULL;
+
+ if (debug) {
+ i_debug("test client: echo: leaving ioloop for %u...%u",
+ ioloop_nested_first, ioloop_nested_last);
+ }
+ ioloop_nested_first = ioloop_nested_last = 0;
+ }
}
static void
if (debug)
i_debug("server: PID=%s", my_pid);
/* child: server */
+ ioloop_nested = FALSE;
ioloop = io_loop_create();
test_server_init(server_set);
io_loop_run(ioloop);
i_debug("client: PID=%s", my_pid);
i_close_fd(&fd_listen);
/* parent: client */
+ ioloop_nested = FALSE;
ioloop = io_loop_create();
client_init(client_set);
io_loop_run(ioloop);
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
blocking = FALSE;
request_100_continue = TRUE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
blocking = TRUE;
request_100_continue = TRUE;
read_server_partial = 0;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
blocking = FALSE;
request_100_continue = FALSE;
read_server_partial = 1024;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
blocking = TRUE;
request_100_continue = FALSE;
read_server_partial = 1024;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_echo);
test_run_pipeline(test_client_echo);
test_run_parallel(test_client_echo);
request_100_continue = FALSE;
read_server_partial = 0;
read_client_partial = 1024;
+ client_ioloop_nesting = 0;
test_run_sequential(test_client_download);
test_run_pipeline(test_client_download);
test_run_parallel(test_client_download);
test_end();
}
+static void test_download_client_nested_ioloop(void)
+{
+ test_begin("http payload echo (client nested ioloop)");
+ blocking = FALSE;
+ request_100_continue = FALSE;
+ read_server_partial = 0;
+ read_client_partial = 0;
+ client_ioloop_nesting = 10;
+ test_run_parallel(test_client_echo);
+ test_end();
+}
+
static void (*test_functions[])(void) = {
test_download_server_nonblocking,
test_download_server_blocking,
test_echo_server_nonblocking_partial,
test_echo_server_blocking_partial,
test_download_client_partial,
+ test_download_client_nested_ioloop,
NULL
};