diff options
-rw-r--r-- | include/apr_network_io.h | 20 | ||||
-rw-r--r-- | network_io/unix/socket_util.c | 63 | ||||
-rw-r--r-- | test/testsock.c | 17 |
3 files changed, 63 insertions, 37 deletions
diff --git a/include/apr_network_io.h b/include/apr_network_io.h index 19955301f..690be8553 100644 --- a/include/apr_network_io.h +++ b/include/apr_network_io.h @@ -375,17 +375,19 @@ APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock, apr_sockaddr_t *sa); /** - * Check whether the send part of the socket is still open on the - * peer or that there is still data in the socket's read buffer. - * If this is false the next read on the socket will return APR_EOF. + * Determine whether the receive part of the socket has been closed by + * the peer (such that a subsequent call to apr_socket_read would + * return APR_EOF), if the socket's receive buffer is empty. This + * function does not block waiting for I/O. + * * @param socket The socket to check - * @remark - * <PRE> - * This function does not block on the socket but returns immediately in - * any case. - * </PRE> + * @param atreadeof If APR_SUCCESS is returned, *atreadeof is set to + * non-zero if a subsequent read would return APR_EOF + * @return an error is returned if it was not possible to determine the + * status, in which case *atreadeof is not changed. */ -APR_DECLARE(int) apr_socket_is_connected(apr_socket_t *socket); +APR_DECLARE(apr_status_t) apr_socket_atreadeof(apr_socket_t *sock, + int *atreadeof); /** * Create apr_sockaddr_t from hostname, address family, and port. diff --git a/network_io/unix/socket_util.c b/network_io/unix/socket_util.c index bf98f100c..b23aed6b2 100644 --- a/network_io/unix/socket_util.c +++ b/network_io/unix/socket_util.c @@ -17,41 +17,58 @@ #include "apr_network_io.h" #include "apr_poll.h" -int apr_socket_is_connected(apr_socket_t *socket) +apr_status_t apr_socket_atreadeof(apr_socket_t *sock, int *atreadeof) { apr_pollfd_t pfds[1]; - apr_status_t status; + apr_status_t rv; apr_int32_t nfds; + /* The purpose here is to return APR_SUCCESS only in cases in + * which it can be unambiguously determined whether or not the + * socket will return EOF on next read. In case of an unexpected + * error, return that. */ + pfds[0].reqevents = APR_POLLIN; pfds[0].desc_type = APR_POLL_SOCKET; - pfds[0].desc.s = socket; + pfds[0].desc.s = sock; do { - status = apr_poll(&pfds[0], 1, &nfds, 0); - } while (APR_STATUS_IS_EINTR(status)); + rv = apr_poll(&pfds[0], 1, &nfds, 0); + } while (APR_STATUS_IS_EINTR(rv)); - if (status == APR_SUCCESS && nfds == 1 && - pfds[0].rtnevents == APR_POLLIN) { + if (APR_STATUS_IS_TIMEUP(rv)) { + /* Read buffer empty -> subsequent reads would block, so, + * definitely not at EOF. */ + *atreadeof = 0; + return APR_SUCCESS; + } + else if (rv) { + /* Some other error -> unexpected error. */ + return rv; + } + else if (nfds == 1 && pfds[0].rtnevents == APR_POLLIN) { apr_sockaddr_t unused; apr_size_t len = 1; - char buf[1]; - /* The socket might be closed in which case - * the poll will return POLLIN. - * If there is no data available the socket - * is closed. - */ - status = apr_socket_recvfrom(&unused, socket, MSG_PEEK, - &buf[0], &len); - if (status == APR_SUCCESS && len) - return 1; - else - return 0; - } - else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) { - return 1; + char buf; + + /* The socket is readable - peek to see whether it returns EOF + * without consuming bytes from the socket buffer. */ + rv = apr_socket_recvfrom(&unused, sock, MSG_PEEK, &buf, &len); + if (rv == APR_EOF) { + *atreadeof = 1; + return APR_SUCCESS; + } + else if (rv) { + /* Read error -> unexpected error. */ + return rv; + } + else { + *atreadeof = 0; + return APR_SUCCESS; + } } - return 0; + /* Should not fall through here. */ + return APR_EGENERAL; } diff --git a/test/testsock.c b/test/testsock.c index d902f69b2..ff6483a2f 100644 --- a/test/testsock.c +++ b/test/testsock.c @@ -204,7 +204,7 @@ static void test_recv(abts_case *tc, void *data) APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv); } -static void test_is_connected(abts_case *tc, void *data) +static void test_atreadeof(abts_case *tc, void *data) { apr_status_t rv; apr_socket_t *sock; @@ -212,6 +212,7 @@ static void test_is_connected(abts_case *tc, void *data) apr_proc_t proc; apr_size_t length = STRLEN; char datastr[STRLEN]; + int atreadeof = -1; sock = setup_socket(tc); if (!sock) return; @@ -222,7 +223,9 @@ static void test_is_connected(abts_case *tc, void *data) APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv); /* Check that the remote socket is still open */ - ABTS_INT_EQUAL(tc, 1, apr_socket_is_connected(sock2)); + rv = apr_socket_atreadeof(sock2, &atreadeof); + APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #1", rv); + ABTS_INT_EQUAL(tc, 0, atreadeof); memset(datastr, 0, STRLEN); apr_socket_recv(sock2, datastr, &length); @@ -232,7 +235,9 @@ static void test_is_connected(abts_case *tc, void *data) ABTS_SIZE_EQUAL(tc, strlen(datastr), wait_child(tc, &proc)); /* The child is dead, so should be the remote socket */ - ABTS_INT_EQUAL(tc, 0, apr_socket_is_connected(sock2)); + rv = apr_socket_atreadeof(sock2, &atreadeof); + APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #2", rv); + ABTS_INT_EQUAL(tc, 1, atreadeof); rv = apr_socket_close(sock2); APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv); @@ -243,7 +248,9 @@ static void test_is_connected(abts_case *tc, void *data) APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv); /* The child closed the socket instantly */ - ABTS_INT_EQUAL(tc, 0, apr_socket_is_connected(sock2)); + rv = apr_socket_atreadeof(sock2, &atreadeof); + APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #3", rv); + ABTS_INT_EQUAL(tc, 1, atreadeof); wait_child(tc, &proc); rv = apr_socket_close(sock2); @@ -400,7 +407,7 @@ abts_suite *testsock(abts_suite *suite) abts_run_test(suite, test_create_bind_listen, NULL); abts_run_test(suite, test_send, NULL); abts_run_test(suite, test_recv, NULL); - abts_run_test(suite, test_is_connected, NULL); + abts_run_test(suite, test_atreadeof, NULL); abts_run_test(suite, test_timeout, NULL); abts_run_test(suite, test_print_addr, NULL); abts_run_test(suite, test_get_addr, NULL); |