blob: 241cb202d213e96fa452c73f55da7cc2a8a46624 [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.
*/
/**
* Facility: AOLserver plugin for Jakarta/Tomcat
* Description: Implementation of JK2 HTTP request phases
* Author: Alexander Leykekh, aolserver@aol.net
* Version: $Revision$
*/
/*
* Jakarta (jk_) include files
*/
#include "jk_ns.h"
#define NULL_FOR_EMPTY(x) ((((x)!=NULL) && (strlen((x))!=0)) ? (x) : NULL )
static int JK_METHOD jk2_service_ns_head(jk_env_t *env, jk_ws_service_t *s )
{
int h;
int numheaders;
Ns_Conn *conn;
jk_map_t *headers;
int debug=1;
char *name, *val;
time_t lastMod;
if(env==NULL || env->l==NULL || env->l->jkLog==NULL)
return JK_ERR;
if(s==NULL || s->ws_private==NULL || s->headers_out==NULL || s->pool==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.head() NullPointerException\n");
return JK_ERR;
}
if( s->uriEnv!=NULL && s->uriEnv->mbean!=NULL)
debug=s->uriEnv->mbean->debug;
conn = (Ns_Conn *)s->ws_private;
if (conn == NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.head() no current connection\n");
return JK_ERR;
}
if(s->msg==NULL) {
s->msg = "";
}
headers=s->headers_out;
numheaders = headers->size(env, headers);
if( debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"service.head() %d %d %#lx\n", s->status,
numheaders, s->uriEnv);
for(h = 0 ; h < numheaders; h++) {
name=headers->nameAt( env, headers, h );
val=headers->valueAt( env, headers, h );
if (name==NULL || val==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.head() NullPointerException\n");
return JK_ERR;
}
name=s->pool->pstrdup( env, s->pool, name );
val=s->pool->pstrdup( env, s->pool, val );
if (name==NULL || val==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.head() NullPointerException\n");
return JK_ERR;
}
if( debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"service.head() %s: %s %d %d\n", name, val, h, headers->size( env, headers ));
if(!strcasecmp(name, "Content-type")) {
Ns_ConnSetTypeHeader (conn, Ns_StrToLower (val));
} else if(!strcasecmp(name, "Last-Modified")) {
/*
* If the script gave us a Last-Modified header, we can't just
* pass it on blindly because of restrictions on future values.
*
*/
lastMod = apr_date_parse_http (val);
Ns_ConnSetLastModifiedHeader (conn, &lastMod);
} else {
Ns_ConnSetHeaders (conn, name, val);
}
}
if (Ns_ConnFlushHeaders (conn, s->status) != NS_OK) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.head() cannot flush headers\n");
return JK_ERR;
}
s->response_started = JK_TRUE;
return JK_OK;
}
/*
* Read a chunk of the request body into a buffer. Attempt to read len
* bytes into the buffer. Write the number of bytes actually read into
* actually_read.
*
* Think of this function as a method of the AOLserver-specific subclass of
* the jk_ws_service class. Think of the *s param as a "this" or "self"
* pointer.
*/
static int JK_METHOD jk2_service_ns_read(jk_env_t *env, jk_ws_service_t *s,
void *b, unsigned len,
unsigned *actually_read)
{
int rv;
if(s==NULL || s->ws_private==NULL || b==NULL || actually_read==NULL || s->ws_private==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.read() NullPointerException\n");
return JK_ERR;
}
s->read_body_started = JK_TRUE;
if ((rv = Ns_ConnRead((Ns_Conn *)s->ws_private, b, len)) == NS_ERROR) {
*actually_read = 0;
} else {
*actually_read = (unsigned) rv;
}
return JK_OK;
}
/*
* Write a chunk of response data back to the browser. If the headers
* haven't yet been sent over, send over default header values (Status =
* 200, basically).
*
* Write len bytes from buffer b.
*
* Think of this function as a method of the AOLserver-specific subclass of
* the jk_ws_service class. Think of the *s param as a "this" or "self"
* pointer.
*/
static int JK_METHOD jk2_service_ns_write(jk_env_t *env, jk_ws_service_t *s,
const void *b, unsigned len)
{
int r = 0;
char *bb=(char *)b;
Ns_Conn *conn;
Ns_Set *outHeaders;
int i;
int headerCount;
int debug=1;
int rc;
if (env->l->jkLog == NULL)
return JK_ERR;
if(s==NULL || s->ws_private == NULL || b==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.write() NullPointerException\n");
return JK_ERR;
}
if( s->uriEnv != NULL )
debug=s->uriEnv->mbean->debug;
if(len==0 ) {
return JK_OK;
}
conn=(Ns_Conn *)s->ws_private;
if(!s->response_started) {
if( debug > 0 )
env->l->jkLog(env, env->l, JK_LOG_DEBUG,
"service.write() default head\n");
rc=s->head(env, s);
if( rc != JK_OK) {
return rc;
}
if (debug > 0) {
outHeaders = Ns_ConnOutputHeaders (conn);
headerCount = Ns_SetSize (outHeaders);
if (outHeaders && headerCount) {
for (i = 0 ; i < headerCount ; i++) {
env->l->jkLog(env, env->l, JK_LOG_DEBUG, "OutHeaders %s: %s\n",
Ns_SetKey (outHeaders, i), Ns_SetValue (outHeaders, i));
}
}
}
}
while( len > 0 ) {
r = Ns_ConnWrite (conn, bb, len);
if (r == -1)
return JK_ERR;
len -= r;
bb += r;
}
return JK_OK;
}
/* ========================================================================= */
/* Utility functions */
/* ========================================================================= */
static int JK_METHOD jk2_init_ws_service(jk_env_t *env, jk_ws_service_t *s,
jk_worker_t *worker, void *serverObj)
{
jk_workerEnv_t *workerEnv;
Ns_Conn *conn=serverObj;
int need_content_length_header=JK_FALSE;
char *query_str_begin, *query_str_end;
Ns_DString dstr;
int count, i;
char *name, *val;
Ns_Set *reqHdrs = Ns_ConnHeaders (conn);
char* override = s->server_name; /* jk2_requtil_initRequest will null s->server_name */
if (env == NULL)
return JK_ERR;
if (s==NULL || s->pool==NULL || worker==NULL || serverObj==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"init_ws_service() NullPointerException\n");
return JK_ERR;
}
workerEnv = worker->workerEnv;
/* Common initialization */
jk2_requtil_initRequest(env, s);
s->ws_private = conn;
s->response_started = JK_FALSE;
s->read_body_started = JK_FALSE;
s->workerEnv=workerEnv;
/* Unless s->realWorker is set, JNI channel will not detach thread from JVM at
the end of request
s->jvm_route = worker->route;
s->realWorker = worker;
*/
s->remote_user = NULL_FOR_EMPTY(Ns_ConnAuthUser (conn));
s->auth_type = (s->remote_user==NULL? NULL : "Basic");
/* conn->request->line has HTTP request line */
if (conn->request!=NULL && conn->request->line!=NULL)
s->protocol = strrchr (conn->request->line, ' ');
if (s->protocol != NULL)
s->protocol++;
else
s->protocol = "HTTP/1.0";
s->remote_host = s->remote_addr = NULL_FOR_EMPTY(Ns_ConnPeer (conn));
/* get server name */
if (override != NULL)
s->server_name = override;
else {
if (reqHdrs != NULL)
s->server_name = Ns_SetIGet (reqHdrs, "Host");
if (s->server_name == NULL)
s->server_name= Ns_ConnServer (conn);
}
/* get the real port (otherwise redirect failed) */
s->server_port = Ns_ConnPort (conn);
s->server_software = Ns_InfoServerVersion ();
s->method = conn->request->method;
s->content_length = Ns_ConnContentLength (conn);
s->is_chunked = JK_FALSE;
s->no_more_chunks = JK_FALSE;
s->query_string = conn->request->query;
s->startTime = time (NULL);
/*
* The 2.2 servlet spec errata says the uri from
* HttpServletRequest.getRequestURI() should remain encoded.
* [http://java.sun.com/products/servlet/errata_042700.html]
*
* We use JkOptions to determine which method to be used
*
*/
switch (workerEnv->options & JK_OPT_FWDURIMASK) {
case JK_OPT_FWDURICOMPATUNPARSED :
/* Parse away method, query and protocol from request line, e.g.,
*
* GET /x/y?q HTTP/1.1
*
*/
query_str_begin = strchr (conn->request->line, ' ');
if (query_str_begin == NULL) {
s->req_uri = NULL;
break;
}
query_str_end = strchr (++query_str_begin, '?'); /* exclude query */
if (query_str_end == NULL) {
query_str_end = strchr (query_str_begin, ' '); /* no query? find where URI ends */
if (query_str_end == NULL)
query_str_end = conn->request->line+strlen(conn->request->line);
}
s->req_uri = s->pool->alloc (env, s->pool, query_str_end-query_str_begin);
if (s->req_uri == NULL)
break;
memcpy (s->req_uri, query_str_begin, query_str_end-query_str_begin+1); /* include 0 terminator */
break;
case JK_OPT_FWDURICOMPAT :
s->req_uri = conn->request->url;
break;
case JK_OPT_FWDURIESCAPED :
Ns_DStringInit (&dstr);
query_str_begin = Ns_EncodeUrl (&dstr, conn->request->url);
if (query_str_begin != NULL) {
s->req_uri = s->pool->pstrdup (env, s->pool, query_str_begin+1); /* +1 because string starts with ? */
} else {
s->req_uri = NULL;
}
Ns_DStringFree (&dstr);
break;
default :
return JK_ERR;
}
s->is_ssl = JK_FALSE;
s->ssl_cert = NULL;
s->ssl_cert_len = 0;
s->ssl_cipher = NULL; /* required by Servlet 2.3 Api,
allready in original ajp13 */
s->ssl_session = NULL;
s->ssl_key_size = -1; /* required by Servlet 2.3 Api, added in jtc */
if (workerEnv->ssl_enable || workerEnv->envvars_in_use) {
if (workerEnv->ssl_enable) {
name = Ns_ConnDriverName (conn);
if (name!=NULL && strcmp(name, "nsssl")==0)
s->is_ssl = JK_TRUE;
else
s->is_ssl = JK_FALSE;
if (s->is_ssl == JK_TRUE) {
s->ssl_cert = NULL; /* workerEnv->certs_indicator "SSL_CLIENT_CERT" */
if (s->ssl_cert) {
s->ssl_cert_len = strlen(s->ssl_cert);
}
/* Servlet 2.3 API */
s->ssl_cipher = NULL; /* workerEnv->cipher_indicator "HTTPS_CIPHER"/"SSL_CIPHER" */
s->ssl_session = NULL; /* workerEnv->session_indicator "SSL_SESSION_ID" */
if (workerEnv->options & JK_OPT_FWDKEYSIZE) {
/* Servlet 2.3 API */
s->ssl_key_size = 0;
}
}
}
jk2_map_default_create(env, &s->attributes, s->pool );
if (workerEnv->envvars_in_use && reqHdrs) {
count = workerEnv->envvars->size( env, workerEnv->envvars );
for ( i=0; i< count ; i++ ) {
name= workerEnv->envvars->nameAt( env, workerEnv->envvars, i );
val= Ns_SetIGet (reqHdrs, name);
if (val==NULL) {
val=workerEnv->envvars->valueAt( env, workerEnv->envvars, i );
}
s->attributes->put( env, s->attributes, name, val, NULL );
}
}
}
jk2_map_default_create(env, &s->headers_in, s->pool );
if (reqHdrs && (count=Ns_SetSize (reqHdrs))) {
for (i = 0 ; i < count ; i++) {
s->headers_in->add( env, s->headers_in, Ns_SetKey (reqHdrs, i), Ns_SetValue (reqHdrs, i));
}
}
if(!s->is_chunked && s->content_length == 0) {
/* if r->contentLength == 0 I assume there's no header
or a header with '0'. In the second case, put will override it
*/
s->headers_in->put( env, s->headers_in, "content-length", "0", NULL );
}
jk2_map_default_create(env, &s->headers_out, s->pool );
return JK_OK;
}
/*
* If the servlet engine didn't consume all of the
* request data, consume and discard all further
* characters left to read from client
*
*/
static void JK_METHOD jk2_service_ns_afterRequest(jk_env_t *env, jk_ws_service_t *s )
{
Ns_Conn *conn;
char buff[2048];
int rd;
struct jk_worker *w;
if (env==NULL)
return;
if (s==NULL) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.afterRequest() NullPointerException\n");
return;
}
if (s->content_read < s->content_length ||
(s->is_chunked && ! s->no_more_chunks)) {
conn = s->ws_private;
while ((rd = Ns_ConnRead (conn, buff, sizeof(buff))) > 0) {
s->content_read += rd;
}
}
if (s->realWorker) {
w = s->realWorker;
if (w != NULL && w->channel != NULL && w->channel->afterRequest != NULL) {
w->channel->afterRequest( env, w->channel, w, NULL, s );
}
}
}
int JK_METHOD jk2_service_ns_init(jk_env_t *env, jk_ws_service_t *s, char* serverNameOverride)
{
if(s==NULL ) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"service.init() NullPointerException\n");
return JK_ERR;
}
s->head = jk2_service_ns_head;
s->read = jk2_service_ns_read;
s->write = jk2_service_ns_write;
s->init = jk2_init_ws_service;
s->afterRequest = jk2_service_ns_afterRequest;
s->server_name = serverNameOverride;
return JK_OK;
}