bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / common / jk_uri_worker_map.c
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17
18 /***************************************************************************
19  * Description: URI to worker map object.                                  *
20  *                                                                         *
21  * Author:      Gal Shachor <shachor@il.ibm.com>                           *
22  * Author:      Mladen Turk <mturk@apache.org>                             *
23  * Version:     $Revision: 890739 $                                          *
24  ***************************************************************************/
25
26 #include "jk_pool.h"
27 #include "jk_util.h"
28 #include "jk_map.h"
29 #include "jk_mt.h"
30 #include "jk_uri_worker_map.h"
31 #include "jk_worker.h"
32 #include "jk_lb_worker.h"
33
34 #ifdef WIN32
35 #define JK_STRCMP   strcasecmp
36 #define JK_STRNCMP  strnicmp
37 #else
38 #define JK_STRCMP   strcmp
39 #define JK_STRNCMP  strncmp
40 #endif
41
42 #define JK_UWMAP_EXTENSION_REPLY_TIMEOUT  "reply_timeout="
43 #define JK_UWMAP_EXTENSION_ACTIVE         "active="
44 #define JK_UWMAP_EXTENSION_DISABLED       "disabled="
45 #define JK_UWMAP_EXTENSION_STOPPED        "stopped="
46 #define JK_UWMAP_EXTENSION_FAIL_ON_STATUS "fail_on_status="
47 #define JK_UWMAP_EXTENSION_USE_SRV_ERRORS "use_server_errors="
48
49 #define IND_SWITCH(x)                      (((x)+1) % 2)
50 #define IND_THIS(x)                        ((x)[uw_map->index])
51 #define IND_NEXT(x)                        ((x)[IND_SWITCH(uw_map->index)])
52
53 #define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
54
55 static const char *uri_worker_map_source_type[] = {
56     "unknown",
57     SOURCE_TYPE_TEXT_WORKERDEF,
58     SOURCE_TYPE_TEXT_JKMOUNT,
59     SOURCE_TYPE_TEXT_URIMAP,
60     SOURCE_TYPE_TEXT_DISCOVER,
61     NULL
62 };
63
64
65 /* Return the string representation of the uwr source */
66 const char *uri_worker_map_get_source(uri_worker_record_t *uwr, jk_logger_t *l)
67 {
68     return uri_worker_map_source_type[uwr->source_type];
69 }
70
71 /* Return the string representation of the uwr match type */
72 char *uri_worker_map_get_match(uri_worker_record_t *uwr, char *buf, jk_logger_t *l)
73 {
74     unsigned int match;
75
76     buf[0] = '\0';
77     match = uwr->match_type;
78
79     if (match & MATCH_TYPE_DISABLED)
80         strcat(buf, "Disabled ");
81 /* deprecated
82     if (match & MATCH_TYPE_STOPPED)
83         strcat(buf, "Stopped ");
84  */
85     if (match & MATCH_TYPE_NO_MATCH)
86         strcat(buf, "Unmount ");
87     if (match & MATCH_TYPE_EXACT)
88         strcat(buf, "Exact");
89     else if (match & MATCH_TYPE_WILDCHAR_PATH)
90         strcat(buf, "Wildchar");
91 /* deprecated
92     else if (match & MATCH_TYPE_CONTEXT)
93         strcat(buf, "Context");
94     else if (match & MATCH_TYPE_CONTEXT_PATH)
95         strcat(buf, "Context Path");
96     else if (match & MATCH_TYPE_SUFFIX)
97         strcat(buf, "Suffix");
98     else if (match & MATCH_TYPE_GENERAL_SUFFIX)
99         return "General Suffix";
100  */
101     else
102         strcat(buf, "Unknown");
103     return buf;
104 }
105
106 /*
107  * Given context uri, count the number of path tokens.
108  *
109  * Servlet specification 2.4, SRV.11.1 says
110
111  *   The container will recursively try tomatch the longest
112  *   path-prefix. This is done by stepping down the path tree a
113  *   directory at a time, using the / character as a path
114  *   separator. The longest match determines the servlet selected.
115  *
116  * The implication seems to be `most uri path elements is most exact'.
117  * This is a little helper function to count uri tokens, so we can
118  * keep the worker map sorted with most specific first.
119  */
120 static int worker_count_context_uri_tokens(const char * context)
121 {
122     const char * c = context;
123     int count = 0;
124     while (c && *c) {
125         if ('/' == *c++)
126             count++;
127     }
128     return count;
129 }
130
131 static int worker_compare(const void *elem1, const void *elem2)
132 {
133     uri_worker_record_t *e1 = *(uri_worker_record_t **)elem1;
134     uri_worker_record_t *e2 = *(uri_worker_record_t **)elem2;
135     int e1_tokens = 0;
136     int e2_tokens = 0;
137
138     e1_tokens = worker_count_context_uri_tokens(e1->context);
139     e2_tokens = worker_count_context_uri_tokens(e2->context);
140
141     if (e1_tokens != e2_tokens) {
142         return (e2_tokens - e1_tokens);
143     }
144     /* given the same number of URI tokens, use character
145      * length as a tie breaker
146      */
147     if(e2->context_len != e1->context_len)
148         return ((int)e2->context_len - (int)e1->context_len);
149
150     return ((int)e2->source_type - (int)e1->source_type);
151 }
152
153 static void worker_qsort(jk_uri_worker_map_t *uw_map)
154 {
155
156    /* Sort remaining args using Quicksort algorithm: */
157    qsort((void *)IND_NEXT(uw_map->maps), IND_NEXT(uw_map->size),
158          sizeof(uri_worker_record_t *), worker_compare );
159
160 }
161
162 /* Dump the map contents - only call if debug log is active. */
163 static void uri_worker_map_dump(jk_uri_worker_map_t *uw_map,
164                                 const char *reason, jk_logger_t *l)
165 {
166     JK_TRACE_ENTER(l);
167     if (uw_map) {
168         int i, off, k;
169         uri_worker_record_t *uwr = NULL;
170         char buf[32];
171         jk_log(l, JK_LOG_DEBUG, "uri map dump %s: index=%d file='%s' reject_unsafe=%d "
172                "reload=%d modified=%d checked=%d",
173                reason, uw_map->index, STRNULL_FOR_NULL(uw_map->fname), uw_map->reject_unsafe,
174                uw_map->reload, uw_map->modified, uw_map->checked);
175         for(i=0;i<=1;i++) {
176             jk_log(l, JK_LOG_DEBUG, "generation %d: size=%d nosize=%d capacity=%d",
177                    i, uw_map->size[i], uw_map->nosize[i], uw_map->capacity[i], uw_map->maps[i]);
178         }
179
180         off = uw_map->index;
181         for(i=0;i<=1;i++) {
182             unsigned int j;
183             k = (i + off) % 2;
184             for (j = 0; j < uw_map->size[k]; j++) {
185                 uwr = uw_map->maps[k][j];
186                 jk_log(l, JK_LOG_DEBUG, "%s (%d) map #%d: uri=%s worker=%s context=%s "
187                        "source=%s type=%s len=%d",
188                        i ? "NEXT" : "THIS", i, j,
189                        STRNULL_FOR_NULL(uwr->uri), STRNULL_FOR_NULL(uwr->worker_name),
190                        STRNULL_FOR_NULL(uwr->context), STRNULL_FOR_NULL(uri_worker_map_get_source(uwr,l)),
191                        STRNULL_FOR_NULL(uri_worker_map_get_match(uwr,buf,l)), uwr->context_len);
192             }
193         }
194     }
195     JK_TRACE_EXIT(l);
196 }
197
198
199 int uri_worker_map_alloc(jk_uri_worker_map_t **uw_map_p,
200                          jk_map_t *init_data, jk_logger_t *l)
201 {
202     int i;
203
204     JK_TRACE_ENTER(l);
205
206     if (uw_map_p) {
207         int rc;
208         jk_uri_worker_map_t *uw_map;
209         *uw_map_p = (jk_uri_worker_map_t *)calloc(1, sizeof(jk_uri_worker_map_t));
210         uw_map = *uw_map_p;
211
212         JK_INIT_CS(&(uw_map->cs), rc);
213         if (rc == JK_FALSE) {
214             jk_log(l, JK_LOG_ERROR,
215                    "creating thread lock (errno=%d)",
216                    errno);
217             JK_TRACE_EXIT(l);
218             return JK_FALSE;
219         }
220
221         jk_open_pool(&(uw_map->p),
222                      uw_map->buf, sizeof(jk_pool_atom_t) * BIG_POOL_SIZE);
223         for(i=0;i<=1;i++) {
224             jk_open_pool(&(uw_map->p_dyn[i]),
225                          uw_map->buf_dyn[i], sizeof(jk_pool_atom_t) * BIG_POOL_SIZE);
226             uw_map->size[i] = 0;
227             uw_map->nosize[i] = 0;
228             uw_map->capacity[i] = 0;
229             uw_map->maps[i] = NULL;
230         }
231         uw_map->index = 0;
232         uw_map->fname = NULL;
233         uw_map->reject_unsafe = 0;
234         uw_map->reload = JK_URIMAP_DEF_RELOAD;
235         uw_map->modified = 0;
236         uw_map->checked = 0;
237
238         if (init_data)
239             rc = uri_worker_map_open(uw_map, init_data, l);
240         JK_TRACE_EXIT(l);
241         return rc;
242     }
243
244     JK_LOG_NULL_PARAMS(l);
245     JK_TRACE_EXIT(l);
246
247     return JK_FALSE;
248 }
249
250 static int uri_worker_map_close(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
251 {
252     JK_TRACE_ENTER(l);
253
254     if (uw_map) {
255         int i;
256         JK_DELETE_CS(&(uw_map->cs), i);
257         jk_close_pool(&uw_map->p_dyn[0]);
258         jk_close_pool(&uw_map->p_dyn[1]);
259         jk_close_pool(&uw_map->p);
260         JK_TRACE_EXIT(l);
261         return JK_TRUE;
262     }
263
264     JK_LOG_NULL_PARAMS(l);
265     JK_TRACE_EXIT(l);
266     return JK_FALSE;
267 }
268
269 int uri_worker_map_free(jk_uri_worker_map_t **uw_map, jk_logger_t *l)
270 {
271     JK_TRACE_ENTER(l);
272
273     if (uw_map && *uw_map) {
274         uri_worker_map_close(*uw_map, l);
275         free(*uw_map);
276         *uw_map = NULL;
277         JK_TRACE_EXIT(l);
278         return JK_TRUE;
279     }
280     else
281         JK_LOG_NULL_PARAMS(l);
282
283     JK_TRACE_EXIT(l);
284     return JK_FALSE;
285 }
286
287 /*
288  * Ensure there will be memory in context info to store Context Bases
289  */
290
291 #define UW_INC_SIZE 4           /* 4 URI->WORKER STEP */
292
293 static int uri_worker_map_realloc(jk_uri_worker_map_t *uw_map)
294 {
295     if (IND_NEXT(uw_map->size) == IND_NEXT(uw_map->capacity)) {
296         uri_worker_record_t **uwr;
297         int capacity = IND_NEXT(uw_map->capacity) + UW_INC_SIZE;
298
299         uwr =
300             (uri_worker_record_t **) jk_pool_alloc(&IND_NEXT(uw_map->p_dyn),
301                                                    sizeof(uri_worker_record_t
302                                                           *) * capacity);
303
304         if (!uwr)
305             return JK_FALSE;
306
307         if (IND_NEXT(uw_map->capacity) && IND_NEXT(uw_map->maps))
308             memcpy(uwr, IND_NEXT(uw_map->maps),
309                    sizeof(uri_worker_record_t *) * IND_NEXT(uw_map->capacity));
310
311         IND_NEXT(uw_map->maps) = uwr;
312         IND_NEXT(uw_map->capacity) = capacity;
313     }
314
315     return JK_TRUE;
316 }
317
318
319 /*
320  * Delete all entries of a given source type
321  */
322
323 static int uri_worker_map_clear(jk_uri_worker_map_t *uw_map,
324                                 jk_logger_t *l)
325 {
326     uri_worker_record_t *uwr = NULL;
327     unsigned int i;
328     unsigned int new_size = 0;
329     unsigned int new_nosize = 0;
330
331     JK_TRACE_ENTER(l);
332
333     IND_NEXT(uw_map->maps) =
334             (uri_worker_record_t **) jk_pool_alloc(&(IND_NEXT(uw_map->p_dyn)),
335                                                    sizeof(uri_worker_record_t
336                                                           *) * IND_THIS(uw_map->size));
337     IND_NEXT(uw_map->capacity) = IND_THIS(uw_map->size);
338     IND_NEXT(uw_map->size) = 0;
339     IND_NEXT(uw_map->nosize) = 0;
340     for (i = 0; i < IND_THIS(uw_map->size); i++) {
341         uwr = IND_THIS(uw_map->maps)[i];
342         if (uwr->source_type == SOURCE_TYPE_URIMAP) {
343             if (JK_IS_DEBUG_LEVEL(l))
344                 jk_log(l, JK_LOG_DEBUG,
345                        "deleting map rule '%s=%s' source '%s'",
346                        uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
347         }
348         else {
349             IND_NEXT(uw_map->maps)[new_size] = uwr;
350             new_size++;
351             if (uwr->match_type & MATCH_TYPE_NO_MATCH)
352                 new_nosize++;
353         }
354     }
355     IND_NEXT(uw_map->size) = new_size;
356     IND_NEXT(uw_map->nosize) = new_nosize;
357
358     JK_TRACE_EXIT(l);
359     return JK_TRUE;
360 }
361
362 static void extract_activation(jk_uri_worker_map_t *uw_map,
363                                uri_worker_record_t *uwr,
364                                lb_worker_t *lb,
365                                int *activations,
366                                char *workers,
367                                int activation,
368                                jk_logger_t *l)
369 {
370     unsigned int i;
371     jk_pool_t *p;
372     char *worker;
373 #ifdef _MT_CODE_PTHREAD
374     char *lasts;
375 #endif
376
377     JK_TRACE_ENTER(l);
378
379     if (uwr->source_type == SOURCE_TYPE_URIMAP)
380         p = &IND_NEXT(uw_map->p_dyn);
381     else
382         p = &uw_map->p;
383     worker = jk_pool_strdup(p, workers);
384
385 #ifdef _MT_CODE_PTHREAD
386     for (worker = strtok_r(worker, ", ", &lasts);
387          worker; worker = strtok_r(NULL, ", ", &lasts)) {
388 #else
389     for (worker = strtok(worker, ", "); worker; worker = strtok(NULL, ", ")) {
390 #endif
391         for (i=0; i<lb->num_of_workers; i++) {
392             if (!strcmp(worker, lb->lb_workers[i].name)) {
393                 if (activations[i] != JK_LB_ACTIVATION_UNSET)
394                     jk_log(l, JK_LOG_WARNING,
395                            "inconsistent activation overwrite for member %s "
396                            "of load balancer %s: '%s' replaced by '%s'",
397                            worker, lb->name,
398                            jk_lb_get_activation_direct(activations[i], l),
399                            jk_lb_get_activation_direct(activation, l));
400                 activations[i] = activation;
401                 break;
402             }
403         }
404         if (i >= lb->num_of_workers)
405             jk_log(l, JK_LOG_WARNING,
406                    "could not find member %s of load balancer %s",
407                    worker, lb->name);
408     }
409
410     JK_TRACE_EXIT(l);
411
412 }
413
414 static void extract_fail_on_status(jk_uri_worker_map_t *uw_map,
415                                    uri_worker_record_t *uwr,
416                                    jk_logger_t *l)
417 {
418     unsigned int i;
419     int j;
420     int cnt = 1;
421     jk_pool_t *p;
422     char *status;
423 #ifdef _MT_CODE_PTHREAD
424     char *lasts;
425 #endif
426
427     JK_TRACE_ENTER(l);
428
429     for (i=0; i<strlen(uwr->extensions.fail_on_status_str); i++) {
430         if (uwr->extensions.fail_on_status_str[i] == ',' ||
431             uwr->extensions.fail_on_status_str[i] == ' ')
432             cnt++;
433     }
434     uwr->extensions.fail_on_status_size = cnt;
435
436     if (uwr->source_type == SOURCE_TYPE_URIMAP)
437         p = &IND_NEXT(uw_map->p_dyn);
438     else
439         p = &uw_map->p;
440     status = jk_pool_strdup(p, uwr->extensions.fail_on_status_str);
441     uwr->extensions.fail_on_status = (int *)jk_pool_alloc(p,
442                                             uwr->extensions.fail_on_status_size * sizeof(int));
443     if (!uwr->extensions.fail_on_status) {
444         jk_log(l, JK_LOG_ERROR,
445                "can't alloc extensions fail_on_status list");
446         JK_TRACE_EXIT(l);
447         return;
448     } else if (JK_IS_DEBUG_LEVEL(l))
449         jk_log(l, JK_LOG_DEBUG,
450                "Allocated fail_on_status array of size %d for worker %s",
451                uwr->extensions.fail_on_status_size, uwr->worker_name);
452
453
454     for (j=0; j<uwr->extensions.fail_on_status_size; j++) {
455         uwr->extensions.fail_on_status[j] = 0;
456     }
457
458     cnt = 0;
459 #ifdef _MT_CODE_PTHREAD
460     for (status = strtok_r(status, ", ", &lasts);
461          status; status = strtok_r(NULL, ", ", &lasts)) {
462 #else
463     for (status = strtok(status, ", "); status; status = strtok(NULL, ", ")) {
464 #endif
465         uwr->extensions.fail_on_status[cnt] = atoi(status);
466         cnt++;
467     }
468
469     JK_TRACE_EXIT(l);
470
471 }
472
473 void uri_worker_map_switch(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
474 {
475     int new_index;
476
477     JK_TRACE_ENTER(l);
478
479     if (uw_map) {
480         new_index = IND_SWITCH(uw_map->index);
481         if (JK_IS_DEBUG_LEVEL(l))
482             jk_log(l, JK_LOG_DEBUG,
483                    "Switching uri worker map from index %d to index %d",
484                    uw_map->index, new_index);
485         uw_map->index = new_index;
486         jk_reset_pool(&(IND_NEXT(uw_map->p_dyn)));
487     }
488
489     JK_TRACE_EXIT(l);
490
491 }
492
493 void uri_worker_map_ext(jk_uri_worker_map_t *uw_map, jk_logger_t *l)
494 {
495     unsigned int i;
496
497     JK_TRACE_ENTER(l);
498
499     for (i = 0; i < IND_NEXT(uw_map->size); i++) {
500         uri_worker_record_t *uwr = IND_NEXT(uw_map->maps)[i];
501         jk_worker_t *jw;
502         if(uwr->match_type & MATCH_TYPE_NO_MATCH)
503             continue;
504         jw = wc_get_worker_for_name(uwr->worker_name, l);
505         if(!jw) {
506             jk_log(l, JK_LOG_ERROR,
507                    "Could not find worker with name '%s' in uri map post processing.",
508                    uwr->worker_name);
509             continue;
510         }
511         if (JK_IS_DEBUG_LEVEL(l))
512             jk_log(l, JK_LOG_DEBUG,
513                    "Checking extension for worker %d: %s of type %s (%d)",
514                    i, uwr->worker_name, wc_get_name_for_type(jw->type,l), jw->type);
515
516         if (jw->type == JK_LB_WORKER_TYPE &&
517             (uwr->extensions.active || uwr->extensions.disabled || uwr->extensions.stopped)) {
518             int j;
519             lb_worker_t *lb = (lb_worker_t *)jw->worker_private;
520             jk_pool_t *p;
521             if (!uwr->extensions.activation) {
522                 uwr->extensions.activation_size = lb->num_of_workers;
523                 if (uwr->source_type == SOURCE_TYPE_URIMAP)
524                     p = &IND_NEXT(uw_map->p_dyn);
525                 else
526                     p = &uw_map->p;
527                 uwr->extensions.activation = (int *)jk_pool_alloc(p,
528                                                         uwr->extensions.activation_size * sizeof(int));
529                 if (!uwr->extensions.activation) {
530                     jk_log(l, JK_LOG_ERROR,
531                            "can't alloc extensions activation list");
532                     continue;
533                 } else if (JK_IS_DEBUG_LEVEL(l))
534                     jk_log(l, JK_LOG_DEBUG,
535                            "Allocated activations array of size %d for lb worker %s",
536                            uwr->extensions.activation_size, uwr->worker_name);
537                 for (j=0; j<uwr->extensions.activation_size; j++) {
538                     uwr->extensions.activation[j] = JK_LB_ACTIVATION_UNSET;
539                 }
540             }
541             if (uwr->extensions.active)
542                 extract_activation(uw_map, uwr, lb, uwr->extensions.activation,
543                                    uwr->extensions.active, JK_LB_ACTIVATION_ACTIVE, l);
544             if (uwr->extensions.disabled)
545                 extract_activation(uw_map, uwr, lb, uwr->extensions.activation,
546                                    uwr->extensions.disabled, JK_LB_ACTIVATION_DISABLED, l);
547             if (uwr->extensions.stopped)
548                 extract_activation(uw_map, uwr, lb, uwr->extensions.activation,
549                                    uwr->extensions.stopped, JK_LB_ACTIVATION_STOPPED, l);
550         }
551         else if (uwr->extensions.active) {
552             jk_log(l, JK_LOG_WARNING,
553                    "Worker %s is not of type lb, activation extension "
554                    JK_UWMAP_EXTENSION_ACTIVE " for %s ignored",
555                    uwr->worker_name, uwr->extensions.active);
556         }
557         else if (uwr->extensions.disabled) {
558             jk_log(l, JK_LOG_WARNING,
559                    "Worker %s is not of type lb, activation extension "
560                    JK_UWMAP_EXTENSION_DISABLED " for %s ignored",
561                    uwr->worker_name, uwr->extensions.disabled);
562         }
563         else if (uwr->extensions.stopped) {
564             jk_log(l, JK_LOG_WARNING,
565                    "Worker %s is not of type lb, activation extension "
566                    JK_UWMAP_EXTENSION_STOPPED " for %s ignored",
567                    uwr->worker_name, uwr->extensions.stopped);
568         }
569         if (uwr->extensions.fail_on_status_str) {
570             extract_fail_on_status(uw_map, uwr, l);
571         }
572     }
573     if (JK_IS_DEBUG_LEVEL(l))
574         uri_worker_map_dump(uw_map, "after extension stripping", l);
575
576     JK_TRACE_EXIT(l);
577     return;
578
579 }
580
581 /* Add new entry to NEXT generation */
582 int uri_worker_map_add(jk_uri_worker_map_t *uw_map,
583                        const char *puri, const char *worker,
584                        unsigned int source_type, jk_logger_t *l)
585 {
586     uri_worker_record_t *uwr = NULL;
587     char *uri;
588     jk_pool_t *p;
589     unsigned int match_type = 0;
590
591     JK_TRACE_ENTER(l);
592
593     if (*puri == '-') {
594         /* Disable urimap.
595          * This way you can disable already mounted
596          * context.
597          */
598         match_type = MATCH_TYPE_DISABLED;
599         puri++;
600     }
601     if (*puri == '!') {
602         match_type |= MATCH_TYPE_NO_MATCH;
603         puri++;
604     }
605
606     if (uri_worker_map_realloc(uw_map) == JK_FALSE) {
607         JK_TRACE_EXIT(l);
608         return JK_FALSE;
609     }
610     if (source_type == SOURCE_TYPE_URIMAP)
611         p = &IND_NEXT(uw_map->p_dyn);
612     else
613         p = &uw_map->p;
614
615     uwr = (uri_worker_record_t *)jk_pool_alloc(p, sizeof(uri_worker_record_t));
616     if (!uwr) {
617         jk_log(l, JK_LOG_ERROR,
618                "can't alloc map entry");
619         JK_TRACE_EXIT(l);
620         return JK_FALSE;
621     }
622
623     uri = jk_pool_strdup(p, puri);
624     if (!uri || !worker) {
625         jk_log(l, JK_LOG_ERROR,
626                "can't alloc uri/worker strings");
627         JK_TRACE_EXIT(l);
628         return JK_FALSE;
629     }
630
631     if (*uri == '/') {
632         char *w;
633         char *param;
634 #ifdef _MT_CODE_PTHREAD
635         char *lasts = NULL;
636 #endif
637
638         w = jk_pool_strdup(p, worker);
639         uwr->extensions.reply_timeout = -1;
640         uwr->extensions.active = NULL;
641         uwr->extensions.disabled = NULL;
642         uwr->extensions.stopped = NULL;
643         uwr->extensions.activation_size = 0;
644         uwr->extensions.activation = NULL;
645         uwr->extensions.fail_on_status_size = 0;
646         uwr->extensions.fail_on_status = NULL;
647         uwr->extensions.fail_on_status_str = NULL;
648         uwr->extensions.use_server_error_pages = 0;
649
650 #ifdef _MT_CODE_PTHREAD
651         param = strtok_r(w, ";", &lasts);
652 #else
653         param = strtok(w, ";");
654 #endif
655         if (param) {
656 #ifdef _MT_CODE_PTHREAD
657             for (param = strtok_r(NULL, ";", &lasts); param; param = strtok_r(NULL, ";", &lasts)) {
658 #else
659             for (param = strtok(NULL, ";"); param; param = strtok(NULL, ";")) {
660 #endif
661                 if (!strncmp(param, JK_UWMAP_EXTENSION_REPLY_TIMEOUT, strlen(JK_UWMAP_EXTENSION_REPLY_TIMEOUT))) {
662                     uwr->extensions.reply_timeout = atoi(param + strlen(JK_UWMAP_EXTENSION_REPLY_TIMEOUT));
663                 }
664                 else if (!strncmp(param, JK_UWMAP_EXTENSION_USE_SRV_ERRORS, strlen(JK_UWMAP_EXTENSION_USE_SRV_ERRORS))) {
665                     uwr->extensions.use_server_error_pages = atoi(param + strlen(JK_UWMAP_EXTENSION_USE_SRV_ERRORS));
666                 }
667                 else if (!strncmp(param, JK_UWMAP_EXTENSION_ACTIVE, strlen(JK_UWMAP_EXTENSION_ACTIVE))) {
668                     if (uwr->extensions.active)
669                         jk_log(l, JK_LOG_WARNING,
670                                "extension '%s' in uri worker map only allowed once",
671                                JK_UWMAP_EXTENSION_ACTIVE);
672                     else
673                         uwr->extensions.active = param + strlen(JK_UWMAP_EXTENSION_ACTIVE);
674                 }
675                 else if (!strncmp(param, JK_UWMAP_EXTENSION_DISABLED, strlen(JK_UWMAP_EXTENSION_DISABLED))) {
676                     if (uwr->extensions.disabled)
677                         jk_log(l, JK_LOG_WARNING,
678                                "extension '%s' in uri worker map only allowed once",
679                                JK_UWMAP_EXTENSION_DISABLED);
680                     else
681                         uwr->extensions.disabled = param + strlen(JK_UWMAP_EXTENSION_DISABLED);
682                 }
683                 else if (!strncmp(param, JK_UWMAP_EXTENSION_STOPPED, strlen(JK_UWMAP_EXTENSION_STOPPED))) {
684                     if (uwr->extensions.stopped)
685                         jk_log(l, JK_LOG_WARNING,
686                                "extension '%s' in uri worker map only allowed once",
687                                JK_UWMAP_EXTENSION_STOPPED);
688                     else
689                         uwr->extensions.stopped = param + strlen(JK_UWMAP_EXTENSION_STOPPED);
690                 }
691                 else if (!strncmp(param, JK_UWMAP_EXTENSION_FAIL_ON_STATUS, strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS))) {
692                     if (uwr->extensions.fail_on_status_str)
693                         jk_log(l, JK_LOG_WARNING,
694                                "extension '%s' in uri worker map only allowed once",
695                                JK_UWMAP_EXTENSION_FAIL_ON_STATUS);
696                     else
697                         uwr->extensions.fail_on_status_str = param + strlen(JK_UWMAP_EXTENSION_FAIL_ON_STATUS);
698                 }
699                 else {
700                     jk_log(l, JK_LOG_WARNING,
701                            "unknown extension '%s' in uri worker map",
702                            param);
703                 }
704             }
705         }
706
707         uwr->source_type = source_type;
708         uwr->worker_name = w;
709         uwr->uri = uri;
710         uwr->context = uri;
711         uwr->context_len = strlen(uwr->context);
712         if (strchr(uri, '*') ||
713             strchr(uri, '?')) {
714             /* Something like
715              * /context/ * /user/ *
716              * /context/ *.suffix
717              */
718             match_type |= MATCH_TYPE_WILDCHAR_PATH;
719             if (JK_IS_DEBUG_LEVEL(l))
720                 jk_log(l, JK_LOG_DEBUG,
721                        "wildchar rule '%s=%s' source '%s' was added",
722                        uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
723
724         }
725         else {
726             /* Something like:  JkMount /login/j_security_check ajp13 */
727             match_type |= MATCH_TYPE_EXACT;
728             if (JK_IS_DEBUG_LEVEL(l))
729                 jk_log(l, JK_LOG_DEBUG,
730                        "exact rule '%s=%s' source '%s' was added",
731                        uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
732         }
733     }
734     else {
735         /*
736          * JFC: please check...
737          * Not sure what to do, but I try to prevent problems.
738          * I have fixed jk_mount_context() in apaches/mod_jk.c so we should
739          * not arrive here when using Apache.
740          */
741         jk_log(l, JK_LOG_ERROR,
742                "invalid context '%s': does not begin with '/'",
743                uri);
744         JK_TRACE_EXIT(l);
745         return JK_FALSE;
746     }
747     uwr->match_type = match_type;
748     IND_NEXT(uw_map->maps)[IND_NEXT(uw_map->size)] = uwr;
749     IND_NEXT(uw_map->size)++;
750     if (match_type & MATCH_TYPE_NO_MATCH) {
751         /* If we split the mappings this one will be calculated */
752         IND_NEXT(uw_map->nosize)++;
753     }
754     worker_qsort(uw_map);
755     JK_TRACE_EXIT(l);
756     return JK_TRUE;
757 }
758
759 int uri_worker_map_open(jk_uri_worker_map_t *uw_map,
760                         jk_map_t *init_data, jk_logger_t *l)
761 {
762     int rc = JK_TRUE;
763
764     JK_TRACE_ENTER(l);
765
766     if (uw_map) {
767         int sz = jk_map_size(init_data);
768
769         if (JK_IS_DEBUG_LEVEL(l))
770             jk_log(l, JK_LOG_DEBUG,
771                    "rule map size is %d",
772                    sz);
773
774         if (sz > 0) {
775             int i;
776             for (i = 0; i < sz; i++) {
777                 const char *u = jk_map_name_at(init_data, i);
778                 const char *w = jk_map_value_at(init_data, i);
779                 /* Multiple mappings like :
780                  * /servlets-examples|/ *
781                  * will create two mappings:
782                  * /servlets-examples
783                  * and:
784                  * /servlets-examples/ *
785                  */
786                 if (strchr(u, '|')) {
787                     char *s, *r = strdup(u);
788                     s = strchr(r, '|');
789                     *(s++) = '\0';
790                     /* Add first mapping */
791                     if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
792                         jk_log(l, JK_LOG_ERROR,
793                         "invalid mapping rule %s->%s", r, w);
794                         rc = JK_FALSE;
795                     }
796                     for (; *s; s++)
797                         *(s - 1) = *s;
798                     *(s - 1) = '\0';
799                     /* add second mapping */
800                     if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_JKMOUNT, l)) {
801                         jk_log(l, JK_LOG_ERROR,
802                                "invalid mapping rule %s->%s", r, w);
803                         rc = JK_FALSE;
804                     }
805                     free(r);
806                 }
807                 else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_JKMOUNT, l)) {
808                     jk_log(l, JK_LOG_ERROR,
809                            "invalid mapping rule %s->%s",
810                            u, w);
811                     rc = JK_FALSE;
812                     break;
813                 }
814                 if (rc == JK_FALSE)
815                     break;
816             }
817         }
818
819         if (rc == JK_FALSE) {
820             jk_log(l, JK_LOG_ERROR,
821                    "there was an error, freeing buf");
822             jk_close_pool(&uw_map->p_dyn[0]);
823             jk_close_pool(&uw_map->p_dyn[1]);
824             jk_close_pool(&uw_map->p);
825         }
826         else if (JK_IS_DEBUG_LEVEL(l))
827             uri_worker_map_dump(uw_map, "after map open", l);
828     }
829
830     JK_TRACE_EXIT(l);
831     return rc;
832 }
833
834 static int find_match(jk_uri_worker_map_t *uw_map,
835                       const char *url, jk_logger_t *l)
836 {
837     unsigned int i;
838
839     JK_TRACE_ENTER(l);
840
841     for (i = 0; i < IND_THIS(uw_map->size); i++) {
842         uri_worker_record_t *uwr = IND_THIS(uw_map->maps)[i];
843
844         /* Check for match types */
845         if ((uwr->match_type & MATCH_TYPE_DISABLED) ||
846             (uwr->match_type & MATCH_TYPE_NO_MATCH))
847             continue;
848
849         if (JK_IS_DEBUG_LEVEL(l))
850             jk_log(l, JK_LOG_DEBUG, "Attempting to map context URI '%s=%s' source '%s'",
851                    uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
852
853         if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
854             /* Map is already sorted by context_len */
855             if (jk_wildchar_match(url, uwr->context,
856 #ifdef WIN32
857                                0
858 #else
859                                0
860 #endif
861                                ) == 0) {
862                     if (JK_IS_DEBUG_LEVEL(l))
863                         jk_log(l, JK_LOG_DEBUG,
864                                "Found a wildchar match '%s=%s'",
865                                uwr->context, uwr->worker_name);
866                     JK_TRACE_EXIT(l);
867                     return i;
868              }
869         }
870         else if (JK_STRNCMP(uwr->context, url, uwr->context_len) == 0) {
871             if (strlen(url) == uwr->context_len) {
872                 if (JK_IS_DEBUG_LEVEL(l))
873                     jk_log(l, JK_LOG_DEBUG,
874                            "Found an exact match '%s=%s'",
875                            uwr->context, uwr->worker_name);
876                 JK_TRACE_EXIT(l);
877                 return i;
878             }
879         }
880     }
881
882     JK_TRACE_EXIT(l);
883     return -1;
884 }
885
886 static int is_nomatch(jk_uri_worker_map_t *uw_map,
887                       const char *uri, int match,
888                       jk_logger_t *l)
889 {
890     unsigned int i;
891     const char *worker = IND_THIS(uw_map->maps)[match]->worker_name;
892
893     JK_TRACE_ENTER(l);
894
895     for (i = 0; i < IND_THIS(uw_map->size); i++) {
896         uri_worker_record_t *uwr = IND_THIS(uw_map->maps)[i];
897
898         /* Check only nomatch mappings */
899         if (!(uwr->match_type & MATCH_TYPE_NO_MATCH) ||
900             (uwr->match_type & MATCH_TYPE_DISABLED))
901             continue;
902         /* Check only matching workers */
903         if (strcmp(uwr->worker_name, worker) &&
904             strcmp(uwr->worker_name, "*"))
905             continue;
906         if (uwr->match_type & MATCH_TYPE_WILDCHAR_PATH) {
907             /* Map is already sorted by context_len */
908             if (jk_wildchar_match(uri, uwr->context,
909 #ifdef WIN32
910                                0
911 #else
912                                0
913 #endif
914                                ) == 0) {
915                     if (JK_IS_DEBUG_LEVEL(l))
916                         jk_log(l, JK_LOG_DEBUG,
917                                "Found a wildchar no match '%s=%s' source '%s'",
918                                uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
919                     JK_TRACE_EXIT(l);
920                     return JK_TRUE;
921              }
922         }
923         else if (JK_STRNCMP(uwr->context, uri, uwr->context_len) == 0) {
924             if (strlen(uri) == uwr->context_len) {
925                 if (JK_IS_DEBUG_LEVEL(l))
926                     jk_log(l, JK_LOG_DEBUG,
927                            "Found an exact no match '%s=%s' source '%s'",
928                            uwr->context, uwr->worker_name, uri_worker_map_get_source(uwr, l));
929                 JK_TRACE_EXIT(l);
930                 return JK_TRUE;
931             }
932         }
933     }
934
935     JK_TRACE_EXIT(l);
936     return JK_FALSE;
937 }
938
939 const char *map_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
940                                   const char *uri, const char *vhost,
941                                   rule_extension_t **extensions,
942                                   int *index,
943                                   jk_logger_t *l)
944 {
945     unsigned int i;
946     unsigned int vhost_len;
947     int reject_unsafe;
948     int rv = -1;
949     char  url[JK_MAX_URI_LEN+1];
950
951     JK_TRACE_ENTER(l);
952
953     if (!uw_map || !uri || !extensions) {
954         JK_LOG_NULL_PARAMS(l);
955         JK_TRACE_EXIT(l);
956         return NULL;
957     }
958     *extensions = NULL;
959     if (index)
960         *index = -1;
961     if (*uri != '/') {
962         jk_log(l, JK_LOG_WARNING,
963                 "Uri %s is invalid. Uri must start with /", uri);
964         JK_TRACE_EXIT(l);
965         return NULL;
966     }
967     if (uw_map->fname) {
968         uri_worker_map_update(uw_map, 0, l);
969         if (!IND_THIS(uw_map->size)) {
970             jk_log(l, JK_LOG_INFO,
971                    "No worker maps defined for %s.",
972                    uw_map->fname);
973             JK_TRACE_EXIT(l);
974             return NULL;
975         }
976     }
977     /* Make the copy of the provided uri and strip
978      * everything after the first ';' char.
979      */
980     reject_unsafe = uw_map->reject_unsafe;
981     vhost_len = 0;
982     /*
983      * In case we got a vhost, we prepend a slash
984      * and the vhost to the url in order to enable
985      * vhost mapping rules especially for IIS.
986      */
987     if (vhost) {
988         int off = 0;
989         /* Add leading '/' if necessary */
990         if (vhost[0] != '/') {
991             url[0] = '/';
992             off = 1;
993         }
994         /* Size including leading slash. */
995         vhost_len = strlen(vhost);
996         if (vhost_len + off >= JK_MAX_URI_LEN) {
997             vhost_len = 0;
998             jk_log(l, JK_LOG_WARNING,
999                    "Host prefix %s for URI %s is invalid and will be ignored."
1000                    " It must be smaller than %d chars",
1001                    vhost, JK_MAX_URI_LEN - off);
1002         }
1003         else {
1004             strncpy(&url[off], vhost, vhost_len + 1);
1005         }
1006         vhost_len += off;
1007     }
1008     for (i = 0; i < strlen(uri); i++) {
1009         if (i == JK_MAX_URI_LEN) {
1010             jk_log(l, JK_LOG_WARNING,
1011                    "URI %s is invalid. URI must be smaller than %d chars",
1012                    uri, JK_MAX_URI_LEN);
1013             JK_TRACE_EXIT(l);
1014             return NULL;
1015         }
1016         if (uri[i] == ';')
1017             break;
1018         else {
1019             url[i + vhost_len] = uri[i];
1020             if (reject_unsafe && (uri[i] == '%' || uri[i] == '\\')) {
1021                 jk_log(l, JK_LOG_INFO, "Potentially unsafe request url '%s' rejected", uri);
1022                 JK_TRACE_EXIT(l);
1023                 return NULL;
1024             }
1025         }
1026     }
1027     url[i + vhost_len] = '\0';
1028
1029     if (JK_IS_DEBUG_LEVEL(l)) {
1030         char *url_rewrite = strstr(uri, JK_PATH_SESSION_IDENTIFIER);
1031         if (url_rewrite)
1032             jk_log(l, JK_LOG_DEBUG, "Found session identifier '%s' in url '%s'",
1033                    url_rewrite, uri);
1034     }
1035     if (JK_IS_DEBUG_LEVEL(l))
1036         jk_log(l, JK_LOG_DEBUG, "Attempting to map URI '%s' from %d maps",
1037                url, IND_THIS(uw_map->size));
1038     rv = find_match(uw_map, url, l);
1039     /* If this doesn't find a match, try without the vhost. */
1040     if (rv < 0 && vhost_len) {
1041         rv = find_match(uw_map, &url[vhost_len], l);
1042     }
1043
1044     /* In case we found a match, check for the unmounts. */
1045     if (rv >= 0 && IND_THIS(uw_map->nosize)) {
1046         /* Again first including vhost. */
1047         int rc = is_nomatch(uw_map, url, rv, l);
1048         /* If no unmount was find, try without vhost. */
1049         if (!rc && vhost_len)
1050             rc = is_nomatch(uw_map, &url[vhost_len], rv, l);
1051         if (rc) {
1052             if (JK_IS_DEBUG_LEVEL(l)) {
1053                 jk_log(l, JK_LOG_DEBUG,
1054                        "Denying match for worker %s by nomatch rule",
1055                        IND_THIS(uw_map->maps)[rv]->worker_name);
1056             }
1057             rv = -1;
1058         }
1059     }
1060
1061     if (rv >= 0) {
1062         *extensions = &(IND_THIS(uw_map->maps)[rv]->extensions);
1063         if (index)
1064             *index = rv;
1065         JK_TRACE_EXIT(l);
1066         return IND_THIS(uw_map->maps)[rv]->worker_name;
1067     }
1068     JK_TRACE_EXIT(l);
1069     return NULL;
1070 }
1071
1072 rule_extension_t *get_uri_to_worker_ext(jk_uri_worker_map_t *uw_map,
1073                                         int index)
1074 {
1075     if (index >= 0) {
1076         return &(IND_THIS(uw_map->maps)[index]->extensions);
1077     }
1078     else {
1079         return NULL;
1080     }
1081 }
1082
1083 const char *map_uri_to_worker(jk_uri_worker_map_t *uw_map,
1084                               const char *uri, const char *vhost,
1085                               jk_logger_t *l)
1086 {
1087     rule_extension_t *ext;
1088     return map_uri_to_worker_ext(uw_map, uri, vhost, &ext, NULL, l);
1089 }
1090
1091 int uri_worker_map_load(jk_uri_worker_map_t *uw_map,
1092                         jk_logger_t *l)
1093 {
1094     int rc = JK_FALSE;
1095     jk_map_t *map;
1096
1097     jk_map_alloc(&map);
1098     if (jk_map_read_properties(map, NULL, uw_map->fname, &uw_map->modified,
1099                                JK_MAP_HANDLE_NORMAL, l)) {
1100         int i;
1101         if (JK_IS_DEBUG_LEVEL(l))
1102             jk_log(l, JK_LOG_DEBUG,
1103                    "Loading urimaps from %s with reload check interval %d seconds",
1104                    uw_map->fname, uw_map->reload);
1105         uri_worker_map_clear(uw_map, l);
1106         for (i = 0; i < jk_map_size(map); i++) {
1107             const char *u = jk_map_name_at(map, i);
1108             const char *w = jk_map_value_at(map, i);
1109             /* Multiple mappings like :
1110              * /servlets-examples|/ *
1111              * will create two mappings:
1112              * /servlets-examples
1113              * and:
1114              * /servlets-examples/ *
1115              */
1116             if (strchr(u, '|')) {
1117                 char *s, *r = strdup(u);
1118                 s = strchr(r, '|');
1119                 *(s++) = '\0';
1120                 /* Add first mapping */
1121                 if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
1122                     jk_log(l, JK_LOG_ERROR,
1123                     "invalid mapping rule %s->%s", r, w);
1124                 }
1125                 for (; *s; s++)
1126                    *(s - 1) = *s;
1127                 *(s - 1) = '\0';
1128                 /* add second mapping */
1129                 if (!uri_worker_map_add(uw_map, r, w, SOURCE_TYPE_URIMAP, l)) {
1130                     jk_log(l, JK_LOG_ERROR,
1131                     "invalid mapping rule %s->%s", r, w);
1132                 }
1133                 free(r);
1134             }
1135             else if (!uri_worker_map_add(uw_map, u, w, SOURCE_TYPE_URIMAP, l)) {
1136                 jk_log(l, JK_LOG_ERROR,
1137                        "invalid mapping rule %s->%s",
1138                        u, w);
1139             }
1140         }
1141         uw_map->checked = time(NULL);
1142         if (JK_IS_DEBUG_LEVEL(l))
1143             uri_worker_map_dump(uw_map, "after file load", l);
1144         rc = JK_TRUE;
1145     }
1146     jk_map_free(&map);
1147     return rc;
1148 }
1149
1150 int uri_worker_map_update(jk_uri_worker_map_t *uw_map,
1151                           int force, jk_logger_t *l)
1152 {
1153     int rc = JK_TRUE;
1154     time_t now = time(NULL);
1155
1156     if (force || (uw_map->reload > 0 && difftime(now, uw_map->checked) >
1157                   uw_map->reload)) {
1158         struct stat statbuf;
1159         uw_map->checked = now;
1160         if ((rc = jk_stat(uw_map->fname, &statbuf)) == -1) {
1161             jk_log(l, JK_LOG_ERROR,
1162                    "Unable to stat the %s (errno=%d)",
1163                    uw_map->fname, errno);
1164             return JK_FALSE;
1165         }
1166         if (statbuf.st_mtime == uw_map->modified) {
1167             if (JK_IS_DEBUG_LEVEL(l))
1168                 jk_log(l, JK_LOG_DEBUG,
1169                        "File %s is not modified",
1170                        uw_map->fname);
1171             return JK_TRUE;
1172         }
1173         JK_ENTER_CS(&(uw_map->cs), rc);
1174         /* Check if some other thread updated status */
1175         if (statbuf.st_mtime == uw_map->modified) {
1176             JK_LEAVE_CS(&(uw_map->cs), rc);
1177             if (JK_IS_DEBUG_LEVEL(l))
1178                 jk_log(l, JK_LOG_DEBUG,
1179                        "File %s  is not modified",
1180                        uw_map->fname);
1181             return JK_TRUE;
1182         }
1183         rc = uri_worker_map_load(uw_map, l);
1184         uri_worker_map_ext(uw_map, l);
1185         uri_worker_map_switch(uw_map, l);
1186         JK_LEAVE_CS(&(uw_map->cs), rc);
1187         jk_log(l, JK_LOG_INFO,
1188                "Reloaded urimaps from %s", uw_map->fname);
1189     }
1190     return JK_TRUE;
1191 }