blob: b9e4bc833308a6cfaafc6c6561bb1fc27e6251e7 [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: Apache1.3 adapter
*/
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_main.h"
#include "http_log.h"
#include "util_script.h"
#include "util_date.h"
/*
* Jakarta (jk_) include files
*/
#include "jk_global.h"
#include "jk_map.h"
#include "jk_pool.h"
#include "jk_env.h"
#include "jk_service.h"
#include "jk_worker.h"
#include "jk_workerEnv.h"
#include "jk_uriMap.h"
#include "jk_requtil.h"
#define NULL_FOR_EMPTY(x) ((x && !strlen(x)) ? NULL : x)
static int JK_METHOD jk2_service_apache13_head(jk_env_t *env,
jk_ws_service_t *s)
{
int h;
int numheaders;
request_rec *r;
jk_map_t *headers;
if (s == NULL || s->ws_private == NULL)
return JK_ERR;
r = (request_rec *) s->ws_private;
if (s->msg == NULL) {
s->msg = "";
}
r->status = s->status;
r->status_line = ap_psprintf(r->pool, "%d %s", s->status, s->msg);
headers = s->headers_out;
numheaders = headers->size(env, headers);
/* XXX As soon as we switch to jk_map_apache13, this will not be needed ! */
if (s->uriEnv->mbean->debug > 0)
env->l->jkLog(env, env->l, JK_LOG_INFO,
"service.head() %d %d\n", s->status, numheaders);
for (h = 0; h < numheaders; h++) {
char *name = headers->nameAt(env, headers, h);
char *val = headers->valueAt(env, headers, h);
if (s->uriEnv->mbean->debug > 0)
env->l->jkLog(env, env->l, JK_LOG_INFO,
"service.head() %s: %s %d %d\n",
name, val, h, headers->size(env, headers));
/* the cmp can also be avoided in we do this earlier and use
the header id */
if (strcasecmp(name, "Content-type") == 0) {
/* XXX should be done in handler ! */
char *tmp = ap_pstrdup(r->pool, val);
ap_content_type_tolower(tmp);
r->content_type = tmp;
ap_table_set(r->headers_out, name, val);
}
else if (strcasecmp(name, "Location") == 0) {
/* XXX setn */
ap_table_set(r->headers_out, name, val);
}
else if (strcasecmp(name, "Content-Length") == 0) {
ap_table_set(r->headers_out, name, val);
}
else if (strcasecmp(name, "Transfer-Encoding") == 0) {
ap_table_set(r->headers_out, name, val);
}
else if (strcasecmp(name, "Last-Modified") == 0) {
/*
* If the script gave us a Last-Modified header, we can't just
* pass it on blindly because of restrictions on future values.
*/
ap_update_mtime(r, ap_parseHTTPdate(val));
ap_set_last_modified(r);
ap_table_set(r->headers_out, name, val);
}
else {
ap_table_add(r->headers_out, name, val);
/* ap_table_set(r->headers_out, name, val); */
}
}
ap_send_http_header(r);
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 apache1.3-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_apache13_read(jk_env_t *env,
jk_ws_service_t *s, void *b,
unsigned len,
unsigned *actually_read)
{
long rv;
if (s == NULL || s->ws_private == NULL || b == NULL
|| actually_read == NULL) {
return JK_ERR;
}
if (!s->read_body_started) {
if (ap_should_client_block(s->ws_private)) {
s->read_body_started = JK_TRUE;
}
}
rv = ap_get_client_block(s->ws_private, b, len);
if (rv < 0) {
*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 apache1.3-specific subclass of
* the jk_ws_service class. Think of the *s param as a "this" or "self"
* pointer.
*/
/* Works with 4096, fails with 8192 */
#ifndef CHUNK_SIZE
#define CHUNK_SIZE 4096
#endif
static int JK_METHOD jk2_service_apache13_write(jk_env_t *env,
jk_ws_service_t *s,
const void *b,
unsigned int len)
{
int rc;
if (s == NULL || s->ws_private == NULL || b == NULL)
return JK_ERR;
{
size_t rd = 0;
long ll = len;
char *bb = (char *)b;
request_rec *rr = s->ws_private;
BUFF *bf = rr->connection->client;
if (!s->response_started) {
if (s->uriEnv->mbean->debug > 0)
env->l->jkLog(env, env->l, JK_LOG_INFO,
"service.write() default head\n");
rc = s->head(env, s);
if (rc != JK_OK) {
return rc;
}
}
if (rr->header_only) {
ap_bflush(bf);
return JK_OK;
}
/* Debug - try to get around rwrite */
while (ll > 0) {
unsigned long toSend = (ll > CHUNK_SIZE) ? CHUNK_SIZE : ll;
rd = ap_rwrite((const char *)bb, toSend, rr);
if (s->uriEnv->mbean->debug > 0)
env->l->jkLog(env, env->l, JK_LOG_INFO,
"service.write() %ld (%ld) out of %ld \n",
toSend, rd, ll);
ll -= CHUNK_SIZE;
bb += CHUNK_SIZE;
if (toSend != rd) {
return JK_ERR;
}
}
/*
* To allow server push. After writing full buffers
*/
ap_bflush(bf);
}
return JK_OK;
}
/* ========================================================================= */
/* Utility functions */
/* ========================================================================= */
static int jk2_get_content_length(jk_env_t *env, request_rec * r)
{
if (r->clength > 0) {
return r->clength;
}
else {
char *lenp = (char *)ap_table_get(r->headers_in, "Content-Length");
if (lenp) {
int rc = atoi(lenp);
if (rc > 0) {
return rc;
}
}
}
return 0;
}
static int JK_METHOD jk2_init_ws_service(jk_env_t *env, jk_ws_service_t *s,
jk_worker_t *worker, void *serverObj)
{
char *ssl_temp = NULL;
jk_workerEnv_t *workerEnv = worker->workerEnv;
request_rec *r = serverObj;
int need_content_length_header = JK_FALSE;
/* Common initialization */
/* XXX Probably not needed, we're duplicating */
jk2_requtil_initRequest(env, s);
s->ws_private = r;
s->response_started = JK_FALSE;
s->read_body_started = JK_FALSE;
s->workerEnv = workerEnv;
workerEnv->childId = r->connection->child_num;
s->jvm_route = NULL; /* Used for sticky session routing */
s->auth_type = NULL_FOR_EMPTY(r->connection->ap_auth_type);
s->remote_user = NULL_FOR_EMPTY(r->connection->user);
s->protocol = r->protocol;
s->remote_host = (char *)ap_get_remote_host(r->connection,
r->per_dir_config,
REMOTE_HOST);
s->remote_host = NULL_FOR_EMPTY(s->remote_host);
s->remote_addr = NULL_FOR_EMPTY(r->connection->remote_ip);
/* get server name */
s->server_name = (char *)(r->hostname ? r->hostname :
r->server->server_hostname);
s->server_port = htons(r->connection->local_addr.sin_port);
s->server_software = (char *)ap_get_server_version();
s->method = (char *)r->method;
s->content_length = jk2_get_content_length(env, r);
s->is_chunked = r->read_chunked;
s->no_more_chunks = 0;
s->query_string = r->args;
/*
* 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
*
* ap_escape_uri is the latest recommanded but require
* some java decoding (in TC 3.3 rc2)
*
* unparsed_uri is used for strict compliance with spec and
* old Tomcat (3.2.3 for example)
*
* uri is use for compatibilty with mod_rewrite with old Tomcats
*/
switch (workerEnv->options & JK_OPT_FWDURIMASK) {
case JK_OPT_FWDURICOMPATUNPARSED:
s->req_uri = r->unparsed_uri;
if (s->req_uri != NULL) {
char *query_str = strchr(s->req_uri, '?');
if (query_str != NULL) {
*query_str = 0;
}
}
break;
case JK_OPT_FWDURICOMPAT:
s->req_uri = r->uri;
break;
case JK_OPT_FWDURIESCAPED:
s->req_uri = ap_escape_uri(r->pool, r->uri);
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) {
ap_add_common_vars(r);
if (workerEnv->ssl_enable) {
ssl_temp =
(char *)ap_table_get(r->subprocess_env,
workerEnv->https_indicator);
if (ssl_temp && !strcasecmp(ssl_temp, "on")) {
s->is_ssl = JK_TRUE;
s->ssl_cert =
(char *)ap_table_get(r->subprocess_env,
workerEnv->certs_indicator);
if (s->ssl_cert) {
s->ssl_cert_len = strlen(s->ssl_cert);
}
/* Servlet 2.3 API */
s->ssl_cipher =
(char *)ap_table_get(r->subprocess_env,
workerEnv->cipher_indicator);
s->ssl_session =
(char *)ap_table_get(r->subprocess_env,
workerEnv->session_indicator);
if (workerEnv->options & JK_OPT_FWDKEYSIZE) {
/* Servlet 2.3 API */
ssl_temp = (char *)ap_table_get(r->subprocess_env,
workerEnv->
key_size_indicator);
if (ssl_temp)
s->ssl_key_size = atoi(ssl_temp);
}
}
}
jk2_map_default_create(env, &s->attributes, s->pool);
if (workerEnv->envvars_in_use) {
int envCnt = workerEnv->envvars->size(env, workerEnv->envvars);
int i;
for (i = 0; i < envCnt; i++) {
char *name =
workerEnv->envvars->nameAt(env, workerEnv->envvars, i);
char *val = (char *)ap_table_get(r->subprocess_env, 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 (r->headers_in && ap_table_elts(r->headers_in)) {
const array_header *t = ap_table_elts(r->headers_in);
if (t && t->nelts) {
int i;
table_entry *elts = (table_entry *) t->elts;
for (i = 0; i < t->nelts; i++) {
s->headers_in->add(env, s->headers_in,
elts[i].key, elts[i].val);
}
}
}
if (!s->is_chunked && s->content_length == 0) {
/* XXX 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
*
* XXX Is it the right thing to do ????? Why spend the
* bandwith, the servlet decided not to read the POST then
* jk shouldn't do it instead, and the user should get the
* error message !
*/
static void JK_METHOD jk2_service_apache13_afterRequest(jk_env_t *env,
jk_ws_service_t *s)
{
if (s->content_read < s->content_length ||
(s->is_chunked && !s->no_more_chunks)) {
request_rec *r = s->ws_private;
char *buff = ap_palloc(r->pool, 2048);
if (buff != NULL) {
int rd;
while ((rd = ap_get_client_block(r, buff, 2048)) > 0) {
s->content_read += rd;
}
}
}
}
int jk2_service_apache13_init(jk_env_t *env, jk_ws_service_t *s)
{
if (s == NULL) {
return JK_ERR;
}
s->head = jk2_service_apache13_head;
s->read = jk2_service_apache13_read;
s->write = jk2_service_apache13_write;
s->init = jk2_init_ws_service;
s->afterRequest = jk2_service_apache13_afterRequest;
return JK_OK;
}