Mercurial > dovecot > core-2.2
changeset 21049:dbbf345febf2
lib-http: Added DNS-related tests to test-http-client-errors.
author | Stephan Bosch <stephan.bosch@dovecot.fi> |
---|---|
date | Tue, 25 Oct 2016 09:56:34 +0200 |
parents | 4180c1dd0aa2 |
children | 461e5847c268 |
files | src/lib-http/test-http-client-errors.c |
diffstat | 1 files changed, 498 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/test-http-client-errors.c Fri Sep 16 20:29:00 2016 +0200 +++ b/src/lib-http/test-http-client-errors.c Tue Oct 25 09:56:34 2016 +0200 @@ -32,6 +32,7 @@ typedef void (*test_server_init_t)(unsigned int index); typedef void (*test_client_init_t) (const struct http_client_settings *client_set); +typedef void (*test_dns_init_t)(void); /* * State @@ -43,6 +44,9 @@ static struct ioloop *ioloop; static bool debug = FALSE; +/* dns */ +static pid_t dns_pid = (pid_t)-1; + /* server */ static struct io *io_listen; static int fd_listen = -1; @@ -75,7 +79,8 @@ const struct http_client_settings *client_set, test_client_init_t client_test, test_server_init_t server_test, - unsigned int server_tests_count) + unsigned int server_tests_count, + test_dns_init_t dns_test) ATTR_NULL(3); /* @@ -138,7 +143,7 @@ test_begin("host lookup failed"); test_run_client_server(&http_client_set, test_client_host_lookup_failed, - NULL, 0); + NULL, 0, NULL); test_end(); } @@ -212,7 +217,8 @@ test_begin("connection refused"); test_run_client_server(&http_client_set, test_client_connection_refused, - test_server_connection_refused, 1); + test_server_connection_refused, 1, + NULL); test_end(); } @@ -279,7 +285,7 @@ test_begin("connection timed out"); test_run_client_server(&http_client_set, test_client_connection_timed_out, - NULL, 0); + NULL, 0, NULL); test_end(); } @@ -392,21 +398,24 @@ http_client_set.max_redirects = 0; test_run_client_server(&http_client_set, test_client_invalid_redirect, - test_server_invalid_redirect1, 1); + test_server_invalid_redirect1, 1, + NULL); test_end(); test_begin("invalid redirect: bad location"); http_client_set.max_redirects = 1; test_run_client_server(&http_client_set, test_client_invalid_redirect, - test_server_invalid_redirect2, 1); + test_server_invalid_redirect2, 1, + NULL); test_end(); test_begin("invalid redirect: too many"); http_client_set.max_redirects = 1; test_run_client_server(&http_client_set, test_client_invalid_redirect, - test_server_invalid_redirect3, 3); + test_server_invalid_redirect3, 3, + NULL); test_end(); } @@ -486,7 +495,8 @@ test_begin("unseekable redirect"); test_run_client_server(&http_client_set, test_client_unseekable_redirect, - test_server_unseekable_redirect, 2); + test_server_unseekable_redirect, 2, + NULL); test_end(); } @@ -556,7 +566,8 @@ test_begin("unseekable retry"); test_run_client_server(&http_client_set, test_client_unseekable_retry, - test_server_unseekable_retry, 2); + test_server_unseekable_retry, 2, + NULL); test_end(); } @@ -633,7 +644,8 @@ test_begin("broken payload"); test_run_client_server(&http_client_set, test_client_broken_payload, - test_server_broken_payload, 1); + test_server_broken_payload, 1, + NULL); test_end(); } @@ -766,21 +778,24 @@ http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost, - test_server_connection_lost, 1); + test_server_connection_lost, 1, + NULL); test_end(); test_begin("connection lost: two attempts"); http_client_set.max_attempts = 2; test_run_client_server(&http_client_set, test_client_connection_lost, - test_server_connection_lost, 1); + test_server_connection_lost, 1, + NULL); test_end(); test_begin("connection lost: three attempts"); http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_connection_lost, - test_server_connection_lost, 1); + test_server_connection_lost, 1, + NULL); test_end(); test_begin("connection lost: manual retry"); @@ -788,7 +803,8 @@ http_client_set.no_auto_retry = TRUE; test_run_client_server(&http_client_set, test_client_connection_lost, - test_server_connection_lost, 1); + test_server_connection_lost, 1, + NULL); test_end(); } @@ -887,7 +903,8 @@ http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost_100, - test_server_connection_lost_100, 1); + test_server_connection_lost_100, 1, + NULL); test_end(); } @@ -1015,7 +1032,8 @@ http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_connection_lost_sub_ioloop, - test_server_connection_lost_sub_ioloop, 2); + test_server_connection_lost_sub_ioloop, 2, + NULL); test_end(); } @@ -1130,7 +1148,8 @@ test_begin("early succes"); test_run_client_server(&http_client_set, test_client_early_success, - test_server_early_success, 1); + test_server_early_success, 1, + NULL); test_end(); } @@ -1214,7 +1233,8 @@ test_begin("bad response"); test_run_client_server(&http_client_set, test_client_bad_response, - test_server_bad_response, 1); + test_server_bad_response, 1, + NULL); test_end(); } @@ -1296,7 +1316,8 @@ http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_request_timed_out, - test_server_request_timed_out, 1); + test_server_request_timed_out, 1, + NULL); test_end(); test_begin("request timed out: two attempts"); @@ -1304,7 +1325,8 @@ http_client_set.max_attempts = 1; test_run_client_server(&http_client_set, test_client_request_timed_out, - test_server_request_timed_out, 1); + test_server_request_timed_out, 1, + NULL); test_end(); test_begin("request absolutely timed out"); @@ -1313,7 +1335,8 @@ http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_request_timed_out, - test_server_request_timed_out, 1); + test_server_request_timed_out, 1, + NULL); test_end(); test_begin("request double timed out"); @@ -1322,7 +1345,8 @@ http_client_set.max_attempts = 3; test_run_client_server(&http_client_set, test_client_request_timed_out, - test_server_request_timed_out, 1); + test_server_request_timed_out, 1, + NULL); test_end(); } @@ -1432,7 +1456,8 @@ test_begin("request aborted early"); test_run_client_server(&http_client_set, test_client_request_aborted_early, - test_server_request_aborted_early, 1); + test_server_request_aborted_early, 1, + NULL); test_end(); } @@ -1532,7 +1557,8 @@ test_begin("client deinit early"); test_run_client_server(&http_client_set, test_client_client_deinit_early, - test_server_client_deinit_early, 1); + test_server_client_deinit_early, 1, + NULL); test_end(); } @@ -1636,7 +1662,408 @@ test_begin("retry with delay"); test_run_client_server(&http_client_set, test_client_retry_with_delay, - test_server_retry_with_delay, 1); + test_server_retry_with_delay, 1, + NULL); + test_end(); +} + +/* + * DNS service failure + */ + +/* client */ + +struct _dns_service_failure { + unsigned int count; +}; + +static void +test_client_dns_service_failure_response( + const struct http_response *resp, + struct _dns_service_failure *ctx) +{ + if (debug) + i_debug("RESPONSE: %u %s", resp->status, resp->reason); + + test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); + test_assert(resp->reason != NULL && *resp->reason != '\0'); + + if (--ctx->count == 0) { + i_free(ctx); + io_loop_stop(ioloop); + } +} + +static void +test_client_dns_service_failure(const struct http_client_settings *client_set) +{ + struct http_client_request *hreq; + struct _dns_service_failure *ctx; + + ctx = i_new(struct _dns_service_failure, 1); + ctx->count = 2; + + http_client = http_client_init(client_set); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-service-failure.txt", + test_client_dns_service_failure_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-service-failure2.txt", + test_client_dns_service_failure_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); +} + +/* test */ + +static void test_dns_service_failure(void) +{ + struct http_client_settings http_client_set; + + test_client_defaults(&http_client_set); + http_client_set.dns_client_socket_path = "./frop"; + + test_begin("dns service failure"); + test_run_client_server(&http_client_set, + test_client_dns_service_failure, + NULL, 0, NULL); + test_end(); +} + +/* + * DNS timeout + */ + +/* dns */ + +static void +test_dns_timeout_input(struct server_connection *conn ATTR_UNUSED) +{ + /* hang */ + sleep(100); + server_connection_deinit(&conn); +} + +static void test_dns_dns_timeout(void) +{ + test_server_input = test_dns_timeout_input; + test_server_run(0); +} + +/* client */ + +struct _dns_timeout { + unsigned int count; +}; + +static void +test_client_dns_timeout_response( + const struct http_response *resp, + struct _dns_timeout *ctx) +{ + if (debug) + i_debug("RESPONSE: %u %s", resp->status, resp->reason); + + test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); + test_assert(resp->reason != NULL && *resp->reason != '\0'); + + if (--ctx->count == 0) { + i_free(ctx); + io_loop_stop(ioloop); + } +} + +static void +test_client_dns_timeout(const struct http_client_settings *client_set) +{ + struct http_client_request *hreq; + struct _dns_timeout *ctx; + + ctx = i_new(struct _dns_timeout, 1); + ctx->count = 2; + + http_client = http_client_init(client_set); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-timeout.txt", + test_client_dns_timeout_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-timeout2.txt", + test_client_dns_timeout_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); +} + +/* test */ + +static void test_dns_timeout(void) +{ + struct http_client_settings http_client_set; + + test_client_defaults(&http_client_set); + http_client_set.request_timeout_msecs = 2000; + http_client_set.connect_timeout_msecs = 2000; + http_client_set.dns_client_socket_path = "./dns-test"; + + test_begin("dns timeout"); + test_run_client_server(&http_client_set, + test_client_dns_timeout, + NULL, 0, + test_dns_dns_timeout); + test_end(); +} + +/* + * DNS lookup failure + */ + +/* dns */ + +static void +test_dns_lookup_failure_input(struct server_connection *conn) +{ + o_stream_nsend_str(conn->conn.output, + t_strdup_printf("%d\n", EAI_FAIL)); + server_connection_deinit(&conn); +} + +static void test_dns_dns_lookup_failure(void) +{ + test_server_input = test_dns_lookup_failure_input; + test_server_run(0); +} + +/* client */ + +struct _dns_lookup_failure { + unsigned int count; +}; + +static void +test_client_dns_lookup_failure_response( + const struct http_response *resp, + struct _dns_lookup_failure *ctx) +{ + if (debug) + i_debug("RESPONSE: %u %s", resp->status, resp->reason); + + test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); + test_assert(resp->reason != NULL && *resp->reason != '\0'); + + if (--ctx->count == 0) { + i_free(ctx); + io_loop_stop(ioloop); + } +} + +static void +test_client_dns_lookup_failure(const struct http_client_settings *client_set) +{ + struct http_client_request *hreq; + struct _dns_lookup_failure *ctx; + + ctx = i_new(struct _dns_lookup_failure, 1); + ctx->count = 2; + + http_client = http_client_init(client_set); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-lookup-failure.txt", + test_client_dns_lookup_failure_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); + + hreq = http_client_request(http_client, + "GET", "example.com", "/dns-lookup-failure2.txt", + test_client_dns_lookup_failure_response, ctx); + http_client_request_set_port(hreq, 80); + http_client_request_submit(hreq); +} + +/* test */ + +static void test_dns_lookup_failure(void) +{ + struct http_client_settings http_client_set; + + test_client_defaults(&http_client_set); + http_client_set.dns_client_socket_path = "./dns-test"; + + test_begin("dns lookup failure"); + test_run_client_server(&http_client_set, + test_client_dns_lookup_failure, + NULL, 0, + test_dns_dns_lookup_failure); + test_end(); +} + +/* + * DNS lookup ttl + */ + +/* dns */ + +static void +test_dns_lookup_ttl_input(struct server_connection *conn) +{ + static unsigned int count = 0; + const char *line; + + while ((line=i_stream_read_next_line(conn->conn.input)) != NULL) { + if (debug) + i_debug("DNS REQUEST %u: %s", count, line); + + if (count == 0) { + o_stream_nsend_str(conn->conn.output, + "0 1\n127.0.0.1\n"); + } else { + o_stream_nsend_str(conn->conn.output, + t_strdup_printf("%d\n", EAI_FAIL)); + if (count > 4) + server_connection_deinit(&conn); + } + count++; + } +} + +static void test_dns_dns_lookup_ttl(void) +{ + test_server_input = test_dns_lookup_ttl_input; + test_server_run(0); +} + +/* server */ + +static void +test_server_dns_lookup_ttl_input(struct server_connection *conn) +{ + string_t *resp; + + resp = t_str_new(512); + str_printfa(resp, + "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "\r\n"); + o_stream_nsend(conn->conn.output, + str_data(resp), str_len(resp)); + server_connection_deinit(&conn); +} + +static void test_server_dns_lookup_ttl(unsigned int index) +{ + test_server_input = test_server_dns_lookup_ttl_input; + test_server_run(index); +} + +/* client */ + +struct _dns_lookup_ttl { + struct http_client *client; + unsigned int count; + struct timeout *to; +}; + +static void +test_client_dns_lookup_ttl_response_stage2( + const struct http_response *resp, + struct _dns_lookup_ttl *ctx) +{ + if (debug) + i_debug("RESPONSE: %u %s", resp->status, resp->reason); + + test_assert(resp->status == HTTP_CLIENT_REQUEST_ERROR_HOST_LOOKUP_FAILED); + test_assert(resp->reason != NULL && *resp->reason != '\0'); + + if (--ctx->count == 0) { + i_free(ctx); + io_loop_stop(ioloop); + } +} + +static void +test_client_dns_lookup_ttl_stage2_start(struct _dns_lookup_ttl *ctx) +{ + struct http_client_request *hreq; + + timeout_remove(&ctx->to); + + ctx->count = 2; + + hreq = http_client_request(ctx->client, + "GET", "example.com", "/dns-lookup-ttl-stage2.txt", + test_client_dns_lookup_ttl_response_stage2, ctx); + http_client_request_set_port(hreq, bind_ports[0]); + http_client_request_submit(hreq); + + hreq = http_client_request(ctx->client, + "GET", "example.com", "/dns-lookup-ttl2-stage2.txt", + test_client_dns_lookup_ttl_response_stage2, ctx); + http_client_request_set_port(hreq, bind_ports[0]); + http_client_request_submit(hreq); +} + +static void +test_client_dns_lookup_ttl_response_stage1( + const struct http_response *resp, + struct _dns_lookup_ttl *ctx) +{ + if (debug) + i_debug("RESPONSE: %u %s", resp->status, resp->reason); + + test_assert(resp->status == 200); + + if (--ctx->count == 0) { + ctx->to = timeout_add(2000, + test_client_dns_lookup_ttl_stage2_start, ctx); + } +} + +static void +test_client_dns_lookup_ttl(const struct http_client_settings *client_set) +{ + struct http_client_request *hreq; + struct _dns_lookup_ttl *ctx; + + ctx = i_new(struct _dns_lookup_ttl, 1); + ctx->count = 2; + + ctx->client = http_client = http_client_init(client_set); + + hreq = http_client_request(ctx->client, + "GET", "example.com", "/dns-lookup-ttl-stage1.txt", + test_client_dns_lookup_ttl_response_stage1, ctx); + http_client_request_set_port(hreq, bind_ports[0]); + http_client_request_submit(hreq); + + hreq = http_client_request(ctx->client, + "GET", "example.com", "/dns-lookup-ttl2-stage1.txt", + test_client_dns_lookup_ttl_response_stage1, ctx); + http_client_request_set_port(hreq, bind_ports[0]); + http_client_request_submit(hreq); +} + +/* test */ + +static void test_dns_lookup_ttl(void) +{ + struct http_client_settings http_client_set; + + test_client_defaults(&http_client_set); + http_client_set.dns_client_socket_path = "./dns-test"; + http_client_set.dns_ttl_msecs = 1000; + + test_begin("dns lookup ttl"); + test_run_client_server(&http_client_set, + test_client_dns_lookup_ttl, + test_server_dns_lookup_ttl, 1, + test_dns_dns_lookup_ttl); test_end(); } @@ -1661,6 +2088,10 @@ test_request_aborted_early, test_client_deinit_early, test_retry_with_delay, + test_dns_service_failure, + test_dns_timeout, + test_dns_lookup_failure, + test_dns_lookup_ttl, NULL }; @@ -1816,13 +2247,20 @@ } } server_pids_count = 0; + + if (dns_pid != (pid_t)-1) { + (void)kill(dns_pid, SIGKILL); + (void)waitpid(dns_pid, NULL, 0); + dns_pid = (pid_t)-1; + } } static void test_run_client_server( const struct http_client_settings *client_set, test_client_init_t client_test, test_server_init_t server_test, - unsigned int server_tests_count) + unsigned int server_tests_count, + test_dns_init_t dns_test) { unsigned int i; @@ -1872,6 +2310,38 @@ i_debug("client: PID=%s", my_pid); } + if (dns_test != NULL) { + int fd; + + i_unlink_if_exists("./dns-test"); + fd = net_listen_unix("./dns-test", 128); + if (fd == -1) { + i_fatal("listen(./dns-test) failed: %m"); + } + + fd_listen = fd; + if ((dns_pid = fork()) == (pid_t)-1) + i_fatal("fork() failed: %m"); + if (dns_pid == 0) { + dns_pid = (pid_t)-1; + hostpid_init(); + if (debug) + i_debug("dns server: PID=%s", my_pid); + /* child: server */ + ioloop = io_loop_create(); + dns_test(); + io_loop_destroy(&ioloop); + if (fd_listen != -1) + i_close_fd(&fd_listen); + /* 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) + i_close_fd(&fd_listen); + } + /* parent: client */ usleep(100000); /* wait a little for server setup */ @@ -1885,6 +2355,8 @@ test_servers_kill_all(); i_free(server_pids); i_free(bind_ports); + + i_unlink_if_exists("./dns-test"); } /*