From 9401f816dd0d9d550fe98a8507224bde51c4b847 Mon Sep 17 00:00:00 2001 From: hongbotian Date: Mon, 30 Nov 2015 02:41:33 -0500 Subject: upload tomcat JIRA: BOTTLENECK-7 Change-Id: I875d474869efd76ca203c30b60ebc0c3ee606d0e Signed-off-by: hongbotian --- .../native/apache-1.3/mod_jk.c | 3338 ++++++++++++++++++++ 1 file changed, 3338 insertions(+) create mode 100644 rubbos/app/tomcat-connectors-1.2.32-src/native/apache-1.3/mod_jk.c (limited to 'rubbos/app/tomcat-connectors-1.2.32-src/native/apache-1.3/mod_jk.c') diff --git a/rubbos/app/tomcat-connectors-1.2.32-src/native/apache-1.3/mod_jk.c b/rubbos/app/tomcat-connectors-1.2.32-src/native/apache-1.3/mod_jk.c new file mode 100644 index 00000000..2783df2a --- /dev/null +++ b/rubbos/app/tomcat-connectors-1.2.32-src/native/apache-1.3/mod_jk.c @@ -0,0 +1,3338 @@ +/* + * 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. + */ + +/*************************************************************************** + * Description: Apache 1.3 plugin for Tomcat * + * See ../common/jk_service.h for general mod_jk info * + * Author: Gal Shachor * + * Dan Milstein * + * Henri Gomez * + * Version: $Revision: 1126561 $ * + ***************************************************************************/ + +/* + * mod_jk: keeps all servlet related ramblings together. + */ + +/* #include "ap_config.h" */ +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#include "util_script.h" +#include "util_date.h" +#include "http_conf_globals.h" + +/* + * jk_ include files + */ +#ifdef NETWARE +#define _SYS_TYPES_H_ +#define _NETDB_H_INCLUDED +#define _IN_ +#define _INET_ +#define _SYS_TIMEVAL_H_ +#define _SYS_SOCKET_H_ +#endif +#include "jk_global.h" +#include "jk_util.h" +#include "jk_map.h" +#include "jk_pool.h" +#include "jk_service.h" +#include "jk_worker.h" +#include "jk_uri_worker_map.h" +#include "jk_ajp13.h" +#include "jk_shm.h" +#include "jk_url.h" + +#define JK_LOG_DEF_FILE ("logs/mod_jk.log") +#define JK_SHM_DEF_FILE ("logs/jk-runtime-status") +#define JK_ENV_REMOTE_ADDR ("JK_REMOTE_ADDR") +#define JK_ENV_REMOTE_PORT ("JK_REMOTE_PORT") +#define JK_ENV_REMOTE_HOST ("JK_REMOTE_HOST") +#define JK_ENV_REMOTE_USER ("JK_REMOTE_USER") +#define JK_ENV_AUTH_TYPE ("JK_AUTH_TYPE") +#define JK_ENV_LOCAL_NAME ("JK_LOCAL_NAME") +#define JK_ENV_LOCAL_PORT ("JK_LOCAL_PORT") +#define JK_ENV_HTTPS ("HTTPS") +#define JK_ENV_CERTS ("SSL_CLIENT_CERT") +#define JK_ENV_CIPHER ("SSL_CIPHER") +#define JK_ENV_SESSION ("SSL_SESSION_ID") +#define JK_ENV_KEY_SIZE ("SSL_CIPHER_USEKEYSIZE") +#define JK_ENV_CERTCHAIN_PREFIX ("SSL_CLIENT_CERT_CHAIN_") +#define JK_ENV_REPLY_TIMEOUT ("JK_REPLY_TIMEOUT") +#define JK_ENV_WORKER_NAME ("JK_WORKER_NAME") +#define JK_NOTE_WORKER_NAME ("JK_WORKER_NAME") +#define JK_NOTE_WORKER_TYPE ("JK_WORKER_TYPE") +#define JK_NOTE_REQUEST_DURATION ("JK_REQUEST_DURATION") +#define JK_NOTE_WORKER_ROUTE ("JK_WORKER_ROUTE") +#define JK_HANDLER ("jakarta-servlet") +#define JK_MAGIC_TYPE ("application/x-jakarta-servlet") +#define NULL_FOR_EMPTY(x) ((x && !strlen(x)) ? NULL : x) +#define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)") + +/* + * If you are not using SSL, comment out the following line. It will make + * apache run faster. + * + * Personally, I (DM), think this may be a lie. + */ +#define ADD_SSL_INFO + +module MODULE_VAR_EXPORT jk_module; +#ifdef WIN32 +extern __declspec(dllimport) module dir_module; +#else +extern module dir_module; +#endif + +static int xfer_flags = (O_WRONLY | O_APPEND | O_CREAT); +#if defined(OS2) || defined(WIN32) || defined(NETWARE) +/* OS/2 dosen't support users and groups */ +static mode_t xfer_mode = (S_IREAD | S_IWRITE); +#else +static mode_t xfer_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif + +/* + * Environment variable forward object + */ +typedef struct +{ + int has_default; + char *name; + char *value; +} envvar_item; + +/* + * Configuration object for the mod_jk module. + */ +typedef struct +{ + + /* + * Log stuff + */ + char *log_file; + int log_fd; + int log_level; + jk_logger_t *log; + + /* + * Mount stuff + */ + char *mount_file; + int mount_file_reload; + jk_map_t *uri_to_context; + + int mountcopy; + + jk_uri_worker_map_t *uw_map; + + int was_initialized; + + /* + * Automatic context path apache alias + */ + char *alias_dir; + + /* + * Request Logging + */ + + char *stamp_format_string; + char *format_string; + array_header *format; + + /* + * Setting target worker via environment + */ + char *worker_indicator; + + /* + * Configurable environment variables to overwrite + * request information using meta data send by a + * proxy in front of us. + */ + char *remote_addr_indicator; + char *remote_port_indicator; + char *remote_host_indicator; + char *remote_user_indicator; + char *auth_type_indicator; + char *local_name_indicator; + char *local_port_indicator; + + /* + * SSL Support + */ + int ssl_enable; + char *https_indicator; + char *certs_indicator; + char *cipher_indicator; + char *session_indicator; + char *key_size_indicator; + char *certchain_prefix; /* Client certificate chain prefix */ + + /* + * Jk Options + */ + int options; + int exclude_options; + + int strip_session; + /* + * Environment variables support + */ + int envvars_has_own; + table *envvars; + table *envvars_def; + array_header *envvar_items; + + server_rec *s; +} jk_server_conf_t; + + +/* + * The "private", or subclass portion of the web server service class for + * Apache 1.3. An instance of this class is created for each request + * handled. See jk_service.h for details about the ws_service object in + * general. + */ +struct apache_private_data +{ + /* + * For memory management for this request. Aliased to be identical to + * the pool in the superclass (jk_ws_service). + */ + jk_pool_t p; + + /* True iff request body data has been read from Apache */ + int read_body_started; + + /* Apache request structure */ + request_rec *r; +}; +typedef struct apache_private_data apache_private_data_t; + +typedef struct dir_config_struct +{ + array_header *index_names; +} dir_config_rec; + +static server_rec *main_server = NULL; +static jk_logger_t *main_log = NULL; +static table *jk_log_fds = NULL; +static jk_worker_env_t worker_env; +static char *jk_shm_file = NULL; +static size_t jk_shm_size = 0; +/* + * Worker stuff +*/ +static jk_map_t *jk_worker_properties = NULL; +static char *jk_worker_file = NULL; +static int jk_mount_copy_all = JK_FALSE; + +static int JK_METHOD ws_start_response(jk_ws_service_t *s, + int status, + const char *reason, + const char *const *header_names, + const char *const *header_values, + unsigned num_of_headers); + +static int JK_METHOD ws_read(jk_ws_service_t *s, + void *b, unsigned l, unsigned *a); + +static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l); + +static void JK_METHOD ws_add_log_items(jk_ws_service_t *s, + const char *const *log_names, + const char *const *log_values, + unsigned num_of_log_items); + +static void * JK_METHOD ws_next_vhost(void *d); + +static void JK_METHOD ws_vhost_to_text(void *d, char *buf, size_t len); + +static jk_uri_worker_map_t * JK_METHOD ws_vhost_to_uw_map(void *d); + +/* srevilak - new function prototypes */ +static void jk_server_cleanup(void *data); +static void jk_generic_cleanup(server_rec * s); + + + +/* ====================================================================== */ +/* JK Service step callbacks */ +/* ====================================================================== */ + + +/* + * Send the HTTP response headers back to the browser. + * + * Think of this function as a method of the apache1.3-specific subclass of + * the jk_ws_service class. Think of the *s param as a "this" or "self" + * pointer. + */ +static int JK_METHOD ws_start_response(jk_ws_service_t *s, + int status, + const char *reason, + const char *const *header_names, + const char *const *header_values, + unsigned num_of_headers) +{ + if (s && s->ws_private) { + unsigned h; + + /* Obtain a subclass-specific "this" pointer */ + apache_private_data_t *p = s->ws_private; + request_rec *r = p->r; + + /* If we use proxy error pages, still pass + * through context headers needed for special status codes. + */ + if (s->extension.use_server_error_pages && + status >= s->extension.use_server_error_pages) { + if (status == HTTP_UNAUTHORIZED) { + int found = JK_FALSE; + for (h = 0; h < num_of_headers; h++) { + if (!strcasecmp(header_names[h], "WWW-Authenticate")) { + char *tmp = ap_pstrdup(r->pool, header_values[h]); + ap_table_set(r->err_headers_out, + "WWW-Authenticate", tmp); + found = JK_TRUE; + } + } + if (found == JK_FALSE) { + jk_server_conf_t *conf = (jk_server_conf_t *) + ap_get_module_config(r->server->module_config, + &jk_module); + jk_log(conf->log, JK_LOG_INFO, + "origin server sent 401 without" + " WWW-Authenticate header"); + } + } + return JK_TRUE; + } + + if (!reason) { + reason = ""; + } + r->status = status; + r->status_line = ap_psprintf(r->pool, "%d %s", status, reason); + + for (h = 0; h < num_of_headers; h++) { + if (!strcasecmp(header_names[h], "Content-type")) { + char *tmp = ap_pstrdup(r->pool, header_values[h]); + ap_content_type_tolower(tmp); + r->content_type = tmp; + } + else if (!strcasecmp(header_names[h], "Location")) { + ap_table_set(r->headers_out, header_names[h], + header_values[h]); + } + else if (!strcasecmp(header_names[h], "Content-Length")) { + ap_table_set(r->headers_out, header_names[h], + header_values[h]); + } + else if (!strcasecmp(header_names[h], "Transfer-Encoding")) { + ap_table_set(r->headers_out, header_names[h], + header_values[h]); + } + else if (!strcasecmp(header_names[h], "Last-Modified")) { + /* + * If the script gave us a Last-Modified header, we can't just + * pass it on blindly because of restrictions on future values. + */ + ap_update_mtime(r, ap_parseHTTPdate(header_values[h])); + ap_set_last_modified(r); + } + else { + ap_table_add(r->headers_out, header_names[h], + header_values[h]); + } + } + + ap_send_http_header(r); + s->response_started = JK_TRUE; + + return JK_TRUE; + } + return JK_FALSE; +} + +/* + * Read a chunk of the request body into a buffer. Attempt to read len + * bytes into the buffer. Write the number of bytes actually read into + * actually_read. + * + * Think of this function as a method of the apache1.3-specific subclass of + * the jk_ws_service class. Think of the *s param as a "this" or "self" + * pointer. + */ +static int JK_METHOD ws_read(jk_ws_service_t *s, + void *b, unsigned len, unsigned *actually_read) +{ + if (s && s->ws_private && b && actually_read) { + apache_private_data_t *p = s->ws_private; + if (!p->read_body_started) { + if (ap_should_client_block(p->r)) { + p->read_body_started = JK_TRUE; + } + } + + if (p->read_body_started) { + long rv; + if ((rv = ap_get_client_block(p->r, b, len)) < 0) { + *actually_read = 0; + } + else { + *actually_read = (unsigned)rv; + } + /* reset timeout after successful read */ + ap_reset_timeout(p->r); + return JK_TRUE; + } + } + return JK_FALSE; +} + +static void JK_METHOD ws_flush(jk_ws_service_t *s) +{ + if (s && s->ws_private) { + apache_private_data_t *p = s->ws_private; + BUFF *bf = p->r->connection->client; + ap_bflush(bf); + } +} + +/* + * Write a chunk of response data back to the browser. If the headers + * haven't yet been sent over, send over default header values (Status = + * 200, basically). + * + * Write len bytes from buffer b. + * + * Think of this function as a method of the apache1.3-specific subclass of + * the jk_ws_service class. Think of the *s param as a "this" or "self" + * pointer. + */ +static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned len) +{ + if (s && s->ws_private && b) { + apache_private_data_t *p = s->ws_private; + + if (len) { + char *buf = (char *)b; + int w = (int)len; + int r = 0; + + if (!s->response_started) { + if (main_log) + jk_log(main_log, JK_LOG_INFO, + "Write without start, starting with defaults"); + if (!s->start_response(s, 200, NULL, NULL, NULL, 0)) { + return JK_FALSE; + } + } + + if (p->r->header_only) { + BUFF *bf = p->r->connection->client; + ap_bflush(bf); + return JK_TRUE; + } + + while (len && !p->r->connection->aborted) { + w = ap_bwrite(p->r->connection->client, &buf[r], len); + if (w > 0) { + /* reset timeout after successful write */ + ap_reset_timeout(p->r); + r += w; + len -= w; + } + else if (w < 0) { + /* Error writing data -- abort */ + if (!p->r->connection->aborted) { + ap_bsetflag(p->r->connection->client, B_EOUT, 1); + p->r->connection->aborted = 1; + } + return JK_FALSE; + } + + } + if (len && p->r->connection->aborted) { + /* Fail if there is something left to send and + * the connection was aborted by the client + */ + return JK_FALSE; + } + } + + return JK_TRUE; + } + return JK_FALSE; +} + +static void JK_METHOD ws_add_log_items(jk_ws_service_t *s, + const char *const *log_names, + const char *const *log_values, + unsigned num_of_log_items) +{ + unsigned h; + apache_private_data_t *p = s->ws_private; + request_rec *r = p->r; + + for (h = 0; h < num_of_log_items; h++) { + if (log_names[h] && log_values[h]) { + ap_table_setn(r->notes, log_names[h], log_values[h]); + } + } +} + +static void * JK_METHOD ws_next_vhost(void *d) +{ + server_rec *s = (server_rec *)d; + if (s == NULL) + return main_server; + return s->next; +} + +static void JK_METHOD ws_vhost_to_text(void *d, char *buf, size_t len) +{ + server_rec *s = (server_rec *)d; + size_t used = 0; + + if (s->server_hostname) + used += strlen(s->server_hostname); + if (!s->is_virtual) { + if (s->port) + used += strlen(":XXXXX"); + } + else if (s->addrs) { + used += strlen(" ["); + if (s->addrs->virthost) + used += strlen(s->addrs->virthost); + if (s->addrs->host_port) + used += strlen(":XXXXX"); + used += strlen("]"); + } + + if (len < used && len > strlen("XXX")) { + strcpy(buf, "XXX"); + return; + } + + used = 0; + + if (s->server_hostname) { + strcpy(buf + used, s->server_hostname); + used += strlen(s->server_hostname); + } + if (!s->is_virtual) { + if (s->port) { + sprintf(buf + used, ":%hu", s->port); + used = strlen(buf); + } + } + else if (s->addrs) { + strcpy(buf + used, " ["); + used += strlen(" ["); + if (s->addrs->virthost) { + strcpy(buf + used, s->addrs->virthost); + used += strlen(s->addrs->virthost); + } + if (s->addrs->host_port) { + sprintf(buf + used, ":%hu", s->addrs->host_port); + used = strlen(buf); + } + strcpy(buf + used, "]"); + used += strlen("]"); + } +} + +static jk_uri_worker_map_t * JK_METHOD ws_vhost_to_uw_map(void *d) +{ + server_rec *s = (server_rec *)d; + jk_server_conf_t *conf = NULL; + if (s == NULL) + return NULL; + conf = (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + return conf->uw_map; +} + +/* ====================================================================== */ +/* Utility functions */ +/* ====================================================================== */ + +/* Log something to JK log file then exit */ +static void jk_error_exit(const char *file, + int line, + int level, + const server_rec * s, + ap_pool * p, const char *fmt, ...) +{ + va_list ap; + char *res; + char *ch; + + va_start(ap, fmt); + res = ap_pvsprintf(p, fmt, ap); + va_end(ap); + /* Replace all format characters in the resulting message */ + /* because we feed the message to ap_log_error(). */ + ch = res; + while (*ch) { + if (*ch == '%') { + *ch = '#'; + } + ch++; + } + + ap_log_error(file, line, level, s, res); + if ( s ) { + ap_log_error(file, line, level, NULL, res); + } + + /* Exit process */ + exit(1); +} + +/* Return the content length associated with an Apache request structure */ +static jk_uint64_t get_content_length(request_rec * r) +{ + if (r->clength > 0) { + return (jk_uint64_t)r->clength; + } + else { + char *lenp = (char *)ap_table_get(r->headers_in, "Content-Length"); + + if (lenp) { + jk_uint64_t rc = 0; + if (sscanf(lenp, "%" JK_UINT64_T_FMT, &rc) > 0 && rc > 0) { + return rc; + } + } + } + + return 0; +} + +/* Retrieve string value from env var, use default if env var does not exist. */ +static const char *get_env_string(request_rec *r, const char *def, + char *env, int null_for_empty) +{ + char *value = (char *)ap_table_get(r->subprocess_env, env); + if (value) + return null_for_empty ? NULL_FOR_EMPTY(value) : value; + return null_for_empty ? NULL_FOR_EMPTY(def) : def; +} + +/* Retrieve integer value from env var, use default if env var does not exist. */ +static int get_env_int(request_rec *r, int def, char *env) +{ + char *value = (char *)ap_table_get(r->subprocess_env, env); + if (value) + return atoi(value); + return def; +} + +/* + * Set up an instance of a ws_service object for a single request. This + * particular instance will be of the Apache 1.3-specific subclass. Copies + * all of the important request information from the Apache request object + * into the jk_ws_service_t object. + * + * Params + * + * private_data: The subclass-specific data structure, already initialized + * (with a pointer to the Apache request_rec structure, among other things) + * + * s: The base class object. + * + * conf: Configuration information + * + * Called from jk_handler(). See jk_service.h for explanations of what most + * of these fields mean. + */ +static int init_ws_service(apache_private_data_t * private_data, + jk_ws_service_t *s, jk_server_conf_t * conf) +{ + int size; + request_rec *r = private_data->r; + char *ssl_temp = NULL; + const char *reply_timeout = NULL; + rule_extension_t *e; + + /* Copy in function pointers (which are really methods) */ + s->start_response = ws_start_response; + s->read = ws_read; + s->write = ws_write; + s->flush = ws_flush; + s->add_log_items = ws_add_log_items; + s->next_vhost = ws_next_vhost; + s->vhost_to_text = ws_vhost_to_text; + s->vhost_to_uw_map = ws_vhost_to_uw_map; + + s->auth_type = get_env_string(r, r->connection->ap_auth_type, + conf->auth_type_indicator, 1); + s->remote_user = get_env_string(r, r->connection->user, + conf->remote_user_indicator, 1); + + s->protocol = r->protocol; + s->remote_host = + (char *)ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_HOST); + s->remote_host = get_env_string(r, s->remote_host, + conf->remote_host_indicator, 1); + + if (conf->options & JK_OPT_FWDLOCAL) { + s->remote_addr = r->connection->local_ip; + /* We don't know the client port of the backend connection. */ + s->remote_port = "0"; + } + else { + s->remote_addr = r->connection->remote_ip; + s->remote_port = ap_psprintf(r->pool, "%d", + ntohs(r->connection->remote_addr.sin_port)); + } + s->remote_addr = get_env_string(r, s->remote_addr, + conf->remote_addr_indicator, 1); + s->remote_port = get_env_string(r, s->remote_port, + conf->remote_port_indicator, 1); + + if (conf->options & JK_OPT_FLUSHPACKETS) + s->flush_packets = 1; + if (conf->options & JK_OPT_FLUSHEADER) + s->flush_header = 1; + + e = (rule_extension_t *)ap_get_module_config(r->request_config, &jk_module); + if (e) { + s->extension.reply_timeout = e->reply_timeout; + s->extension.use_server_error_pages = e->use_server_error_pages; + if (e->activation) { + s->extension.activation = ap_palloc(r->pool, e->activation_size * sizeof(int)); + memcpy(s->extension.activation, e->activation, e->activation_size * sizeof(int)); + } + if (e->fail_on_status_size > 0) { + s->extension.fail_on_status_size = e->fail_on_status_size; + s->extension.fail_on_status = ap_palloc(r->pool, e->fail_on_status_size * sizeof(int)); + memcpy(s->extension.fail_on_status, e->fail_on_status, e->fail_on_status_size * sizeof(int)); + } + } + reply_timeout = ap_table_get(r->subprocess_env, "JK_REPLY_TIMEOUT"); + if (reply_timeout) { + int r = atoi(reply_timeout); + if (r >= 0) + s->extension.reply_timeout = r; + } + + if (conf->options & JK_OPT_DISABLEREUSE) + s->disable_reuse = 1; + + /* get server name */ + /* s->server_name = (char *)(r->hostname ? r->hostname : r->server->server_hostname); */ + /* XXX : a la jk2 */ + s->server_name = get_env_string(r, (char *)ap_get_server_name(r), + conf->local_name_indicator, 0); + + /* get the real port (otherwise redirect failed) */ + /* s->server_port = htons( r->connection->local_addr.sin_port ); */ + /* XXX : a la jk2 */ + s->server_port = get_env_int(r, ap_get_server_port(r), + conf->local_port_indicator); + + s->server_software = (char *)ap_get_server_version(); + + s->method = (char *)r->method; + s->content_length = get_content_length(r); + s->is_chunked = r->read_chunked; + s->no_more_chunks = 0; + s->query_string = r->args; + + /* + * The 2.2 servlet spec errata says the uri from + * HttpServletRequest.getRequestURI() should remain encoded. + * [http://java.sun.com/products/servlet/errata_042700.html] + * + * We use JkOptions to determine which method to be used + * + * ap_escape_uri is the latest recommanded but require + * some java decoding (in TC 3.3 rc2) + * + * unparsed_uri is used for strict compliance with spec and + * old Tomcat (3.2.3 for example) + * + * uri is use for compatibilty with mod_rewrite with old Tomcats + */ + + switch (conf->options & JK_OPT_FWDURIMASK) { + + case JK_OPT_FWDURICOMPATUNPARSED: + s->req_uri = r->unparsed_uri; + if (s->req_uri != NULL) { + char *query_str = strchr(s->req_uri, '?'); + if (query_str != NULL) { + *query_str = 0; + } + } + + break; + + case JK_OPT_FWDURICOMPAT: + s->req_uri = r->uri; + break; + + case JK_OPT_FWDURIPROXY: + size = 3 * strlen(r->uri) + 1; + s->req_uri = ap_palloc(r->pool, size); + jk_canonenc(r->uri, s->req_uri, size); + break; + + case JK_OPT_FWDURIESCAPED: + s->req_uri = ap_escape_uri(r->pool, r->uri); + break; + + default: + return JK_FALSE; + } + + if (conf->ssl_enable || conf->envvars) { + ap_add_common_vars(r); + + if (conf->ssl_enable) { + ssl_temp = + (char *)ap_table_get(r->subprocess_env, + conf->https_indicator); + if (ssl_temp && !strcasecmp(ssl_temp, "on")) { + s->is_ssl = JK_TRUE; + s->ssl_cert = + (char *)ap_table_get(r->subprocess_env, + conf->certs_indicator); + + if (conf->options & JK_OPT_FWDCERTCHAIN) { + array_header *t = ap_table_elts(r->subprocess_env); + if (t && t->nelts) { + int i; + table_entry *elts = (table_entry *) t->elts; + array_header *certs = ap_make_array(r->pool, 1, + sizeof(char *)); + *(const char **)ap_push_array(certs) = s->ssl_cert; + for (i = 0; i < t->nelts; i++) { + if (!elts[i].key) + continue; + if (!strncasecmp(elts[i].key, + conf->certchain_prefix, + strlen(conf->certchain_prefix))) + *(const char **)ap_push_array(certs) = elts[i].val; + } + s->ssl_cert = ap_array_pstrcat(r->pool, certs, '\0'); + } + } + + if (s->ssl_cert) { + s->ssl_cert_len = strlen(s->ssl_cert); + if (JK_IS_DEBUG_LEVEL(conf->log)) { + jk_log(conf->log, JK_LOG_DEBUG, + "SSL client certificate (%d bytes): %s", + s->ssl_cert_len, s->ssl_cert); + } + } + /* Servlet 2.3 API */ + s->ssl_cipher = + (char *)ap_table_get(r->subprocess_env, + conf->cipher_indicator); + s->ssl_session = + (char *)ap_table_get(r->subprocess_env, + conf->session_indicator); + + if (conf->options & JK_OPT_FWDKEYSIZE) { + /* Servlet 2.3 API */ + ssl_temp = + (char *)ap_table_get(r->subprocess_env, + conf->key_size_indicator); + if (ssl_temp) + s->ssl_key_size = atoi(ssl_temp); + } + } + } + + if (conf->envvars) { + const array_header *t = conf->envvar_items; + if (t && t->nelts) { + int i; + int j = 0; + envvar_item *elts = (envvar_item *) t->elts; + s->attributes_names = + ap_palloc(r->pool, sizeof(char *) * t->nelts); + s->attributes_values = + ap_palloc(r->pool, sizeof(char *) * t->nelts); + + for (i = 0; i < t->nelts; i++) { + s->attributes_names[i - j] = elts[i].name; + s->attributes_values[i - j] = + (char *)ap_table_get(r->subprocess_env, elts[i].name); + if (!s->attributes_values[i - j]) { + if (elts[i].has_default) { + s->attributes_values[i - j] = elts[i].value; + } + else { + s->attributes_values[i - j] = ""; + s->attributes_names[i - j] = ""; + j++; + } + } + } + + s->num_attributes = t->nelts - j; + } + } + } + + if (r->headers_in && ap_table_elts(r->headers_in)) { + int need_content_length_header = (!s->is_chunked + && s->content_length == + 0) ? JK_TRUE : JK_FALSE; + array_header *t = ap_table_elts(r->headers_in); + if (t && t->nelts) { + int i; + table_entry *elts = (table_entry *) t->elts; + s->num_headers = t->nelts; + /* allocate an extra header slot in case we need to add a content-length header */ + s->headers_names = + ap_palloc(r->pool, sizeof(char *) * (t->nelts + 1)); + s->headers_values = + ap_palloc(r->pool, sizeof(char *) * (t->nelts + 1)); + if (!s->headers_names || !s->headers_values) + return JK_FALSE; + for (i = 0; i < t->nelts; i++) { + char *hname = ap_pstrdup(r->pool, elts[i].key); + s->headers_values[i] = ap_pstrdup(r->pool, elts[i].val); + s->headers_names[i] = hname; + if (need_content_length_header && + !strcasecmp(s->headers_names[i], "content-length")) { + need_content_length_header = JK_FALSE; + } + } + /* Add a content-length = 0 header if needed. + * Ajp13 assumes an absent content-length header means an unknown, + * but non-zero length body. + */ + if (need_content_length_header) { + s->headers_names[s->num_headers] = "content-length"; + s->headers_values[s->num_headers] = "0"; + s->num_headers++; + } + } + /* Add a content-length = 0 header if needed. */ + else if (need_content_length_header) { + s->headers_names = ap_palloc(r->pool, sizeof(char *)); + s->headers_values = ap_palloc(r->pool, sizeof(char *)); + if (!s->headers_names || !s->headers_values) + return JK_FALSE; + s->headers_names[0] = "content-length"; + s->headers_values[0] = "0"; + s->num_headers++; + } + } + s->uw_map = conf->uw_map; + + /* Dump all connection param so we can trace what's going to + * the remote tomcat + */ + if (JK_IS_DEBUG_LEVEL(conf->log)) { + jk_log(conf->log, JK_LOG_DEBUG, + "Service protocol=%s method=%s ssl=%s host=%s addr=%s name=%s port=%d auth=%s user=%s laddr=%s raddr=%s uri=%s", + STRNULL_FOR_NULL(s->protocol), + STRNULL_FOR_NULL(s->method), + s->is_ssl ? "true" : "false", + STRNULL_FOR_NULL(s->remote_host), + STRNULL_FOR_NULL(s->remote_addr), + STRNULL_FOR_NULL(s->server_name), + s->server_port, + STRNULL_FOR_NULL(s->auth_type), + STRNULL_FOR_NULL(s->remote_user), + STRNULL_FOR_NULL(r->connection->local_ip), + STRNULL_FOR_NULL(r->connection->remote_ip), + STRNULL_FOR_NULL(s->req_uri)); + } + + return JK_TRUE; +} + +/* + * The JK module command processors + * + * The below are all installed so that Apache calls them while it is + * processing its config files. This allows configuration info to be + * copied into a jk_server_conf_t object, which is then used for request + * filtering/processing. + * + * See jk_cmds definition below for explanations of these options. + */ + +/* + * JkMountCopy directive handling + * + * JkMountCopy On/Off/All + */ + +static const char *jk_set_mountcopy(cmd_parms * cmd, + void *dummy, char *mount_copy) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + if (! strcasecmp(mount_copy, "all")) { + jk_mount_copy_all = JK_TRUE; + } + else if (strcasecmp(mount_copy, "on") && strcasecmp(mount_copy, "off")) { + return "JkMountCopy must be All, On or Off"; + } + else { + conf->mountcopy = strcasecmp(mount_copy, "off") ? JK_TRUE : JK_FALSE; + } + + return NULL; +} + +/* + * JkMount directive handling + * + * JkMount URI(context) worker + */ + +static const char *jk_mount_context(cmd_parms * cmd, + void *dummy, + char *context, + char *worker) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + const char *c, *w; + + if (worker != NULL && cmd->path == NULL ) { + c = context; + w = worker; + } + else if (worker == NULL && cmd->path != NULL) { + c = cmd->path; + w = context; + } + else { + if (worker == NULL) + return "JkMount needs a path when not defined in a location"; + else + return "JkMount can not have a path when defined in a location"; + } + + if (c[0] != '/') + return "JkMount context should start with /"; + + if (!conf->uri_to_context) { + if (!jk_map_alloc(&(conf->uri_to_context))) { + return "JkMount Memory error"; + } + } + /* + * Add the new worker to the alias map. + */ + jk_map_put(conf->uri_to_context, c, w, NULL); + return NULL; +} + +/* + * JkUnMount directive handling + * + * JkUnMount URI(context) worker + */ + +static const char *jk_unmount_context(cmd_parms * cmd, + void *dummy, + const char *context, + const char *worker) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + char *uri; + const char *c, *w; + + if (worker != NULL && cmd->path == NULL ) { + c = context; + w = worker; + } + else if (worker == NULL && cmd->path != NULL) { + c = cmd->path; + w = context; + } + else { + if (worker == NULL) + return "JkUnMount needs a path when not defined in a location"; + else + return "JkUnMount can not have a path when defined in a location"; + } + if (c[0] != '/') + return "JkUnMount context should start with /"; + uri = ap_pstrcat(cmd->temp_pool, "!", c, NULL); + + if (!conf->uri_to_context) { + if (!jk_map_alloc(&(conf->uri_to_context))) { + return "JkUnMount Memory error"; + } + } + /* + * Add the new worker to the alias map. + */ + jk_map_put(conf->uri_to_context, uri, w, NULL); + return NULL; +} + +/* + * JkWorkersFile Directive Handling + * + * JkWorkersFile file + */ + +static const char *jk_set_worker_file(cmd_parms * cmd, + void *dummy, char *worker_file) +{ + struct stat statbuf; + + if (jk_worker_file != NULL) + return "JkWorkersFile only allowed once"; + + /* we need an absolute path */ + jk_worker_file = ap_server_root_relative(cmd->pool, worker_file); + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(jk_worker_file, 0); +#endif + + if (jk_worker_file == worker_file) + jk_worker_file = ap_pstrdup(cmd->pool, worker_file); + + if (jk_worker_file == NULL) + return "JkWorkersFile file name invalid"; + + if (stat(jk_worker_file, &statbuf) == -1) + return "JkWorkersFile: Can't find the workers file specified"; + + return NULL; +} + +/* + * JkMountFile Directive Handling + * + * JkMountFile file + */ + +static const char *jk_set_mount_file(cmd_parms * cmd, + void *dummy, char *mount_file) +{ + server_rec *s = cmd->server; + struct stat statbuf; + + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + /* we need an absolute path (ap_server_root_relative does the ap_pstrdup) */ + conf->mount_file = ap_server_root_relative(cmd->pool, mount_file); + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(conf->mount_file, 0); +#endif + + if (conf->mount_file == NULL) + return "JkMountFile file name invalid"; + + if (stat(conf->mount_file, &statbuf) == -1) + return "JkMountFile: Can't find the mount file specified"; + + if (!conf->uri_to_context) { + if (!jk_map_alloc(&(conf->uri_to_context))) { + return "JkMountFile Memory error"; + } + } + + return NULL; +} + +/* + * JkMountFileReload Directive Handling + * + * JkMountFileReload seconds + */ + +static const char *jk_set_mount_file_reload(cmd_parms * cmd, + void *dummy, char *mount_file_reload) +{ + server_rec *s = cmd->server; + int interval; + + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + interval = atoi(mount_file_reload); + if (interval < 0) { + interval = 0; + } + + conf->mount_file_reload = interval; + + return NULL; +} + +/* + * JkLogFile Directive Handling + * + * JkLogFile file + */ + +static const char *jk_set_log_file(cmd_parms * cmd, + void *dummy, char *log_file) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + /* we need an absolute path */ + if (*log_file != '|') { + conf->log_file = ap_server_root_relative(cmd->pool, log_file); + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(conf->log_file, 0); +#endif + + } + else + conf->log_file = ap_pstrdup(cmd->pool, log_file); + + if (conf->log_file == NULL) + return "JkLogFile file name invalid"; + + return NULL; +} + +/* + * JkShmFile Directive Handling + * + * JkShmFile file + */ + +static const char *jk_set_shm_file(cmd_parms * cmd, + void *dummy, char *shm_file) +{ + + /* we need an absolute path */ + jk_shm_file = ap_server_root_relative(cmd->pool, shm_file); + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(jk_shm_file, 0); +#endif + + if (jk_shm_file == shm_file) + jk_shm_file = ap_pstrdup(cmd->pool, shm_file); + + if (jk_shm_file == NULL) + return "JkShmFile file name invalid"; + + return NULL; +} + +/* + * JkShmSize Directive Handling + * + * JkShmSize size in kilobytes + */ + +static const char *jk_set_shm_size(cmd_parms * cmd, + void *dummy, const char *shm_size) +{ + int sz = 0; + /* we need an absolute path */ + sz = atoi(shm_size) * 1024; + if (sz < JK_SHM_DEF_SIZE) + sz = JK_SHM_DEF_SIZE; + else + sz = JK_SHM_ALIGN(sz); + jk_shm_size = (size_t)sz; + return NULL; +} + +/* + * JkLogLevel Directive Handling + * + * JkLogLevel debug/info/request/error/emerg + */ + +static const char *jk_set_log_level(cmd_parms * cmd, + void *dummy, char *log_level) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->log_level = jk_parse_log_level(log_level); + + return NULL; +} + +/* + * JkLogStampFormat Directive Handling + * + * JkLogStampFormat "[%a %b %d %H:%M:%S %Y] " + */ + +static const char *jk_set_log_fmt(cmd_parms * cmd, + void *dummy, char *log_format) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->stamp_format_string = ap_pstrdup(cmd->pool, log_format); + + return NULL; +} + +/* + * JkAutoAlias Directive Handling + * + * JkAutoAlias application directory + */ + +static const char *jk_set_auto_alias(cmd_parms * cmd, + void *dummy, char *directory) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->alias_dir = directory; + + if (conf->alias_dir == NULL) + return "JkAutoAlias directory invalid"; + + return NULL; +} + +/* + * JkStripSession directive handling + * + * JkStripSession On/Off + */ + +static const char *jk_set_strip_session(cmd_parms * cmd, void *dummy, int flag) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + /* Set up our value */ + conf->strip_session = flag ? JK_TRUE : JK_FALSE; + + return NULL; +} + +/***************************************************************** + * + * Actually logging. + */ + +typedef const char *(*item_key_func) (request_rec *, char *); + +typedef struct +{ + item_key_func func; + char *arg; +} request_log_format_item; + +static const char *process_item(request_rec * r, + request_log_format_item * item) +{ + const char *cp; + + cp = (*item->func) (r, item->arg); + return cp ? cp : "-"; +} + +static void request_log_transaction(request_rec * r, jk_server_conf_t * conf) +{ + request_log_format_item *items; + char *str, *s; + int i; + int len = 0; + const char **strs; + int *strl; + array_header *format = conf->format; + + strs = ap_palloc(r->pool, sizeof(char *) * (format->nelts)); + strl = ap_palloc(r->pool, sizeof(int) * (format->nelts)); + items = (request_log_format_item *) format->elts; + for (i = 0; i < format->nelts; ++i) { + strs[i] = process_item(r, &items[i]); + } + for (i = 0; i < format->nelts; ++i) { + len += strl[i] = strlen(strs[i]); + } + str = ap_palloc(r->pool, len + 1); + for (i = 0, s = str; i < format->nelts; ++i) { + memcpy(s, strs[i], strl[i]); + s += strl[i]; + } + *s = 0; + jk_log(conf->log, JK_LOG_REQUEST, "%s", str); +} + +/***************************************************************** + * + * Parsing the log format string + */ + +static char *format_integer(pool * p, int i) +{ + return ap_psprintf(p, "%d", i); +} + +static char *pfmt(pool * p, int i) +{ + if (i <= 0) { + return "-"; + } + else { + return format_integer(p, i); + } +} + +static const char *constant_item(request_rec * dummy, char *stuff) +{ + return stuff; +} + +static const char *log_worker_name(request_rec * r, char *a) +{ + return ap_table_get(r->notes, JK_NOTE_WORKER_NAME); +} + +static const char *log_worker_route(request_rec * r, char *a) +{ + return ap_table_get(r->notes, JK_NOTE_WORKER_ROUTE); +} + +static const char *log_request_duration(request_rec * r, char *a) +{ + return ap_table_get(r->notes, JK_NOTE_REQUEST_DURATION); +} + +static const char *log_request_line(request_rec * r, char *a) +{ + /* NOTE: If the original request contained a password, we + * re-write the request line here to contain XXXXXX instead: + * (note the truncation before the protocol string for HTTP/0.9 requests) + * (note also that r->the_request contains the unmodified request) + */ + return (r->parsed_uri.password) ? ap_pstrcat(r->pool, r->method, " ", + ap_unparse_uri_components(r-> + pool, + &r-> + parsed_uri, + 0), + r->assbackwards ? NULL : " ", + r->protocol, NULL) + : r->the_request; +} + +/* These next two routines use the canonical name:port so that log + * parsers don't need to duplicate all the vhost parsing crud. + */ +static const char *log_virtual_host(request_rec * r, char *a) +{ + return r->server->server_hostname; +} + +static const char *log_server_port(request_rec * r, char *a) +{ + return ap_psprintf(r->pool, "%u", + r->server->port ? r->server-> + port : ap_default_port(r)); +} + +/* This respects the setting of UseCanonicalName so that + * the dynamic mass virtual hosting trick works better. + */ +static const char *log_server_name(request_rec * r, char *a) +{ + return ap_get_server_name(r); +} + +static const char *log_request_uri(request_rec * r, char *a) +{ + return r->uri; +} +static const char *log_request_method(request_rec * r, char *a) +{ + return r->method; +} + +static const char *log_request_protocol(request_rec * r, char *a) +{ + return r->protocol; +} +static const char *log_request_query(request_rec * r, char *a) +{ + return (r->args != NULL) ? ap_pstrcat(r->pool, "?", r->args, NULL) + : ""; +} +static const char *log_status(request_rec * r, char *a) +{ + return pfmt(r->pool, r->status); +} + +static const char *clf_log_bytes_sent(request_rec * r, char *a) +{ + if (!r->sent_bodyct) { + return "-"; + } + else { + long int bs; + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + return ap_psprintf(r->pool, "%ld", bs); + } +} + +static const char *log_bytes_sent(request_rec * r, char *a) +{ + if (!r->sent_bodyct) { + return "0"; + } + else { + long int bs; + ap_bgetopt(r->connection->client, BO_BYTECT, &bs); + return ap_psprintf(r->pool, "%ld", bs); + } +} + +static struct log_item_list +{ + char ch; + item_key_func func; +} log_item_keys[] = { + + { + 'T', log_request_duration}, { + 'r', log_request_line}, { + 'U', log_request_uri}, { + 's', log_status}, { + 'b', clf_log_bytes_sent}, { + 'B', log_bytes_sent}, { + 'V', log_server_name}, { + 'v', log_virtual_host}, { + 'p', log_server_port}, { + 'H', log_request_protocol}, { + 'm', log_request_method}, { + 'q', log_request_query}, { + 'w', log_worker_name}, { + 'R', log_worker_route}, { + '\0'} +}; + +static struct log_item_list *find_log_func(char k) +{ + int i; + + for (i = 0; log_item_keys[i].ch; ++i) + if (k == log_item_keys[i].ch) { + return &log_item_keys[i]; + } + + return NULL; +} + +static char *parse_request_log_misc_string(pool * p, + request_log_format_item * it, + const char **sa) +{ + const char *s; + char *d; + + it->func = constant_item; + + s = *sa; + while (*s && *s != '%') { + s++; + } + /* + * This might allocate a few chars extra if there's a backslash + * escape in the format string. + */ + it->arg = ap_palloc(p, s - *sa + 1); + + d = it->arg; + s = *sa; + while (*s && *s != '%') { + if (*s != '\\') { + *d++ = *s++; + } + else { + s++; + switch (*s) { + case '\\': + *d++ = '\\'; + s++; + break; + case 'n': + *d++ = '\n'; + s++; + break; + case 't': + *d++ = '\t'; + s++; + break; + default: + /* copy verbatim */ + *d++ = '\\'; + /* + * Allow the loop to deal with this *s in the normal + * fashion so that it handles end of string etc. + * properly. + */ + break; + } + } + } + *d = '\0'; + + *sa = s; + return NULL; +} + +static char *parse_request_log_item(pool * p, + request_log_format_item * it, + const char **sa) +{ + const char *s = *sa; + struct log_item_list *l; + + if (*s != '%') { + return parse_request_log_misc_string(p, it, sa); + } + + ++s; + it->arg = ""; /* For safety's sake... */ + + l = find_log_func(*s++); + if (!l) { + char dummy[2]; + + dummy[0] = s[-1]; + dummy[1] = '\0'; + return ap_pstrcat(p, "Unrecognized JkRequestLogFormat directive %", + dummy, NULL); + } + it->func = l->func; + *sa = s; + return NULL; +} + +static array_header *parse_request_log_string(pool * p, const char *s, + const char **err) +{ + array_header *a = ap_make_array(p, 0, sizeof(request_log_format_item)); + char *res; + + while (*s) { + if ((res = + parse_request_log_item(p, + (request_log_format_item *) + ap_push_array(a), &s))) { + *err = res; + return NULL; + } + } + + return a; +} + +/* + * JkRequestLogFormat Directive Handling + * + * JkRequestLogFormat format string + * + * %b - Bytes sent, excluding HTTP headers. In CLF format + * %B - Bytes sent, excluding HTTP headers. + * %H - The request protocol + * %m - The request method + * %p - The canonical Port of the server serving the request + * %q - The query string (prepended with a ? if a query string exists, + * otherwise an empty string) + * %r - First line of request + * %s - request HTTP status code + * %T - Requset duration, elapsed time to handle request in seconds '.' micro seconds + * %U - The URL path requested, not including any query string. + * %v - The canonical ServerName of the server serving the request. + * %V - The server name according to the UseCanonicalName setting. + * %w - Tomcat worker name + */ + +static const char *jk_set_request_log_format(cmd_parms * cmd, + void *dummy, char *format) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->format_string = ap_pstrdup(cmd->pool, format); + + return NULL; +} + +/* + * JkWorkerIndicator Directive Handling + * + * JkWorkerIndicator JkWorker + */ + +static const char *jk_set_worker_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->worker_indicator = ap_pstrdup(cmd->pool, indicator); + + return NULL; +} + +/* + * Directives Handling for setting various environment names + * used to overwrite the following request information: + * - remote_addr + * - remote_port + * - remote_host + * - remote_user + * - auth_type + * - server_name + * - server_port + */ +static const char *jk_set_remote_addr_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->remote_addr_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_remote_port_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->remote_port_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_remote_host_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->remote_host_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_remote_user_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->remote_user_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_auth_type_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->auth_type_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_local_name_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->local_name_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +static const char *jk_set_local_port_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, &jk_module); + conf->local_port_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkExtractSSL Directive Handling + * + * JkExtractSSL On/Off + */ + +static const char *jk_set_enable_ssl(cmd_parms * cmd, void *dummy, int flag) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + /* Set up our value */ + conf->ssl_enable = flag ? JK_TRUE : JK_FALSE; + return NULL; +} + +/* + * JkHTTPSIndicator Directive Handling + * + * JkHTTPSIndicator HTTPS + */ + +static const char *jk_set_https_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->https_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkCERTSIndicator Directive Handling + * + * JkCERTSIndicator SSL_CLIENT_CERT + */ + +static const char *jk_set_certs_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->certs_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkCIPHERIndicator Directive Handling + * + * JkCIPHERIndicator SSL_CIPHER + */ + +static const char *jk_set_cipher_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->cipher_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkCERTCHAINPrefix Directive Handling + * + * JkCERTCHAINPrefix SSL_CLIENT_CERT_CHAIN_ + */ + +static const char *jk_set_certchain_prefix(cmd_parms * cmd, + void *dummy, const char *prefix) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->certchain_prefix = ap_pstrdup(cmd->pool, prefix); + + return NULL; +} + +/* + * JkSESSIONIndicator Directive Handling + * + * JkSESSIONIndicator SSL_SESSION_ID + */ + +static const char *jk_set_session_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->session_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkKEYSIZEIndicator Directive Handling + * + * JkKEYSIZEIndicator SSL_CIPHER_USEKEYSIZE + */ + +static const char *jk_set_key_size_indicator(cmd_parms * cmd, + void *dummy, char *indicator) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->key_size_indicator = ap_pstrdup(cmd->pool, indicator); + return NULL; +} + +/* + * JkOptions Directive Handling + * + * + * +ForwardSSLKeySize => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2 + * -ForwardSSLKeySize => Don't Forward SSL Key Size, will make mod_jk works with all TC release + * ForwardURICompat => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC) + * ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC) + * ForwardURIEscaped => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part + * ForwardDirectories => Forward all directory requests with no index files to Tomcat + * +ForwardSSLCertChain => Forward SSL certificate chain + * -ForwardSSLCertChain => Don't forward SSL certificate chain + */ + +const char *jk_set_options(cmd_parms * cmd, void *dummy, const char *line) +{ + int opt = 0; + int mask = 0; + char action; + char *w; + + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + while (line[0] != 0) { + w = ap_getword_conf(cmd->pool, &line); + action = 0; + + if (*w == '+' || *w == '-') { + action = *(w++); + } + + mask = 0; + + if (action == '-' && !strncasecmp(w, "ForwardURI", strlen("ForwardURI"))) + return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '-", w, + "': ForwardURI* options can not be disabled", NULL); + + if (!strcasecmp(w, "ForwardURICompat")) { + opt = JK_OPT_FWDURICOMPAT; + mask = JK_OPT_FWDURIMASK; + } + else if (!strcasecmp(w, "ForwardURICompatUnparsed")) { + opt = JK_OPT_FWDURICOMPATUNPARSED; + mask = JK_OPT_FWDURIMASK; + } + else if (!strcasecmp(w, "ForwardURIEscaped")) { + opt = JK_OPT_FWDURIESCAPED; + mask = JK_OPT_FWDURIMASK; + } + else if (!strcasecmp(w, "ForwardURIProxy")) { + opt = JK_OPT_FWDURIPROXY; + mask = JK_OPT_FWDURIMASK; + } + else if (!strcasecmp(w, "ForwardDirectories")) { + opt = JK_OPT_FWDDIRS; + } + else if (!strcasecmp(w, "ForwardLocalAddress")) { + opt = JK_OPT_FWDLOCAL; + } + else if (!strcasecmp(w, "FlushPackets")) { + opt = JK_OPT_FLUSHPACKETS; + } + else if (!strcasecmp(w, "FlushHeader")) { + opt = JK_OPT_FLUSHEADER; + } + else if (!strcasecmp(w, "DisableReuse")) { + opt = JK_OPT_DISABLEREUSE; + } + else if (!strcasecmp(w, "ForwardSSLCertChain")) { + opt = JK_OPT_FWDCERTCHAIN; + } + else if (!strcasecmp(w, "ForwardKeySize")) { + opt = JK_OPT_FWDKEYSIZE; + } + else if (!strcasecmp(w, "RejectUnsafeURI")) { + opt = JK_OPT_REJECTUNSAFE; + } + else + return ap_pstrcat(cmd->pool, "JkOptions: Illegal option '", w, + "'", NULL); + + conf->options &= ~mask; + + if (action == '-') { + conf->exclude_options |= opt; + } + else if (action == '+') { + conf->options |= opt; + } + else { /* for now +Opt == Opt */ + conf->options |= opt; + } + } + return NULL; +} + +/* + * JkEnvVar Directive Handling + * + * JkEnvVar MYOWNDIR + */ + +static const char *jk_add_env_var(cmd_parms * cmd, + void *dummy, + char *env_name, char *default_value) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + conf->envvars_has_own = JK_TRUE; + if (!conf->envvars) { + conf->envvars = ap_make_table(cmd->pool, 0); + conf->envvars_def = ap_make_table(cmd->pool, 0); + conf->envvar_items = ap_make_array(cmd->pool, 0, + sizeof(envvar_item)); + } + + /* env_name is mandatory, default_value is optional. + * No value means send the attribute only, if the env var is set during runtime. + */ + ap_table_setn(conf->envvars, env_name, default_value ? default_value : ""); + ap_table_setn(conf->envvars_def, env_name, default_value ? "1" : "0"); + + return NULL; +} + +/* + * JkWorkerProperty Directive Handling + * + * JkWorkerProperty name=value + */ + +static const char *jk_set_worker_property(cmd_parms * cmd, + void *dummy, + const char *line) +{ + server_rec *s = cmd->server; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + if (!jk_worker_properties) + jk_map_alloc(&jk_worker_properties); + if (jk_map_read_property(jk_worker_properties, NULL, line, + JK_MAP_HANDLE_DUPLICATES, conf->log) == JK_FALSE) + return ap_pstrcat(cmd->temp_pool, "Invalid JkWorkerProperty ", line, NULL); + + return NULL; +} + +static const command_rec jk_cmds[] = { + /* + * JkWorkersFile specifies a full path to the location of the worker + * properties file. + * + * This file defines the different workers used by apache to redirect + * servlet requests. + */ + {"JkWorkersFile", jk_set_worker_file, NULL, RSRC_CONF, TAKE1, + "The name of a worker file for the Tomcat servlet containers"}, + + /* + * JkMountFile specifies a full path to the location of the + * uriworker properties file. + * + * This file defines the different mapping for workers used by apache + * to redirect servlet requests. + */ + {"JkMountFile", jk_set_mount_file, NULL, RSRC_CONF, TAKE1, + "The name of a mount file for the Tomcat servlet uri mappings"}, + + /* + * JkMountFileReload specifies the reload check interval for the + * uriworker properties file. + * + * Default value is: JK_URIMAP_DEF_RELOAD + */ + {"JkMountFileReload", jk_set_mount_file_reload, NULL, RSRC_CONF, TAKE1, + "The reload check interval of the mount file"}, + + /* + * JkMount mounts a url prefix to a worker (the worker need to be + * defined in the worker properties file. + */ + {"JkMount", jk_mount_context, NULL, RSRC_CONF|ACCESS_CONF, TAKE12, + "A mount point from a context to a servlet-engine worker"}, + + /* + * JkUnMount unmounts a url prefix to a worker (the worker need to be + * defined in the worker properties file. + */ + {"JkUnMount", jk_unmount_context, NULL, RSRC_CONF|ACCESS_CONF, TAKE12, + "A no mount point from a context to a servlet-engine worker"}, + + /* + * JkMountCopy specifies if mod_jk should copy the mount points + * from the main server to the virtual servers. + */ + {"JkMountCopy", jk_set_mountcopy, NULL, RSRC_CONF, TAKE1, + "Should the base server mounts be copied to the virtual server"}, + + /* + * JkStripSession specifies if mod_jk should strip the ;jsessionid + * from the unmapped urls + */ + {"JkStripSession", jk_set_strip_session, NULL, RSRC_CONF, FLAG, + "Should the server strip the jsessionid from unmapped URLs"}, + + /* + * JkLogFile & JkLogLevel specifies to where should the plugin log + * its information and how much. + * JkLogStampFormat specify the time-stamp to be used on log + */ + {"JkLogFile", jk_set_log_file, NULL, RSRC_CONF, TAKE1, + "Full path to the mod_jk module log file"}, + {"JkShmFile", jk_set_shm_file, NULL, RSRC_CONF, TAKE1, + "Full path to the mod_jk module shared memory file"}, + {"JkShmSize", jk_set_shm_size, NULL, RSRC_CONF, TAKE1, + "Size of the shared memory file in KBytes"}, + {"JkLogLevel", jk_set_log_level, NULL, RSRC_CONF, TAKE1, + "The mod_jk module log level, can be debug, info, request, error, or emerg"}, + {"JkLogStampFormat", jk_set_log_fmt, NULL, RSRC_CONF, TAKE1, + "The mod_jk module log format, follow strftime syntax"}, + {"JkRequestLogFormat", jk_set_request_log_format, NULL, RSRC_CONF, TAKE1, + "The mod_jk module request log format string"}, + + /* + * Automatically Alias webapp context directories into the Apache + * document space. + */ + {"JkAutoAlias", jk_set_auto_alias, NULL, RSRC_CONF, TAKE1, + "The mod_jk module automatic context apache alias directory"}, + + /* + * Enable worker name to be set in an environment variable. + * This way one can use LocationMatch together with mod_env, + * mod_setenvif and mod_rewrite to set the target worker. + * Use this in combination with SetHandler jakarta-servlet to + * make mod_jk the handler for the request. + * + */ + {"JkWorkerIndicator", jk_set_worker_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the worker name"}, + + /* + * Environment variables used to overwrite the following + * request information which gets forwarded: + * - remote_addr + * - remote_port + * - remote_host + * - remote_user + * - auth_type + * - server_name + * - server_port + */ + {"JkRemoteAddrIndicator", jk_set_remote_addr_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the remote address"}, + {"JkRemotePortIndicator", jk_set_remote_port_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the remote port"}, + {"JkRemoteHostIndicator", jk_set_remote_host_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the remote host name"}, + {"JkRemoteUserIndicator", jk_set_remote_user_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the remote user name"}, + {"JkAuthTypeIndicator", jk_set_auth_type_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the type of authentication"}, + {"JkLocalNameIndicator", jk_set_local_name_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the local name"}, + {"JkLocalPortIndicator", jk_set_local_port_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains the local port"}, + + /* + * Apache has multiple SSL modules (for example apache_ssl, stronghold + * IHS ...). Each of these can have a different SSL environment names + * The following properties let the administrator specify the envoiroment + * variables names. + * + * HTTPS - indication for SSL + * CERTS - Base64-Der-encoded client certificates. + * CIPHER - A string specifing the ciphers suite in use. + * SESSION - A string specifing the current SSL session. + * KEYSIZE - Size of Key used in dialogue (#bits are secure) + */ + {"JkHTTPSIndicator", jk_set_https_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains SSL indication"}, + {"JkCERTSIndicator", jk_set_certs_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains SSL client certificates"}, + {"JkCIPHERIndicator", jk_set_cipher_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains SSL client cipher"}, + {"JkCERTCHAINPrefix", jk_set_certchain_prefix, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment (prefix) that contains SSL client chain certificates"}, + {"JkSESSIONIndicator", jk_set_session_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains SSL session"}, + {"JkKEYSIZEIndicator", jk_set_key_size_indicator, NULL, RSRC_CONF, TAKE1, + "Name of the Apache environment that contains SSL key size in use"}, + {"JkExtractSSL", jk_set_enable_ssl, NULL, RSRC_CONF, FLAG, + "Turns on SSL processing and information gathering by mod_jk"}, + + /* + * Options to tune mod_jk configuration + * for now we understand : + * +ForwardSSLKeySize => Forward SSL Key Size, to follow 2.3 specs but may broke old TC 3.2 + * -ForwardSSLKeySize => Don't Forward SSL Key Size, will make mod_jk works with all TC release + * ForwardURICompat => Forward URI normally, less spec compliant but mod_rewrite compatible (old TC) + * ForwardURICompatUnparsed => Forward URI as unparsed, spec compliant but broke mod_rewrite (old TC) + * ForwardURIEscaped => Forward URI escaped and Tomcat (3.3 rc2) stuff will do the decoding part + * +ForwardSSLCertChain => Forward SSL certificate chain + * -ForwardSSLCertChain => Don't forward SSL certificate chain + */ + {"JkOptions", jk_set_options, NULL, RSRC_CONF, RAW_ARGS, + "Set one of more options to configure the mod_jk module"}, + + /* + * JkEnvVar let user defines envs var passed from WebServer to + * Servlet Engine + */ + {"JkEnvVar", jk_add_env_var, NULL, RSRC_CONF, TAKE12, + "Adds a name of environment variable and an optional value " + "that should be sent to servlet-engine"}, + + {"JkWorkerProperty", jk_set_worker_property, NULL, RSRC_CONF, RAW_ARGS, + "Set workers.properties formated directive"}, + + {NULL} +}; + +/* ====================================================================== */ +/* The JK module handlers */ +/* ====================================================================== */ + +/* + * Called to handle a single request. + */ +static int jk_handler(request_rec * r) +{ + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(r->server-> + module_config, + &jk_module); + /* Retrieve the worker name stored by jk_translate() */ + const char *worker_name = ap_table_get(r->notes, JK_NOTE_WORKER_NAME); + int rc; + + JK_TRACE_ENTER(conf->log); + + if (ap_table_get(r->subprocess_env, "no-jk")) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Into handler no-jk env var detected for uri=%s, declined", + r->uri); + JK_TRACE_EXIT(conf->log); + return DECLINED; + } + + if (r->proxyreq) { + jk_log(conf->log, JK_LOG_ERROR, + "Request has proxyreq flag set in mod_jk handler - aborting."); + JK_TRACE_EXIT(conf->log); + return HTTP_INTERNAL_SERVER_ERROR; + } + + /* Set up r->read_chunked flags for chunked encoding, if present */ + if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK))) { + jk_log(conf->log, JK_LOG_ERROR, + "Could not setup client_block for chunked encoding - aborting"); + JK_TRACE_EXIT(conf->log); + return rc; + } + + if (worker_name == NULL && r->handler && !strcmp(r->handler, JK_HANDLER)) { + /* we may be here because of a manual directive ( that overrides + * translate and + * sets the handler directly ). We still need to know the worker. + */ + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Retrieving environment %s", conf->worker_indicator); + worker_name = (char *)ap_table_get(r->subprocess_env, conf->worker_indicator); + if (worker_name) { + /* The JkWorkerIndicator environment variable has + * been used to explicitely set the worker without JkMount. + * This is useful in combination with LocationMatch or mod_rewrite. + */ + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Retrieved worker (%s) from env %s for %s", + worker_name, conf->worker_indicator, r->uri); + } + else if (worker_env.num_of_workers == 1) { + /* We have a single worker ( the common case ). + * ( lb is a bit special, it should count as a single worker but + * I'm not sure how ). We also have a manual config directive that + * explicitely give control to us. + */ + worker_name = worker_env.worker_list[0]; + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Single worker (%s) configuration for %s", + worker_name, r->uri); + } + else if (worker_env.num_of_workers) { + worker_name = worker_env.worker_list[0]; + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Using first worker (%s) from %d workers for %s", + worker_name, worker_env.num_of_workers, r->uri); + } + } + + if (worker_name) { + jk_worker_t *worker; + + worker = wc_get_worker_for_name(worker_name, conf->log); + + if (worker) { +#ifndef NO_GETTIMEOFDAY + struct timeval tv_begin, tv_end; + long micro, seconds; + char *duration = NULL; +#endif + int rc = JK_FALSE; + int is_error = JK_HTTP_SERVER_ERROR; + apache_private_data_t private_data; + jk_ws_service_t s; + jk_pool_atom_t buf[SMALL_POOL_SIZE]; + jk_open_pool(&private_data.p, buf, sizeof(buf)); + + private_data.read_body_started = JK_FALSE; + private_data.r = r; + + wc_maintain(conf->log); + jk_init_ws_service(&s); + + s.ws_private = &private_data; + s.pool = &private_data.p; + ap_table_setn(r->notes, JK_NOTE_WORKER_TYPE, + wc_get_name_for_type(worker->type, conf->log)); +#ifndef NO_GETTIMEOFDAY + gettimeofday(&tv_begin, NULL); +#endif + + if (init_ws_service(&private_data, &s, conf)) { + jk_endpoint_t *end = NULL; + if (worker->get_endpoint(worker, &end, conf->log)) { + rc = end->service(end, &s, conf->log, &is_error); + end->done(&end, conf->log); + + if (s.content_read < s.content_length || + (s.is_chunked && !s.no_more_chunks)) { + /* + * If the servlet engine didn't consume all of the + * request data, consume and discard all further + * characters left to read from client + */ + char *buff = ap_palloc(r->pool, 2048); + if (buff != NULL) { + int rd; + while ((rd = + ap_get_client_block(r, buff, 2048)) > 0) { + s.content_read += rd; + } + } + } + } +#ifndef NO_GETTIMEOFDAY + gettimeofday(&tv_end, NULL); + if (tv_end.tv_usec < tv_begin.tv_usec) { + tv_end.tv_usec += 1000000; + tv_end.tv_sec--; + } + micro = tv_end.tv_usec - tv_begin.tv_usec; + seconds = tv_end.tv_sec - tv_begin.tv_sec; + duration = + ap_psprintf(r->pool, "%.1ld.%.6ld", seconds, micro); + ap_table_setn(r->notes, JK_NOTE_REQUEST_DURATION, duration); +#endif + if (s.route && *s.route) + ap_table_setn(r->notes, JK_NOTE_WORKER_ROUTE, s.route); + if (conf->format != NULL) { + request_log_transaction(r, conf); + } + } + else { + jk_log(conf->log, JK_LOG_ERROR, "Could not init service" + " for worker=%s", + worker_name); + jk_close_pool(&private_data.p); + JK_TRACE_EXIT(conf->log); + return is_error; + } + jk_close_pool(&private_data.p); + + if (rc > 0) { + if (s.extension.use_server_error_pages && + s.http_response_status >= s.extension.use_server_error_pages) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, "Forwarding status=%d" + " for worker=%s", + s.http_response_status, worker_name); + JK_TRACE_EXIT(conf->log); + return s.http_response_status; + } + /* If tomcat returned no body and the status is not OK, + let apache handle the error code */ + if (!r->sent_bodyct && r->status >= HTTP_BAD_REQUEST) { + jk_log(conf->log, JK_LOG_INFO, "No body with status=%d" + " for worker=%s", + r->status, worker_name); + JK_TRACE_EXIT(conf->log); + return r->status; + } + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, "Service finished" + " with status=%d for worker=%s", + r->status, worker_name); + JK_TRACE_EXIT(conf->log); + return OK; /* NOT r->status, even if it has changed. */ + } + else if (rc == JK_CLIENT_ERROR) { + if (is_error != HTTP_REQUEST_ENTITY_TOO_LARGE) + r->connection->aborted = 1; + jk_log(conf->log, JK_LOG_INFO, "Aborting connection" + " for worker=%s", + worker_name); + JK_TRACE_EXIT(conf->log); + return is_error; + } + else { + jk_log(conf->log, JK_LOG_INFO, "Service error=%d" + " for worker=%s", + rc, worker_name); + JK_TRACE_EXIT(conf->log); + return is_error; + } + } + else { + jk_log(conf->log, JK_LOG_ERROR, "Could not init service" + " for worker=%s", + worker_name); + JK_TRACE_EXIT(conf->log); + return HTTP_INTERNAL_SERVER_ERROR; + } + } + + JK_TRACE_EXIT(conf->log); + return HTTP_INTERNAL_SERVER_ERROR; +} + +/* + * Create a default config object. + */ +static void *create_jk_config(ap_pool * p, server_rec * s) +{ + jk_server_conf_t *c = + (jk_server_conf_t *) ap_pcalloc(p, sizeof(jk_server_conf_t)); + + c->log_fd = -1; + c->mountcopy = JK_FALSE; + c->was_initialized = JK_FALSE; + + if (s->is_virtual) { + c->mountcopy = JK_UNSET; + c->mount_file_reload = JK_UNSET; + c->log_level = JK_UNSET; + c->ssl_enable = JK_UNSET; + c->strip_session = JK_UNSET; + } else { + if (!jk_map_alloc(&(c->uri_to_context))) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, "Memory error"); + } + c->mountcopy = JK_FALSE; + c->mount_file_reload = JK_URIMAP_DEF_RELOAD; + c->log_level = JK_LOG_DEF_LEVEL; + c->options = JK_OPT_DEFAULT; + c->worker_indicator = JK_ENV_WORKER_NAME; + + /* + * Configurable environment variables to overwrite + * request information using meta data send by a + * proxy in front of us. + */ + c->remote_addr_indicator = JK_ENV_REMOTE_ADDR; + c->remote_port_indicator = JK_ENV_REMOTE_PORT; + c->remote_host_indicator = JK_ENV_REMOTE_HOST; + c->remote_user_indicator = JK_ENV_REMOTE_USER; + c->auth_type_indicator = JK_ENV_AUTH_TYPE; + c->local_name_indicator = JK_ENV_LOCAL_NAME; + c->local_port_indicator = JK_ENV_LOCAL_PORT; + + /* + * By default we will try to gather SSL info. + * Disable this functionality through JkExtractSSL + */ + c->ssl_enable = JK_TRUE; + /* + * The defaults ssl indicators match those in mod_ssl (seems + * to be in more use). + */ + c->https_indicator = JK_ENV_HTTPS; + c->certs_indicator = JK_ENV_CERTS; + c->cipher_indicator = JK_ENV_CIPHER; + c->certchain_prefix = JK_ENV_CERTCHAIN_PREFIX; + c->session_indicator = JK_ENV_SESSION; + c->key_size_indicator = JK_ENV_KEY_SIZE; + c->strip_session = JK_FALSE; + } + c->envvars_has_own = JK_FALSE; + + c->s = s; + + return c; +} + + +/* + * Utility - copy items from apr table src to dst, + * for keys that exist in src but not in dst. + */ +static void merge_apr_table(table *src, table *dst) +{ + int i; + const array_header *arr; + const table_entry *elts; + + arr = ap_table_elts(src); + elts = (const table_entry *)arr->elts; + for (i = 0; i < arr->nelts; ++i) { + if (!ap_table_get(dst, elts[i].key)) { + ap_table_setn(dst, elts[i].key, elts[i].val); + } + } +} + + +static void *merge_jk_config(ap_pool * p, void *basev, void *overridesv) +{ + jk_server_conf_t *base = (jk_server_conf_t *) basev; + jk_server_conf_t *overrides = (jk_server_conf_t *) overridesv; + + if (!overrides->log_file) + overrides->log_file = base->log_file; + if (overrides->log_level == JK_UNSET) + overrides->log_level = base->log_level; + + if (!overrides->stamp_format_string) + overrides->stamp_format_string = base->stamp_format_string; + if (!overrides->format_string) + overrides->format_string = base->format_string; + + if (!overrides->worker_indicator) + overrides->worker_indicator = base->worker_indicator; + + if (!overrides->remote_addr_indicator) + overrides->remote_addr_indicator = base->remote_addr_indicator; + if (!overrides->remote_port_indicator) + overrides->remote_port_indicator = base->remote_port_indicator; + if (!overrides->remote_host_indicator) + overrides->remote_host_indicator = base->remote_host_indicator; + if (!overrides->remote_user_indicator) + overrides->remote_user_indicator = base->remote_user_indicator; + if (!overrides->auth_type_indicator) + overrides->auth_type_indicator = base->auth_type_indicator; + if (!overrides->local_name_indicator) + overrides->local_name_indicator = base->local_name_indicator; + if (!overrides->local_port_indicator) + overrides->local_port_indicator = base->local_port_indicator; + + if (overrides->ssl_enable == JK_UNSET) + overrides->ssl_enable = base->ssl_enable; + if (!overrides->https_indicator) + overrides->https_indicator = base->https_indicator; + if (!overrides->certs_indicator) + overrides->certs_indicator = base->certs_indicator; + if (!overrides->cipher_indicator) + overrides->cipher_indicator = base->cipher_indicator; + if (!overrides->certchain_prefix) + overrides->certchain_prefix = base->certchain_prefix; + if (!overrides->session_indicator) + overrides->session_indicator = base->session_indicator; + if (!overrides->key_size_indicator) + overrides->key_size_indicator = base->key_size_indicator; + +/* Don't simply accumulate bits in the JK_OPT_FWDURIMASK region, */ +/* because those are multi-bit values. */ + if (overrides->options & JK_OPT_FWDURIMASK) + overrides->options |= (base->options & ~base->exclude_options) & ~JK_OPT_FWDURIMASK; + else + overrides->options |= (base->options & ~base->exclude_options); + + if (base->envvars) { + if (overrides->envvars && overrides->envvars_has_own) { +/* merge_apr_table() preserves existing entries in overrides table */ + merge_apr_table(base->envvars, overrides->envvars); + merge_apr_table(base->envvars_def, overrides->envvars_def); + } + else { + overrides->envvars = base->envvars; + overrides->envvars_def = base->envvars_def; + overrides->envvar_items = base->envvar_items; + } + } + + if (overrides->mountcopy == JK_UNSET && jk_mount_copy_all == JK_TRUE) { + overrides->mountcopy = JK_TRUE; + } + if (overrides->uri_to_context && overrides->mountcopy == JK_TRUE) { +/* jk_map_copy() preserves existing entries in overrides map */ + if (jk_map_copy(base->uri_to_context, overrides->uri_to_context) == JK_FALSE) { + jk_error_exit(APLOG_MARK, APLOG_EMERG, overrides->s, p, "Memory error"); + } + if (!overrides->mount_file) + overrides->mount_file = base->mount_file; + } + if (overrides->mountcopy == JK_TRUE) { + if (!overrides->alias_dir) + overrides->alias_dir = base->alias_dir; + } + if (overrides->mount_file_reload == JK_UNSET) + overrides->mount_file_reload = base->mount_file_reload; + if (overrides->strip_session == JK_UNSET) + overrides->strip_session = base->strip_session; + + return overrides; +} + +static int JK_METHOD jk_log_to_file(jk_logger_t *l, int level, + int used, char *what) +{ + if (l && + (l->level <= level || level == JK_LOG_REQUEST_LEVEL) && + l->logger_private && what && used > 0) { + jk_file_logger_t *flp = l->logger_private; + int log_fd = flp->log_fd; + if (log_fd >= 0) { +#if defined(WIN32) + what[used++] = '\r'; +#endif + what[used++] = '\n'; + if (write(log_fd, what, used) < 0 ) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL, + "mod_jk: jk_log_to_file %s failed", + what); + } + } + + return JK_TRUE; + } + + return JK_FALSE; +} + +static int log_fd_get(char *key) +{ + const char *buf=ap_table_get(jk_log_fds, key); + if (buf) + return atoi(buf); + return 0; +} + +static void log_fd_set(pool *p, char *key, int v) +{ + char *buf=(char *)ap_pcalloc(p, 8*sizeof(char)); + ap_snprintf(buf, 8, "%d", v); + ap_table_setn(jk_log_fds, key, buf); +} + +static void open_jk_log(server_rec *s, pool *p) +{ + const char *fname; + int jklogfd; + piped_log *pl; + jk_logger_t *jkl; + jk_file_logger_t *flp; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + if (!s->is_virtual && !conf->log_file) { + conf->log_file = ap_server_root_relative(p, JK_LOG_DEF_FILE); + if (conf->log_file) + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, s, + "No JkLogFile defined in httpd.conf. " + "Using default %s", conf->log_file); + } + + if (s->is_virtual && conf->log_file == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_jk: Invalid JkLogFile NULL"); + conf->log = main_log; + return; + } + if (s->is_virtual && *(conf->log_file) == '\0') { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_jk: Invalid JkLogFile EMPTY"); + conf->log = main_log; + return; + } + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(conf->log_file, 0); +#endif + + jklogfd = log_fd_get(conf->log_file); + if (!jklogfd) { + if (*conf->log_file == '|') { + if ((pl = ap_open_piped_log(p, conf->log_file + 1)) == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_jk: could not open reliable pipe " + "to jk log %s", conf->log_file + 1); + exit(1); + } + jklogfd = ap_piped_log_write_fd(pl); + } + else { + fname = ap_server_root_relative(p, conf->log_file); + if (!fname) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_jk: Invalid JkLog " "path %s", conf->log_file); + exit(1); + } +#if AP_MODULE_MAGIC_AT_LEAST(19990320,14) + if ((jklogfd = ap_popenf_ex(p, fname, xfer_flags, xfer_mode, 1)) + < 0) { +#else + if ((jklogfd = ap_popenf(p, fname, xfer_flags, xfer_mode)) + < 0) { +#endif + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "mod_jk: could not open JkLog " "file %s", fname); + exit(1); + } + } + log_fd_set(p, conf->log_file, jklogfd); + } + conf->log_fd = jklogfd; + jkl = (jk_logger_t *)ap_palloc(p, sizeof(jk_logger_t)); + flp = (jk_file_logger_t *)ap_palloc(p, sizeof(jk_file_logger_t)); + if (jkl && flp) { + jkl->log = jk_log_to_file; + jkl->level = conf->log_level; + jkl->logger_private = flp; + flp->log_fd = conf->log_fd; + conf->log = jkl; + jk_set_time_fmt(conf->log, conf->stamp_format_string); + if (main_log == NULL) + main_log = conf->log; + return; + } + + return; +} + +static void jk_init(server_rec * s, ap_pool * p) +{ + int rc; + server_rec *srv = s; + const char *err_string = NULL; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + if (!jk_worker_properties) + jk_map_alloc(&jk_worker_properties); + jk_map_put(jk_worker_properties, "ServerRoot", ap_server_root, NULL); + + main_server = s; + jk_log_fds = ap_make_table(p, 0); + + /* step through the servers and open each jk logfile + * and do additional post config initialization. + */ + for (; srv; srv = srv->next) { + jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config, + &jk_module); + +/* + * If a virtual server contains no JK directive, httpd shares + * the config structure. But we don't want to share some settings + * by default, especially the JkMount rules. + * Therefore we check, if this config structure really belongs to this + * vhost, otherwise we create a new one and merge. + */ + if (sconf && sconf->s != srv) { + jk_server_conf_t *srvconf = (jk_server_conf_t *)create_jk_config(p, srv); + sconf = (jk_server_conf_t *)merge_jk_config(p, sconf, srvconf); + ap_set_module_config(srv->module_config, &jk_module, sconf); + + } + + if (sconf && sconf->was_initialized == JK_FALSE) { + sconf->was_initialized = JK_TRUE; + open_jk_log(srv, p); + sconf->options &= ~sconf->exclude_options; + if (sconf->uri_to_context) { + if (!uri_worker_map_alloc(&(sconf->uw_map), + sconf->uri_to_context, sconf->log)) + jk_error_exit(APLOG_MARK, APLOG_EMERG, srv, + p, "Memory error"); + if (sconf->options & JK_OPT_REJECTUNSAFE) + sconf->uw_map->reject_unsafe = 1; + else + sconf->uw_map->reject_unsafe = 0; + if (sconf->mount_file) { + sconf->uw_map->fname = sconf->mount_file; + sconf->uw_map->reload = sconf->mount_file_reload; + uri_worker_map_switch(sconf->uw_map, sconf->log); + uri_worker_map_load(sconf->uw_map, sconf->log); + } + } + else { + if (sconf->mountcopy == JK_TRUE) { + sconf->uw_map = conf->uw_map; + } + } + if (sconf->format_string) { + sconf->format = + parse_request_log_string(p, sconf->format_string, &err_string); + if (sconf->format == NULL) + ap_log_error(APLOG_MARK, APLOG_ERR, srv, + "JkRequestLogFormat format array NULL"); + } + if (sconf->envvars && sconf->envvars_has_own) { + int i; + const array_header *arr; + const table_entry *elts; + envvar_item *item; + const char *envvar_def; + + arr = ap_table_elts(sconf->envvars); + if (arr) { + elts = (const table_entry *)arr->elts; + for (i = 0; i < arr->nelts; ++i) { + item = (envvar_item *)ap_push_array(sconf->envvar_items); + if (!item) + jk_error_exit(APLOG_MARK, APLOG_EMERG, srv, + p, "Memory error"); + item->name = elts[i].key; + envvar_def = ap_table_get(sconf->envvars_def, elts[i].key); + if (envvar_def && !strcmp("1", envvar_def) ) { + item->value = elts[i].val; + item->has_default = 1; + } + else { + item->value = ""; + item->has_default = 0; + } + } + } + } + } + } + + if ((jk_worker_file != NULL) && + !jk_map_read_properties(jk_worker_properties, NULL, jk_worker_file, NULL, + JK_MAP_HANDLE_DUPLICATES, conf->log)) { + jk_error_exit(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s, p, + "Error in reading worker properties from '%s'", + jk_worker_file); + } + + if (jk_map_resolve_references(jk_worker_properties, "worker.", + 1, 1, conf->log) == JK_FALSE) { + jk_error_exit(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s, p, + "Error in resolving configuration references"); + } + +#if !defined(WIN32) && !defined(NETWARE) + if (!jk_shm_file) { + jk_shm_file = ap_server_root_relative(p, JK_SHM_DEF_FILE); + +#ifdef CHROOTED_APACHE + ap_server_strip_chroot(jk_shm_file, 0); +#endif + + if (jk_shm_file) + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, s, + "No JkShmFile defined in httpd.conf. " + "Using default %s", jk_shm_file); + } +#endif + + if (jk_shm_size == 0) + jk_shm_size = jk_shm_calculate_size(jk_worker_properties, conf->log); + else { + jk_log(conf->log, JK_LOG_WARNING, + "The optimal shared memory size can now be determined automatically."); + jk_log(conf->log, JK_LOG_WARNING, + "You can remove the JkShmSize directive if you want to use the optimal size."); + } + if ((rc = jk_shm_open(jk_shm_file, jk_shm_size, conf->log)) != 0) + jk_log(conf->log, JK_LOG_ERROR, + "Initializing shm:%s errno=%d. Load balancing workers will not function properly.", + jk_shm_name(), rc); + + /* SREVILAK -- register cleanup handler to clear resources on restart, + * to make sure log file gets closed in the parent process */ + ap_register_cleanup(p, s, jk_server_cleanup, ap_null_cleanup); + + /* we add the URI->WORKER MAP since workers using AJP14 will feed it */ + worker_env.uri_to_worker = conf->uw_map; + worker_env.virtual = "*"; /* for now */ + worker_env.server_name = (char *)ap_get_server_version(); + worker_env.pool = NULL; + + if (wc_open(jk_worker_properties, &worker_env, conf->log)) { +#if MODULE_MAGIC_NUMBER >= 19980527 + /* Tell apache we're here */ + ap_add_version_component(JK_EXPOSED_VERSION); +#endif + jk_log(conf->log, JK_LOG_INFO, + "%s initialized", + JK_FULL_EXPOSED_VERSION); + } + else { + jk_error_exit(APLOG_MARK, APLOG_EMERG | APLOG_NOERRNO, s, p, + "Error in creating the workers." + " Please consult your mod_jk log file '%s'.", conf->log_file); + } + if (conf->uw_map) { + uri_worker_map_ext(conf->uw_map, conf->log); + uri_worker_map_switch(conf->uw_map, conf->log); + } + for (srv = s; srv; srv = srv->next) { + jk_server_conf_t *sconf = (jk_server_conf_t *)ap_get_module_config(srv->module_config, + &jk_module); + if (conf->uw_map != sconf->uw_map && sconf->uw_map) { + uri_worker_map_ext(sconf->uw_map, sconf->log); + uri_worker_map_switch(sconf->uw_map, sconf->log); + } + } + +} + +/* + * Perform uri to worker mapping, and store the name of the relevant worker + * in the notes fields of the request_rec object passed in. This will then + * get picked up in jk_handler(). + */ +static int jk_translate(request_rec * r) +{ + ap_set_module_config(r->request_config, &jk_module, NULL); + + if (!r->proxyreq) { + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(r->server-> + module_config, + &jk_module); + + if (conf) { + char *clean_uri = ap_pstrdup(r->pool, r->uri); + const char *worker; + + if (ap_table_get(r->subprocess_env, "no-jk")) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Into translate no-jk env var detected for uri=%s, declined", + r->uri); + return DECLINED; + } + + ap_no2slash(clean_uri); + if (!conf->uw_map) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "missing uri map for %s:%s", + conf->s->server_hostname ? conf->s->server_hostname : "_default_", + r->uri); + return DECLINED; + } + else { + rule_extension_t *e; + worker = map_uri_to_worker_ext(conf->uw_map, clean_uri, + NULL, &e, NULL, conf->log); + ap_set_module_config(r->request_config, &jk_module, e); + } + + /* Don't know the worker, ForwardDirectories is set, there is a + * previous request for which the handler is JK_HANDLER (as set by + * jk_fixups) and the request is for a directory: + * --> forward to Tomcat, via default worker */ + if (!worker && (conf->options & JK_OPT_FWDDIRS) && + r->prev && r->prev->handler && + !strcmp(r->prev->handler, JK_HANDLER) && clean_uri && + strlen(clean_uri) && clean_uri[strlen(clean_uri) - 1] == '/') { + + if (worker_env.num_of_workers) { + /* Nothing here to do but assign the first worker since we + * already tried mapping and it didn't work out */ + worker = worker_env.worker_list[0]; + + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, "Manual configuration for %s %s", + clean_uri, worker_env.worker_list[0]); + } + } + + if (worker) { + r->handler = ap_pstrdup(r->pool, JK_HANDLER); + ap_table_setn(r->notes, JK_NOTE_WORKER_NAME, worker); + } + else if (conf->alias_dir != NULL) { + /* Automatically map uri to a context static file */ + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "check alias_dir: %s", + conf->alias_dir); + if (strlen(clean_uri) > 1) { + /* Get the context directory name */ + char *context_dir = NULL; + char *context_path = NULL; + char *child_dir = NULL; + char *index = clean_uri; + char *suffix = strchr(index + 1, '/'); + if (suffix != NULL) { + int size = suffix - index; + context_dir = ap_pstrndup(r->pool, index, size); + /* Get the context child directory name */ + index = index + size + 1; + suffix = strchr(index, '/'); + if (suffix != NULL) { + size = suffix - index; + child_dir = ap_pstrndup(r->pool, index, size); + } + else { + child_dir = index; + } + /* Deny access to WEB-INF and META-INF directories */ + if (child_dir != NULL) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "AutoAlias child_dir: %s", + child_dir); + if (!strcasecmp(child_dir, "WEB-INF") || + !strcasecmp(child_dir, "META-INF")) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "AutoAlias HTTP_NOT_FOUND for URI: %s", + r->uri); + return HTTP_NOT_FOUND; + } + } + } + else { + context_dir = ap_pstrdup(r->pool, index); + } + + context_path = ap_pstrcat(r->pool, conf->alias_dir, + ap_os_escape_path(r->pool, + context_dir, + 1), NULL); + if (context_path != NULL) { + DIR *dir = ap_popendir(r->pool, context_path); + if (dir != NULL) { + char *escurl = + ap_os_escape_path(r->pool, clean_uri, 1); + char *ret = + ap_pstrcat(r->pool, conf->alias_dir, escurl, + NULL); + ap_pclosedir(r->pool, dir); + /* Add code to verify real path ap_os_canonical_name */ + if (ret != NULL) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "AutoAlias OK for file: %s", + ret); + r->filename = ret; + return OK; + } + } + else { + /* Deny access to war files in web app directory */ + int size = strlen(context_dir); + if (size > 4 + && !strcasecmp(context_dir + (size - 4), + ".war")) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "AutoAlias FORBIDDEN for URI: %s", + r->uri); + return FORBIDDEN; + } + } + } + } + } + else { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "no match for %s found", + r->uri); + if (conf->strip_session == JK_TRUE) { + char *jsessionid; + if (r->uri) { + jsessionid = strstr(r->uri, JK_PATH_SESSION_IDENTIFIER); + if (jsessionid) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "removing session identifier [%s] for non servlet url [%s]", + jsessionid, r->uri); + *jsessionid = '\0'; + } + } + if (r->filename) { + jsessionid = strstr(r->filename, JK_PATH_SESSION_IDENTIFIER); + if (jsessionid) + *jsessionid = '\0'; + } + } + } + } + } + + return DECLINED; +} + +/* In case ForwardDirectories option is set, we need to know if all files + * mentioned in DirectoryIndex have been exhausted without success. If yes, we + * need to let mod_dir know that we want Tomcat to handle the directory + */ +static int jk_fixups(request_rec * r) +{ + /* This is a sub-request, probably from mod_dir */ + if (r->main) { + jk_server_conf_t *conf = (jk_server_conf_t *) + ap_get_module_config(r->server->module_config, &jk_module); + char *worker = (char *)ap_table_get(r->notes, JK_NOTE_WORKER_NAME); + + if (ap_table_get(r->subprocess_env, "no-jk")) { + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, + "Into fixup no-jk env var detected for uri=%s, declined", + r->uri); + return DECLINED; + } + + /* Only if we have no worker and ForwardDirectories is set */ + if (!worker && (conf->options & JK_OPT_FWDDIRS)) { + char *dummy_ptr[1], **names_ptr, *idx; + int num_names; + dir_config_rec *d = (dir_config_rec *) + ap_get_module_config(r->per_dir_config, &dir_module); + + /* Direct lift from mod_dir */ + if (d->index_names) { + names_ptr = (char **)d->index_names->elts; + num_names = d->index_names->nelts; + } + else { + dummy_ptr[0] = DEFAULT_INDEX; + names_ptr = dummy_ptr; + num_names = 1; + } + + /* Where the index file would start within the filename */ + idx = r->filename + strlen(r->filename) - + strlen(names_ptr[num_names - 1]); + + /* The requested filename has the last index file at the end */ + if (idx >= r->filename && !strcmp(idx, names_ptr[num_names - 1])) { + r->uri = r->main->uri; /* Trick mod_dir with URI */ + r->finfo.st_mode = S_IFREG; /* Trick mod_dir with file stat */ + + /* We'll be checking for handler in r->prev later on */ + r->main->handler = ap_pstrdup(r->pool, JK_HANDLER); + + if (JK_IS_DEBUG_LEVEL(conf->log)) + jk_log(conf->log, JK_LOG_DEBUG, "ForwardDirectories on: %s", + r->uri); + } + } + } + + return DECLINED; +} + +static void child_exit_handler(server_rec * s, ap_pool * p) +{ + /* srevilak - refactor cleanup body to jk_generic_cleanup() */ + jk_generic_cleanup(s); + jk_shm_close(); +} + +static void child_init_handler(server_rec * s, ap_pool * p) +{ + int rc; + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + JK_TRACE_ENTER(conf->log); + + if ((rc = jk_shm_attach(jk_shm_file, jk_shm_size, conf->log)) != 0) + jk_log(conf->log, JK_LOG_ERROR, "Attaching shm:%s errno=%d", + jk_shm_name(), rc); + + JK_TRACE_EXIT(conf->log); + +} + + +/** srevilak -- registered as a cleanup handler in jk_init */ +static void jk_server_cleanup(void *data) +{ + jk_generic_cleanup((server_rec *) data); + jk_shm_close(); +} + + +/** BEGIN SREVILAK + * body taken from exit_handler() + */ +static void jk_generic_cleanup(server_rec *s) +{ + + if (jk_worker_properties) { + jk_map_free(&jk_worker_properties); + jk_worker_properties = NULL; + jk_worker_file = NULL; + jk_mount_copy_all = JK_FALSE; + } + + /* loop through all available servers to clean up all configuration + * records we've created + */ + while (NULL != s) { + jk_server_conf_t *conf = + (jk_server_conf_t *) ap_get_module_config(s->module_config, + &jk_module); + + if (conf && conf->was_initialized == JK_TRUE) { + /* On pool cleanup pass NULL for the jk_logger to + prevent segmentation faults on Windows because + we can't guarantee what order pools get cleaned + up between APR implementations. */ + wc_close(NULL); + if (conf->uri_to_context) { + jk_map_free(&conf->uri_to_context); + /* We cannot have allocated uw_map + * unless we've allocated uri_to_context + */ + if (conf->uw_map) + uri_worker_map_free(&conf->uw_map, NULL); + } + conf->was_initialized = JK_FALSE; + } + s = s->next; + } +} + +/** END SREVILAK **/ + + +static const handler_rec jk_handlers[] = { + {JK_MAGIC_TYPE, jk_handler}, + {JK_HANDLER, jk_handler}, + {NULL} +}; + +module MODULE_VAR_EXPORT jk_module = { + STANDARD_MODULE_STUFF, + jk_init, /* module initializer */ + NULL, /* per-directory config creator */ + NULL, /* dir config merger */ + create_jk_config, /* server config creator */ + merge_jk_config, /* server config merger */ + jk_cmds, /* command table */ + jk_handlers, /* [7] list of handlers */ + jk_translate, /* [2] filename-to-URI translation */ + NULL, /* [5] check/validate user_id */ + NULL, /* [6] check user_id is valid *here* */ + NULL, /* [4] check access by host address */ + NULL, /* XXX [7] MIME type checker/setter */ + jk_fixups, /* [8] fixups */ + NULL, /* [10] logger */ + NULL, /* [3] header parser */ + child_init_handler, /* apache child process initializer */ + child_exit_handler, /* apache child process exit/cleanup */ + NULL /* [1] post read_request handling */ +#ifdef EAPI + /* + * Extended module APIs, needed when using SSL. + * STDC say that we do not have to have them as NULL but + * why take a chance + */ + , NULL, /* add_module */ + NULL, /* remove_module */ + NULL, /* rewrite_command */ + NULL, /* new_connection */ + NULL /* close_connection */ +#endif /* EAPI */ +}; -- cgit 1.2.3-korg