diff options
Diffstat (limited to 'test/testsock.c')
-rw-r--r-- | test/testsock.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/test/testsock.c b/test/testsock.c new file mode 100644 index 0000000..2ca3113 --- /dev/null +++ b/test/testsock.c @@ -0,0 +1,297 @@ +/* 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 "testutil.h" +#include "testsock.h" +#include "apr_thread_proc.h" +#include "apr_network_io.h" +#include "apr_errno.h" +#include "apr_general.h" +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_poll.h" + +static void launch_child(abts_case *tc, apr_proc_t *proc, const char *arg1, apr_pool_t *p) +{ + apr_procattr_t *procattr; + const char *args[3]; + apr_status_t rv; + + rv = apr_procattr_create(&procattr, p); + APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv); + + rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE, + APR_NO_PIPE); + APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv); + + rv = apr_procattr_error_check_set(procattr, 1); + APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv); + + args[0] = "sockchild" EXTENSION; + args[1] = arg1; + args[2] = NULL; + rv = apr_proc_create(proc, TESTBINPATH "sockchild" EXTENSION, args, NULL, + procattr, p); + APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv); +} + +static int wait_child(abts_case *tc, apr_proc_t *proc) +{ + int exitcode; + apr_exit_why_e why; + + ABTS_ASSERT(tc, "Error waiting for child process", + apr_proc_wait(proc, &exitcode, &why, APR_WAIT) == APR_CHILD_DONE); + + ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT); + return exitcode; +} + +static void test_addr_info(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_sockaddr_t *sa; + + rv = apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 80, 0, p); + APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv); + + rv = apr_sockaddr_info_get(&sa, "127.0.0.1", APR_UNSPEC, 80, 0, p); + APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv); + ABTS_STR_EQUAL(tc, "127.0.0.1", sa->hostname); +} + +static apr_socket_t *setup_socket(abts_case *tc) +{ + apr_status_t rv; + apr_sockaddr_t *sa; + apr_socket_t *sock; + + rv = apr_sockaddr_info_get(&sa, "127.0.0.1", APR_INET, 8021, 0, p); + APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv); + + rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, p); + APR_ASSERT_SUCCESS(tc, "Problem creating socket", rv); + + rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1); + APR_ASSERT_SUCCESS(tc, "Could not set REUSEADDR on socket", rv); + + rv = apr_socket_bind(sock, sa); + APR_ASSERT_SUCCESS(tc, "Problem binding to port", rv); + if (rv) return NULL; + + rv = apr_socket_listen(sock, 5); + APR_ASSERT_SUCCESS(tc, "Problem listening on socket", rv); + + return sock; +} + +static void test_create_bind_listen(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_socket_t *sock = setup_socket(tc); + + if (!sock) return; + + rv = apr_socket_close(sock); + APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv); +} + +static void test_send(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_socket_t *sock; + apr_socket_t *sock2; + apr_proc_t proc; + int protocol; + apr_size_t length; + + sock = setup_socket(tc); + if (!sock) return; + + launch_child(tc, &proc, "read", p); + + rv = apr_socket_accept(&sock2, sock, p); + APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv); + + apr_socket_protocol_get(sock2, &protocol); + ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol); + + length = strlen(DATASTR); + apr_socket_send(sock2, DATASTR, &length); + + /* Make sure that the client received the data we sent */ + ABTS_SIZE_EQUAL(tc, strlen(DATASTR), 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_recv(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_socket_t *sock; + apr_socket_t *sock2; + apr_proc_t proc; + int protocol; + apr_size_t length = STRLEN; + char datastr[STRLEN]; + + 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); + + apr_socket_protocol_get(sock2, &protocol); + ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol); + + 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)); + + 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; + apr_socket_t *sock; + apr_socket_t *sock2; + apr_proc_t proc; + int protocol; + int exit; + + sock = setup_socket(tc); + if (!sock) return; + + launch_child(tc, &proc, "read", p); + + rv = apr_socket_accept(&sock2, sock, p); + APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv); + + apr_socket_protocol_get(sock2, &protocol); + ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol); + + exit = wait_child(tc, &proc); + ABTS_INT_EQUAL(tc, SOCKET_TIMEOUT, exit); + + /* We didn't write any data, so make sure the child program returns + * an error. + */ + 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_get_addr(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_socket_t *ld, *sd, *cd; + apr_sockaddr_t *sa, *ca; + char a[128], b[128]; + + ld = setup_socket(tc); + + APR_ASSERT_SUCCESS(tc, + "get local address of bound socket", + apr_socket_addr_get(&sa, APR_LOCAL, ld)); + + rv = apr_socket_create(&cd, sa->family, SOCK_STREAM, + APR_PROTO_TCP, p); + APR_ASSERT_SUCCESS(tc, "create client socket", rv); + + APR_ASSERT_SUCCESS(tc, "enable non-block mode", + apr_socket_opt_set(cd, APR_SO_NONBLOCK, 1)); + + /* It is valid for a connect() on a socket with NONBLOCK set to + * succeed (if the connection can be established synchronously), + * but if it does, this test cannot proceed. */ + rv = apr_socket_connect(cd, sa); + if (rv == APR_SUCCESS) { + apr_socket_close(ld); + apr_socket_close(cd); + ABTS_NOT_IMPL(tc, "Cannot test if connect completes " + "synchronously"); + return; + } + + if (!APR_STATUS_IS_EINPROGRESS(rv)) { + apr_socket_close(ld); + apr_socket_close(cd); + APR_ASSERT_SUCCESS(tc, "connect to listener", rv); + return; + } + + APR_ASSERT_SUCCESS(tc, "accept connection", + apr_socket_accept(&sd, ld, p)); + + { + /* wait for writability */ + apr_pollfd_t pfd; + int n; + + pfd.p = p; + pfd.desc_type = APR_POLL_SOCKET; + pfd.reqevents = APR_POLLOUT|APR_POLLHUP; + pfd.desc.s = cd; + pfd.client_data = NULL; + + APR_ASSERT_SUCCESS(tc, "poll for connect completion", + apr_poll(&pfd, 1, &n, 5 * APR_USEC_PER_SEC)); + + } + + APR_ASSERT_SUCCESS(tc, "get local address of server socket", + apr_socket_addr_get(&sa, APR_LOCAL, sd)); + + APR_ASSERT_SUCCESS(tc, "get remote address of client socket", + apr_socket_addr_get(&ca, APR_REMOTE, cd)); + + apr_snprintf(a, sizeof(a), "%pI", sa); + apr_snprintf(b, sizeof(b), "%pI", ca); + + ABTS_STR_EQUAL(tc, a, b); + + apr_socket_close(cd); + apr_socket_close(sd); + apr_socket_close(ld); +} + +abts_suite *testsock(abts_suite *suite) +{ + suite = ADD_SUITE(suite) + + abts_run_test(suite, test_addr_info, NULL); + 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_timeout, NULL); + abts_run_test(suite, test_get_addr, NULL); + + return suite; +} + |