You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bu...@apache.org on 2003/03/28 15:55:10 UTC

DO NOT REPLY [Bug 18472] New: - mod_jk2 doesn't handle vhosts correctly when using the JkUriSet commands

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=18472>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=18472

mod_jk2 doesn't handle vhosts correctly when using the JkUriSet commands

           Summary: mod_jk2 doesn't handle vhosts correctly when using the
                    JkUriSet commands
           Product: Tomcat 4
           Version: 4.1.24
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Major
          Priority: Other
         Component: Connector:Coyote JK 2
        AssignedTo: tomcat-dev@jakarta.apache.org
        ReportedBy: david.cassidy@db.com


When using the JkUriSet commands within a Location block the configuration gets
confused between vhosts.

Example:

<VirtualHost example1.com>
#define remote server
JkSet channel.socket:example1.port 8009
JkSet channel.socket:example1.host server3
JkSet ajp13:example1.channel channel.socket:example1

<Location /examples/>
  JkSetUri group ajp13:example1
</Location>

....
</VirtualHost>

<VirtualHost example2.com>
#define remote server
JkSet channel.socket:example2.port 8500
JkSet channel.socket:example2.host server4
JkSet ajp13:example2.channel channel.socket:example2

<Location /examples/>
  JkSetUri group ajp13:example2
</Location>

....
</VirtualHost>

Assuming that the hosts are specified in the order example1, example2 then all
requests for /examples/ will go to ajp13:example2 .

In mod_jk.c 
static void *jk2_create_dir_config(apr_pool_t *p, char *path)

calls for the given path 

jk_bean_t *jkb=workerEnv->globalEnv->createBean2( 
     workerEnv->globalEnv,
     workerEnv->pool, "uri",
     (path==NULL)? "":path );

createBean2 then checks to see if there is an existing config for the given
path. If it is then the existing config is updated and returned. This means
that the first /examples will be created correctly and when the 2nd config is
encountered createBean2 will return the config for the first /examples. This
will then be updated with the config of the second and so both /examples will 
use the same config.

I suggest (have implemented and tested) the following alteration to fix this
problem.

The create_dir_config sends a unique path name (by adding a numerical suffix)
this will ensure that createBean2 will generate a unique bean for apache to hand
back to jk2_uriSet.

So that jkstatus will make any degree of sense I have updated jk2_uriSet so that
 it re-sets the path back to the original path without the numeric suffix.

Here is the code for apache1.3 as a diff -c against the standard 4.1.21

Please let me know what you think 

David Cassidy

***
../../jakarta-tomcat-connectors-4.1.21-src-ORIG/jk/native2/server/apache13/mod_jk2.c
2003-02-25 16:30:56.000000000 +0000
--- native2/server/apache13/mod_jk2.c	2003-03-27 11:31:59.000000000 +0000
***************
*** 1,3 ****
--- 1,4 ----
+ //  4.1.18 version
  /* ========================================================================= *
   *                                                                           *
   *                 The Apache Software License,  Version 1.1                 *
***************
*** 105,110 ****
--- 106,142 ----
   * 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;
+ 
+ /* This gives a slightly better 500 error message than the standard :)
+  */
+ static int jk_error( request_rec *r, char *extras)
+ {
+     // this is a little hack to get around the fact that the lb worker 
+     // might have already sent a page back ....
+     if ( r->bytes_sent > 0 ) 
+ 	    return  OK;
+ 
+     ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
+         "<p>The mod_jk2 connector was unable handle the request for ",
+         "<em><a href=\"", ap_escape_uri(r->pool, r->uri), "\">",
+         ap_escape_html(r->pool, r->uri), "</a></em></p>\n",
+         "<p>This may mean an application server could not be contacted to "
+         "service this request. ",extras," <P>Please try again later.", NULL));
+ 
+     fprintf(stderr, "Error .... send %d bytes ...\n", r->bytes_sent );
+     /* Allow "error-notes" string to be printed by ap_send_error_response() */
+     ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*"));
+ 
+     r->status_line = ap_psprintf(r->pool, "%3.3u mod_jk2 error",
+                                  HTTP_INTERNAL_SERVER_ERROR);
+     return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ 
  
  /* ==================== Options setters ==================== */
  
***************
*** 127,133 ****
          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);
--- 159,165 ----
          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);
***************
*** 136,141 ****
--- 168,311 ----
      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, *tmp2;
+ 	char *tmp_virtual=NULL, *tmp_full_url=NULL;
+     server_rec *s = cmd->server;
+ 
+     // 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->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 )) ;
+ 	sprintf(tmp_virtual,  "%s:%d", s->server_hostname, s->port);
+ 	sprintf(tmp_full_url, "%s:%d%s", s->server_hostname, s->port, 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;
+ 
+     path==NULL?a=10:(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
+ 
+     jk_bean_t *jkb=workerEnv->globalEnv->createBean2( workerEnv->globalEnv,
+                                                       workerEnv->pool, "uri",
+                                                       tmp);
+     jk_uriEnv_t *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;
+ 
+ }
+ 
+ 
+ 
  #ifdef HAS_APR
  apr_pool_t *jk_globalPool;
  #endif
***************
*** 224,257 ****
          */
          { "JkSet", jk2_set2, NULL, RSRC_CONF, TAKE2,
            "Set a jk property, same syntax and rules as in JkWorkersFile" },
!         NULL
      };
  
