From 74dd0183e4e56b07cedfa87eae7a8fb3166f01e8 Mon Sep 17 00:00:00 2001 From: Lorry Date: Tue, 28 Aug 2012 15:30:14 +0100 Subject: Tarball conversion --- test/server/test_server.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 test/server/test_server.c (limited to 'test/server/test_server.c') diff --git a/test/server/test_server.c b/test/server/test_server.c new file mode 100644 index 0000000..1ad2c9e --- /dev/null +++ b/test/server/test_server.c @@ -0,0 +1,359 @@ +/* Copyright 2011 Justin Erenkrantz and Greg Stein + * + * Licensed 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.h" +#include "apr_pools.h" +#include +#include +#include + +#include "serf.h" + +#include "test_server.h" + +struct serv_ctx_t { + /* Pool for resource allocation. */ + apr_pool_t *pool; + + apr_int32_t options; + + /* Array of actions which server will replay when client connected. */ + test_server_action_t *action_list; + /* Size of action_list array. */ + apr_size_t action_count; + /* Index of current action. */ + apr_size_t cur_action; + + /* Array of messages the server will receive from the client. */ + test_server_message_t *message_list; + /* Size of message_list array. */ + apr_size_t message_count; + /* Index of current message. */ + apr_size_t cur_message; + + /* Number of messages received that the server didn't respond to yet. */ + apr_size_t outstanding_responses; + + /* Position in message buffer (incoming messages being read). */ + apr_size_t message_buf_pos; + + /* Position in action buffer. (outgoing messages being sent). */ + apr_size_t action_buf_pos; + + /* Address for server binding. */ + apr_sockaddr_t *serv_addr; + apr_socket_t *serv_sock; + + /* Accepted client socket. NULL if there is no client socket. */ + apr_socket_t *client_sock; + +}; + +/* Replay support functions */ +static void next_message(serv_ctx_t *servctx) +{ + servctx->cur_message++; +} + +static void next_action(serv_ctx_t *servctx) +{ + servctx->cur_action++; + servctx->action_buf_pos = 0; +} + +/* Verify received requests and take the necessary actions + (return a response, kill the connection ...) */ +static apr_status_t replay(serv_ctx_t *servctx, + apr_int16_t rtnevents, + apr_pool_t *pool) +{ + apr_status_t status = APR_SUCCESS; + test_server_action_t *action; + + if (rtnevents & APR_POLLIN) { + if (servctx->message_list == NULL) { + /* we're not expecting any requests to reach this server! */ + printf("Received request where none was expected\n"); + + return APR_EGENERAL; + } + + if (servctx->cur_action >= servctx->action_count) { + char buf[128]; + apr_size_t len = sizeof(buf); + + status = apr_socket_recv(servctx->client_sock, buf, &len); + if (! APR_STATUS_IS_EAGAIN(status)) { + /* we're out of actions! */ + printf("Received more requests than expected.\n"); + + return APR_EGENERAL; + } + return status; + } + + action = &servctx->action_list[servctx->cur_action]; + + if (action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) { + char buf[128]; + apr_size_t len = sizeof(buf); + + status = apr_socket_recv(servctx->client_sock, buf, &len); + + if (status == APR_EOF) { + apr_socket_close(servctx->client_sock); + servctx->client_sock = NULL; + next_action(servctx); + return APR_SUCCESS; + } + + return status; + } + else if (action->kind == SERVER_RECV || + (action->kind == SERVER_RESPOND && + servctx->outstanding_responses == 0)) { + apr_size_t msg_len, len; + char buf[128]; + test_server_message_t *message; + + message = &servctx->message_list[servctx->cur_message]; + msg_len = strlen(message->text); + + len = msg_len - servctx->message_buf_pos; + if (len > sizeof(buf)) + len = sizeof(buf); + + status = apr_socket_recv(servctx->client_sock, buf, &len); + if (status != APR_SUCCESS) + return status; + + if (servctx->options & TEST_SERVER_DUMP) + fwrite(buf, len, 1, stdout); + + if (strncmp(buf, message->text + servctx->message_buf_pos, len) != 0) { + /* ## TODO: Better diagnostics. */ + printf("Expected: (\n"); + fwrite(message->text + servctx->message_buf_pos, len, 1, stdout); + printf(")\n"); + printf("Actual: (\n"); + fwrite(buf, len, 1, stdout); + printf(")\n"); + + return APR_EGENERAL; + } + + servctx->message_buf_pos += len; + + if (servctx->message_buf_pos >= msg_len) { + next_message(servctx); + servctx->message_buf_pos -= msg_len; + if (action->kind == SERVER_RESPOND) + servctx->outstanding_responses++; + if (action->kind == SERVER_RECV) + next_action(servctx); + } + } + } + if (rtnevents & APR_POLLOUT) { + action = &servctx->action_list[servctx->cur_action]; + + if (action->kind == SERVER_RESPOND && servctx->outstanding_responses) { + apr_size_t msg_len; + apr_size_t len; + + msg_len = strlen(action->text); + len = msg_len - servctx->action_buf_pos; + + status = apr_socket_send(servctx->client_sock, + action->text + servctx->action_buf_pos, &len); + if (status != APR_SUCCESS) + return status; + + if (servctx->options & TEST_SERVER_DUMP) + fwrite(action->text + servctx->action_buf_pos, len, 1, stdout); + + servctx->action_buf_pos += len; + + if (servctx->action_buf_pos >= msg_len) { + next_action(servctx); + servctx->outstanding_responses--; + } + } + else if (action->kind == SERVER_KILL_CONNECTION || + action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) { + apr_socket_close(servctx->client_sock); + servctx->client_sock = NULL; + next_action(servctx); + } + } + else if (rtnevents & APR_POLLIN) { + /* ignore */ + } + else { + printf("Unknown rtnevents: %d\n", rtnevents); + abort(); + } + + return status; +} + +apr_status_t test_server_run(serv_ctx_t *servctx, + apr_short_interval_time_t duration, + apr_pool_t *pool) +{ + apr_status_t status; + apr_pollset_t *pollset; + apr_int32_t num; + const apr_pollfd_t *desc; + + /* create a new pollset */ + status = apr_pollset_create(&pollset, 32, pool, 0); + if (status != APR_SUCCESS) + return status; + + /* Don't accept new connection while processing client connection. At + least for present time.*/ + if (servctx->client_sock) { + apr_pollfd_t pfd = { pool, APR_POLL_SOCKET, APR_POLLIN | APR_POLLOUT, 0, + { NULL }, NULL }; + pfd.desc.s = servctx->client_sock; + status = apr_pollset_add(pollset, &pfd); + if (status != APR_SUCCESS) + goto cleanup; + } + else { + apr_pollfd_t pfd = { pool, APR_POLL_SOCKET, APR_POLLIN, 0, + { NULL }, NULL }; + pfd.desc.s = servctx->serv_sock; + status = apr_pollset_add(pollset, &pfd); + if (status != APR_SUCCESS) + goto cleanup; + } + + status = apr_pollset_poll(pollset, APR_USEC_PER_SEC >> 1, &num, &desc); + if (status != APR_SUCCESS) + goto cleanup; + + while (num--) { + if (desc->desc.s == servctx->serv_sock) { + status = apr_socket_accept(&servctx->client_sock, servctx->serv_sock, + servctx->pool); + if (status != APR_SUCCESS) + goto cleanup; + + apr_socket_opt_set(servctx->client_sock, APR_SO_NONBLOCK, 1); + apr_socket_timeout_set(servctx->client_sock, 0); + + status = APR_SUCCESS; + goto cleanup; + } + + if (desc->desc.s == servctx->client_sock) { + /* Replay data to socket. */ + status = replay(servctx, desc->rtnevents, pool); + + if (APR_STATUS_IS_EOF(status)) { + apr_socket_close(servctx->client_sock); + servctx->client_sock = NULL; + } + else if (APR_STATUS_IS_EAGAIN(status)) { + status = APR_SUCCESS; + } + else if (status != APR_SUCCESS) { + /* Real error. */ + goto cleanup; + } + } + + desc++; + } + +cleanup: + apr_pollset_destroy(pollset); + + return status; +} + +/* Start a TCP server on port SERV_PORT in thread THREAD. srv_replay is a array + of action to replay when connection started. replay_count is count of + actions in srv_replay. */ +apr_status_t test_start_server(serv_ctx_t **servctx_p, + apr_sockaddr_t *address, + test_server_message_t *message_list, + apr_size_t message_count, + test_server_action_t *action_list, + apr_size_t action_count, + apr_int32_t options, + apr_pool_t *pool) +{ + apr_status_t status; + apr_socket_t *serv_sock; + serv_ctx_t *servctx; + + servctx = apr_pcalloc(pool, sizeof(*servctx)); + *servctx_p = servctx; + + servctx->serv_addr = address; + servctx->options = options; + servctx->pool = pool; + servctx->message_list = message_list; + servctx->message_count = message_count; + servctx->action_list = action_list; + servctx->action_count = action_count; + + /* create server socket */ +#if APR_VERSION_AT_LEAST(1, 0, 0) + status = apr_socket_create(&serv_sock, address->family, SOCK_STREAM, 0, + pool); +#else + status = apr_socket_create(&serv_sock, address->family, SOCK_STREAM, pool); +#endif + + if (status != APR_SUCCESS) + return status; + + apr_socket_opt_set(serv_sock, APR_SO_NONBLOCK, 1); + apr_socket_timeout_set(serv_sock, 0); + apr_socket_opt_set(serv_sock, APR_SO_REUSEADDR, 1); + + status = apr_socket_bind(serv_sock, servctx->serv_addr); + if (status != APR_SUCCESS) + return status; + + /* Start replay from first action. */ + servctx->cur_action = 0; + servctx->action_buf_pos = 0; + servctx->outstanding_responses = 0; + + /* listen for clients */ + apr_socket_listen(serv_sock, SOMAXCONN); + if (status != APR_SUCCESS) + return status; + + servctx->serv_sock = serv_sock; + servctx->client_sock = NULL; + return APR_SUCCESS; +} + +apr_status_t test_server_destroy(serv_ctx_t *servctx, apr_pool_t *pool) +{ + apr_socket_close(servctx->serv_sock); + + if (servctx->client_sock) { + apr_socket_close(servctx->client_sock); + } + + return APR_SUCCESS; +} -- cgit v1.2.1