]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add HTTP timeout recovery test
authorEvan Hunt <each@isc.org>
Wed, 21 Apr 2021 18:55:40 +0000 (11:55 -0700)
committerEvan Hunt <each@isc.org>
Thu, 22 Apr 2021 19:40:04 +0000 (12:40 -0700)
NOTE: this test currently fails

lib/isc/tests/doh_test.c

index d27b1f7b9bfb59cf38225bb074042cc5fb634468..d5f4c83e349ee11b293b0c4a9e752e251e2be3da 100644 (file)
@@ -55,19 +55,19 @@ static uint64_t stop_magic = 0;
 static uv_buf_t send_msg = { .base = (char *)&send_magic,
                             .len = sizeof(send_magic) };
 
-static atomic_int_fast64_t nsends;
-
-static atomic_uint_fast64_t ssends;
-static atomic_uint_fast64_t sreads;
-
-static atomic_uint_fast64_t csends;
-static atomic_uint_fast64_t creads;
+static atomic_int_fast64_t nsends = ATOMIC_VAR_INIT(0);
+static atomic_int_fast64_t ssends = ATOMIC_VAR_INIT(0);
+static atomic_int_fast64_t sreads = ATOMIC_VAR_INIT(0);
+static atomic_int_fast64_t csends = ATOMIC_VAR_INIT(0);
+static atomic_int_fast64_t creads = ATOMIC_VAR_INIT(0);
+static atomic_int_fast64_t ctimeouts = ATOMIC_VAR_INIT(0);
 
 static atomic_bool was_error;
 
 static unsigned int workers = 0;
 
 static bool reuse_supported = true;
+static bool noanswer = false;
 
 static atomic_bool POST = ATOMIC_VAR_INIT(true);
 
