test-http-client-errors.c revision 8bc62170803a474b8ec0cb7d1d9586b01b17d767
/* Copyright (c) 2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "str.h"
#include "hostpid.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "time-util.h"
#include "connection.h"
#include "test-common.h"
#include "http-url.h"
#include "http-request.h"
#include "http-client.h"
#include <signal.h>
#include <unistd.h>
/*
* Types
*/
struct server_connection {
struct connection conn;
};
typedef void (*test_server_init_t)(unsigned int index);
typedef void (*test_client_init_t)
(const struct http_client_settings *client_set);
/*
* State
*/
/* common */
static in_port_t *bind_ports = 0;
/* server */
static int fd_listen = -1;
static unsigned int server_pids_count = 0;
static struct connection_list *server_conn_list;
static size_t server_read_max = 0;
static unsigned int server_index;
/* client */
/*
* Forward declarations
*/
/* server */
static void test_server_run(unsigned int index);
static void
/* client */
static void
static void test_client_deinit(void);
/* test*/
static void test_run_client_server(
const struct http_client_settings *client_set,
unsigned int server_tests_count)
ATTR_NULL(3);
/*
* Host lookup failed
*/
/* client */
struct _host_lookup_failed {
unsigned int count;
};
static void
const struct http_response *resp,
struct _host_lookup_failed *ctx)
{
if (debug)
}
}
static void
{
struct http_client_request *hreq;
struct _host_lookup_failed *ctx;
}
/* test */
static void test_host_lookup_failed(void)
{
struct http_client_settings http_client_set;
test_begin("host lookup failed");
NULL, 0);
test_end();
}
/*
* Connection refused
*/
/* server */
static void
{
}
/* client */
struct _connection_refused {
unsigned int count;
};
static void
const struct http_response *resp,
struct _connection_refused *ctx)
{
if (debug)
}
}
static void
{
struct http_client_request *hreq;
struct _connection_refused *ctx;
}
/* test */
static void test_connection_refused(void)
{
struct http_client_settings http_client_set;
test_begin("connection refused");
test_end();
}
/*
* Connection timed out
*/
/* client */
struct _connection_timed_out {
unsigned int count;
};
static void
const struct http_response *resp,
struct _connection_timed_out *ctx)
{
if (debug)
}
}
static void
const struct http_client_settings *client_set)
{
struct http_client_request *hreq;
struct _connection_timed_out *ctx;
}
/* test */
static void test_connection_timed_out(void)
{
struct http_client_settings http_client_set;
test_begin("connection timed out");
NULL, 0);
test_end();
}
/*
* Invalid redirect
*/
/* server */
/* -> not accepted */
static void
{
static const char *resp =
"HTTP/1.1 302 Redirect\r\n"
"Location: http://localhost:4444\r\n"
"\r\n";
}
static void test_server_invalid_redirect1(unsigned int index)
{
}
/* -> bad location */
static void
{
static const char *resp =
"HTTP/1.1 302 Redirect\r\n"
"Location: unix:/var/run/dovecot/auth-master\r\n"
"\r\n";
}
static void test_server_invalid_redirect2(unsigned int index)
{
}
/* -> too many */
static void
{
"HTTP/1.1 302 Redirect\r\n"
"Location: http://%s:%u/friep.txt\r\n"
"\r\n",
}
static void test_server_invalid_redirect3(unsigned int index)
{
}
/* client */
static void
const struct http_response *resp,
void *context ATTR_UNUSED)
{
if (debug)
}
static void
{
struct http_client_request *hreq;
}
/* test */
static void test_invalid_redirect(void)
{
struct http_client_settings http_client_set;
test_begin("invalid redirect: not accepted");
test_end();
test_begin("invalid redirect: bad location");
test_end();
test_begin("invalid redirect: too many");
test_end();
}
/*
* Unseekable redirect
*/
/* server */
static void
{
"HTTP/1.1 302 Redirect\r\n"
"Location: http://%s:%u/frml.txt\r\n"
"\r\n",
}
static void test_server_unseekable_redirect(unsigned int index)
{
}
/* client */
static void
const struct http_response *resp,
void *context ATTR_UNUSED)
{
if (debug)
}
static void
{
struct http_client_request *hreq;
}
/* test */
static void test_unseekable_redirect(void)
{
struct http_client_settings http_client_set;
test_begin("unseekable redirect");
test_end();
}
/*
* Unseekable retry
*/
/* server */
static void
{
}
static void test_server_unseekable_retry(unsigned int index)
{
}
/* client */
static void
const struct http_response *resp,
void *context ATTR_UNUSED)
{
if (debug)
}
static void
{
struct http_client_request *hreq;
}
/* test */
static void test_unseekable_retry(void)
{
struct http_client_settings http_client_set;
test_begin("unseekable retry");
test_end();
}
/*
* Broken payload
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 200 OK\r\n"
"Content-Length: 18\r\n"
"\r\n"
"Everything is OK\r\n";
}
static void test_server_broken_payload(unsigned int index)
{
}
/* client */
static void
const struct http_response *resp,
void *context ATTR_UNUSED)
{
if (debug)
}
static void
{
struct http_client_request *hreq;
}
/* test */
static void test_broken_payload(void)
{
struct http_client_settings http_client_set;
test_begin("broken payload");
test_end();
}
/*
* Connection lost
*/
/* server */
static void
{
if (server_read_max == 0) {
return;
}
if (ret == -2) {
return;
}
if (ret < 0) {
i_fatal("server: Client stream ended prematurely");
else
i_fatal("server: Streem error: %s",
}
}
static void test_server_connection_lost(unsigned int index)
{
}
/* client */
struct _connection_lost_ctx {
unsigned int count;
};
struct _connection_lost_request_ctx {
struct _connection_lost_ctx *ctx;
struct http_client_request *req;
};
static void
const struct http_response *resp,
struct _connection_lost_request_ctx *rctx)
{
if (debug)
if (debug)
i_debug("retrying");
return;
}
}
}
static void
{
static const char payload[] =
"This is a useless payload that only serves as a means to give the "
"server the opportunity to close the connection before the payload "
"is finished.";
struct _connection_lost_ctx *ctx;
struct _connection_lost_request_ctx *rctx;
struct http_client_request *hreq;
}
/* test */
static void test_connection_lost(void)
{
struct http_client_settings http_client_set;
server_read_max = 0;
test_begin("connection lost: one attempt");
test_end();
test_begin("connection lost: two attempts");
test_end();
test_begin("connection lost: three attempts");
test_end();
test_begin("connection lost: manual retry");
test_end();
}
/*
* Connection lost after 100-continue
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 100 Continue\r\n"
"\r\n";
}
static void test_server_connection_lost_100(unsigned int index)
{
}
/* client */
struct _connection_lost_100_ctx {
unsigned int count;
};
static void
const struct http_response *resp,
struct _connection_lost_100_ctx *ctx)
{
if (debug)
}
}
static void
const struct http_client_settings *client_set)
{
static const char payload[] =
"This is a useless payload that only serves as a means to give the "
"server the opportunity to close the connection before the payload "
"is finished.";
struct _connection_lost_100_ctx *ctx;
struct http_client_request *hreq;
}
/* test */
static void test_connection_lost_100(void)
{
struct http_client_settings http_client_set;
server_read_max = 0;
test_begin("connection lost after 100-continue");
test_end();
}
/*
* Connection lost in sub-ioloop
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"\r\n";
}
static void test_server_connection_lost_sub_ioloop(unsigned int index)
{
}
/* client */
struct _connection_lost_sub_ioloop_ctx {
unsigned int count;
};
static void
const struct http_response *resp,
struct ioloop *sub_ioloop)
{
if (debug)
}
static void
const struct http_response *resp,
struct _connection_lost_sub_ioloop_ctx *ctx)
{
struct http_client_request *hreq;
struct ioloop *sub_ioloop;
if (debug)
sub_ioloop = io_loop_create();
}
}
static void
const struct http_client_settings *client_set)
{
static const char payload[] =
"This is a useless payload that only serves as a means to give the "
"server the opportunity to close the connection before the payload "
"is finished.";
struct _connection_lost_sub_ioloop_ctx *ctx;
struct http_client_request *hreq;
}
/* test */
static void test_connection_lost_sub_ioloop(void)
{
struct http_client_settings http_client_set;
server_read_max = 0;
test_begin("connection lost while running sub-ioloop");
test_end();
}
/*
* Early success
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 200 OK\r\n"
"Content-Length: 18\r\n"
"\r\n"
"Everything is OK\r\n";
usleep(200000);
}
static void test_server_early_success(unsigned int index)
{
}
/* client */
struct _early_success_ctx {
unsigned int count;
};
static void
const struct http_response *resp,
struct _early_success_ctx *ctx)
{
if (debug)
else
}
}
static void
{
struct http_client_request *hreq;
struct _early_success_ctx *ctx;
unsigned int i;
T_BEGIN {
for (i = 0; i < 3200; i++) {
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n");
}
} T_END;
}
/* test */
static void test_early_success(void)
{
struct http_client_settings http_client_set;
test_begin("early succes");
test_end();
}
/*
* Bad response
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 666 Really bad response\r\n"
"\r\n";
}
static void test_server_bad_response(unsigned int index)
{
}
/* client */
struct _bad_response_ctx {
unsigned int count;
};
static void
const struct http_response *resp,
struct _bad_response_ctx *ctx)
{
if (debug)
}
}
static void
{
struct http_client_request *hreq;
struct _bad_response_ctx *ctx;
}
/* test */
static void test_bad_response(void)
{
struct http_client_settings http_client_set;
test_begin("bad response");
test_end();
}
/*
* Request timed out
*/
/* server */
static void
{
/* do nothing */
}
static void test_server_request_timed_out(unsigned int index)
{
}
/* client */
struct _request_timed_out_ctx {
unsigned int count;
};
static void
const struct http_response *resp,
struct _request_timed_out_ctx *ctx)
{
if (debug)
}
}
static void
{
struct http_client_request *hreq;
struct _request_timed_out_ctx *ctx;
}
/* test */
static void test_request_timed_out(void)
{
struct http_client_settings http_client_set;
test_begin("request timed out: one attempt");
test_end();
test_begin("request timed out: two attempts");
test_end();
test_begin("request absolutely timed out");
test_end();
test_begin("request double timed out");
test_end();
}
/*
* Request aborted early
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 404 Not Found\r\n"
"\r\n";
/* wait one second to respon */
sleep(1);
/* respond */
}
static void test_server_request_aborted_early(unsigned int index)
{
}
/* client */
struct _request_aborted_early_ctx {
};
static void
const struct http_response *resp,
{
if (debug)
/* abort does not trigger callback */
}
static void
struct _request_aborted_early_ctx *ctx)
{
/* abort early */
/* wait a little for server to actually respond to an
already aborted request */
} else {
/* all done */
}
}
static void
const struct http_client_settings *client_set)
{
struct http_client_request *hreq;
struct _request_aborted_early_ctx *ctx;
}
/* test */
static void test_request_aborted_early(void)
{
struct http_client_settings http_client_set;
test_begin("request aborted early");
test_end();
}
/*
* Client deinit early
*/
/* server */
static void
{
static const char *resp =
"HTTP/1.1 404 Not Found\r\n"
"\r\n";
/* wait one second to respon */
sleep(1);
/* respond */
}
static void test_server_client_deinit_early(unsigned int index)
{
}
/* client */
struct _client_deinit_early_ctx {
};
static void
const struct http_response *resp,
{
if (debug)
/* abort does not trigger callback */
}
static void
struct _client_deinit_early_ctx *ctx)
{
/* deinit early */
/* all done */
}
static void
{
struct http_client_request *hreq;
struct _client_deinit_early_ctx *ctx;
}
/* test */
static void test_client_deinit_early(void)
{
struct http_client_settings http_client_set;
test_begin("client deinit early");
test_end();
}
/*
* Retry with delay
*/
/* server */
static void
{
"HTTP/1.1 500 Internal Server Error\r\n"
"\r\n");
}
static void test_server_retry_with_delay(unsigned int index)
{
}
/* client */
struct _client_retry_with_delay_ctx {
struct http_client_request *req;
unsigned int retries;
};
static void
const struct http_response *resp,
struct _client_retry_with_delay_ctx *ctx)
{
int real_delay, exp_delay;
if (debug)
/* check delay */
if (real_delay < exp_delay) {
i_fatal("Retry delay is too short %d < %d",
}
}
if (debug)
i_debug("retrying");
return;
}
}
static void
{
struct http_client_request *hreq;
struct _client_retry_with_delay_ctx *ctx;
}
/* test */
static void test_retry_with_delay(void)
{
struct http_client_settings http_client_set;
test_begin("retry with delay");
test_end();
}
/*
* All tests
*/
static void (*test_functions[])(void) = {
};
/*
* Test client
*/
static void
{
/* client settings */
http_set->max_redirects = 0;
}
static void test_client_deinit(void)
{
if (http_client != NULL)
http_client = NULL;
}
/*
* Test server
*/
/* client connection */
static void
{
}
static void
server_connection_init(int fd)
{
struct server_connection *conn;
}
static void
{
}
static void
{
struct server_connection *conn =
(struct server_connection *)_conn;
}
static void
{
int fd;
/* accept new client */
if (fd == -1)
return;
if (fd == -2) {
i_fatal("test server: accept() failed: %m");
}
}
/* */
static struct connection_settings server_connection_set = {
};
static const struct connection_vfuncs server_connection_vfuncs = {
};
static void test_server_run(unsigned int index)
{
/* open server socket */
/* close server socket */
}
/*
* Tests
*/
{
if (debug)
if (fd == -1) {
i_fatal("listen(%s:%u) failed: %m",
}
return fd;
}
static void test_servers_kill_all(void)
{
unsigned int i;
if (server_pids_count > 0) {
for (i = 0; i < server_pids_count; i++) {
server_pids[i] = -1;
}
}
}
server_pids_count = 0;
}
static void test_run_client_server(
const struct http_client_settings *client_set,
unsigned int server_tests_count)
{
unsigned int i;
server_pids = NULL;
server_pids_count = 0;
if (server_tests_count > 0) {
int fds[server_tests_count];
for (i = 0; i < server_tests_count; i++)
for (i = 0; i < server_tests_count; i++)
for (i = 0; i < server_tests_count; i++) {
i_fatal("fork() failed: %m");
if (server_pids[i] == 0) {
server_pids_count = 0;
hostpid_init();
if (debug)
/* child: server */
ioloop = io_loop_create();
server_test(i);
if (fd_listen != -1)
/* wait for it to be killed; this way, valgrind will not
object to this process going away inelegantly. */
sleep(60);
exit(1);
}
if (fd_listen != -1)
}
if (debug)
}
/* parent: client */
ioloop = io_loop_create();
}
/*
* Main
*/
volatile sig_atomic_t terminating = 0;
static void
test_signal_handler(int signo)
{
if (terminating != 0)
terminating = 1;
/* make sure we don't leave any pesky children alive */
}
static void test_atexit(void)
{
}
{
int c;
switch (c) {
case 'D':
break;
default:
}
}
/* listen on localhost */
}