| /* Copyright 2000-2004 The Apache Software Foundation |
| * |
| * 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_lib.h" |
| #include "apr_strings.h" |
| #include "apr_buckets.h" |
| #include "apr_md5.h" |
| #include "apr_network_io.h" |
| #include "apr_pools.h" |
| #include "apr_strings.h" |
| #include "apr_uri.h" |
| #include "apr_date.h" |
| #include "apr_fnmatch.h" |
| #define APR_WANT_STRFUNC |
| #include "apr_want.h" |
| |
| #include "apr_hooks.h" |
| #include "apr_optional_hooks.h" |
| #include "apr_buckets.h" |
| |
| #include "ajp.h" |
| |
| #if APR_HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if APR_HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| |
| #define TEST_POST_DATA "This document is a proposal of evolution of the current " \ |
| "Apache JServ Protocol version 1.3, also known as ajp13. " \ |
| "I'll not cover here the full protocol but only the add-on from ajp13. " \ |
| "This nth pass include comments from the tomcat-dev list and " \ |
| "misses discovered during developpment." |
| |
| #define TEST_CASE_URL "http://localhost/servlets-examples/servlet/RequestHeaderExample" |
| |
| /* Main process */ |
| static process_rec *main_process; |
| /* Default server */ |
| static server_rec *main_server; |
| |
| /* This function is part of backend. |
| * The backend return connected socket and conn_rec |
| */ |
| static apr_status_t connect_to_backend(apr_socket_t **socket, conn_rec **con, |
| const char *host, apr_uint16_t port, |
| server_rec *server, apr_pool_t *pool) |
| { |
| apr_status_t rv; |
| apr_sockaddr_t *remote_sa; |
| |
| if ((rv = apr_sockaddr_info_get(&remote_sa, host, APR_UNSPEC, |
| port, 0, pool)) != APR_SUCCESS) |
| return rv; |
| if ((rv = apr_socket_create(socket, remote_sa->family, SOCK_STREAM, |
| #if (APR_MAJOR_VERSION > 0) |
| APR_PROTO_TCP, |
| #endif |
| pool)) != APR_SUCCESS) |
| return rv; |
| if ((rv = apr_socket_timeout_set(*socket, |
| apr_time_from_sec(3))) != APR_SUCCESS) |
| return rv; |
| if ((rv = apr_socket_connect(*socket, remote_sa)) != APR_SUCCESS) |
| return rv; |
| |
| if (!(*con = ap_run_create_connection(pool, server, *socket, |
| 0, NULL, NULL))) |
| return APR_EGENERAL; |
| |
| return APR_SUCCESS; |
| } |
| |
| #if APR_HAS_THREADS |
| |
| #define TEST_THREAD_COUNT 10 |
| static apr_thread_t *threads[TEST_THREAD_COUNT]; |
| |
| static void * APR_THREAD_FUNC thread_worker_func(apr_thread_t *thd, void *data) |
| { |
| request_rec *r; |
| conn_rec *c = (conn_rec *)data; |
| |
| /* Create an empty request */ |
| if (!(r = ap_wrap_create_request(c))) |
| goto cleanup; |
| |
| /* TODO: do something usefull */ |
| apr_sleep(apr_time_from_sec(1)); |
| |
| /* Clean up the request */ |
| apr_pool_destroy(r->pool); |
| |
| apr_thread_exit(thd, APR_SUCCESS); |
| return NULL; |
| cleanup: |
| apr_thread_exit(thd, APR_EGENERAL); |
| return NULL; |
| } |
| |
| static int create_threads(server_rec *server) |
| { |
| int i; |
| apr_status_t rv; |
| |
| for (i = 0; i < TEST_THREAD_COUNT; i++) { |
| conn_rec *c; |
| /* Create a single client connection. The dummy one of course. */ |
| if (!(c = ap_run_create_connection(server->process->pool, server, |
| NULL, 0, NULL, NULL))) |
| return -1; |
| |
| if ((rv = apr_thread_create(&threads[i], NULL, |
| thread_worker_func, (void *)c, |
| server->process->pool)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_INFO, rv, NULL, "apr_create_thread failed"); |
| return -1; |
| } |
| } |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Created %d worker threads", i); |
| |
| return 0; |
| } |
| |
| static int join_threads() |
| { |
| int i, rc = 0; |
| apr_status_t rv; |
| |
| for (i = 0; i < TEST_THREAD_COUNT; i++) { |
| apr_thread_join(&rv, threads[i]); |
| if (rv != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "Worker thread %d failed", i); |
| rc = -1; |
| } |
| } |
| |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "All (%d) worker threads joined", i); |
| |
| return rc; |
| } |
| |
| #endif |
| |
| int main(int argc, const char * const * argv, const char * const *env) |
| { |
| int rv = 0; |
| apr_status_t rc; |
| conn_rec *c, *con; |
| request_rec *r; |
| apr_socket_t *sock; |
| ajp_msg_t *msg; |
| char *buf; |
| apr_size_t len; |
| |
| apr_app_initialize(&argc, &argv, &env); |
| |
| /* This is done in httpd.conf using LogLevel debug directive. |
| * We are setting this directly. |
| */ |
| ap_default_loglevel = APLOG_DEBUG; |
| |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Testing ajp..."); |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "Creating main server..."); |
| |
| main_process = ap_wrap_create_process(argc, argv); |
| /* Create the main server_rec */ |
| main_server = ap_wrap_create_server(main_process, main_process->pool); |
| /* Create a single client connection. The dummy one of course. */ |
| if (!(c = ap_run_create_connection(main_process->pool, main_server, |
| NULL, 0, NULL, NULL))) { |
| rv = -1; |
| goto finished; |
| } |
| /* ... and a empty request */ |
| if (!(r = ap_wrap_create_request(c))) { |
| rv = -1; |
| goto finished; |
| } |
| |
| |
| /* 0. Fill in the request data */ |
| if (ap_wrap_make_request(r, TEST_CASE_URL, |
| "POST", |
| NULL, //"application/x-www-form-urlencoded", |
| NULL, |
| sizeof(TEST_POST_DATA) - 1, |
| TEST_POST_DATA) != APR_SUCCESS) { |
| goto finished; |
| } |
| /* |
| * Up to here HTTPD created that for each request. |
| * From now on, we have a server_rec, conn_rec, and request_rec |
| * Who will ever need something else :) |
| */ |
| |
| /* 1. Obtain a connection to backend */ |
| if ((rc = connect_to_backend(&sock, &con, AJP13_DEF_HOST, AJP13_DEF_PORT, |
| main_server, r->pool)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "connect_to_backend"); |
| rv = -1; |
| goto finished; |
| } |
| |
| /* 2. Create AJP message */ |
| if ((rc = ajp_send_header(sock, r)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "ajp_send_header"); |
| rv = -1; |
| goto finished; |
| } |
| /* 3. Send AJP message */ |
| |
| ajp_alloc_data_msg(r, &buf, &len, &msg); |
| |
| /* Send the initial POST BODY */ |
| if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, |
| "Error ajp_marshal_into_msgb - " |
| "Can't setting client block"); |
| } |
| if (ap_should_client_block(r)) { |
| len = ap_get_client_block(r, buf, len); |
| } |
| else |
| len = ap_get_client_block(r, buf, len); |
| |
| |
| ajp_send_data_msg(sock, r, msg, len); |
| |
| /* 4. Read AJP response */ |
| if ((rc = ajp_read_header(sock, r, &msg)) != APR_SUCCESS) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, rc, NULL, "ajp_read_header"); |
| rv = -1; |
| goto finished; |
| } |
| |
| ajp_msg_create(r->pool, &msg); |
| ajp_msg_reset(msg); |
| ajp_ilink_receive(sock, msg); |
| |
| /* 5. Display results */ |
| #if 1 |
| ajp_msg_dump(msg, ""); |
| #endif |
| { |
| apr_uint16_t blen; |
| ajp_parse_data(r, msg, &blen, &buf); |
| fputs(buf, stdout); |
| } |
| /* 6. Release the connection */ |
| |
| |
| #if APR_HAS_THREADS_remove_this |
| /* or make the few requests in paralel |
| * to test the threading. |
| * the upper 7 steps will go to thread_worker_func |
| */ |
| if ((rv = create_threads(main_server))) |
| goto finished; |
| |
| if ((rv = join_threads())) |
| goto finished; |
| #endif |
| |
| finished: |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "%s", rv == 0 ? "OK" : "FAILED"); |
| apr_terminate(); |
| |
| return rv; |
| } |