| /* |
| * 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. |
| */ |
| |
| /** |
| * Utils for processing various request components |
| * |
| * Author: Gal Shachor <shachor@il.ibm.com> |
| * Author: Henri Gomez <hgomez@apache.org> |
| * Author: Costin Manolache |
| */ |
| |
| /* XXX make them virtual methods, allow servers to override |
| */ |
| |
| #include "jk_global.h" |
| #include "jk_channel.h" |
| #include "jk_env.h" |
| #include "jk_requtil.h" |
| |
| #ifdef AS400 |
| #include "util_ebcdic.h" |
| #endif |
| |
| #define CHUNK_BUFFER_PAD (12) |
| |
| static const char *response_trans_headers[] = { |
| "Content-Type", |
| "Content-Language", |
| "Content-Length", |
| "Date", |
| "Last-Modified", |
| "Location", |
| "Set-Cookie", |
| "Set-Cookie2", |
| "Servlet-Engine", |
| "Status", |
| "WWW-Authenticate" |
| }; |
| |
| /* |
| * Conditional request attributes |
| * |
| */ |
| #define SC_A_CONTEXT (unsigned char)1 |
| #define SC_A_SERVLET_PATH (unsigned char)2 |
| #define SC_A_REMOTE_USER (unsigned char)3 |
| #define SC_A_AUTH_TYPE (unsigned char)4 |
| #define SC_A_QUERY_STRING (unsigned char)5 |
| #define SC_A_JVM_ROUTE (unsigned char)6 |
| #define SC_A_SSL_CERT (unsigned char)7 |
| #define SC_A_SSL_CIPHER (unsigned char)8 |
| #define SC_A_SSL_SESSION (unsigned char)9 |
| #define SC_A_REQ_ATTRIBUTE (unsigned char)10 |
| /* only in if JkOptions +ForwardKeySize */ |
| #define SC_A_SSL_KEY_SIZE (unsigned char)11 |
| #define SC_A_SECRET (unsigned char)12 |
| #define SC_A_STORED_METHOD (unsigned char)13 |
| #define SC_A_ARE_DONE (unsigned char)0xFF |
| |
| /* |
| * Forward a request from the web server to the servlet container. |
| */ |
| #define JK_AJP13_FORWARD_REQUEST (unsigned char)2 |
| |
| /* Important: ajp13 protocol has the strange habit of sending |
| a second ( untyped ) message imediately following the request, |
| with a first chunk of POST body. This is nice for small post |
| requests, since it avoids a roundtrip, but it's horrible |
| because it brakes the model. So we'll have to remember this |
| as an exception to the rule as long as ajp13 is alive |
| */ |
| |
| |
| /** Get header value using a lookup table. |
| * |
| * |
| * long_res_header_for_sc |
| */ |
| const char *jk2_requtil_getHeaderById(jk_env_t *env, int sc) |
| { |
| const char *rc = NULL; |
| if (sc <= SC_RES_HEADERS_NUM && sc > 0) { |
| rc = response_trans_headers[sc - 1]; |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * Get method id. |
| * |
| * sc_for_req_method |
| */ |
| int jk2_requtil_getMethodId(jk_env_t *env, const char *method, |
| unsigned char *sc) |
| { |
| int rc = JK_OK; |
| if (0 == strcmp(method, "GET")) { |
| *sc = SC_M_GET; |
| } |
| else if (0 == strcmp(method, "POST")) { |
| *sc = SC_M_POST; |
| } |
| else if (0 == strcmp(method, "HEAD")) { |
| *sc = SC_M_HEAD; |
| } |
| else if (0 == strcmp(method, "PUT")) { |
| *sc = SC_M_PUT; |
| } |
| else if (0 == strcmp(method, "DELETE")) { |
| *sc = SC_M_DELETE; |
| } |
| else if (0 == strcmp(method, "OPTIONS")) { |
| *sc = SC_M_OPTIONS; |
| } |
| else if (0 == strcmp(method, "TRACE")) { |
| *sc = SC_M_TRACE; |
| } |
| else if (0 == strcmp(method, "PROPFIND")) { |
| *sc = SC_M_PROPFIND; |
| } |
| else if (0 == strcmp(method, "PROPPATCH")) { |
| *sc = SC_M_PROPPATCH; |
| } |
| else if (0 == strcmp(method, "MKCOL")) { |
| *sc = SC_M_MKCOL; |
| } |
| else if (0 == strcmp(method, "COPY")) { |
| *sc = SC_M_COPY; |
| } |
| else if (0 == strcmp(method, "MOVE")) { |
| *sc = SC_M_MOVE; |
| } |
| else if (0 == strcmp(method, "LOCK")) { |
| *sc = SC_M_LOCK; |
| } |
| else if (0 == strcmp(method, "UNLOCK")) { |
| *sc = SC_M_UNLOCK; |
| } |
| else if (0 == strcmp(method, "ACL")) { |
| *sc = SC_M_ACL; |
| } |
| else if (0 == strcmp(method, "REPORT")) { |
| *sc = SC_M_REPORT; |
| } |
| else if (0 == strcmp(method, "VERSION-CONTROL")) { |
| *sc = SC_M_VERSION_CONTROL; |
| } |
| else if (0 == strcmp(method, "CHECKIN")) { |
| *sc = SC_M_CHECKIN; |
| } |
| else if (0 == strcmp(method, "CHECKOUT")) { |
| *sc = SC_M_CHECKOUT; |
| } |
| else if (0 == strcmp(method, "UNCHECKOUT")) { |
| *sc = SC_M_UNCHECKOUT; |
| } |
| else if (0 == strcmp(method, "SEARCH")) { |
| *sc = SC_M_SEARCH; |
| } |
| else if (0 == strcmp(method, "MKWORKSPACE")) { |
| *sc = SC_M_MKWORKSPACE; |
| } |
| else if (0 == strcmp(method, "UPDATE")) { |
| *sc = SC_M_UPDATE; |
| } |
| else if (0 == strcmp(method, "LABEL")) { |
| *sc = SC_M_LABEL; |
| } |
| else if (0 == strcmp(method, "MERGE")) { |
| *sc = SC_M_MERGE; |
| } |
| else if (0 == strcmp(method, "BASELINE-CONTROL")) { |
| *sc = SC_M_BASELINE_CONTROL; |
| } |
| else if (0 == strcmp(method, "MKACTIVITY")) { |
| *sc = SC_M_MKACTIVITY; |
| } |
| else { |
| *sc = SC_M_JK_STORED; |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * Get header id. |
| * |
| * sc_for_req_header |
| */ |
| int jk2_requtil_getHeaderId(jk_env_t *env, const char *header_name, |
| unsigned short *sc) |
| { |
| /* char lowerCased[30]; */ |
| |
| /* if( strlen( header_name ) > 30 ) */ |
| /* return JK_FALSE; */ |
| /* strncpy( lowerCased, header_name, 30 ); */ |
| |
| |
| switch (header_name[0]) { |
| case 'a': |
| case 'A': |
| if (strncasecmp(header_name, "accept", 6) == 0) { |
| if ('-' == header_name[6]) { |
| if (!strcasecmp(header_name + 7, "charset")) { |
| *sc = SC_ACCEPT_CHARSET; |
| } |
| else if (!strcasecmp(header_name + 7, "encoding")) { |
| *sc = SC_ACCEPT_ENCODING; |
| } |
| else if (!strcasecmp(header_name + 7, "language")) { |
| *sc = SC_ACCEPT_LANGUAGE; |
| } |
| else { |
| return JK_ERR; |
| } |
| } |
| else if ('\0' == header_name[6]) { |
| *sc = SC_ACCEPT; |
| } |
| else { |
| return JK_ERR; |
| } |
| } |
| else if (!strcasecmp(header_name, "authorization")) { |
| *sc = SC_AUTHORIZATION; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| case 'c': |
| case 'C': |
| if (!strcasecmp(header_name, "cookie")) { |
| *sc = SC_COOKIE; |
| } |
| else if (!strcasecmp(header_name, "connection")) { |
| *sc = SC_CONNECTION; |
| } |
| else if (!strcasecmp(header_name, "content-type")) { |
| *sc = SC_CONTENT_TYPE; |
| } |
| else if (!strcasecmp(header_name, "content-length")) { |
| *sc = SC_CONTENT_LENGTH; |
| } |
| else if (!strcasecmp(header_name, "cookie2")) { |
| *sc = SC_COOKIE2; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| case 'h': |
| case 'H': |
| if (!strcasecmp(header_name, "host")) { |
| *sc = SC_HOST; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| case 'p': |
| case 'P': |
| if (!strcasecmp(header_name, "pragma")) { |
| *sc = SC_PRAGMA; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| case 'r': |
| case 'R': |
| if (!strcasecmp(header_name, "referer")) { |
| *sc = SC_REFERER; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| case 'u': |
| case 'U': |
| if (!strcasecmp(header_name, "user-agent")) { |
| *sc = SC_USER_AGENT; |
| } |
| else { |
| return JK_ERR; |
| } |
| break; |
| |
| default: |
| /* env->l->jkLog(env, env->l, JK_LOG_INFO, */ |
| /* "requtil.getHeaderId() long header %s\n", header_name); */ |
| |
| return JK_ERR; |
| } |
| return JK_OK; |
| } |
| |
| /** Retrieve the cookie with the given name |
| */ |
| char *jk2_requtil_getCookieByName(jk_env_t *env, jk_ws_service_t *s, |
| const char *name) |
| { |
| int i; |
| jk_map_t *headers = s->headers_in; |
| |
| /* XXX use 'get' - and make sure jk_map has support for |
| case insensitive search */ |
| for (i = 0; i < headers->size(NULL, headers); i++) { |
| if (0 == strcasecmp(headers->nameAt(NULL, headers, i), "cookie")) { |
| |
| char *id_start; |
| for (id_start = strstr(headers->valueAt(NULL, headers, i), name); |
| id_start; id_start = strstr(id_start + 1, name)) { |
| if ('=' == id_start[strlen(name)]) { |
| /* |
| * Session cookie was found, get it's value |
| */ |
| id_start += (1 + strlen(name)); |
| if (strlen(id_start)) { |
| char *id_end; |
| id_start = s->pool->pstrdup(env, s->pool, id_start); |
| if (id_end = strchr(id_start, ';')) { |
| *id_end = '\0'; |
| } |
| return id_start; |
| } |
| } |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* Retrieve the parameter with the given name |
| */ |
| char *jk2_requtil_getPathParam(jk_env_t *env, jk_ws_service_t *s, |
| const char *name) |
| { |
| char *id_start = NULL; |
| for (id_start = strstr(s->req_uri, name); |
| id_start; id_start = strstr(id_start + 1, name)) { |
| if ('=' == id_start[strlen(name)]) { |
| /* |
| * Session path-cookie was found, get it's value |
| */ |
| id_start += (1 + strlen(name)); |
| if (strlen(id_start)) { |
| char *id_end; |
| id_start = s->pool->pstrdup(env, s->pool, id_start); |
| /* |
| * The query string is not part of req_uri, however |
| * to be on the safe side lets remove the trailing query |
| * string if appended... |
| */ |
| if (id_end = strchr(id_start, '?')) { |
| *id_end = '\0'; |
| } |
| return id_start; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /** Retrieve session id from the cookie or the parameter |
| * (parameter first) |
| */ |
| char *jk2_requtil_getSessionId(jk_env_t *env, jk_ws_service_t *s) |
| { |
| char *val; |
| val = jk2_requtil_getPathParam(env, s, JK_PATH_SESSION_IDENTIFIER); |
| if (!val) { |
| val = jk2_requtil_getCookieByName(env, s, JK_SESSION_IDENTIFIER); |
| } |
| return val; |
| } |
| |
| /** Extract the 'route' from the session id. The route is |
| * the id of the worker that generated the session and where all |
| * further requests in that session will be sent. |
| */ |
| char *jk2_requtil_getSessionRoute(jk_env_t *env, jk_ws_service_t *s) |
| { |
| char *sessionid = jk2_requtil_getSessionId(env, s); |
| char *ch; |
| |
| if (!sessionid) { |
| return NULL; |
| } |
| |
| /* |
| * Balance parameter is appended to the end |
| */ |
| ch = strrchr(sessionid, '.'); |
| if (!ch) { |
| return 0; |
| } |
| ch++; |
| if (*ch == '\0') { |
| return NULL; |
| } |
| return ch; |
| } |
| |
| /* |
| * Read data from the web server. |
| * |
| * Socket API didn't garanty all the data will be kept in a single |
| * read, so we must loop up to all awaited data are received |
| */ |
| int jk2_requtil_readFully(jk_env_t *env, jk_ws_service_t *s, |
| unsigned char *buf, unsigned len) |
| { |
| unsigned rdlen = 0; |
| unsigned padded_len = len; |
| |
| if (s->is_chunked && s->no_more_chunks) { |
| return 0; |
| } |
| if (s->is_chunked) { |
| /* Corner case: buf must be large enough to hold next |
| * chunk size (if we're on or near a chunk border). |
| * Pad the length to a reasonable value, otherwise the |
| * read fails and the remaining chunks are tossed. |
| */ |
| padded_len = (len < CHUNK_BUFFER_PAD) ? len : len - CHUNK_BUFFER_PAD; |
| } |
| |
| while (rdlen < padded_len) { |
| unsigned this_time = 0; |
| if (s->read(env, s, buf + rdlen, len - rdlen, &this_time)) { |
| return -1; |
| } |
| |
| if (0 == this_time) { |
| if (s->is_chunked) { |
| s->no_more_chunks = 1; /* read no more */ |
| } |
| break; |
| } |
| rdlen += this_time; |
| } |
| |
| return (int)rdlen; |
| } |
| |
| |
| /* -------------------- Printf writing -------------------- */ |
| |
| #define JK_BUF_SIZE 4096 |
| |
| static int jk2_requtil_createBuffer(jk_env_t *env, jk_ws_service_t *s) |
| { |
| int bsize = JK_BUF_SIZE; |
| |
| s->outSize = bsize; |
| s->outBuf = (char *)s->pool->alloc(env, s->pool, bsize); |
| |
| return JK_OK; |
| } |
| |
| static void jk2_requtil_printf(jk_env_t *env, jk_ws_service_t *s, char *fmt, |
| ...) |
| { |
| va_list vargs; |
| int ret = 0; |
| |
| if (s->outBuf == NULL) { |
| jk2_requtil_createBuffer(env, s); |
| } |
| |
| va_start(vargs, fmt); |
| s->outPos = 0; /* Temp - we don't buffer */ |
| ret = |
| apr_vsnprintf(s->outBuf + s->outPos, s->outSize - s->outPos, fmt, vargs); |
| va_end(vargs); |
| |
| s->write(env, s, s->outBuf, strlen(s->outBuf)); |
| } |
| |
| /* -------------------- Request serialization -------------------- */ |
| /* XXX optimization - this can be overriden by server to avoid |
| multiple copies |
| */ |
| /** |
| Message structure |
| |
| |
| AJPV13_REQUEST/AJPV14_REQUEST= |
| request_prefix (1) (byte) |
| method (byte) |
| protocol (string) |
| req_uri (string) |
| remote_addr (string) |
| remote_host (string) |
| server_name (string) |
| server_port (short) |
| is_ssl (boolean) |
| num_headers (short) |
| num_headers*(req_header_name header_value) |
| |
| ?context (byte)(string) |
| ?servlet_path (byte)(string) |
| ?remote_user (byte)(string) |
| ?auth_type (byte)(string) |
| ?query_string (byte)(string) |
| ?jvm_route (byte)(string) |
| ?ssl_cert (byte)(string) |
| ?ssl_cipher (byte)(string) |
| ?ssl_session (byte)(string) |
| ?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize |
| request_terminator (byte) |
| ?body content_length*(var binary) |
| |
| Was: ajp_marshal_into_msgb |
| */ |
| int jk2_serialize_request13(jk_env_t *env, jk_msg_t *msg, |
| jk_ws_service_t *s, jk_endpoint_t *ae) |
| { |
| unsigned char method; |
| int i; |
| int headerCount; |
| int rc; |
| int debug = 0; |
| |
| if (s->uriEnv != NULL) { |
| debug = s->uriEnv->mbean->debug; |
| } |
| |
| rc = jk2_requtil_getMethodId(env, s->method, &method); |
| if (rc != JK_OK) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "Error ajp_marshal_into_msgb - method %s\n", |
| s->method); |
| return JK_ERR; |
| } |
| |
| headerCount = s->headers_in->size(env, s->headers_in); |
| |
| if (msg->appendByte(env, msg, JK_AJP13_FORWARD_REQUEST) || |
| msg->appendByte(env, msg, method) || |
| msg->appendString(env, msg, s->protocol) || |
| msg->appendString(env, msg, s->req_uri) || |
| msg->appendString(env, msg, s->remote_addr) || |
| msg->appendString(env, msg, s->remote_host) || |
| msg->appendString(env, msg, s->server_name) || |
| msg->appendInt(env, msg, (unsigned short)s->server_port) || |
| msg->appendByte(env, msg, (unsigned char)(s->is_ssl)) || |
| msg->appendInt(env, msg, (unsigned short)(headerCount))) { |
| |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing the message head\n"); |
| return JK_ERR; |
| } |
| |
| for (i = 0; i < headerCount; i++) { |
| unsigned short sc; |
| |
| char *name = s->headers_in->nameAt(env, s->headers_in, i); |
| |
| if (jk2_requtil_getHeaderId(env, name, &sc) == JK_OK) { |
| /* env->l->jkLog(env, env->l, JK_LOG_INFO, */ |
| /* "serialize.request() Add headerId %s %d\n", name, sc); */ |
| if (msg->appendInt(env, msg, sc)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "serialize.request() Error serializing header id\n"); |
| return JK_ERR; |
| } |
| } |
| else { |
| if (debug > 0) |
| env->l->jkLog(env, env->l, JK_LOG_DEBUG, |
| "serialize.request() Add headerName %s\n", |
| name); |
| if (msg->appendString(env, msg, name)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "serialize.request() Error serializing header name\n"); |
| return JK_ERR; |
| } |
| } |
| |
| if (msg->appendString(env, msg, |
| s->headers_in->valueAt(env, s->headers_in, |
| i))) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "serialize.request() Error serializing header value\n"); |
| return JK_ERR; |
| } |
| } |
| |
| if (s->remote_user) { |
| if (msg->appendByte(env, msg, SC_A_REMOTE_USER) || |
| msg->appendString(env, msg, s->remote_user)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "serialize.request() Error serializing user name\n"); |
| return JK_ERR; |
| } |
| } |
| if (s->auth_type) { |
| if (msg->appendByte(env, msg, SC_A_AUTH_TYPE) || |
| msg->appendString(env, msg, s->auth_type)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing auth type\n"); |
| return JK_ERR; |
| } |
| } |
| if (s->query_string) { |
| if (msg->appendByte(env, msg, SC_A_QUERY_STRING) || |
| #ifdef AS400 |
| msg->appendAsciiString(env, msg, s->query_string)) { |
| #else |
| msg->appendString(env, msg, s->query_string)) { |
| #endif |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing query string\n"); |
| return JK_ERR; |
| } |
| } |
| /* XXX This can be sent only on startup ( ajp14 ) */ |
| |
| if (s->jvm_route) { |
| if (msg->appendByte(env, msg, SC_A_JVM_ROUTE) || |
| msg->appendString(env, msg, s->jvm_route)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing worker id\n"); |
| return JK_ERR; |
| } |
| } |
| |
| if (s->ssl_cert_len) { |
| if (msg->appendByte(env, msg, SC_A_SSL_CERT) || |
| msg->appendString(env, msg, s->ssl_cert)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing SSL cert\n"); |
| return JK_ERR; |
| } |
| } |
| |
| if (s->ssl_cipher) { |
| if (msg->appendByte(env, msg, SC_A_SSL_CIPHER) || |
| msg->appendString(env, msg, s->ssl_cipher)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing SSL cipher\n"); |
| return JK_ERR; |
| } |
| } |
| if (s->ssl_session) { |
| if (msg->appendByte(env, msg, SC_A_SSL_SESSION) || |
| msg->appendString(env, msg, s->ssl_session)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing SSL session\n"); |
| return JK_ERR; |
| } |
| } |
| |
| /* |
| * ssl_key_size is required by Servlet 2.3 API |
| * added support only in ajp14 mode |
| * JFC removed: ae->proto == AJP14_PROTO |
| */ |
| if (s->ssl_key_size != -1) { |
| if (msg->appendByte(env, msg, SC_A_SSL_KEY_SIZE) || |
| msg->appendInt(env, msg, (unsigned short)s->ssl_key_size)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing SSL key size\n"); |
| return JK_ERR; |
| } |
| } |
| |
| if (ae->worker->secret) { |
| if (msg->appendByte(env, msg, SC_A_SECRET) || |
| msg->appendString(env, msg, ae->worker->secret)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing secret\n"); |
| return JK_ERR; |
| } |
| } |
| |
| /* If the method was unrecognized, encode it as an attribute */ |
| if (method == SC_M_JK_STORED) { |
| if (msg->appendByte(env, msg, SC_A_STORED_METHOD) || |
| msg->appendString(env, msg, s->method)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error encoding method %s\n", |
| s->method); |
| } |
| } |
| |
| if (s->attributes->size(env, s->attributes) > 0) { |
| for (i = 0; i < s->attributes->size(env, s->attributes); i++) { |
| char *name = s->attributes->nameAt(env, s->attributes, i); |
| char *val = s->attributes->valueAt(env, s->attributes, i); |
| if (msg->appendByte(env, msg, SC_A_REQ_ATTRIBUTE) || |
| msg->appendString(env, msg, name) || |
| msg->appendString(env, msg, val)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing attribute %s=%s\n", |
| name, val); |
| return JK_ERR; |
| } |
| } |
| } |
| |
| if (msg->appendByte(env, msg, SC_A_ARE_DONE)) { |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handle.request() Error serializing end marker\n"); |
| return JK_ERR; |
| } |
| |
| |
| if (debug > 0) |
| env->l->jkLog(env, env->l, JK_LOG_DEBUG, |
| "serialize.request() serialized %s\n", s->req_uri); |
| |
| /* msg->dump( env, msg, "Dump: " ); */ |
| return JK_OK; |
| } |
| |
| |
| /** The inital BODY chunk |
| */ |
| int jk2_serialize_postHead(jk_env_t *env, jk_msg_t *msg, |
| jk_ws_service_t *r, jk_endpoint_t *ae) |
| { |
| int len = r->left_bytes_to_send; |
| |
| if (len > AJP13_MAX_SEND_BODY_SZ) { |
| len = AJP13_MAX_SEND_BODY_SZ; |
| } |
| if (len <= 0) { |
| len = 0; |
| return JK_OK; |
| } |
| |
| len = msg->appendFromServer(env, msg, r, ae, len); |
| /* the right place to add file storage for upload */ |
| if (len >= 0) { |
| r->content_read += len; |
| return JK_OK; |
| } |
| |
| env->l->jkLog(env, env->l, JK_LOG_ERROR, |
| "handler.marshalPostHead() - error len=%d\n", len); |
| return JK_ERR; |
| } |
| |
| /* -------------------- Query decoding -------------------- */ |
| |
| /** Read a query string into the map |
| */ |
| int jk2_requtil_queryRead(jk_env_t *env, jk_map_t *m, const char *query) |
| { |
| char *sep; |
| char *value; |
| char *qry = m->pool->pstrdup(env, m->pool, query); |
| |
| while (qry != NULL) { |
| sep = strchr(qry, '&'); |
| if (sep != NULL) { |
| *sep = '\0'; |
| sep++; |
| } |
| |
| value = strchr(qry, '='); |
| if (value == NULL) { |
| value = ""; |
| } |
| else { |
| *value = '\0'; |
| value++; |
| } |
| m->add(env, m, m->pool->pstrdup(env, m->pool, qry), |
| m->pool->pstrdup(env, m->pool, value)); |
| qry = sep; |
| } |
| return JK_OK; |
| } |
| |
| |
| /* -------------------- Request encoding -------------------- */ |
| /* Moved from IIS adapter */ |
| |
| #define T_OS_ESCAPE_PATH (4) |
| |
| static const unsigned char test_char_table[256] = { |
| 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 14, |
| 14, |
| 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 7, 6, 1, 6, 1, 1, |
| 9, 9, 1, 0, 8, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 15, |
| 15, 8, 15, 15, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 7, 0, 7, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 0, 0, 0, 15, 7, 15, 1, 14, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 |
| }; |
| |
| #define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f)) |
| |
| static const char c2x_table[] = "0123456789abcdef"; |
| |
| static unsigned char *c2x(unsigned what, unsigned char *where) |
| { |
| *where++ = '%'; |
| *where++ = c2x_table[what >> 4]; |
| *where++ = c2x_table[what & 0xf]; |
| return where; |
| } |
| |
| int jk_requtil_escapeUrl(const char *path, char *dest, int destsize) |
| { |
| const unsigned char *s = (const unsigned char *)path; |
| unsigned char *d = (unsigned char *)dest; |
| unsigned char *e = (unsigned char *)(dest + destsize - 1); |
| unsigned char *ee = (unsigned char *)(dest + destsize - 3); |
| unsigned c; |
| |
| while ((c = *s)) { |
| if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { |
| if (d >= ee) |
| return JK_ERR; |
| d = c2x(c, d); |
| } |
| else { |
| if (d >= e) |
| return JK_ERR; |
| *d++ = c; |
| } |
| ++s; |
| } |
| *d = '\0'; |
| return JK_OK; |
| } |
| |
| /* XXX Make it a default checking in uri worker map |
| */ |
| int jk_requtil_uriIsWebInf(char *uri) |
| { |
| char *c = uri; |
| while (*c) { |
| *c = tolower(*c); |
| c++; |
| } |
| if (strstr(uri, "web-inf")) { |
| return JK_TRUE; |
| } |
| if (strstr(uri, "meta-inf")) { |
| return JK_TRUE; |
| } |
| |
| return JK_FALSE; |
| } |
| |
| static char x2c(const char *what) |
| { |
| register char digit; |
| |
| digit = |
| ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); |
| digit *= 16; |
| digit += |
| (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); |
| return (digit); |
| } |
| |
| int jk_requtil_unescapeUrl(char *url) |
| { |
| register int x, y, badesc, badpath; |
| |
| badesc = 0; |
| badpath = 0; |
| for (x = 0, y = 0; url[y]; ++x, ++y) { |
| if (url[y] != '%') |
| url[x] = url[y]; |
| else { |
| if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) { |
| badesc = 1; |
| url[x] = '%'; |
| } |
| else { |
| url[x] = x2c(&url[y + 1]); |
| y += 2; |
| if (url[x] == '/' || url[x] == '\0') |
| badpath = 1; |
| } |
| } |
| } |
| url[x] = '\0'; |
| if (badesc) |
| return -1; |
| else if (badpath) |
| return -2; |
| else |
| return JK_OK; |
| } |
| |
| void jk_requtil_getParents(char *name) |
| { |
| int l, w; |
| |
| /* Four paseses, as per RFC 1808 */ |
| /* a) remove ./ path segments */ |
| |
| for (l = 0, w = 0; name[l] != '\0';) { |
| if (name[l] == '.' && name[l + 1] == '/' |
| && (l == 0 || name[l - 1] == '/')) |
| l += 2; |
| else |
| name[w++] = name[l++]; |
| } |
| |
| /* b) remove trailing . path, segment */ |
| if (w == 1 && name[0] == '.') |
| w--; |
| else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/') |
| w--; |
| name[w] = '\0'; |
| |
| /* c) remove all xx/../ segments. (including leading ../ and /../) */ |
| l = 0; |
| |
| while (name[l] != '\0') { |
| if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' && |
| (l == 0 || name[l - 1] == '/')) { |
| register int m = l + 3, n; |
| |
| l = l - 2; |
| if (l >= 0) { |
| while (l >= 0 && name[l] != '/') |
| l--; |
| l++; |
| } |
| else |
| l = 0; |
| n = l; |
| while ((name[n] = name[m])) |
| (++n, ++m); |
| } |
| else |
| ++l; |
| } |
| |
| /* d) remove trailing xx/.. segment. */ |
| if (l == 2 && name[0] == '.' && name[1] == '.') |
| name[0] = '\0'; |
| else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' |
| && name[l - 3] == '/') { |
| l = l - 4; |
| if (l >= 0) { |
| while (l >= 0 && name[l] != '/') |
| l--; |
| l++; |
| } |
| else |
| l = 0; |
| name[l] = '\0'; |
| } |
| } |
| |
| |
| |
| |
| static const char begin_cert[] = "-----BEGIN CERTIFICATE-----\r\n"; |
| |
| static const char end_cert[] = "-----END CERTIFICATE-----\r\n"; |
| |
| static const char basis_64[] = |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| |
| int jk_requtil_base64CertLen(int len) |
| { |
| int n = ((len + 2) / 3 * 4) + 1; /* base64 encoded size */ |
| n += (n + 63 / 64) * 2; /* add CRLF's */ |
| n += sizeof(begin_cert) + sizeof(end_cert) - 2; /* add enclosing strings. */ |
| return n; |
| } |
| |
| int jk_requtil_base64EncodeCert(char *encoded, |
| const unsigned char *string, int len) |
| { |
| int i, c; |
| char *p; |
| const char *t; |
| |
| p = encoded; |
| |
| t = begin_cert; |
| while (*t != '\0') |
| *p++ = *t++; |
| |
| c = 0; |
| for (i = 0; i < len - 2; i += 3) { |
| *p++ = basis_64[(string[i] >> 2) & 0x3F]; |
| *p++ = basis_64[((string[i] & 0x3) << 4) | |
| ((int)(string[i + 1] & 0xF0) >> 4)]; |
| *p++ = basis_64[((string[i + 1] & 0xF) << 2) | |
| ((int)(string[i + 2] & 0xC0) >> 6)]; |
| *p++ = basis_64[string[i + 2] & 0x3F]; |
| c += 4; |
| if (c >= 64) { |
| *p++ = '\r'; |
| *p++ = '\n'; |
| c = 0; |
| } |
| } |
| if (i < len) { |
| *p++ = basis_64[(string[i] >> 2) & 0x3F]; |
| if (i == (len - 1)) { |
| *p++ = basis_64[((string[i] & 0x3) << 4)]; |
| *p++ = '='; |
| } |
| else { |
| *p++ = basis_64[((string[i] & 0x3) << 4) | |
| ((int)(string[i + 1] & 0xF0) >> 4)]; |
| *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; |
| } |
| *p++ = '='; |
| c++; |
| } |
| if (c != 0) { |
| *p++ = '\r'; |
| *p++ = '\n'; |
| } |
| |
| t = end_cert; |
| while (*t != '\0') |
| *p++ = *t++; |
| |
| *p++ = '\0'; |
| return p - encoded; |
| } |
| |
| |
| |
| |
| /** Initialize the request |
| * |
| * jk_init_ws_service |
| */ |
| void jk2_requtil_initRequest(jk_env_t *env, jk_ws_service_t *s) |
| { |
| s->ws_private = NULL; |
| s->method = NULL; |
| s->protocol = NULL; |
| s->req_uri = NULL; |
| s->remote_addr = NULL; |
| s->remote_host = NULL; |
| s->remote_user = NULL; |
| s->auth_type = NULL; |
| s->query_string = NULL; |
| s->server_name = NULL; |
| s->server_port = 80; |
| s->server_software = NULL; |
| s->content_length = 0; |
| s->is_chunked = 0; |
| s->no_more_chunks = 0; |
| s->content_read = 0; |
| s->is_ssl = JK_FALSE; |
| s->ssl_cert = NULL; |
| s->ssl_cert_len = 0; |
| s->ssl_cipher = NULL; |
| s->ssl_session = NULL; |
| s->jvm_route = NULL; |
| s->uriEnv = NULL; |
| s->outBuf = NULL; |
| s->msg = NULL; |
| |
| s->jkprintf = jk2_requtil_printf; |
| } |