@@ -292,12 +292,15 @@ nm_setup(void **state) {
        atomic_store(&creads, 0);
        atomic_store(&sreads, 0);
        atomic_store(&ssends, 0);
+       atomic_store(&ctimeouts, 0);
 
        atomic_store(&was_error, false);
 
        atomic_store(&POST, false);
        atomic_store(&use_TLS, false);
 
+       noanswer = false;
+
        isc_nonce_buf(&send_magic, sizeof(send_magic));
        isc_nonce_buf(&stop_magic, sizeof(stop_magic));
        if (send_magic == stop_magic) {
@@ -428,11 +431,16 @@ doh_receive_request_cb(isc_nmhandle_t *handle, isc_result_t eresult,
                        tcp_buffer_length);
 
                if (magic == send_magic) {
-                       isc_nm_send(handle, region, doh_reply_sent_cb, NULL);
+                       if (!noanswer) {
+                               isc_nm_send(handle, region, doh_reply_sent_cb,
+                                           NULL);
+                       }
                        return;
                } else if (magic == stop_magic) {
-                       /* We are done, so we don't send anything back */
-                       /* There should be no more packets in the buffer */
+                       /*
+                        * We are done, so we don't send anything back.
+                        * There should be no more packets in the buffer.
+                        */
                        assert_int_equal(tcp_buffer_length, 0);
                }
        }
@@ -543,6 +551,134 @@ doh_noresponse_GET(void **state) {
        doh_noresponse(state);
 }
 
+static void
+timeout_query_sent_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                     void *cbarg) {
+       UNUSED(eresult);
+       UNUSED(cbarg);
+
+       assert_non_null(handle);
+
+       if (eresult == ISC_R_SUCCESS) {
+               atomic_fetch_add(&csends, 1);
+       }
+
+       isc_nmhandle_detach(&handle);
+}
+
+static void
+timeout_retry_cb(isc_nmhandle_t *handle, isc_result_t eresult,
+                isc_region_t *region, void *arg) {
+       UNUSED(region);
+       UNUSED(arg);
+
+       assert_non_null(handle);
+
+       atomic_fetch_add(&ctimeouts, 1);
+
+       if (eresult == ISC_R_TIMEDOUT && atomic_load(&ctimeouts) < 5) {
+               isc_nmhandle_settimeout(handle, 50);
+               return;
+       }
+
+       /*
+        * XXX: We should be attaching to a readhandle in
+        * timeout_request_cb() and detaching it here, but we
+        * don't do this because the handle passed to this function
+        * is for the tcpsocket, not the httpsocket. This is a
+        * bug.
+        */
+       /* isc_nmhandle_detach(&handle); */
+}
+
+static void
+timeout_request_cb(isc_nmhandle_t *handle, isc_result_t result, void *arg) {
+       isc_nmhandle_t *sendhandle = NULL;
+       /* isc_nmhandle_t *readhandle = NULL; */
+
+       REQUIRE(VALID_NMHANDLE(handle));
+
+       if (result != ISC_R_SUCCESS) {
+               goto error;
+       }
+
+       isc_nmhandle_attach(handle, &sendhandle);
+       isc_nm_send(handle,
+                   &(isc_region_t){ .base = (uint8_t *)send_msg.base,
+                                    .length = send_msg.len },
+                   timeout_query_sent_cb, arg);
+
+       /* isc_nmhandle_attach(handle, &readhandle); */
+       isc_nm_read(handle, timeout_retry_cb, NULL);
+       return;
+
+error:
+       atomic_store(&was_error, true);
+}
+
+static void
+doh_timeout_recovery(void **state) {
+       isc_nm_t **nm = (isc_nm_t **)*state;
+       isc_nm_t *listen_nm = nm[0];
+       isc_nm_t *connect_nm = nm[1];
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_nmsocket_t *listen_sock = NULL;
+       isc_tlsctx_t *ctx = atomic_load(&use_TLS) ? server_tlsctx : NULL;
+       char req_url[256];
+
+       result = isc_nm_listenhttp(listen_nm, (isc_nmiface_t *)&tcp_listen_addr,
+                                  0, NULL, NULL, &listen_sock);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       /*
+        * Accept connections but don't send responses, forcing client
+        * reads to time out.
+        */
+       noanswer = true;
+       result = isc_nm_http_endpoint(listen_sock, DOH_PATH,
+                                     doh_receive_request_cb, NULL, 0);
+       assert_int_equal(result, ISC_R_SUCCESS);
+
+       /*
+        * Shorten all the TCP client timeouts to 0.05 seconds.
+        * timeout_retry_cb() will give up after five timeouts.
+        */
+       isc_nm_settimeouts(connect_nm, 50, 50, 50, 50);
+       sockaddr_to_url(&tcp_listen_addr, false, req_url, sizeof(req_url),
+                       DOH_PATH);
+       isc_nm_httpconnect(connect_nm, NULL, (isc_nmiface_t *)&tcp_listen_addr,
+                          req_url, atomic_load(&POST), timeout_request_cb,
+                          NULL, ctx, 50, 0);
+
+       /*
+        * Sleep until sends reaches 5.
+        */
+       for (size_t i = 0; i < 1000; i++) {
+               if (atomic_load(&ctimeouts) == 5) {
+                       break;
+               }
+               usleep(1000);
+       }
+       assert_true(atomic_load(&ctimeouts) == 5);
+
+       isc_nm_stoplistening(listen_sock);
+       isc_nmsocket_close(&listen_sock);
+       assert_null(listen_sock);
+       isc_nm_closedown(connect_nm);
+}
+
+static void
+doh_timeout_recovery_POST(void **state) {
+       atomic_store(&POST, true);
+       doh_timeout_recovery(state);
+}
+
+static void
+doh_timeout_recovery_GET(void **state) {
+       atomic_store(&POST, false);
+       doh_timeout_recovery(state);
+}
+
 static void
 doh_receive_send_reply_cb(isc_nmhandle_t *handle, isc_result_t eresult,
                          isc_region_t *region, void *cbarg) {
@@ -1636,6 +1772,10 @@ main(void) {
                                                nm_teardown),
                cmocka_unit_test_setup_teardown(doh_noresponse_GET, nm_setup,
                                                nm_teardown),
+               cmocka_unit_test_setup_teardown(doh_timeout_recovery_POST,
+                                               nm_setup, nm_teardown),
+               cmocka_unit_test_setup_teardown(doh_timeout_recovery_GET,
+                                               nm_setup, nm_teardown),
                cmocka_unit_test_setup_teardown(doh_recv_one_POST, nm_setup,
                                                nm_teardown),
                cmocka_unit_test_setup_teardown(doh_recv_one_GET, nm_setup,