blob: 3ffa6530e92ca515196fe82d04cbc0c000151be3 [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: NSAPI plugin for Netscape servers *
* Author: Gal Shachor <shachor@il.ibm.com> *
* Version: $Revision$ *
***************************************************************************/
#include "jk_global.h"
#include "jk_util.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_service.h"
#include "jk_worker.h"
#include "nsapi.h"
#define URI_PATTERN "path"
#define DEFAULT_WORKER_NAME ("ajp13")
struct nsapi_private_data {
jk_pool_t p;
int request_started;
pblock *pb;
Session *sn;
Request *rq;
};
typedef struct nsapi_private_data nsapi_private_data_t;
static int init_on_other_thread_is_done = JK_FALSE;
static int init_on_other_thread_is_ok = JK_FALSE;
static jk_logger_t *logger = NULL;
static jk_worker_env_t worker_env;
#ifdef NETWARE
int (*PR_IsSocketSecure)(SYS_NETFD *csd); /* pointer to PR_IsSocketSecure function */
#endif
static int JK_METHOD start_response(jk_ws_service_t *s,
int status,
const char *reason,
const char * const *header_names,
const char * const *header_values,
unsigned num_of_headers);
static int JK_METHOD ws_read(jk_ws_service_t *s,
void *b,
unsigned l,
unsigned *a);
static int JK_METHOD ws_write(jk_ws_service_t *s,
const void *b,
unsigned l);
NSAPI_PUBLIC int jk_init(pblock *pb,
Session *sn,
Request *rq);
NSAPI_PUBLIC void jk_term(void *p);
NSAPI_PUBLIC int jk_service(pblock *pb,
Session *sn,
Request *rq);
static int init_ws_service(nsapi_private_data_t *private_data,
jk_ws_service_t *s);
static int setup_http_headers(nsapi_private_data_t *private_data,
jk_ws_service_t *s);
static void init_workers_on_other_threads(void *init_d)
{
jk_map_t *init_map = (jk_map_t *)init_d;
/* we add the URI->WORKER MAP since workers using AJP14 will feed it */
/* but where are they here in Netscape ? */
if(wc_open(init_map, &worker_env, logger)) {
init_on_other_thread_is_ok = JK_TRUE;
} else {
jk_log(logger, JK_LOG_EMERG, "In init_workers_on_other_threads, failed\n");
}
init_on_other_thread_is_done = JK_TRUE;
}
static int JK_METHOD start_response(jk_ws_service_t *s,
int status,
const char *reason,
const char * const *header_names,
const char * const *header_values,
unsigned num_of_headers)
{
if(s && s->ws_private) {
nsapi_private_data_t *p = s->ws_private;
if(!p->request_started) {
unsigned i;
p->request_started = JK_TRUE;
/* Remove "old" content type */
param_free(pblock_remove("content-type", p->rq->srvhdrs));
if(num_of_headers) {
for (i = 0 ; i < (int)num_of_headers ; i++) {
pblock_nvinsert(header_names[i],
header_values[i],
p->rq->srvhdrs);
}
} else {
pblock_nvinsert("content-type",
"text/plain",
p->rq->srvhdrs);
}
protocol_status(p->sn,
p->rq,
status,
(char *)reason);
protocol_start_response(p->sn, p->rq);
}
return JK_TRUE;
}
return JK_FALSE;
}
static int JK_METHOD ws_read(jk_ws_service_t *s,
void *b,
unsigned l,
unsigned *a)
{
if(s && s->ws_private && b && a) {
nsapi_private_data_t *p = s->ws_private;
*a = 0;
if(l) {
char *buf = b;
unsigned i;
netbuf *inbuf = p->sn->inbuf;
/* Until we get a service pack for NW5.1 and earlier that has the latest */
/* Enterprise Server, we have to go through the else version of this code*/
#if defined(netbuf_getbytes) && !defined(NETWARE)
i = netbuf_getbytes(inbuf, b, l);
if(NETBUF_EOF == i || NETBUF_ERROR == i) {
return JK_FALSE;
}
#else
int ch;
for(i = 0 ; i < l ; i++) {
ch = netbuf_getc(inbuf);
/*
* IO_EOF is 0 (zero) which is a very reasonable byte
* when it comes to binary data. So we are not breaking
* out of the read loop when reading it.
*
* We are protected from an infinit loop by the Java part of
* Tomcat.
*/
if(IO_ERROR == ch) {
break;
}
buf[i] = ch;
}
if(0 == i) {
return JK_FALSE;
}
#endif
*a = i;
}
return JK_TRUE;
}
return JK_FALSE;
}
static int JK_METHOD ws_write(jk_ws_service_t *s,
const void *b,
unsigned l)
{
if(s && s->ws_private && b) {
nsapi_private_data_t *p = s->ws_private;
if(l) {
if(!p->request_started) {
start_response(s, 200, NULL, NULL, NULL, 0);
}
if(net_write(p->sn->csd, (char *)b, (int)l) == IO_ERROR) {
return JK_FALSE;
}
}
return JK_TRUE;
}
return JK_FALSE;
}
NSAPI_PUBLIC int jk_init(pblock *pb,
Session *sn,
Request *rq)
{
char *worker_prp_file = pblock_findval(JK_WORKER_FILE_TAG, pb);
char *log_level_str = pblock_findval(JK_LOG_LEVEL_TAG, pb);
char *log_file = pblock_findval(JK_LOG_FILE_TAG, pb);
int rc = REQ_ABORTED;
jk_map_t *init_map;
fprintf(stderr, "In jk_init.\n Worker file = %s.\n Log level = %s.\n Log File = %s\n",worker_prp_file, log_level_str, log_file);
if(!worker_prp_file) {
worker_prp_file = JK_WORKER_FILE_DEF;
}
if(!log_level_str) {
log_level_str = JK_LOG_LEVEL_DEF;
}
if(!jk_open_file_logger(&logger, log_file,
jk_parse_log_level(log_level_str))) {
logger = NULL;
}
if(map_alloc(&init_map)) {
if(map_read_properties(init_map, worker_prp_file)) {
int sleep_cnt;
SYS_THREAD s;
s = systhread_start(SYSTHREAD_DEFAULT_PRIORITY,
0,
init_workers_on_other_threads,
init_map);
for(sleep_cnt = 0 ; sleep_cnt < 60 ; sleep_cnt++) {
systhread_sleep(1000);
jk_log(logger, JK_LOG_DEBUG, "jk_init, a second passed\n");
if(init_on_other_thread_is_done) {
break;
}
}
if(init_on_other_thread_is_done &&
init_on_other_thread_is_ok) {
magnus_atrestart(jk_term, NULL);
rc = REQ_PROCEED;
}
/* if(wc_open(init_map, NULL, logger)) {
magnus_atrestart(jk_term, NULL);
rc = REQ_PROCEED;
}
*/
}
map_free(&init_map);
}
#ifdef NETWARE
PR_IsSocketSecure = (int (*)(void **))ImportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
#endif
return rc;
}
NSAPI_PUBLIC void jk_term(void *p)
{
#ifdef NETWARE
if (NULL != PR_IsSocketSecure)
{
UnimportSymbol(GetNLMHandle(), "PR_IsSocketSecure");
PR_IsSocketSecure = NULL;
}
#endif
wc_close(logger);
if(logger) {
jk_close_file_logger(&logger);
}
}
NSAPI_PUBLIC int jk_service(pblock *pb,
Session *sn,
Request *rq)
{
char *worker_name = pblock_findval(JK_WORKER_NAME_TAG, pb);
char *uri_pattern = pblock_findval(URI_PATTERN, pb);
jk_worker_t *worker;
int rc = REQ_ABORTED;
if(uri_pattern) {
char *uri = pblock_findval("uri", rq->reqpb);
if(0 != shexp_match(uri, uri_pattern)) {
return REQ_NOACTION;
}
}
if(!worker_name) {
worker_name = DEFAULT_WORKER_NAME;
}
worker = wc_get_worker_for_name(worker_name, logger);
if(worker) {
nsapi_private_data_t private_data;
jk_ws_service_t s;
jk_pool_atom_t buf[SMALL_POOL_SIZE];
jk_open_pool(&private_data.p, buf, sizeof(buf));
private_data.request_started = JK_FALSE;
private_data.pb = pb;
private_data.sn = sn;
private_data.rq = rq;
jk_init_ws_service(&s);
s.ws_private = &private_data;
s.pool = &private_data.p;
if(init_ws_service(&private_data, &s)) {
jk_endpoint_t *e = NULL;
if(worker->get_endpoint(worker, &e, logger)) {
int recover = JK_FALSE;
if(e->service(e, &s, logger, &recover)) {
rc = REQ_PROCEED;
}
e->done(&e, logger);
}
}
jk_close_pool(&private_data.p);
}
return rc;
}
static int init_ws_service(nsapi_private_data_t *private_data,
jk_ws_service_t *s)
{
char *tmp;
int rc;
s->jvm_route = NULL;
s->start_response = start_response;
s->read = ws_read;
s->write = ws_write;
/* Clear RECO status */
s->reco_status = RECO_NONE;
s->auth_type = pblock_findval("auth-type", private_data->rq->vars);
s->remote_user = pblock_findval("auth-user", private_data->rq->vars);
s->content_length = 0;
tmp = NULL;
rc = request_header("content-length",
&tmp,
private_data->sn,
private_data->rq);
if((rc != REQ_ABORTED) && tmp) {
s->content_length = atoi(tmp);
}
s->method = pblock_findval("method", private_data->rq->reqpb);
s->protocol = pblock_findval("protocol", private_data->rq->reqpb);
s->remote_host = session_dns(private_data->sn);
s->remote_addr = pblock_findval("ip", private_data->sn->client);
s->req_uri = pblock_findval("uri", private_data->rq->reqpb);
s->query_string = pblock_findval("query", private_data->rq->reqpb);
s->server_name = server_hostname;
#ifdef NETWARE
/* On NetWare, since we have virtual servers, we have a different way of
* getting the port that we need to try first.
*/
tmp = pblock_findval("server_port", private_data->sn->client);
if (NULL != tmp)
s->server_port = atoi(tmp);
else
#endif
s->server_port = server_portnum;
s->server_software = system_version();
s->headers_names = NULL;
s->headers_values = NULL;
s->num_headers = 0;
#ifdef NETWARE
/* on NetWare, we can have virtual servers that are secure.
* PR_IsSocketSecure is an api made available with virtual servers to check
* if the socket is secure or not
*/
if (NULL != PR_IsSocketSecure)
s->is_ssl = PR_IsSocketSecure(private_data->sn->csd);
else
#endif
s->is_ssl = security_active;
s->ssl_key_size = -1; /* required by Servlet 2.3 Api, added in jtc */
if(s->is_ssl) {
s->ssl_cert = pblock_findval("auth-cert", private_data->rq->vars);
if(s->ssl_cert) {
s->ssl_cert_len = strlen(s->ssl_cert);
}
s->ssl_cipher = pblock_findval("cipher", private_data->sn->client);
s->ssl_session = pblock_findval("ssl-id", private_data->sn->client);
} else {
s->ssl_cert = NULL;
s->ssl_cert_len = 0;
s->ssl_cipher = NULL;
s->ssl_session = NULL;
}
return setup_http_headers(private_data, s);
}
static int setup_http_headers(nsapi_private_data_t *private_data,
jk_ws_service_t *s)
{
int need_content_length_header = (s->content_length == 0) ? JK_TRUE : JK_FALSE;
pblock *headers_jar = private_data->rq->headers;
int cnt;
int i;
for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
struct pb_entry *h = headers_jar->ht[i];
while(h && h->param) {
cnt++;
h = h->next;
}
}
s->headers_names = NULL;
s->headers_values = NULL;
s->num_headers = cnt;
if(cnt) {
/* allocate an extra header slot in case we need to add a content-length header */
s->headers_names = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
s->headers_values = jk_pool_alloc(&private_data->p, (cnt + 1) * sizeof(char *));
if(s->headers_names && s->headers_values) {
for(i = 0, cnt = 0 ; i < headers_jar->hsize ; i++) {
struct pb_entry *h = headers_jar->ht[i];
while(h && h->param) {
s->headers_names[cnt] = h->param->name;
s->headers_values[cnt] = h->param->value;
if(need_content_length_header &&
!strncmp(h->param->name,"content-length",14)) {
need_content_length_header = JK_FALSE;
}
cnt++;
h = h->next;
}
}
/* Add a content-length = 0 header if needed.
* Ajp13 assumes an absent content-length header means an unknown,
* but non-zero length body.
*/
if(need_content_length_header) {
s->headers_names[cnt] = "content-length";
s->headers_values[cnt] = "0";
cnt++;
}
s->num_headers = cnt;
return JK_TRUE;
}
} else {
if (need_content_length_header) {
s->headers_names = jk_pool_alloc(&private_data->p, sizeof(char *));
s->headers_values = jk_pool_alloc(&private_data->p, sizeof(char *));
if(s->headers_names && s->headers_values) {
s->headers_names[0] = "content-length";
s->headers_values[0] = "0";
s->num_headers++;
return JK_TRUE;
}
}
else
return JK_TRUE;
}
return JK_FALSE;
}