bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / tomcat-connectors-1.2.32-src / native / netscape / jk_nsapi_plugin.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: NSAPI plugin for Netscape servers                          *
20  * Author:      Gal Shachor <shachor@il.ibm.com>                           *
21  * Version:     $Revision: 1078805 $                                           *
22  ***************************************************************************/
23
24 #if defined(WIN32)
25 #ifndef _WIN32_WINNT
26 #define _WIN32_WINNT 0x0500
27 #endif
28 #include <winsock2.h>
29 #endif
30
31 #include "nsapi.h"
32 #include "jk_global.h"
33 #include "jk_url.h"
34 #include "jk_util.h"
35 #include "jk_map.h"
36 #include "jk_pool.h"
37 #include "jk_service.h"
38 #include "jk_worker.h"
39 #include "jk_shm.h"
40 #include "jk_ajp13.h"
41
42 #define URI_PATTERN "path"
43 #define DEFAULT_WORKER_NAME ("ajp13")
44 #define REJECT_UNSAFE_TAG   "reject_unsafe"
45
46 #define STRNULL_FOR_NULL(x) ((x) ? (x) : "(null)")
47
48 struct nsapi_private_data
49 {
50     jk_pool_t p;
51
52     pblock *pb;
53     Session *sn;
54     Request *rq;
55 };
56 typedef struct nsapi_private_data nsapi_private_data_t;
57
58 static int init_on_other_thread_is_done = JK_FALSE;
59 static int init_on_other_thread_is_ok = JK_FALSE;
60
61 static const char ssl_cert_start[] = "-----BEGIN CERTIFICATE-----\r\n";
62 static const char ssl_cert_end[] = "\r\n-----END CERTIFICATE-----\r\n";
63
64 static jk_logger_t *logger = NULL;
65 static jk_worker_env_t worker_env;
66 static jk_map_t *init_map = NULL;
67 static jk_uri_worker_map_t *uw_map = NULL;
68 static size_t jk_shm_size = 0;
69
70 #ifdef NETWARE
71 int (*PR_IsSocketSecure) (SYS_NETFD * csd);     /* pointer to PR_IsSocketSecure function */
72 #endif
73
74 static int JK_METHOD start_response(jk_ws_service_t *s,
75                                     int status,
76                                     const char *reason,
77                                     const char *const *header_names,
78                                     const char *const *header_values,
79                                     unsigned num_of_headers);
80
81 static int JK_METHOD ws_read(jk_ws_service_t *s,
82                              void *b, unsigned l, unsigned *a);
83
84 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l);
85
86 NSAPI_PUBLIC int jk_init(pblock * pb, Session * sn, Request * rq);
87
88 NSAPI_PUBLIC void jk_term(void *p);
89
90 NSAPI_PUBLIC int jk_service(pblock * pb, Session * sn, Request * rq);
91
92 static int init_ws_service(nsapi_private_data_t * private_data,
93                            jk_ws_service_t *s);
94
95 static int setup_http_headers(nsapi_private_data_t * private_data,
96                               jk_ws_service_t *s);
97
98 static void init_workers_on_other_threads(void *init_d)
99 {
100     init_map = (jk_map_t *)init_d;
101     /* we add the URI->WORKER MAP since workers using AJP14 will feed it */
102     /* but where are they here in Netscape ? */
103     if (uri_worker_map_alloc(&uw_map, NULL, logger)) {
104         uw_map->fname = "";
105         uw_map->reload = JK_URIMAP_DEF_RELOAD;
106         uw_map->reject_unsafe = jk_map_get_bool(init_map, "worker." REJECT_UNSAFE_TAG, JK_FALSE);
107         worker_env.uri_to_worker = uw_map;
108         worker_env.pool = NULL;
109
110         if (wc_open(init_map, &worker_env, logger)) {
111             init_on_other_thread_is_ok = JK_TRUE;
112             uri_worker_map_ext(uw_map, logger);
113             uri_worker_map_switch(uw_map, logger);
114         }
115         else {
116             jk_log(logger, JK_LOG_EMERG,
117                    "In init_workers_on_other_threads, failed");
118         }
119     }
120     else {
121         jk_log(logger, JK_LOG_EMERG,
122                "In init_workers_on_other_threads, failed");
123     }
124
125     init_on_other_thread_is_done = JK_TRUE;
126 }
127
128 /*
129  * Convert string to lower case.
130  * If string is longer than the provided buffer,
131  * just return the original string.
132  */
133 static const char *to_lower(const char *str, char *buf, int bufsz)
134 {
135     const char *from = str;
136     char *to = buf;
137     char *end = buf + (bufsz - 1);
138     while (to != end && *from) {
139         *to = (char)tolower(*from);
140         to++;
141         from++;
142     }
143     if (to != end) {
144         *to = '\0';
145         return buf;
146     }
147     return str;
148 }
149
150 static int JK_METHOD start_response(jk_ws_service_t *s,
151                                     int status,
152                                     const char *reason,
153                                     const char *const *header_names,
154                                     const char *const *header_values,
155                                     unsigned num_of_headers)
156 {
157     if (s && s->ws_private) {
158         nsapi_private_data_t *p = s->ws_private;
159         if (!s->response_started) {
160             unsigned i;
161
162             s->response_started = JK_TRUE;
163
164             /* Remove "old" content type */
165             param_free(pblock_remove("content-type", p->rq->srvhdrs));
166
167             if (num_of_headers) {
168                 /*
169                  * NSAPI expects http header names to be lower case.
170                  * This is only relevant for the server itself or other
171                  * plugins searching for headers in the pblock.
172                  * We use a buffer of limited length, because conforming
173                  * with this rule should only matter for well-known headers.
174                  */
175                 char name_buf[64];
176                 for (i = 0; i < (int)num_of_headers; i++) {
177                     pblock_nvinsert(to_lower(header_names[i], name_buf, 64),
178                                     header_values[i], p->rq->srvhdrs);
179                 }
180             }
181             else {
182                 pblock_nvinsert("content-type", "text/plain", p->rq->srvhdrs);
183             }
184
185             protocol_status(p->sn, p->rq, status, (char *)reason);
186
187             protocol_start_response(p->sn, p->rq);
188         }
189         return JK_TRUE;
190
191     }
192     return JK_FALSE;
193 }
194
195 static int JK_METHOD ws_read(jk_ws_service_t *s,
196                              void *b, unsigned l, unsigned *a)
197 {
198     if (s && s->ws_private && b && a) {
199         nsapi_private_data_t *p = s->ws_private;
200
201         *a = 0;
202         if (l) {
203             unsigned i;
204             netbuf *inbuf = p->sn->inbuf;
205
206 /* Until we get a service pack for NW5.1 and earlier that has the latest */
207 /* Enterprise Server, we have to go through the else version of this code*/
208 #if defined(netbuf_getbytes) && !defined(NETWARE)
209             i = netbuf_getbytes(inbuf, b, l);
210             if (NETBUF_EOF == i || NETBUF_ERROR == i) {
211                 return JK_FALSE;
212             }
213
214 #else
215             char *buf = b;
216             int ch;
217             for (i = 0; i < l; i++) {
218                 ch = netbuf_getc(inbuf);
219                 /*
220                  * IO_EOF is 0 (zero) which is a very reasonable byte
221                  * when it comes to binary data. So we are not breaking
222                  * out of the read loop when reading it.
223                  *
224                  * We are protected from an infinit loop by the Java part of
225                  * Tomcat.
226                  */
227                 if (IO_ERROR == ch) {
228                     break;
229                 }
230
231                 buf[i] = ch;
232             }
233
234             if (0 == i) {
235                 return JK_FALSE;
236             }
237 #endif
238             *a = i;
239
240         }
241         return JK_TRUE;
242     }
243     return JK_FALSE;
244 }
245
246 static int JK_METHOD ws_write(jk_ws_service_t *s, const void *b, unsigned l)
247 {
248     if (s && s->ws_private && b) {
249         nsapi_private_data_t *p = s->ws_private;
250
251         if (l) {
252             if (!s->response_started) {
253                 start_response(s, 200, NULL, NULL, NULL, 0);
254             }
255
256             if (net_write(p->sn->csd, (char *)b, (int)l) == IO_ERROR) {
257                 return JK_FALSE;
258             }
259         }
260
261         return JK_TRUE;
262
263     }
264     return JK_FALSE;
265 }
266
267 NSAPI_PUBLIC int jk_init(pblock * pb, Session * sn, Request * rq)
268 {
269     char *worker_prp_file = pblock_findval(JK_WORKER_FILE_TAG, pb);
270     char *log_level_str = pblock_findval(JK_LOG_LEVEL_TAG, pb);
271     char *log_file = pblock_findval(JK_LOG_FILE_TAG, pb);
272     char *shm_file = pblock_findval(JK_SHM_FILE_TAG, pb);
273     char *shm_file_safe = "";
274     char *reject_unsafe = pblock_findval(REJECT_UNSAFE_TAG, pb);
275
276     int rc = REQ_ABORTED;
277
278     if (!worker_prp_file) {
279         worker_prp_file = JK_WORKER_FILE_DEF;
280     }
281
282     if (!log_level_str) {
283         log_level_str = JK_LOG_DEF_VERB;
284     }
285
286     if (!log_file) {
287         fprintf(stderr,
288                 "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_LOG_FILE_TAG);
289         return rc;
290     }
291
292     if (shm_file) {
293         shm_file_safe = shm_file;
294     }
295 #if !defined(WIN32) && !defined(NETWARE)
296     else {
297         fprintf(stderr,
298                 "Missing attribute %s in magnus.conf (jk_init) - aborting!\n", JK_SHM_FILE_TAG);
299         return rc;
300     }
301 #endif
302
303     fprintf(stderr,
304             "In jk_init.\n   Worker file = %s.\n   Log level = %s.\n   Log File = %s\n   SHM File = %s\n",
305             worker_prp_file, log_level_str, log_file, shm_file);
306
307     if (!jk_open_file_logger(&logger, log_file,
308                              jk_parse_log_level(log_level_str))) {
309         logger = NULL;
310     }
311
312     if (jk_map_alloc(&init_map)) {
313         if (jk_map_read_properties(init_map, NULL, worker_prp_file, NULL,
314                                    JK_MAP_HANDLE_DUPLICATES, logger)) {
315             int rv;
316             int sleep_cnt;
317             SYS_THREAD s;
318
319             if (jk_map_resolve_references(init_map, "worker.", 1, 1, logger) == JK_FALSE) {
320                 jk_log(logger, JK_LOG_ERROR, "Error in resolving configuration references");
321             }
322
323             if (reject_unsafe) {
324                 jk_map_add(init_map, "worker." REJECT_UNSAFE_TAG, reject_unsafe);
325             }
326
327             jk_shm_size = jk_shm_calculate_size(init_map, logger);
328             if ((rv = jk_shm_open(shm_file, jk_shm_size, logger)) != 0)
329                 jk_log(logger, JK_LOG_ERROR,
330                        "Initializing shm:%s errno=%d. Load balancing workers will not function properly.",
331                        jk_shm_name(), rv);
332
333             s = systhread_start(SYSTHREAD_DEFAULT_PRIORITY,
334                                 0, init_workers_on_other_threads, init_map);
335             for (sleep_cnt = 0; sleep_cnt < 60; sleep_cnt++) {
336                 systhread_sleep(1000);
337                 jk_log(logger, JK_LOG_DEBUG, "jk_init, a second passed");
338                 if (init_on_other_thread_is_done) {
339                     break;
340                 }
341             }
342
343             if (init_on_other_thread_is_done && init_on_other_thread_is_ok) {
344                 magnus_atrestart(jk_term, NULL);
345                 rc = REQ_PROCEED;
346                 jk_log(logger, JK_LOG_INFO, "%s initialized", JK_FULL_EXPOSED_VERSION);
347             }
348
349 /*            if(wc_open(init_map, NULL, logger)) {
350                 magnus_atrestart(jk_term, NULL);
351                 rc = REQ_PROCEED;
352             }
353 */
354         }
355     }
356
357 #ifdef NETWARE
358     PR_IsSocketSecure =
359         (int (*)(void **))ImportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
360 #endif
361     return rc;
362 }
363
364 NSAPI_PUBLIC void jk_term(void *p)
365 {
366 #ifdef NETWARE
367     if (NULL != PR_IsSocketSecure) {
368         UnimportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
369         PR_IsSocketSecure = NULL;
370     }
371 #endif
372     if (uw_map) {
373         uri_worker_map_free(&uw_map, logger);
374     }
375
376     if (init_map) {
377         jk_map_free(&init_map);
378     }
379
380     wc_close(logger);
381     jk_shm_close();
382     if (logger) {
383         jk_close_file_logger(&logger);
384     }
385 }
386
387 NSAPI_PUBLIC int jk_service(pblock * pb, Session * sn, Request * rq)
388 {
389     char *worker_name = pblock_findval(JK_WORKER_NAME_TAG, pb);
390     char *uri_pattern = pblock_findval(URI_PATTERN, pb);
391     jk_worker_t *worker;
392     int rc = REQ_ABORTED;
393
394     if (uri_pattern) {
395         char *uri = pblock_findval("uri", rq->reqpb);
396
397         if (0 != shexp_match(uri, uri_pattern)) {
398             return REQ_NOACTION;
399         }
400     }
401
402     if (!worker_name) {
403         worker_name = DEFAULT_WORKER_NAME;
404     }
405
406     worker = wc_get_worker_for_name(worker_name, logger);
407     if (worker) {
408         nsapi_private_data_t private_data;
409         jk_ws_service_t s;
410         jk_pool_atom_t buf[SMALL_POOL_SIZE];
411
412         jk_open_pool(&private_data.p, buf, sizeof(buf));
413
414         private_data.pb = pb;
415         private_data.sn = sn;
416         private_data.rq = rq;
417
418         jk_init_ws_service(&s);
419
420         s.ws_private = &private_data;
421         s.pool = &private_data.p;
422
423         wc_maintain(logger);
424         if (init_ws_service(&private_data, &s)) {
425             jk_endpoint_t *e = NULL;
426             if (worker->get_endpoint(worker, &e, logger)) {
427                 int is_error = JK_HTTP_SERVER_ERROR;
428                 int result;
429                 if ((result = e->service(e, &s, logger, &is_error)) > 0) {
430                     rc = REQ_PROCEED;
431                     if (JK_IS_DEBUG_LEVEL(logger))
432                         jk_log(logger, JK_LOG_DEBUG,
433                                "service() returned OK");
434                 }
435                 else {
436                     protocol_status(sn, rq, is_error, NULL);
437                     if ((result == JK_CLIENT_ERROR) && (is_error == JK_HTTP_OK)) {
438                         rc = REQ_EXIT;
439                         jk_log(logger, JK_LOG_INFO,
440                                "service() failed because client aborted connection");
441                     }
442                     else {
443                         rc = REQ_ABORTED;
444                         jk_log(logger, JK_LOG_ERROR,
445                                "service() failed with http error %d", is_error);
446                     }
447                 }
448
449                 e->done(&e, logger);
450             }
451         }
452         jk_close_pool(&private_data.p);
453     }
454
455     return rc;
456 }
457
458 static int init_ws_service(nsapi_private_data_t * private_data,
459                            jk_ws_service_t *s)
460 {
461     char *tmp;
462     int size;
463     int rc;
464
465     s->start_response = start_response;
466     s->read = ws_read;
467     s->write = ws_write;
468
469     s->auth_type = pblock_findval("auth-type", private_data->rq->vars);
470     s->remote_user = pblock_findval("auth-user", private_data->rq->vars);
471
472     tmp = NULL;
473     rc = request_header("content-length",
474                         &tmp, private_data->sn, private_data->rq);
475
476     if ((rc != REQ_ABORTED) && tmp) {
477         sscanf(tmp, "%" JK_UINT64_T_FMT, &(s->content_length));
478     }
479
480     s->method = pblock_findval("method", private_data->rq->reqpb);
481     s->protocol = pblock_findval("protocol", private_data->rq->reqpb);
482
483     s->remote_host = session_dns(private_data->sn);
484     s->remote_addr = pblock_findval("ip", private_data->sn->client);
485     /* Remote port is not available from NSAPI. */
486     s->remote_port = "0";
487
488     tmp = pblock_findval("uri", private_data->rq->reqpb);
489     size = 3 * strlen(tmp) + 1;
490     s->req_uri = jk_pool_alloc(s->pool, size);
491     jk_canonenc(tmp, s->req_uri, size);
492
493     s->query_string = pblock_findval("query", private_data->rq->reqpb);
494
495     s->server_name = server_hostname;
496
497 #ifdef NETWARE
498     /* On NetWare, since we have virtual servers, we have a different way of
499      * getting the port that we need to try first.
500      */
501     tmp = pblock_findval("server_port", private_data->sn->client);
502     if (NULL != tmp)
503         s->server_port = atoi(tmp);
504     else
505 #endif
506         s->server_port = server_portnum;
507     s->server_software = system_version();
508
509     s->uw_map = uw_map;
510
511 #ifdef NETWARE
512     /* on NetWare, we can have virtual servers that are secure.
513      * PR_IsSocketSecure is an api made available with virtual servers to check
514      * if the socket is secure or not
515      */
516     if (NULL != PR_IsSocketSecure)
517         s->is_ssl = PR_IsSocketSecure(private_data->sn->csd);
518     else
519 #endif
520         s->is_ssl = security_active;
521
522     if (s->is_ssl) {
523         char *ssl_cert = pblock_findval("auth-cert", private_data->rq->vars);
524         if (ssl_cert != NULL) {
525             s->ssl_cert = jk_pool_alloc(s->pool, sizeof(ssl_cert_start)+
526                                                  strlen(ssl_cert)+
527                                                  sizeof(ssl_cert_end));
528             strcpy(s->ssl_cert, ssl_cert_start);
529             strcat(s->ssl_cert, ssl_cert);
530             strcat(s->ssl_cert, ssl_cert_end);
531             s->ssl_cert_len = strlen(s->ssl_cert);
532         }
533         s->ssl_cipher = pblock_findval("cipher", private_data->sn->client);
534         s->ssl_session = pblock_findval("ssl-id", private_data->sn->client);
535         /* XXX: We need to investigate how to set s->ssl_key_size */
536     }
537
538     rc = setup_http_headers(private_data, s);
539
540     /* Dump all connection param so we can trace what's going to
541      * the remote tomcat
542      */
543     if (JK_IS_DEBUG_LEVEL(logger)) {
544         jk_log(logger, JK_LOG_DEBUG,
545                "Service protocol=%s method=%s host=%s addr=%s name=%s port=%d auth=%s user=%s uri=%s",
546                STRNULL_FOR_NULL(s->protocol),
547                STRNULL_FOR_NULL(s->method),
548                STRNULL_FOR_NULL(s->remote_host),
549                STRNULL_FOR_NULL(s->remote_addr),
550                STRNULL_FOR_NULL(s->server_name),
551                s->server_port,
552                STRNULL_FOR_NULL(s->auth_type),
553                STRNULL_FOR_NULL(s->remote_user),
554                STRNULL_FOR_NULL(s->req_uri));
555     }
556
557     return rc;
558
559 }
560
561 static int setup_http_headers(nsapi_private_data_t * private_data,
562                               jk_ws_service_t *s)
563 {
564     int need_content_length_header =
565         (s->content_length == 0) ? JK_TRUE : JK_FALSE;
566
567     pblock *headers_jar = private_data->rq->headers;
568     int cnt;
569     int i;
570
571     for (i = 0, cnt = 0; i < headers_jar->hsize; i++) {
572         struct pb_entry *h = headers_jar->ht[i];
573         while (h && h->param) {
574             cnt++;
575             h = h->next;
576         }
577     }
578
579     s->headers_names = NULL;
580     s->headers_values = NULL;
581     s->num_headers = cnt;
582     if (cnt) {
583         /* allocate an extra header slot in case we need to add a content-length header */
584         s->headers_names =
585             jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
586         s->headers_values =
587             jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
588
589         if (s->headers_names && s->headers_values) {
590             for (i = 0, cnt = 0; i < headers_jar->hsize; i++) {
591                 struct pb_entry *h = headers_jar->ht[i];
592                 while (h && h->param) {
593                     s->headers_names[cnt] = h->param->name;
594                     s->headers_values[cnt] = h->param->value;
595                     if (need_content_length_header &&
596                         !strncmp(h->param->name, "content-length", 14)) {
597                         need_content_length_header = JK_FALSE;
598                     }
599                     cnt++;
600                     h = h->next;
601                 }
602             }
603             /* Add a content-length = 0 header if needed.
604              * Ajp13 assumes an absent content-length header means an unknown,
605              * but non-zero length body.
606              */
607             if (need_content_length_header) {
608                 s->headers_names[cnt] = "content-length";
609                 s->headers_values[cnt] = "0";
610                 cnt++;
611             }
612             s->num_headers = cnt;
613             return JK_TRUE;
614         }
615     }
616     else {
617         if (need_content_length_header) {
618             s->headers_names =
619                 jk_pool_alloc(&private_data->p, sizeof(char *));
620             s->headers_values =
621                 jk_pool_alloc(&private_data->p, sizeof(char *));
622             if (s->headers_names && s->headers_values) {
623                 s->headers_names[0] = "content-length";
624                 s->headers_values[0] = "0";
625                 s->num_headers++;
626                 return JK_TRUE;
627             }
628         }
629         else
630             return JK_TRUE;
631     }
632
633     return JK_FALSE;
634 }