From 988e61fd42663d9d2a251be93ab3cfa3b0760a7f Mon Sep 17 00:00:00 2001 From: rpluem Date: Fri, 16 Oct 2009 21:08:15 +0000 Subject: 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 --- CHANGES | 4 +++ include/apr_network_io.h | 15 +++++++++ libapr.dsp | 4 +++ network_io/beos/socketcommon.c | 1 + network_io/os2/socket_util.c | 1 + network_io/unix/socket_util.c | 74 ++++++++++++++++++++++++++++++++++++++++++ test/sockchild.c | 4 +++ test/testsock.c | 57 ++++++++++++++++++++++++++++++++ 8 files changed, 160 insertions(+) create mode 100644 network_io/os2/socket_util.c create mode 100644 network_io/unix/socket_util.c 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 @@ -347,6 +347,21 @@ APR_DECLARE(apr_status_t) apr_socket_accept(apr_socket_t **new_sock, 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. 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); -- cgit v1.2.1