blob: 79d218196e75231300e7ad7fdb19a35e8608828b [file] [log] [blame]
/*
* Copyright 1999-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.
*/
/***************************************************************************
* Description: Apache 1.3 plugin for Jakarta/Tomcat *
* Author: Gal Shachor <shachor@il.ibm.com> *
* Henri Gomez <hgomez@apache.org> *
* Version: $Revision$ *
***************************************************************************/
/*
* mod_jk: keeps all servlet/jakarta related ramblings together.
*/
#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 "http_conf_globals.h"
/*
* Jakarta (jk_) include files
*/
#include "jk_global.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_env.h"
#include "jk_service.h"
#include "jk_worker.h"
#include "jk_workerEnv.h"
#include "jk_uriMap.h"
#include "jk_requtil.h"
#ifdef WIN32
static char file_name[_MAX_PATH];
#endif
#define JK_HANDLER ("jakarta-servlet2")
#define JK_MAGIC_TYPE ("application/x-jakarta-servlet2")
module MODULE_VAR_EXPORT jk2_module;
int jk2_service_apache13_init(jk_env_t *env, jk_ws_service_t *s);
/* In apache1.3 this is reset when the module is reloaded ( after
* config. No good way to discover if it's the first time or not.
*/
static jk_workerEnv_t *workerEnv;
/* This is used to ensure that jk2_create_dir_config creates unique
* dir mappings. This prevents vhost configs as configured through
* httpd.conf from getting crossed.
*/
static int dirCounter = 0;
/* ==================== Options setters ==================== */
/*
* JkSet name value
*
* Set jk options. Same as using workers.properties or a
* config application.
*
* Known properties: see workers.properties documentation
*
* XXX Shouldn't abuse it, there is no way to write back
* the properties.
*/
static const char *jk2_set2(cmd_parms * cmd, void *per_dir,
const char *name, char *value)
{
server_rec *s = cmd->server;
jk_uriEnv_t *serverEnv = (jk_uriEnv_t *)
ap_get_module_config(s->module_config, &jk2_module);
jk_env_t *env = workerEnv->globalEnv;
int rc;
rc = workerEnv->config->setPropertyString(env, workerEnv->config,
(char *)name, value);
if (rc != JK_OK) {
fprintf(stderr, "mod_jk2: Unrecognized option %s %s\n", name, value);
}
return NULL;
}
/**
* Set a property associated with a URI, using native <Location>
* directives.
*
* This is used if you want to use the native mapping and
* integrate better into apache.
*
* Same behavior can be achieved by using uri.properties and/or JkSet.
*
* Example:
* <VirtualHost foo.com>
* <Location /examples>
* JkUriSet worker ajp13
* </Location>
* </VirtualHost>
*
* This is the best way to define a webapplication in apache. It is
* scalable ( using apache native optimizations, you can have hundreds
* of hosts and thousands of webapplications ), 'natural' to any
* apache user.
*
* XXX This is a special configuration, for most users just use
* the properties files.
*/
static const char *jk2_uriSet(cmd_parms * cmd, void *per_dir,
const char *name, const char *val)
{
jk_uriEnv_t *uriEnv = (jk_uriEnv_t *)per_dir;
char *tmp_virtual = NULL;
char *tmp_full_url = NULL;
server_rec *s = cmd->server;
uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
(char *)name, (void *)val);
/*
* all of the objects that get passed in now are unique. create_dir adds a incrementing counter to the
* uri that is used to create the object!
* Here we must now 'fix' the content of the object passed in.
* Apache doesn't care what we do here as it has the reference to the unique object that has been
* created. What we need to do is ensure that the data given to mod_jk2 is correct. Hopefully in the long run
* we can ignore some of the mod_jk details...
*/
/* if applicable we will set the hostname etc variables. */
if (s->is_virtual && s->server_hostname != NULL &&
(uriEnv->virtual == NULL || !strchr(uriEnv->virtual, ':') ||
uriEnv->port != s->port)) {
tmp_virtual = (char *)ap_pcalloc(cmd->pool,
sizeof(char *) *
(strlen(s->server_hostname) + 8));
tmp_full_url =
(char *)ap_pcalloc(cmd->pool,
sizeof(char *) * (strlen(s->server_hostname) +
strlen(uriEnv->uri) + 8));
/* do not pass the hostname:0/ scheme */
if (s->port) {
sprintf(tmp_virtual, "%s:%d", s->server_hostname, s->port);
sprintf(tmp_full_url, "%s:%d%s", s->server_hostname, s->port,
uriEnv->uri);
}
else {
strcpy(tmp_virtual, s->server_hostname);
strcpy(tmp_full_url, s->server_hostname);
strcat(tmp_full_url, uriEnv->uri);
}
uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
"uri", tmp_full_url);
uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
"path", cmd->path);
uriEnv->name = tmp_virtual;
uriEnv->virtual = tmp_virtual;
}
/* now lets actually add the parameter set in the <Location> block */
uriEnv->mbean->setAttribute(workerEnv->globalEnv, uriEnv->mbean,
(char *)name, (void *)val);
return NULL;
}
static void *jk2_create_dir_config(pool * p, char *path)
{
/* We don't know the vhost yet - so path is not
* unique. We'll have to generate a unique name
*/
char *tmp = NULL;
int a = 0;
jk_uriEnv_t *newUri;
jk_bean_t *jkb;
if (!path)
return NULL;
a = strlen(path) + 10;
tmp = (char *)ap_pcalloc(p, sizeof(char *) * (a));
sprintf(tmp, "%s-%d", path == NULL ? "" : path, dirCounter++);
/* the greatest annoyance here is that we can't create the uri correctly with the hostname as well.
as apache doesn't give us the hostname .
we'll fix this in JkUriSet
*/
jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
workerEnv->pool, "uri", tmp);
newUri = jkb->object;
newUri->workerEnv = workerEnv;
newUri->mbean->setAttribute(workerEnv->globalEnv, newUri->mbean, "path",
tmp);
/* I'm hoping that setting the id won't break anything. I havn't noticed it breaking anything. */
newUri->mbean->id = (dirCounter - 1);
/* this makes the display in the status display make more sense */
newUri->mbean->localName = path;
return newUri;
}
/*
* Need to re-do this to make more sense - like properly creating a new config and returning the merged config...
* Looks like parent needs to be dominant.
*/
static void *jk2_merge_dir_config(pool * p, void *childv, void *parentv)
{
jk_uriEnv_t *child = (jk_uriEnv_t *)childv;
jk_uriEnv_t *parent = (jk_uriEnv_t *)parentv;
jk_uriEnv_t *winner = NULL;
/* fprintf(stderr,"Merging child & parent. (dir)\n");
fprintf(stderr, "Merging for vhost child(%s) vhost parent(%s) uri child(%s) uri parent(%s) child worker (%s) parentworker(%s)\n",
(child->virtual==NULL)?"":child->virtual,
(parent->virtual==NULL)?"":parent->virtual,
(child->uri==NULL)?"":child->uri,
(parent->uri==NULL)?"":parent->uri,
(child->workerName==NULL)?"":child->workerName,
(parent->workerName==NULL)?"":parent->workerName
); */
if (child == NULL || child->uri == NULL || child->workerName == NULL)
winner = parent;
else if (parent == NULL || parent->uri == NULL
|| parent->workerName == NULL)
winner = child;
/* interresting bit... so far they are equal ... */
else if (strlen(parent->uri) > strlen(child->uri))
winner = parent;
else
winner = child;
/* if ( winner == child )
fprintf(stderr, "Going with the child\n");
else if ( winner == parent )
fprintf(stderr, "Going with the parent\n");
else
fprintf(stderr, "Going with NULL\n");
*/
return (void *)winner;
}
apr_pool_t *jk_globalPool;
/* Create the initial set of objects. You need to cut&paste this and
adapt to your server.
*/
static int jk2_create_workerEnv(ap_pool * p, const server_rec * s)
{
jk_env_t *env;
jk_pool_t *globalPool;
jk_bean_t *jkb;
apr_initialize();
apr_pool_create(&jk_globalPool, NULL);
jk2_pool_apr_create(NULL, &globalPool, NULL, jk_globalPool);
/** Create the global environment. This will register the default
factories, to be overriten later.
*/
env = jk2_env_getEnv(NULL, globalPool);
/* Optional. Register more factories ( or replace existing ones )
Insert your server-specific objects here.
*/
/* Create the logger . We use the default jk logger, will output
to a file. Check the logger for default settings.
*/
jkb = env->createBean2(env, env->globalPool, "logger.file", "");
env->alias(env, "logger.file:", "logger");
env->alias(env, "logger.file:", "logger:");
if (jkb == NULL) {
fprintf(stderr, "Error creating logger ");
return JK_ERR;
}
env->l = jkb->object;
env->alias(env, "logger.file:", "logger");
/* Create the workerEnv
*/
jkb = env->createBean2(env, env->globalPool, "workerEnv", "");
if (jkb == NULL) {
fprintf(stderr, "Error creating workerEnv ");
return JK_ERR;
}
workerEnv = jkb->object;
env->alias(env, "workerEnv:", "workerEnv");
if (workerEnv == NULL || env->l == NULL) {
fprintf(stderr, "Error initializing jk, NULL objects \n");
return JK_ERR;
}
/* serverRoot via ap_server_root
*/
workerEnv->initData->add(env, workerEnv->initData, "serverRoot",
workerEnv->pool->pstrdup(env, workerEnv->pool,
ap_server_root));
/* Local initialization.
*/
env->l->jkLog(env, env->l, JK_LOG_INFO, "Set serverRoot %s\n",
ap_server_root);
workerEnv->_private = (void *)s;
return JK_OK;
}
/* -------------------- Apache specific initialization -------------------- */
/* Command table.
*/
static const command_rec jk2_cmds[] = {
/* This is the 'main' directive for tunning jk2. It takes 2 parameters,
and it behaves _identically_ as a setting in workers.properties.
*/
{"JkSet", jk2_set2, NULL, RSRC_CONF, TAKE2,
"Set a jk property, same syntax and rules as in JkWorkersFile"},
{"JkUriSet", jk2_uriSet, NULL, ACCESS_CONF, TAKE2,
"Defines a jk property associated with a Location"},
NULL
};
/** This makes the config for the specified server_rec s
This will include vhost info.
*/
static void *jk2_create_config(ap_pool * p, server_rec * s)
{
jk_uriEnv_t *newUri;
jk_bean_t *jkb;
char *tmp;
if (workerEnv == NULL) {
jk2_create_workerEnv(p, s);
}
if (s->is_virtual == 1) {
/* Virtual host */
tmp =
(char *)ap_pcalloc(p,
sizeof(char *) * (strlen(s->server_hostname) +
8));
sprintf(tmp, "%s:%d/", s->server_hostname, s->port);
/* for the sake of consistency we must have the port in the uri.
Really it isn't necessary to have one - but I would like in the future for
the server config to hold the workers for that server... */
jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
workerEnv->pool, "uri", tmp);
}
else {
/* Default host */
jkb = workerEnv->globalEnv->createBean2(workerEnv->globalEnv,
workerEnv->pool, "uri", "");
}
newUri = jkb->object;
newUri->workerEnv = workerEnv;
return (void *)newUri;
}
/** Standard apache callback, merge jk options specified in
<Host> context. Used to set per virtual host configs
*/
static void *jk2_merge_config(ap_pool * p, void *basev, void *overridesv)
{
jk_uriEnv_t *base = (jk_uriEnv_t *)basev;
jk_uriEnv_t *overrides = (jk_uriEnv_t *)overridesv;
/* The 'mountcopy' option should be implemented in common.
*/
return overrides;
}
/** Standard apache callback, initialize jk. This is called after all
the settings took place.
*/
static int jk2_init(server_rec * s, ap_pool * pconf)
{
jk_uriEnv_t *serverEnv =
(jk_uriEnv_t *)ap_get_module_config(s->module_config, &jk2_module);
jk_env_t *env = workerEnv->globalEnv;
ap_pool *gPool = NULL;
void *data = NULL;
int rc = JK_OK;
env->l->jkLog(env, env->l, JK_LOG_INFO, "mod_jk child init\n");
if (s->is_virtual)
return OK;
if (!workerEnv->was_initialized) {
workerEnv->was_initialized = JK_TRUE;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.post_config() init worker env\n");
workerEnv->parentInit(env, workerEnv);
workerEnv->init(env, workerEnv);
workerEnv->server_name = (char *)ap_get_server_version();
#if MODULE_MAGIC_NUMBER >= 19980527
/* Tell apache we're here */
ap_add_version_component(JK_EXPOSED_VERSION);
#endif
}
return OK;
}
/* ========================================================================= */
/* The JK module handlers */
/* ========================================================================= */
/** Main service method, called to forward a request to tomcat
*/
static int jk2_handler(request_rec * r)
{
jk_logger_t *l = NULL;
int rc;
jk_worker_t *worker = NULL;
jk_endpoint_t *end = NULL;
jk_uriEnv_t *uriEnv;
jk_env_t *env;
/* If this is a proxy request, we'll notify an error */
if (r->proxyreq) {
return HTTP_INTERNAL_SERVER_ERROR; /* We don't proxy requests. */
}
/* changed from r->request_config to r->per_dir_config. This should give us the one that was set in
either the translate phase (if it was a config added through workers.properties)
or in the create_dir config. */
uriEnv = ap_get_module_config(r->request_config, &jk2_module); /* get one for the dir */
if (uriEnv == NULL) {
uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module); /* get one specific to this request if there isn't a dir one. */
}
/* not for me, try next handler */
if (uriEnv == NULL || strcmp(r->handler, JK_HANDLER) != 0)
return DECLINED;
/* Get an env instance */
env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
/* Set up r->read_chunked flags for chunked encoding, if present */
if (rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) {
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.handler() Can't setup client block %d\n", rc);
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return rc;
}
if (uriEnv == NULL) {
/* SetHandler case - per_dir config should have the worker */
worker = workerEnv->defaultWorker;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.handler() Default worker for %s %s\n",
r->uri, worker->mbean->name);
}
else {
worker = uriEnv->worker;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.handler() per dir worker for %#lx %#lx\n",
worker, uriEnv);
if (worker == NULL && uriEnv->workerName != NULL) {
worker = env->getByName(env, uriEnv->workerName);
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.handler() finding worker for %s %#lx %#lx\n",
uriEnv->workerName, worker, uriEnv);
uriEnv->worker = worker;
}
}
if (worker == NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"mod_jk.handle() No worker for %s\n", r->uri);
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return HTTP_INTERNAL_SERVER_ERROR; /* No worker defined for this uri */
}
{
jk_ws_service_t sOnStack;
jk_ws_service_t *s = &sOnStack;
jk_pool_t *rPool = NULL;
int rc1;
/* Get a pool for the request XXX move it in workerEnv to
be shared with other server adapters */
rPool = worker->rPoolCache->get(env, worker->rPoolCache);
if (rPool == NULL) {
rPool =
worker->mbean->pool->create(env, worker->mbean->pool,
HUGE_POOL_SIZE);
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.handler(): new rpool\n");
}
jk2_service_apache13_init(env, s);
s->pool = rPool;
s->init(env, s, worker, r);
/* reset the reco_status, will be set to INITED in LB mode */
s->reco_status = RECO_NONE;
s->is_recoverable_error = JK_FALSE;
s->uriEnv = uriEnv;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"modjk.handler() Calling %s %#lx\n",
worker->mbean->name, uriEnv);
rc = worker->service(env, worker, s);
s->afterRequest(env, s);
rPool->reset(env, rPool);
rc1 = worker->rPoolCache->put(env, worker->rPoolCache, rPool);
if (rc1 == JK_OK) {
rPool = NULL;
}
else {
rPool->close(env, rPool);
}
}
if (rc == JK_OK) {
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return OK; /* NOT r->status, even if it has changed. */
}
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"mod_jk.handler() Error connecting to tomcat %d %s\n", rc,
worker == NULL ? "" : worker->channelName ==
NULL ? "" : worker->channelName);
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return HTTP_INTERNAL_SERVER_ERROR; /* Is your tomcat server down ? */
}
/** Use the internal mod_jk mappings to find if this is a request for
* tomcat and what worker to use.
*/
static int jk2_translate(request_rec * r)
{
jk_uriEnv_t *uriEnv;
jk_env_t *env;
jk_uriMap_t *uriMap;
char *name = NULL;
int n;
const char *ptr;
if (r->proxyreq) {
return DECLINED;
}
uriEnv = ap_get_module_config(r->per_dir_config, &jk2_module);
if (uriEnv != NULL && uriEnv->workerName != NULL) {
/* jk2_handler tries to get the request_config and then falls back to the per_dir one.
so no point setting the request_config */
r->handler = JK_HANDLER;
return OK;
}
uriMap = workerEnv->uriMap;
/* Get an env instance */
env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
n = uriMap->vhosts->size(env, uriMap->vhosts);
/* Check JkMount directives, if any */
/* if( workerEnv->uriMap->size == 0 ) */
/* return DECLINED; */
/* get_env() */
env = workerEnv->globalEnv->getEnv(workerEnv->globalEnv);
ptr = ap_get_server_name(r);
if (strlen(ptr) > 1024 - 12) {
/* That is probably an invalid request, DECLINED could display jsp source code. */
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"jk2_map_to_storage Host too big %s\n", ptr);
return HTTP_BAD_REQUEST;
}
uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap,
ptr, ap_get_server_port(r), r->uri);
if (uriEnv == NULL || uriEnv->workerName == NULL) {
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return DECLINED;
}
ap_set_module_config(r->request_config, &jk2_module, uriEnv);
r->handler = JK_HANDLER;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"mod_jk.translate(): uriMap %s %s\n",
r->uri, uriEnv->workerName);
workerEnv->globalEnv->releaseEnv(workerEnv->globalEnv, env);
return OK;
}
static const handler_rec jk2_handlers[] = {
{JK_MAGIC_TYPE, jk2_handler},
{JK_HANDLER, jk2_handler},
NULL
};
module MODULE_VAR_EXPORT jk2_module = {
STANDARD_MODULE_STUFF,
jk2_init, /* module initializer */
jk2_create_dir_config, /* per-directory config creator */
jk2_merge_dir_config, /* dir config merger */
jk2_create_config, /* server config creator */
/* jk2_merge_config, / * server config merger */
NULL,
jk2_cmds, /* command table */
jk2_handlers, /* [7] list of handlers */
jk2_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 */
NULL, /* [8] fixups */
NULL, /* [10] logger */
NULL, /* [3] header parser */
NULL, /* apache child process initializer */
NULL, /* exit_handler, *//* apache child process exit/cleanup */
NULL /* [1] post read_request handling */
#ifdef X_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 */
};
#ifdef WIN32
BOOL WINAPI DllMain(HINSTANCE hInst, // Instance Handle of the DLL
ULONG ulReason, // Reason why NT called this DLL
LPVOID lpReserved) // Reserved parameter for future use
{
GetModuleFileName(hInst, file_name, sizeof(file_name));
return TRUE;
}
#endif