summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/apr_network_io.h20
-rw-r--r--network_io/unix/socket_util.c63
-rw-r--r--test/testsock.c17
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);