! /** Create default jk_config.
!     This is the first thing called by apache ( or should be )
   */
  static void *jk2_create_config(ap_pool *p, server_rec *s)
  {
      jk_uriEnv_t *newUri;
      jk_bean_t *jkb;
  
      if(  workerEnv==NULL ) {
          jk2_create_workerEnv(p, s );
      }
      if( s->is_virtual == 1 ) {
          /* Virtual host */
!         fprintf( stderr, "Create config for virtual host\n");
      } else {
          /* Default host */
-         fprintf( stderr, "Create config for main host\n");
-     }
- 
      jkb=workerEnv->globalEnv->createBean2( workerEnv->globalEnv,
                                             workerEnv->pool,
                                             "uri", "" );
      newUri = jkb->object;
      newUri->workerEnv=workerEnv;
!     return newUri;
  }
  
  
--- 394,440 ----
          */
          { "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 );
!     // DNC
      } else {
          /* Default host */
      jkb=workerEnv->globalEnv->createBean2( workerEnv->globalEnv,
                                             workerEnv->pool,
                                             "uri", "" );
+     }
+ 
      newUri = jkb->object;
      newUri->workerEnv=workerEnv;
! 
!     return (void *) newUri;
  }
  
  
***************
*** 266,273 ****
      jk_uriEnv_t *base = (jk_uriEnv_t *) basev;
      jk_uriEnv_t *overrides = (jk_uriEnv_t *)overridesv;
      
-     fprintf(stderr,  "Merging workerEnv \n" );
-     
      /* The 'mountcopy' option should be implemented in common.
       */
      return overrides;
--- 449,454 ----
***************
*** 278,285 ****
   */
  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;
  
--- 459,465 ----
   */
  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;
  
***************
*** 329,341 ****
      jk_uriEnv_t *uriEnv;
      jk_env_t *env;
  
-     uriEnv=ap_get_module_config( r->request_config, &jk2_module );
- 
      /* If this is a proxy request, we'll notify an error */
      if(r->proxyreq) {
!         return HTTP_INTERNAL_SERVER_ERROR;
      }
  
      /* not for me, try next handler */
      if(uriEnv==NULL || strcmp(r->handler,JK_HANDLER)!= 0 )
          return DECLINED;
--- 509,527 ----
      jk_uriEnv_t *uriEnv;
      jk_env_t *env;
  
      /* If this is a proxy request, we'll notify an error */
      if(r->proxyreq) {
! 	 return jk_error(r, "<!-- 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;
***************
*** 369,374 ****
--- 555,561 ----
                            "mod_jk.handler() finding worker for %s %#lx %#lx\n",
                            uriEnv->workerName, worker, uriEnv );
              uriEnv->worker=worker;
+ 
          }
      }
  
***************
*** 376,382 ****
          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 500;
      }
  
      {
--- 563,569 ----
          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 jk_error(r, "<!-- No worker defined for this uri -->");
      }
  
      {
***************
*** 425,433 ****
      }
  
      env->l->jkLog(env, env->l, JK_LOG_ERROR,
!              "mod_jk.handler() Error connecting to tomcat %d\n", rc);
      workerEnv->globalEnv->releaseEnv( workerEnv->globalEnv, env );
!     return 500;
  }
  
  /** Use the internal mod_jk mappings to find if this is a request for
--- 612,621 ----
      }
  
      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 jk_error(r, "<!-- Is your tomcat server down ? -->");
  }
  
  /** Use the internal mod_jk mappings to find if this is a request for
***************
*** 438,454 ****
      jk_uriEnv_t *uriEnv;
      jk_env_t *env;
              
      if(r->proxyreq) {
          return DECLINED;
      }
      
      /* Check JkMount directives, if any */
  /*     if( workerEnv->uriMap->size == 0 ) */
  /*         return DECLINED; */
      
      /* get_env() */
      env = workerEnv->globalEnv->getEnv( workerEnv->globalEnv );
-         
      uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap,
                                         ap_get_server_name(r),
                                         ap_get_server_port(r),
--- 626,656 ----
      jk_uriEnv_t *uriEnv;
      jk_env_t *env;
              
+     jk_uriMap_t *uriMap;
+     char *name=NULL;
+     int i,n,io;
      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;
+     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 );
      uriEnv = workerEnv->uriMap->mapUri(env, workerEnv->uriMap,
                                         ap_get_server_name(r),
                                         ap_get_server_port(r),
***************
*** 473,489 ****
  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 */
!     NULL,                       /* per-directory config creator */
!     NULL,                       /* dir config merger */
      jk2_create_config,           /* server config creator */
!     jk2_merge_config,            /* server config merger */
      jk2_cmds,                    /* command table */
      jk2_handlers,                /* [7] list of handlers */
      jk2_translate,               /* [2] filename-to-URI translation */
--- 675,692 ----
  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 */

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org