summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68>2009-10-16 21:08:15 +0000
committerrpluem <rpluem@13f79535-47bb-0310-9956-ffa450edef68>2009-10-16 21:08:15 +0000
commit988e61fd42663d9d2a251be93ab3cfa3b0760a7f (patch)
tree44df2b64ee9ae0efd5635860dfb89fe4133c6c4c
parentf3439bfc2d0ab52c1363ec24fe88cba2a3c3a8c9 (diff)
downloadlibapr-988e61fd42663d9d2a251be93ab3cfa3b0760a7f.tar.gz
Merge r821524, r822431, r822892, r824500 from trunk:
* Add apr_socket_is_connected to detect whether the remote side of a socket is still open. The origin of apr_socket_is_connected is r473278 from http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/proxy/proxy_util.c in httpd. * Improve the documentation Suggested by: jorton * Improve the documentation even further Reviewed by: jim * include/apr_network_io.h, * network_io/unix/socket_util.c (apr_socket_atreadeof): Renamed from apr_socket_is_connected; adjusted to return an apr_status_t error code, and pass an "at EOF" flag via an output parameter. * test/testsock.c (test_atreadeof): Renamed from test_is_connected, adjusted for new API. Reviewed by: jorton, rpluem git-svn-id: http://svn.apache.org/repos/asf/apr/apr/branches/1.4.x@826089 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES4
-rw-r--r--include/apr_network_io.h15
-rw-r--r--libapr.dsp4
-rw-r--r--network_io/beos/socketcommon.c1
-rw-r--r--network_io/os2/socket_util.c1
-rw-r--r--network_io/unix/socket_util.c74
-rw-r--r--test/sockchild.c4
-rw-r--r--test/testsock.c57
8 files changed, 160 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
index 8a2f9c2df..db58add0d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
-*- coding: utf-8 -*-
Changes for APR 1.4.0
+ *) Add apr_socket_atreadeof to determine whether the receive part of the
+ socket has been closed by the peer.
+ [Ruediger Pluem, Mladen Turk, Joe Orton]
+
*) Make apr_pollset and apr_pollcb implementations using providers.
Added apr_pollset_create_ex and apr_pollcb_create_ex that allows
choosing non-default providers.
diff --git a/include/apr_network_io.h b/include/apr_network_io.h
index 4e3789d2b..d90b37e7f 100644
--- a/include/apr_network_io.h
+++ b/include/apr_network_io.h
@@ -348,6 +348,21 @@ APR_DECLARE(apr_status_t) apr_socket_connect(apr_socket_t *sock,
apr_sockaddr_t *sa);
/**
+ * 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
+ * @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(apr_status_t) apr_socket_atreadeof(apr_socket_t *sock,
+ int *atreadeof);
+
+/**
* Create apr_sockaddr_t from hostname, address family, and port.
* @param sa The new apr_sockaddr_t.
* @param hostname The hostname or numeric address string to resolve/parse, or
diff --git a/libapr.dsp b/libapr.dsp
index 485f1c761..cb54fcb8a 100644
--- a/libapr.dsp
+++ b/libapr.dsp
@@ -436,6 +436,10 @@ SOURCE=.\network_io\unix\multicast.c
# End Source File
# Begin Source File
+SOURCE=.\network_io\unix\socket_util.c
+# End Source File
+# Begin Source File
+
SOURCE=.\network_io\win32\sendrecv.c
# End Source File
# Begin Source File
diff --git a/network_io/beos/socketcommon.c b/network_io/beos/socketcommon.c
index cdadc8561..b9f594bbe 100644
--- a/network_io/beos/socketcommon.c
+++ b/network_io/beos/socketcommon.c
@@ -3,3 +3,4 @@
#include "../unix/sockets.c"
#include "../unix/sockaddr.c"
#include "../unix/sockopt.c"
+#include "../unix/socket_util.c"
diff --git a/network_io/os2/socket_util.c b/network_io/os2/socket_util.c
new file mode 100644
index 000000000..cdc1cea89
--- /dev/null
+++ b/network_io/os2/socket_util.c
@@ -0,0 +1 @@
+#include "../unix/socket_util.c"
diff --git a/network_io/unix/socket_util.c b/network_io/unix/socket_util.c
new file mode 100644
index 000000000..b23aed6b2
--- /dev/null
+++ b/network_io/unix/socket_util.c
@@ -0,0 +1,74 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_network_io.h"
+#include "apr_poll.h"
+
+apr_status_t apr_socket_atreadeof(apr_socket_t *sock, int *atreadeof)
+{
+ apr_pollfd_t pfds[1];
+ 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 = sock;
+
+ do {
+ rv = apr_poll(&pfds[0], 1, &nfds, 0);
+ } while (APR_STATUS_IS_EINTR(rv));
+
+ 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;
+
+ /* 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;
+ }
+ }
+
+ /* Should not fall through here. */
+ return APR_EGENERAL;
+}
+
diff --git a/test/sockchild.c b/test/sockchild.c
index 3803d00af..ec7ffb85c 100644
--- a/test/sockchild.c
+++ b/test/sockchild.c
@@ -76,5 +76,9 @@ int main(int argc, char *argv[])
apr_socket_close(sock);
exit((int)length);
}
+ else if (!strcmp("close", argv[1])) {
+ apr_socket_close(sock);
+ exit(0);
+ }
exit(-1);
}
diff --git a/test/testsock.c b/test/testsock.c
index 50aef5c2b..1684ea655 100644
--- a/test/testsock.c
+++ b/test/testsock.c
@@ -198,6 +198,62 @@ static void test_recv(abts_case *tc, void *data)
APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
}
+static void test_atreadeof(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ apr_size_t length = STRLEN;
+ char datastr[STRLEN];
+ int atreadeof = -1;
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ launch_child(tc, &proc, "write", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ /* Check that the remote socket is still open */
+ 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);
+
+ /* Make sure that the server received the data we sent */
+ ABTS_STR_EQUAL(tc, DATASTR, datastr);
+ ABTS_SIZE_EQUAL(tc, strlen(datastr), wait_child(tc, &proc));
+
+ /* The child is dead, so should be the remote socket */
+ 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);
+
+ launch_child(tc, &proc, "close", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ /* The child closed the socket instantly */
+ 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);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
static void test_timeout(abts_case *tc, void *data)
{
apr_status_t rv;
@@ -345,6 +401,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_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);