summaryrefslogtreecommitdiffstats
path: root/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c')
-rw-r--r--rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c3383
1 files changed, 0 insertions, 3383 deletions
diff --git a/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c b/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c
deleted file mode 100644
index 7b083264..00000000
--- a/rubbos/app/tomcat-connectors-1.2.32-src/native/common/jk_ajp_common.c
+++ /dev/null
@@ -1,3383 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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: common stuff for bi-directional protocols ajp13/ajp14. *
- * Author: Gal Shachor <shachor@il.ibm.com> *
- * Author: Henri Gomez <hgomez@apache.org> *
- * Version: $Revision: 1137200 $ *
- ***************************************************************************/
-
-
-#include "jk_global.h"
-#include "jk_util.h"
-#include "jk_ajp13.h"
-#include "jk_ajp14.h"
-#include "jk_ajp_common.h"
-#include "jk_connect.h"
-#if defined(AS400) && !defined(AS400_UTF8)
-#include "util_ebcdic.h"
-#endif
-#if defined(NETWARE) && defined(__NOVELL_LIBC__)
-#include "novsock2.h"
-#endif
-
-const char *response_trans_headers[] = {
- "Content-Type",
- "Content-Language",
- "Content-Length",
- "Date",
- "Last-Modified",
- "Location",
- "Set-Cookie",
- "Set-Cookie2",
- "Servlet-Engine",
- "Status",
- "WWW-Authenticate"
-};
-
-static const char *long_res_header_for_sc(int sc)
-{
- const char *rc = NULL;
- sc = sc & 0X00FF;
- if (sc <= SC_RES_HEADERS_NUM && sc > 0) {
- rc = response_trans_headers[sc - 1];
- }
-
- return rc;
-}
-
-static const char *ajp_state_type[] = {
- JK_AJP_STATE_TEXT_IDLE,
- JK_AJP_STATE_TEXT_OK,
- JK_AJP_STATE_TEXT_ERROR,
- JK_AJP_STATE_TEXT_PROBE,
- "unknown",
- NULL
-};
-
-#define UNKNOWN_METHOD (-1)
-
-static int sc_for_req_method(const char *method, size_t len)
-{
- /* Note: the following code was generated by the "shilka" tool from
- the "cocom" parsing/compilation toolkit. It is an optimized lookup
- based on analysis of the input keywords. Postprocessing was done
- on the shilka output, but the basic structure and analysis is
- from there. Should new HTTP methods be added, then manual insertion
- into this code is fine, or simply re-running the shilka tool on
- the appropriate input. */
-
- /* Note: it is also quite reasonable to just use our method_registry,
- but I'm assuming (probably incorrectly) we want more speed here
- (based on the optimizations the previous code was doing). */
-
- switch (len)
- {
- case 3:
- switch (method[0])
- {
- case 'A':
- return (method[1] == 'C'
- && method[2] == 'L'
- ? SC_M_ACL : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'U'
- && method[2] == 'T'
- ? SC_M_PUT : UNKNOWN_METHOD);
- case 'G':
- return (method[1] == 'E'
- && method[2] == 'T'
- ? SC_M_GET : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 4:
- switch (method[0])
- {
- case 'H':
- return (method[1] == 'E'
- && method[2] == 'A'
- && method[3] == 'D'
- ? SC_M_HEAD : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'O'
- && method[2] == 'S'
- && method[3] == 'T'
- ? SC_M_POST : UNKNOWN_METHOD);
- case 'M':
- return (method[1] == 'O'
- && method[2] == 'V'
- && method[3] == 'E'
- ? SC_M_MOVE : UNKNOWN_METHOD);
- case 'L':
- return (method[1] == 'O'
- && method[2] == 'C'
- && method[3] == 'K'
- ? SC_M_LOCK : UNKNOWN_METHOD);
- case 'C':
- return (method[1] == 'O'
- && method[2] == 'P'
- && method[3] == 'Y'
- ? SC_M_COPY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 5:
- switch (method[2])
- {
- case 'R':
- return (memcmp(method, "MERGE", 5) == 0
- ? SC_M_MERGE : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "MKCOL", 5) == 0
- ? SC_M_MKCOL : UNKNOWN_METHOD);
- case 'B':
- return (memcmp(method, "LABEL", 5) == 0
- ? SC_M_LABEL : UNKNOWN_METHOD);
- case 'A':
- return (memcmp(method, "TRACE", 5) == 0
- ? SC_M_TRACE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 6:
- switch (method[0])
- {
- case 'U':
- switch (method[5])
- {
- case 'K':
- return (memcmp(method, "UNLOCK", 6) == 0
- ? SC_M_UNLOCK : UNKNOWN_METHOD);
- case 'E':
- return (memcmp(method, "UPDATE", 6) == 0
- ? SC_M_UPDATE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
- case 'R':
- return (memcmp(method, "REPORT", 6) == 0
- ? SC_M_REPORT : UNKNOWN_METHOD);
- case 'S':
- return (memcmp(method, "SEARCH", 6) == 0
- ? SC_M_SEARCH : UNKNOWN_METHOD);
- case 'D':
- return (memcmp(method, "DELETE", 6) == 0
- ? SC_M_DELETE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 7:
- switch (method[1])
- {
- case 'P':
- return (memcmp(method, "OPTIONS", 7) == 0
- ? SC_M_OPTIONS : UNKNOWN_METHOD);
- case 'H':
- return (memcmp(method, "CHECKIN", 7) == 0
- ? SC_M_CHECKIN : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 8:
- switch (method[0])
- {
- case 'P':
- return (memcmp(method, "PROPFIND", 8) == 0
- ? SC_M_PROPFIND : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "CHECKOUT", 8) == 0
- ? SC_M_CHECKOUT : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 9:
- return (memcmp(method, "PROPPATCH", 9) == 0
- ? SC_M_PROPPATCH : UNKNOWN_METHOD);
-
- case 10:
- switch (method[0])
- {
- case 'U':
- return (memcmp(method, "UNCHECKOUT", 10) == 0
- ? SC_M_UNCHECKOUT : UNKNOWN_METHOD);
- case 'M':
- return (memcmp(method, "MKACTIVITY", 10) == 0
- ? SC_M_MKACTIVITY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 11:
- return (memcmp(method, "MKWORKSPACE", 11) == 0
- ? SC_M_MKWORKSPACE : UNKNOWN_METHOD);
-
- case 15:
- return (memcmp(method, "VERSION-CONTROL", 15) == 0
- ? SC_M_VERSION_CONTROL : UNKNOWN_METHOD);
-
- case 16:
- return (memcmp(method, "BASELINE-CONTROL", 16) == 0
- ? SC_M_BASELINE_CONTROL : UNKNOWN_METHOD);
-
- default:
- return UNKNOWN_METHOD;
- }
-
- /* NOTREACHED */
-}
-
-static int sc_for_req_header(const char *header_name)
-{
- char header[16];
- size_t len = strlen(header_name);
- const char *p = header_name;
- int i = 0;
-
- /* ACCEPT-LANGUAGE is the longest header
- * that is of interest.
- */
- if (len < 4 || len > 15)
- return UNKNOWN_METHOD;
-
- while (*p) {
- header[i++] = toupper((unsigned char)*p);
- p++;
- }
-
- header[i] = '\0';
- p = &header[1];
-
-/* Always do memcmp including the final \0-termination character.
- */
- switch (header[0]) {
- case 'A':
- if (memcmp(p, "CCEPT", 6) == 0) {
- if (!header[6])
- return SC_ACCEPT;
- else if (header[6] == '-') {
- p += 6;
- if (memcmp(p, "CHARSET", 8) == 0)
- return SC_ACCEPT_CHARSET;
- else if (memcmp(p, "ENCODING", 9) == 0)
- return SC_ACCEPT_ENCODING;
- else if (memcmp(p, "LANGUAGE", 9) == 0)
- return SC_ACCEPT_LANGUAGE;
- else
- return UNKNOWN_METHOD;
- }
- else
- return UNKNOWN_METHOD;
- }
- else if (memcmp(p, "UTHORIZATION", 13) == 0)
- return SC_AUTHORIZATION;
- else
- return UNKNOWN_METHOD;
- break;
- case 'C':
- if(memcmp(p, "OOKIE2", 7) == 0)
- return SC_COOKIE2;
- else if (memcmp(p, "OOKIE", 6) == 0)
- return SC_COOKIE;
- else if(memcmp(p, "ONNECTION", 10) == 0)
- return SC_CONNECTION;
- else if(memcmp(p, "ONTENT-TYPE", 12) == 0)
- return SC_CONTENT_TYPE;
- else if(memcmp(p, "ONTENT-LENGTH", 14) == 0)
- return SC_CONTENT_LENGTH;
- else
- return UNKNOWN_METHOD;
- break;
- case 'H':
- if(memcmp(p, "OST", 4) == 0)
- return SC_HOST;
- else
- return UNKNOWN_METHOD;
- break;
- case 'P':
- if(memcmp(p, "RAGMA", 6) == 0)
- return SC_PRAGMA;
- else
- return UNKNOWN_METHOD;
- break;
- case 'R':
- if(memcmp(p, "EFERER", 7) == 0)
- return SC_REFERER;
- else
- return UNKNOWN_METHOD;
- break;
- case 'U':
- if(memcmp(p, "SER-AGENT", 10) == 0)
- return SC_USER_AGENT;
- else
- return UNKNOWN_METHOD;
- break;
- default:
- return UNKNOWN_METHOD;
- }
- /* NOTREACHED */
-}
-
-/* Return the string representation of the worker state */
-const char *jk_ajp_get_state(ajp_worker_t *aw, jk_logger_t *l)
-{
- return ajp_state_type[aw->s->state];
-}
-
-/* Return the int representation of the worker state */
-int jk_ajp_get_state_code(const char *v)
-{
- if (!v)
- return JK_AJP_STATE_DEF;
- else if (*v == 'i' || *v == 'I' || *v == 'n' || *v == 'N' || *v == '0')
- return JK_AJP_STATE_IDLE;
- else if (*v == 'o' || *v == 'O' || *v == '1')
- return JK_AJP_STATE_OK;
- else if (*v == 'e' || *v == 'E' || *v == '4')
- return JK_AJP_STATE_ERROR;
- else if (*v == 'p' || *v == 'P' || *v == '6')
- return JK_AJP_STATE_PROBE;
- else
- return JK_AJP_STATE_DEF;
-}
-
-int jk_ajp_get_cping_mode(const char *m, int def)
-{
- int mv = def;
- if (!m)
- return mv;
- while (*m != '\0') {
- if (*m == 'C' || *m == 'c')
- mv |= AJP_CPING_CONNECT;
- else if (*m == 'P' || *m == 'p')
- mv |= AJP_CPING_PREPOST;
- else if (*m == 'I' || *m == 'i')
- mv |= AJP_CPING_INTERVAL;
- else if (*m == 'A' || *m == 'a') {
- mv = AJP_CPING_CONNECT | AJP_CPING_PREPOST | AJP_CPING_INTERVAL;
- break;
- }
- m++;
- }
- return mv;
-}
-
-/*
- * 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)
- ?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)
-
- */
-
-static int ajp_marshal_into_msgb(jk_msg_buf_t *msg,
- jk_ws_service_t *s,
- jk_logger_t *l, ajp_endpoint_t * ae)
-{
- int method;
- unsigned int i;
-
- JK_TRACE_ENTER(l);
-
- if ((method = sc_for_req_method(s->method,
- strlen(s->method))) == UNKNOWN_METHOD)
- method = SC_M_JK_STORED;
-
- if (jk_b_append_byte(msg, JK_AJP13_FORWARD_REQUEST) ||
- jk_b_append_byte(msg, (unsigned char)method) ||
- jk_b_append_string(msg, s->protocol) ||
- jk_b_append_string(msg, s->req_uri) ||
- jk_b_append_string(msg, s->remote_addr) ||
- jk_b_append_string(msg, s->remote_host) ||
- jk_b_append_string(msg, s->server_name) ||
- jk_b_append_int(msg, (unsigned short)s->server_port) ||
- jk_b_append_byte(msg, (unsigned char)(s->is_ssl)) ||
- jk_b_append_int(msg, (unsigned short)(s->num_headers))) {
-
- jk_log(l, JK_LOG_ERROR,
- "failed appending the message begining");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- for (i = 0; i < s->num_headers; i++) {
- int sc;
-
- if ((sc = sc_for_req_header(s->headers_names[i])) != UNKNOWN_METHOD) {
- if (jk_b_append_int(msg, (unsigned short)sc)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the header name");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- else {
- if (jk_b_append_string(msg, s->headers_names[i])) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the header name");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- if (jk_b_append_string(msg, s->headers_values[i])) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the header value");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- if (s->secret) {
- if (jk_b_append_byte(msg, SC_A_SECRET) ||
- jk_b_append_string(msg, s->secret)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending secret");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- if (s->remote_user) {
- if (jk_b_append_byte(msg, SC_A_REMOTE_USER) ||
- jk_b_append_string(msg, s->remote_user)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the remote user");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- if (s->auth_type) {
- if (jk_b_append_byte(msg, SC_A_AUTH_TYPE) ||
- jk_b_append_string(msg, s->auth_type)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the auth type");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- if (s->query_string) {
- if (jk_b_append_byte(msg, SC_A_QUERY_STRING) ||
-#if defined(AS400) && !defined(AS400_UTF8)
- jk_b_append_asciistring(msg, s->query_string)) {
-#else
- jk_b_append_string(msg, s->query_string)) {
-#endif
- jk_log(l, JK_LOG_ERROR,
- "failed appending the query string");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- if (s->route) {
- if (jk_b_append_byte(msg, SC_A_ROUTE) ||
- jk_b_append_string(msg, s->route)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the route");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- if (s->ssl_cert_len) {
- if (jk_b_append_byte(msg, SC_A_SSL_CERT) ||
- jk_b_append_string(msg, s->ssl_cert)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the SSL certificates");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- if (s->ssl_cipher) {
- if (jk_b_append_byte(msg, SC_A_SSL_CIPHER) ||
- jk_b_append_string(msg, s->ssl_cipher)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the SSL ciphers");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- if (s->ssl_session) {
- if (jk_b_append_byte(msg, SC_A_SSL_SESSION) ||
- jk_b_append_string(msg, s->ssl_session)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the SSL session");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- /*
- * 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 (jk_b_append_byte(msg, SC_A_SSL_KEY_SIZE) ||
- jk_b_append_int(msg, (unsigned short)s->ssl_key_size)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the SSL key size");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- /* If the method was unrecognized, encode it as an attribute */
- if (method == SC_M_JK_STORED) {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG, "unknown method %s", s->method);
- if (jk_b_append_byte(msg, SC_A_STORED_METHOD) ||
- jk_b_append_string(msg, s->method)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the request method");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- /* Forward the remote port information, which was forgotten
- * from the builtin data of the AJP 13 protocol.
- * Since the servlet spec allows to retrieve it via getRemotePort(),
- * we provide the port to the Tomcat connector as a request
- * attribute. Modern Tomcat versions know how to retrieve
- * the remote port from this attribute.
- */
- {
- if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
- jk_b_append_string(msg, SC_A_REQ_REMOTE_PORT) ||
- jk_b_append_string(msg, s->remote_port)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the remote port %s",
- s->remote_port);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- /* Forward activation information from the load balancer.
- * It can be used by the backend to deny access by requests,
- * which come with a session id but for an invalid session.
- * Such requests get forwarded to backends even if they
- * are disabled" in the load balancer, because the balancer
- * does not know, which sessions are valid.
- * If the backend can check, that is was "disabled" it can
- * delete the session cookie and respond with a self-referential
- * redirect. The new request will then be balanced to some
- * other node that is not disabled.
- */
- {
- if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
- jk_b_append_string(msg, SC_A_JK_LB_ACTIVATION) ||
- jk_b_append_string(msg, s->activation)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the activation state %s",
- s->activation);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
-
- if (s->num_attributes > 0) {
- for (i = 0; i < s->num_attributes; i++) {
- if (jk_b_append_byte(msg, SC_A_REQ_ATTRIBUTE) ||
- jk_b_append_string(msg, s->attributes_names[i]) ||
- jk_b_append_string(msg, s->attributes_values[i])) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending attribute %s=%s",
- s->attributes_names[i], s->attributes_values[i]);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- }
-
- if (jk_b_append_byte(msg, SC_A_ARE_DONE)) {
- jk_log(l, JK_LOG_ERROR,
- "failed appending the message end");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG, "ajp marshaling done");
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-/*
-AJPV13_RESPONSE/AJPV14_RESPONSE:=
- response_prefix (2)
- status (short)
- status_msg (short)
- num_headers (short)
- num_headers*(res_header_name header_value)
- *body_chunk
- terminator boolean <! -- recycle connection or not -->
-
-req_header_name :=
- sc_req_header_name | (string)
-
-res_header_name :=
- sc_res_header_name | (string)
-
-header_value :=
- (string)
-
-body_chunk :=
- length (short)
- body length*(var binary)
-
- */
-
-
-static int ajp_unmarshal_response(jk_msg_buf_t *msg,
- jk_res_data_t * d,
- ajp_endpoint_t * ae, jk_logger_t *l)
-{
- jk_pool_t *p = &ae->pool;
-
- JK_TRACE_ENTER(l);
-
- d->status = jk_b_get_int(msg);
- if (!d->status) {
- jk_log(l, JK_LOG_ERROR,
- "NULL status");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- d->msg = (char *)jk_b_get_string(msg);
- if (d->msg) {
-#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
- jk_xlate_from_ascii(d->msg, strlen(d->msg));
-#endif
- }
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "status = %d", d->status);
-
- d->num_headers = jk_b_get_int(msg);
- d->header_names = d->header_values = NULL;
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "Number of headers is = %d",
- d->num_headers);
-
- if (d->num_headers) {
- d->header_names = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
- d->header_values = jk_pool_alloc(p, sizeof(char *) * d->num_headers);
-
- if (d->header_names && d->header_values) {
- unsigned int i;
- for (i = 0; i < d->num_headers; i++) {
- unsigned short name = jk_b_pget_int(msg, msg->pos);
-
- if ((name & 0XFF00) == 0XA000) {
- jk_b_get_int(msg);
- name = name & 0X00FF;
- if (name <= SC_RES_HEADERS_NUM) {
- d->header_names[i] =
- (char *)long_res_header_for_sc(name);
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "No such sc (%d)", name);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- else {
- d->header_names[i] = (char *)jk_b_get_string(msg);
- if (!d->header_names[i]) {
- jk_log(l, JK_LOG_ERROR,
- "NULL header name");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
- jk_xlate_from_ascii(d->header_names[i],
- strlen(d->header_names[i]));
-#endif
-
- }
-
- d->header_values[i] = (char *)jk_b_get_string(msg);
- if (!d->header_values[i]) {
- jk_log(l, JK_LOG_ERROR,
- "NULL header value");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
-#if (defined(AS400) && !defined(AS400_UTF8)) || defined(_OSD_POSIX)
- jk_xlate_from_ascii(d->header_values[i],
- strlen(d->header_values[i]));
-#endif
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "Header[%d] [%s] = [%s]",
- i, d->header_names[i], d->header_values[i]);
- }
- }
- }
-
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-/*
- * Abort endpoint use
- */
-static void ajp_abort_endpoint(ajp_endpoint_t * ae, int shutdown, jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
- if (shutdown == JK_TRUE && IS_VALID_SOCKET(ae->sd)) {
- if (ae->hard_close) {
- /* Force unclean connection close to communicate client write errors
- * back to Tomcat by aborting AJP response writes.
- */
- jk_close_socket(ae->sd, l);
- }
- else {
- jk_shutdown_socket(ae->sd, l);
- }
- }
- ae->worker->s->connected--;
- ae->sd = JK_INVALID_SOCKET;
- ae->last_op = JK_AJP13_END_RESPONSE;
- JK_TRACE_EXIT(l);
-}
-
-/*
- * Reset the endpoint (clean buf and close socket)
- */
-static void ajp_reset_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) resetting endpoint with socket %d%s",
- ae->worker->name, ae->sd, ae->reuse? "" : " (socket shutdown)");
- if (!ae->reuse) {
- ajp_abort_endpoint(ae, JK_TRUE, l);
- }
- jk_reset_pool(&(ae->pool));
- JK_TRACE_EXIT(l);
-}
-
-/*
- * Close the endpoint (close pool and close socket)
- */
-void ajp_close_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) closing endpoint with socket %d%s",
- ae->worker->name, ae->sd, ae->reuse ? "" : " (socket shutdown)");
- if (IS_VALID_SOCKET(ae->sd)) {
- jk_shutdown_socket(ae->sd, l);
- }
- ae->sd = JK_INVALID_SOCKET;
- jk_close_pool(&(ae->pool));
- free(ae);
- JK_TRACE_EXIT(l);
-}
-
-
-/** Steal a connection from an idle cache endpoint
- * @param ae endpoint that needs a new connection
- * @param l logger
- * @return JK_FALSE: failure
- * JK_TRUE: success
- * @remark Always closes old socket endpoint
- */
-static int ajp_next_connection(ajp_endpoint_t *ae, jk_logger_t *l)
-{
- int rc;
- int ret = JK_FALSE;
- ajp_worker_t *aw = ae->worker;
-
- JK_TRACE_ENTER(l);
-
- /* Close previous socket */
- if (IS_VALID_SOCKET(ae->sd))
- jk_shutdown_socket(ae->sd, l);
- /* Mark existing endpoint socket as closed */
- ae->sd = JK_INVALID_SOCKET;
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int i;
- for (i = 0; i < aw->ep_cache_sz; i++) {
- /* Find cache slot with usable socket */
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
- ae->sd = aw->ep_cache[i]->sd;
- aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
- break;
- }
- }
- JK_LEAVE_CS(&aw->cs, rc);
- if (IS_VALID_SOCKET(ae->sd)) {
- ret = JK_TRUE;
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) Will try pooled connection socket %d from slot %d",
- ae->worker->name, ae->sd, i);
- }
- }
- JK_TRACE_EXIT(l);
- return ret;
-}
-
-/** Handle the cping/cpong query
- * @param ae endpoint
- * @param timeout wait timeout in milliseconds
- * @param l logger
- * @return JK_FALSE: failure
- * JK_TRUE: success
- * @remark Always closes socket in case of
- * a socket error
- */
-static int ajp_handle_cping_cpong(ajp_endpoint_t * ae, int timeout, jk_logger_t *l)
-{
- int i;
- int cmd;
- jk_msg_buf_t *msg;
-
- JK_TRACE_ENTER(l);
-
- ae->last_errno = 0;
- msg = jk_b_new(&ae->pool);
- if (!msg) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- if (jk_b_set_buffer_size(msg, 16)) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message buffer");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- jk_b_reset(msg);
- jk_b_append_byte(msg, AJP13_CPING_REQUEST);
-
- /* Send CPing query */
- if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) {
- jk_log(l, JK_LOG_INFO,
- "can't send cping query");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- for (i = 0; i < 2; i++) {
- /* wait for Pong reply for timeout milliseconds
- */
- if (jk_is_input_event(ae->sd, timeout, l) == JK_FALSE) {
- ae->last_errno = errno;
- jk_log(l, JK_LOG_INFO, "timeout in reply cpong");
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- /* Read and check for Pong reply
- */
- if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) {
- jk_log(l, JK_LOG_INFO,
- "awaited reply cpong, not received");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- if ((cmd = jk_b_get_byte(msg)) != AJP13_CPONG_REPLY) {
- /* If the respose was not CPONG it means that
- * the previous response was not consumed by the
- * client but the AJP messages was already in
- * the network buffer.
- * silently drop this single extra packet instead
- * recycling the connection
- */
- if (i || ae->last_op == JK_AJP13_END_RESPONSE ||
- cmd < JK_AJP13_SEND_BODY_CHUNK ||
- cmd > AJP13_CPONG_REPLY) {
- jk_log(l, JK_LOG_WARNING,
- "awaited reply cpong, received %d instead. "
- "Closing connection",
- cmd);
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- else {
- jk_log(l, JK_LOG_INFO,
- "awaited reply cpong, received %d instead. "
- "Retrying next packet",
- cmd);
-
- }
- }
- else {
- ae->last_op = AJP13_CPONG_REPLY;
- /* We have received Pong reply */
- break;
- }
- }
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-/** Connect an endpoint to a backend
- * @param ae endpoint
- * @param l logger
- * @return JK_FALSE: failure
- * JK_TRUE: success
- * @remark Always closes socket in case of
- * a socket error
- * @remark Cares about ae->last_errno
- */
-int ajp_connect_to_endpoint(ajp_endpoint_t * ae, jk_logger_t *l)
-{
- char buf[32];
- int rc = JK_TRUE;
-
- JK_TRACE_ENTER(l);
-
- ae->last_errno = 0;
- ae->sd = jk_open_socket(&ae->worker->worker_inet_addr,
- ae->worker->keepalive,
- ae->worker->socket_timeout,
- ae->worker->socket_connect_timeout,
- ae->worker->socket_buf, l);
-
- if (!IS_VALID_SOCKET(ae->sd)) {
- ae->last_errno = errno;
- jk_log(l, JK_LOG_INFO,
- "Failed opening socket to (%s) (errno=%d)",
- jk_dump_hinfo(&ae->worker->worker_inet_addr, buf), ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- ae->worker->s->connected++;
- /* set last_access only if needed */
- if (ae->worker->cache_timeout > 0)
- ae->last_access = time(NULL);
- /* Check if we must execute a logon after the physical connect */
- /* XXX: Not sure, if we really should do logon before cping/cpong */
- /* XXX: and if no cping/cpong is allowed before or after logon. */
- if (ae->worker->logon != NULL) {
- rc = ae->worker->logon(ae, l);
- if (rc == JK_FALSE) {
- jk_log(l, JK_LOG_ERROR,
- "(%s) ajp14 worker logon to the backend server failed",
- ae->worker->name);
- /* Close the socket if unable to logon */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- }
- }
- /* XXX: Should we send a cping also after logon to validate the connection? */
- else if (ae->worker->connect_timeout > 0) {
- rc = ajp_handle_cping_cpong(ae, ae->worker->connect_timeout, l);
- if (rc == JK_FALSE)
- jk_log(l, JK_LOG_ERROR,
- "(%s) cping/cpong after connecting to the backend server failed (errno=%d)",
- ae->worker->name, ae->last_errno);
- }
- JK_TRACE_EXIT(l);
- return rc;
-}
-
-/* Syncing config values from shm */
-void jk_ajp_pull(ajp_worker_t * aw, int locked, jk_logger_t *l)
-{
- int address_change = JK_FALSE;
- int port = 0;
- char host[JK_SHM_STR_SIZ+1];
- struct sockaddr_in inet_addr;
- JK_TRACE_ENTER(l);
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "syncing mem for ajp worker '%s' from shm (%u -> %u) [%u->%u]",
- aw->name, aw->sequence, aw->s->h.sequence, aw->addr_sequence, aw->s->addr_sequence);
- if (locked == JK_FALSE)
- jk_shm_lock();
-
- aw->cache_timeout = aw->s->cache_timeout;
- aw->connect_timeout = aw->s->connect_timeout;
- aw->ping_timeout = aw->s->ping_timeout;
- aw->reply_timeout = aw->s->reply_timeout;
- aw->prepost_timeout = aw->s->prepost_timeout;
- aw->recovery_opts = aw->s->recovery_opts;
- aw->retries = aw->s->retries;
- aw->retry_interval = aw->s->retry_interval;
- aw->max_packet_size = aw->s->max_packet_size;
- aw->sequence = aw->s->h.sequence;
- if (aw->addr_sequence != aw->s->addr_sequence) {
- address_change = JK_TRUE;
- aw->addr_sequence = aw->s->addr_sequence;
- strncpy(host, aw->s->host, JK_SHM_STR_SIZ);
- port = aw->s->port;
- }
- if (locked == JK_FALSE)
- jk_shm_unlock();
-
- if (address_change == JK_TRUE) {
- if (!jk_resolve(host, port, &inet_addr,
- aw->worker.we->pool, l)) {
- jk_log(l, JK_LOG_ERROR,
- "Failed resolving address '%s:%d' for worker '%s'.",
- host, port, aw->name);
- }
- else {
- int rc;
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int i;
- for (i = 0; i < aw->ep_cache_sz; i++) {
- /* Close all connections in the cache */
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
- int sd = aw->ep_cache[i]->sd;
- aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
- aw->ep_cache[i]->addr_sequence = aw->addr_sequence;
- jk_shutdown_socket(sd, l);
- aw->s->connected--;
- }
- }
- }
- aw->port = port;
- strncpy(aw->host, host, JK_SHM_STR_SIZ);
- memcpy(&(aw->worker_inet_addr), &inet_addr, sizeof(inet_addr));
- if (rc) {
- JK_LEAVE_CS(&aw->cs, rc);
- } else {
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)", errno);
- }
- }
- }
-
- JK_TRACE_EXIT(l);
-}
-
-/* Syncing config values to shm */
-void jk_ajp_push(ajp_worker_t * aw, int locked, jk_logger_t *l)
-{
- int address_change = JK_FALSE;
-
- JK_TRACE_ENTER(l);
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "syncing shm for ajp worker '%s' from mem (%u -> %u) [%u->%u]",
- aw->name, aw->s->h.sequence, aw->sequence, aw->s->addr_sequence, aw->addr_sequence);
- if (locked == JK_FALSE)
- jk_shm_lock();
-
- aw->s->cache_timeout = aw->cache_timeout;
- aw->s->connect_timeout = aw->connect_timeout;
- aw->s->ping_timeout = aw->ping_timeout;
- aw->s->reply_timeout = aw->reply_timeout;
- aw->s->prepost_timeout = aw->prepost_timeout;
- aw->s->recovery_opts = aw->recovery_opts;
- aw->s->retries = aw->retries;
- aw->s->retry_interval = aw->retry_interval;
- aw->s->max_packet_size = aw->max_packet_size;
- aw->s->h.sequence = aw->sequence;
- if (aw->s->addr_sequence != aw->addr_sequence) {
- address_change = JK_TRUE;
- strncpy(aw->s->host, aw->host, JK_SHM_STR_SIZ);
- aw->s->port = aw->port;
- aw->s->addr_sequence = aw->addr_sequence;
- }
- if (locked == JK_FALSE)
- jk_shm_unlock();
-
- if (address_change == JK_TRUE) {
- int rc;
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int i;
- for (i = 0; i < aw->ep_cache_sz; i++) {
- /* Close all connections in the cache */
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
- int sd = aw->ep_cache[i]->sd;
- aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
- aw->ep_cache[i]->addr_sequence = aw->addr_sequence;
- jk_shutdown_socket(sd, l);
- aw->s->connected--;
- }
- }
- JK_LEAVE_CS(&aw->cs, rc);
- } else {
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)", errno);
- }
- }
- JK_TRACE_EXIT(l);
-}
-
-/** Send a message to an endpoint, using corresponding PROTO HEADER
- * @param ae endpoint
- * @param msg message to send
- * @param l logger
- * @return JK_FATAL_ERROR: endpoint contains unknown protocol
- * JK_FALSE: other failure
- * JK_TRUE: success
- * @remark Always closes socket in case of
- * a socket error, or JK_FATAL_ERROR
- * @remark Cares about ae->last_errno
- */
-int ajp_connection_tcp_send_message(ajp_endpoint_t * ae,
- jk_msg_buf_t *msg, jk_logger_t *l)
-{
- int rc;
-
- JK_TRACE_ENTER(l);
-
- ae->last_errno = 0;
- if (ae->proto == AJP13_PROTO) {
- jk_b_end(msg, AJP13_WS_HEADER);
- if (JK_IS_DEBUG_LEVEL(l))
- jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp13", msg);
- }
- else if (ae->proto == AJP14_PROTO) {
- jk_b_end(msg, AJP14_WS_HEADER);
- if (JK_IS_DEBUG_LEVEL(l))
- jk_dump_buff(l, JK_LOG_DEBUG, "sending to ajp14", msg);
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "(%s) unknown protocol %d, supported are AJP13/AJP14",
- ae->worker->name, ae->proto);
- /* We've got a protocol error. */
- /* We can't trust this connection any more, */
- /* because we might have send already parts of the request. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
-
- /* This is the only place in this function where we use the socket. */
- /* If sendfull gets an error, it implicitely closes the socket. */
- /* So any socket error inside ajp_connection_tcp_send_message */
- /* results in a socket close and invalidated endpoint connection. */
- if ((rc = jk_tcp_socket_sendfull(ae->sd, msg->buf,
- msg->len, l)) > 0) {
- ae->endpoint.wr += (jk_uint64_t)rc;
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- ae->last_errno = errno;
- jk_log(l, JK_LOG_INFO,
- "sendfull for socket %d returned %d (errno=%d)",
- ae->sd, rc, ae->last_errno);
- ajp_abort_endpoint(ae, JK_FALSE, l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-/** Receive a message from an endpoint, checking PROTO HEADER
- * @param ae endpoint
- * @param msg message to send
- * @param l logger
- * @return JK_TRUE: success
- * JK_FALSE: could not read the AJP packet header
- * JK_AJP_PROTOCOL_ERROR: failure after reading
- * the AJP packet header
- * @remark Always closes socket in case of
- * a socket error
- * @remark Cares about ae->last_errno
- */
-int ajp_connection_tcp_get_message(ajp_endpoint_t * ae,
- jk_msg_buf_t *msg, jk_logger_t *l)
-{
- unsigned char head[AJP_HEADER_LEN];
- int rc;
- int msglen;
- unsigned int header;
- char buf[32];
-
- JK_TRACE_ENTER(l);
-
- ae->last_errno = 0;
- /* If recvfull gets an error, it implicitely closes the socket. */
- /* We will invalidate the endpoint connection. */
- rc = jk_tcp_socket_recvfull(ae->sd, head, AJP_HEADER_LEN, l);
-
- /* If the return code is not negative */
- /* then we always get back the correct number of bytes. */
- if (rc < 0) {
- if (rc == JK_SOCKET_EOF) {
- ae->last_errno = EPIPE;
- jk_log(l, JK_LOG_INFO,
- "(%s) can't receive the response header message from tomcat, "
- "tomcat (%s) has forced a connection close for socket %d",
- ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
- ae->sd);
- }
- else {
- ae->last_errno = -rc;
- jk_log(l, JK_LOG_INFO,
- "(%s) can't receive the response header message from tomcat, "
- "network problems or tomcat (%s) is down (errno=%d)",
- ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
- ae->last_errno);
- }
- ajp_abort_endpoint(ae, JK_FALSE, l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- ae->endpoint.rd += (jk_uint64_t)rc;
- header = ((unsigned int)head[0] << 8) | head[1];
-
- if (ae->proto == AJP13_PROTO) {
- if (header != AJP13_SW_HEADER) {
-
- if (header == AJP14_SW_HEADER) {
- jk_log(l, JK_LOG_ERROR,
- "received AJP14 reply on an AJP13 connection from %s",
- jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "wrong message format 0x%04x from %s",
- header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
- buf));
- }
- /* We've got a protocol error. */
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_AJP_PROTOCOL_ERROR;
- }
- }
- else if (ae->proto == AJP14_PROTO) {
- if (header != AJP14_SW_HEADER) {
-
- if (header == AJP13_SW_HEADER) {
- jk_log(l, JK_LOG_ERROR,
- "received AJP13 reply on an AJP14 connection from %s",
- jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "wrong message format 0x%04x from %s",
- header, jk_dump_hinfo(&ae->worker->worker_inet_addr,
- buf));
- }
- /* We've got a protocol error. */
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_AJP_PROTOCOL_ERROR;
- }
- }
-
- msglen = ((head[2] & 0xff) << 8);
- msglen += (head[3] & 0xFF);
-
- if (msglen > msg->maxlen) {
- jk_log(l, JK_LOG_ERROR,
- "wrong message size %d %d from %s",
- msglen, msg->maxlen,
- jk_dump_hinfo(&ae->worker->worker_inet_addr, buf));
- /* We've got a protocol error. */
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(ae, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_AJP_PROTOCOL_ERROR;
- }
-
- msg->len = msglen;
- msg->pos = 0;
-
- /* If recvfull gets an error, it implicitely closes the socket. */
- /* We will invalidate the endpoint connection. */
- rc = jk_tcp_socket_recvfull(ae->sd, msg->buf, msglen, l);
- /* If the return code is not negative */
- /* then we always get back the correct number of bytes. */
- if (rc < 0) {
- if (rc == JK_SOCKET_EOF) {
- ae->last_errno = EPIPE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) can't receive the response body message from tomcat, "
- "tomcat (%s) has forced a connection close for socket %d",
- ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
- ae->sd);
- }
- else {
- ae->last_errno = -rc;
- jk_log(l, JK_LOG_ERROR,
- "(%s) can't receive the response body message from tomcat, "
- "network problems or tomcat (%s) is down (errno=%d)",
- ae->worker->name, jk_dump_hinfo(&ae->worker->worker_inet_addr, buf),
- ae->last_errno);
- }
- ajp_abort_endpoint(ae, JK_FALSE, l);
- JK_TRACE_EXIT(l);
- /* Although we have a connection, this is effectively a protocol error.
- * We received the AJP header packet, but not the packet payload
- */
- return JK_AJP_PROTOCOL_ERROR;
- }
- ae->endpoint.rd += (jk_uint64_t)rc;
-
- if (JK_IS_DEBUG_LEVEL(l)) {
- if (ae->proto == AJP13_PROTO)
- jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp13", msg);
- else if (ae->proto == AJP14_PROTO)
- jk_dump_buff(l, JK_LOG_DEBUG, "received from ajp14", msg);
- }
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-/*
- * Read all the data from the socket.
- *
- * Socket API doesn't guaranty that all the data will be kept in a
- * single read, so we must loop until all awaited data is received
- */
-
-static int ajp_read_fully_from_server(jk_ws_service_t *s, jk_logger_t *l,
- unsigned char *buf, unsigned int len)
-{
- unsigned int rdlen = 0;
- unsigned int padded_len = len;
-
- JK_TRACE_ENTER(l);
-
- if (s->is_chunked && s->no_more_chunks) {
- JK_TRACE_EXIT(l);
- 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 int this_time = 0;
- if (!s->read(s, buf + rdlen, len - rdlen, &this_time)) {
- /* Remote Client read failed. */
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
-
- if (0 == this_time) {
- if (s->is_chunked) {
- s->no_more_chunks = 1; /* read no more */
- }
- break;
- }
- rdlen += this_time;
- }
-
- JK_TRACE_EXIT(l);
- return (int)rdlen;
-}
-
-
-/*
- * Read data from AJP13/AJP14 protocol
- * Returns -1 on error, else number of bytes read
- */
-
-static int ajp_read_into_msg_buff(ajp_endpoint_t * ae,
- jk_ws_service_t *r,
- jk_msg_buf_t *msg, int len, jk_logger_t *l)
-{
- unsigned char *read_buf = msg->buf;
-
- JK_TRACE_ENTER(l);
-
- jk_b_reset(msg);
-
- read_buf += AJP_HEADER_LEN; /* leave some space for the buffer headers */
- read_buf += AJP_HEADER_SZ_LEN; /* leave some space for the read length */
-
- /* Pick the max size since we don't know the content_length */
- if (r->is_chunked && len == 0) {
- len = AJP13_MAX_SEND_BODY_SZ;
- }
-
- if ((len = ajp_read_fully_from_server(r, l, read_buf, len)) < 0) {
- jk_log(l, JK_LOG_INFO,
- "(%s) receiving data from client failed. "
- "Connection aborted or network problems",
- ae->worker->name);
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
-
- if (!r->is_chunked) {
- ae->left_bytes_to_send -= len;
- }
-
- if (len > 0) {
- /* Recipient recognizes empty packet as end of stream, not
- an empty body packet */
- if (0 != jk_b_append_int(msg, (unsigned short)len)) {
- jk_log(l, JK_LOG_INFO,
- "Failed appending message length");
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
- }
-
- msg->len += len;
-
- JK_TRACE_EXIT(l);
- return len;
-}
-
-
-/*
- * send request to Tomcat via Ajp13
- * - first try to find reuseable socket
- * - if no such available, try to connect
- * - send request, but send must be seen as asynchronous,
- * since send() call will return noerror about 95% of time
- * Hopefully we'll get more information on next read.
- *
- * nb: op->request is the original request msg buffer
- * op->reply is the reply msg buffer which could be scratched
- *
- * Return values of ajp_send_request() function:
- * return value op->recoverable reason
- * JK_FATAL_ERROR JK_FALSE ajp_connection_tcp_send_message() returns JK_FATAL_ERROR
- * Endpoint belongs to unknown protocol.
- * JK_FATAL_ERROR JK_TRUE ajp_connection_tcp_send_message() returns JK_FALSE
- * Sending request or request body in jk_tcp_socket_sendfull() returns with error.
- * JK_FATAL_ERROR JK_TRUE Could not connect to backend
- * JK_CLIENT_RD_ERROR JK_FALSE Error during reading parts of POST body from client
- * JK_TRUE JK_TRUE All other cases (OK)
- */
-static int ajp_send_request(jk_endpoint_t *e,
- jk_ws_service_t *s,
- jk_logger_t *l,
- ajp_endpoint_t * ae, ajp_operation_t * op)
-{
- int err_conn = 0;
- int err_cping = 0;
- int err_send = 0;
- int rc;
- int postlen;
-
- JK_TRACE_ENTER(l);
-
- ae->last_errno = 0;
- /* Up to now, we can recover */
- op->recoverable = JK_TRUE;
-
- /* Check if the previous request really ended
- */
- if (ae->last_op != JK_AJP13_END_RESPONSE &&
- ae->last_op != AJP13_CPONG_REPLY) {
- jk_log(l, JK_LOG_INFO,
- "(%s) did not receive END_RESPONSE, "
- "closing socket %d",
- ae->worker->name, ae->sd);
- ajp_abort_endpoint(ae, JK_TRUE, l);
- }
- /*
- * First try to check open connections...
- */
- while (IS_VALID_SOCKET(ae->sd)) {
- int err = JK_FALSE;
- if (jk_is_socket_connected(ae->sd, l) == JK_FALSE) {
- ae->last_errno = errno;
- jk_log(l, JK_LOG_DEBUG,
- "(%s) failed sending request, "
- "socket %d is not connected any more (errno=%d)",
- ae->worker->name, ae->sd, ae->last_errno);
- ajp_abort_endpoint(ae, JK_FALSE, l);
- err = JK_TRUE;
- err_conn++;
- }
- if (ae->worker->prepost_timeout > 0 && !err) {
- /* handle cping/cpong if prepost_timeout is set
- * If the socket is disconnected no need to handle
- * the cping/cpong
- */
- if (ajp_handle_cping_cpong(ae,
- ae->worker->prepost_timeout, l) == JK_FALSE) {
- jk_log(l, JK_LOG_INFO,
- "(%s) failed sending request, "
- "socket %d prepost cping/cpong failure (errno=%d)",
- ae->worker->name, ae->sd, ae->last_errno);
- /* XXX: Is there any reason to try other
- * connections to the node if one of them fails
- * the cping/cpong heartbeat?
- * Tomcat can be either too busy or simply dead, so
- * there is a chance that all other connections would
- * fail as well.
- */
- err = JK_TRUE;
- err_cping++;
- }
- }
-
- /* We've got a connected socket and the optional
- * cping/cpong worked, so let's send the request now.
- */
- if (err == JK_FALSE) {
- rc = ajp_connection_tcp_send_message(ae, op->request, l);
- /* If this worked, we can break out of the loop
- * and proceed with the request.
- */
- if (rc == JK_TRUE) {
- ae->last_op = JK_AJP13_FORWARD_REQUEST;
- break;
- }
- /* Error during sending the request.
- */
- err_send++;
- if (rc == JK_FATAL_ERROR)
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_INFO,
- "(%s) failed sending request (%srecoverable) "
- "(errno=%d)",
- ae->worker->name,
- op->recoverable ? "" : "un",
- ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- /* If we got an error or can't send data, then try to steal another pooled
- * connection and try again. If we are not successful, break out of this
- * loop and try to open a new connection after the loop.
- */
- if (ajp_next_connection(ae, l) == JK_FALSE)
- break;
- }
-
- /*
- * If we failed to reuse a connection, try to reconnect.
- */
- if (!IS_VALID_SOCKET(ae->sd)) {
- /* Could not steal any connection from an endpoint - backend is disconnected */
- if (err_conn + err_cping + err_send > 0)
- jk_log(l, JK_LOG_INFO,
- "(%s) all endpoints are disconnected, "
- "detected by connect check (%d), cping (%d), send (%d)",
- ae->worker->name, err_conn, err_cping, err_send);
- else if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) all endpoints are disconnected.",
- ae->worker->name);
- /* Connect to the backend.
- */
- if (ajp_connect_to_endpoint(ae, l) != JK_TRUE) {
- jk_log(l, JK_LOG_ERROR,
- "(%s) connecting to backend failed. Tomcat is probably not started "
- "or is listening on the wrong port (errno=%d)",
- ae->worker->name, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- if (ae->worker->connect_timeout <= 0 &&
- ae->worker->prepost_timeout > 0) {
- /* handle cping/cpong if prepost_timeout is set
- * and we didn't already do a connect cping/cpong.
- */
- if (ajp_handle_cping_cpong(ae,
- ae->worker->prepost_timeout, l) == JK_FALSE) {
- jk_log(l, JK_LOG_INFO,
- "(%s) failed sending request, "
- "socket %d prepost cping/cpong failure (errno=%d)",
- ae->worker->name, ae->sd, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- }
-
- /* We've got a connected socket and the optional
- * cping/cpong worked, so let's send the request now.
- */
- rc = ajp_connection_tcp_send_message(ae, op->request, l);
- /* Error during sending the request.
- */
- if (rc != JK_TRUE) {
- if (rc == JK_FATAL_ERROR)
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) failed sending request on a fresh connection (%srecoverable), "
- "socket %d (errno=%d)",
- ae->worker->name, op->recoverable ? "" : "un",
- ae->sd, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- ae->last_op = JK_AJP13_FORWARD_REQUEST;
- }
- else if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) Statistics about invalid connections: "
- "connect check (%d), cping (%d), send (%d)",
- ae->worker->name, err_conn, err_cping, err_send);
-
- /*
- * From now on an error means that we have an internal server error
- * or Tomcat crashed.
- */
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) request body to send %" JK_UINT64_T_FMT " - request body to resend %d",
- ae->worker->name, ae->left_bytes_to_send,
- op->reply->len - AJP_HEADER_LEN);
-
- /*
- * POST recovery job is done here and will work when data to
- * POST are less than 8k, since it's the maximum size of op-post buffer.
- * We send here the first part of data which was sent previously to the
- * remote Tomcat
- */
-
- /* Did we have something to resend (ie the op-post has been feeded previously */
-
- postlen = op->post->len;
- if (postlen > AJP_HEADER_LEN) {
- rc = ajp_connection_tcp_send_message(ae, op->post, l);
- /* Error during sending the request body.
- */
- if (rc != JK_TRUE) {
- if (rc == JK_FATAL_ERROR)
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) failed sending request body of size %d (%srecoverable), "
- "socket %d (errno=%d)",
- ae->worker->name, postlen, op->recoverable ? "" : "un",
- ae->sd, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- else {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG, "Resent the request body (%d)",
- postlen);
- }
- }
- else if (s->reco_status == RECO_FILLED) {
- /* Recovery in LB MODE */
- postlen = s->reco_buf->len;
-
- if (postlen > AJP_HEADER_LEN) {
- rc = ajp_connection_tcp_send_message(ae, s->reco_buf, l);
- /* Error during sending the request body.
- */
- if (rc != JK_TRUE) {
- if (rc == JK_FATAL_ERROR)
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) failed sending request body of size %d (lb mode) (%srecoverable), "
- "socket %d (errno=%d)",
- ae->worker->name, postlen, op->recoverable ? "" : "un",
- ae->sd, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- }
- else {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "Resent the request body (lb mode) (%d)", postlen);
- }
- }
- else {
- /* We never sent any POST data and we check if we have to send at
- * least one block of data (max 8k). These data will be kept in reply
- * for resend if the remote Tomcat is down, a fact we will learn only
- * doing a read (not yet)
- */
- /* || s->is_chunked - this can't be done here. The original protocol
- sends the first chunk of post data ( based on Content-Length ),
- and that's what the java side expects.
- Sending this data for chunked would break other ajp13 servers.
-
- Note that chunking will continue to work - using the normal read.
- */
-
- if (ae->left_bytes_to_send > 0) {
- int len = AJP13_MAX_SEND_BODY_SZ;
- if (ae->left_bytes_to_send < (jk_uint64_t)AJP13_MAX_SEND_BODY_SZ) {
- len = (int)ae->left_bytes_to_send;
- }
- if ((len = ajp_read_into_msg_buff(ae, s, op->post, len, l)) <= 0) {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) browser stop sending data, no need to recover",
- ae->worker->name);
- op->recoverable = JK_FALSE;
- /* Send an empty POST message since per AJP protocol
- * spec whenever we have content length the message
- * packet must be followed with initial POST packet.
- * Size zero will be handled as error in container.
- */
- jk_b_reset(op->post);
- jk_b_append_int(op->post, 0);
- ajp_connection_tcp_send_message(ae, op->post, l);
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
-
- /* If a RECOVERY buffer is available in LB mode, fill it */
- if (s->reco_status == RECO_INITED) {
- jk_b_copy(op->post, s->reco_buf);
- s->reco_status = RECO_FILLED;
- }
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "(%s) sending %d bytes of request body",
- ae->worker->name, len);
-
- s->content_read = (jk_uint64_t)len;
- rc = ajp_connection_tcp_send_message(ae, op->post, l);
- /* Error during sending the request body.
- */
- if (rc != JK_TRUE) {
- if (rc == JK_FATAL_ERROR)
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) failed sending request body of size %d (%srecoverable), "
- "socket %d (errno=%d)",
- ae->worker->name, len, op->recoverable ? "" : "un",
- ae->sd, ae->last_errno);
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- }
- }
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-
-/*
- * What to do with incoming data (dispatcher)
- */
-
-static int ajp_process_callback(jk_msg_buf_t *msg,
- jk_msg_buf_t *pmsg,
- ajp_endpoint_t * ae,
- jk_ws_service_t *r, jk_logger_t *l)
-{
- int code = (int)jk_b_get_byte(msg);
-
- JK_TRACE_ENTER(l);
-
- switch (code) {
- case JK_AJP13_SEND_HEADERS:
- {
- int rc;
- jk_res_data_t res;
- if (ae->last_op == JK_AJP13_SEND_HEADERS) {
- /* Do not send anything to the client.
- * Backend already send us the headers.
- */
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG,
- "Already received AJP13_SEND HEADERS");
- }
- JK_TRACE_EXIT(l);
- return JK_AJP13_ERROR;
- }
- if (!ajp_unmarshal_response(msg, &res, ae, l)) {
- jk_log(l, JK_LOG_ERROR,
- "ajp_unmarshal_response failed");
- JK_TRACE_EXIT(l);
- return JK_AJP13_ERROR;
- }
- r->http_response_status = res.status;
- if (r->extension.fail_on_status_size > 0)
- rc = is_http_status_fail(r->extension.fail_on_status_size,
- r->extension.fail_on_status, res.status);
- else
- rc = is_http_status_fail(ae->worker->http_status_fail_num,
- ae->worker->http_status_fail, res.status);
- if (rc > 0) {
- JK_TRACE_EXIT(l);
- return JK_STATUS_FATAL_ERROR;
- }
- else if (rc < 0) {
- JK_TRACE_EXIT(l);
- return JK_STATUS_ERROR;
- }
-
- if (r->extension.use_server_error_pages &&
- r->http_response_status >= r->extension.use_server_error_pages)
- r->response_blocked = JK_TRUE;
-
- /*
- * Call even if response is blocked, since it also handles
- * forwarding some headers for special http status codes
- * even if the server uses an own error page.
- * Example: The WWW-Authenticate header in case of
- * HTTP_UNAUTHORIZED (401).
- */
- r->start_response(r, res.status, res.msg,
- (const char *const *)res.header_names,
- (const char *const *)res.header_values,
- res.num_headers);
-
- if (!r->response_blocked) {
- if (r->flush && r->flush_header)
- r->flush(r);
- }
- }
- return JK_AJP13_SEND_HEADERS;
-
- case JK_AJP13_SEND_BODY_CHUNK:
- if (ae->last_op == JK_AJP13_FORWARD_REQUEST) {
- /* AJP13_SEND_BODY_CHUNK with length 0 is
- * explicit flush packet message.
- * Ignore those if they are left over from previous responses.
- * Reportedly some versions of JBoss suffer from that problem.
- */
- if (jk_b_get_int(msg) == 0) {
- jk_log(l, JK_LOG_DEBUG,
- "Ignoring flush message received while sending the request");
- return ae->last_op;
- }
- /* We have just send a request but received something
- * that probably originates from buffered response.
- */
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG,
- "Unexpected AJP13_SEND_BODY_CHUNK");
- }
- JK_TRACE_EXIT(l);
- return JK_AJP13_ERROR;
- }
- if (!r->response_blocked) {
- unsigned int len = (unsigned int)jk_b_get_int(msg);
- /*
- * Do a sanity check on len to prevent write reading beyond buffer
- * boundaries and thus revealing possible sensitive memory
- * contents to the client.
- * len cannot be larger than msg->len - 3 because the ajp message
- * contains the magic byte for JK_AJP13_SEND_BODY_CHUNK (1 byte)
- * and the length of the chunk (2 bytes). The remaining part of
- * the message is the chunk.
- */
- if (len > (unsigned int)(msg->len - 3)) {
- jk_log(l, JK_LOG_ERROR,
- "Chunk length too large. Length of AJP message is %i,"
- " chunk length is %i.", msg->len, len);
- JK_TRACE_EXIT(l);
- return JK_INTERNAL_ERROR;
- }
- if (len == 0) {
- /* AJP13_SEND_BODY_CHUNK with length 0 is
- * explicit flush packet message.
- */
- if (r->response_started) {
- if (r->flush) {
- r->flush(r);
- }
- }
- else {
- jk_log(l, JK_LOG_DEBUG,
- "Ignoring flush message received before headers");
- }
- }
- else {
- if (!r->write(r, msg->buf + msg->pos, len)) {
- jk_log(l, JK_LOG_INFO,
- "Writing to client aborted or client network problems");
- JK_TRACE_EXIT(l);
- return JK_CLIENT_WR_ERROR;
- }
- if (r->flush && r->flush_packets)
- r->flush(r);
- }
- }
- break;
-
- case JK_AJP13_GET_BODY_CHUNK:
- {
- int len = (int)jk_b_get_int(msg);
-
- if (len < 0) {
- len = 0;
- }
- if (len > AJP13_MAX_SEND_BODY_SZ) {
- len = AJP13_MAX_SEND_BODY_SZ;
- }
- if ((jk_uint64_t)len > ae->left_bytes_to_send) {
- len = (int)ae->left_bytes_to_send;
- }
-
- /* the right place to add file storage for upload */
- if ((len = ajp_read_into_msg_buff(ae, r, pmsg, len, l)) >= 0) {
- r->content_read += (jk_uint64_t)len;
- JK_TRACE_EXIT(l);
- return JK_AJP13_HAS_RESPONSE;
- }
-
- jk_log(l, JK_LOG_INFO,
- "Reading from client aborted or client network problems");
-
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
- break;
-
- case JK_AJP13_END_RESPONSE:
- ae->reuse = (int)jk_b_get_byte(msg);
- if (!ae->reuse) {
- /*
- * AJP13 protocol reuse flag set to false.
- * Tomcat will close its side of the connection.
- */
- jk_log(l, JK_LOG_WARNING, "AJP13 protocol: Reuse is set to false");
- }
- else if (r->disable_reuse) {
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is disabled");
- }
- ae->reuse = JK_FALSE;
- }
- else {
- /* Reuse in all cases */
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG, "AJP13 protocol: Reuse is OK");
- }
- ae->reuse = JK_TRUE;
- }
- if (!r->response_blocked) {
- if (r->done) {
- /* Done with response */
- r->done(r);
- }
- else if (r->flush && !r->flush_packets) {
- /* Flush after the last write */
- r->flush(r);
- }
- }
- JK_TRACE_EXIT(l);
- return JK_AJP13_END_RESPONSE;
- break;
-
- default:
- jk_log(l, JK_LOG_ERROR,
- "Unknown AJP protocol code: %02X", code);
- JK_TRACE_EXIT(l);
- return JK_AJP13_ERROR;
- }
-
- JK_TRACE_EXIT(l);
- return JK_AJP13_NO_RESPONSE;
-}
-
-/*
- * get replies from Tomcat via Ajp13/Ajp14
- * ajp13/ajp14 is async but handling read/send this way prevent nice recovery
- * In fact if tomcat link is broken during upload (browser -> apache -> tomcat)
- * we'll loose data and we'll have to abort the whole request.
- *
- * Return values of ajp_get_reply() function:
- * return value op->recoverable reason
- * JK_REPLY_TIMEOUT ?recovery_options Reply timeout while waiting for response packet
- * JK_FALSE ?recovery_options Error during ajp_connection_tcp_get_message()
- * Could not read the AJP packet header
- * JK_AJP_PROTOCOL_ERROR: ?recovery_options Error during ajp_connection_tcp_get_message()
- * Failure after reading the AJP packet header
- * JK_STATUS_ERROR mostly JK_TRUE ajp_process_callback() returns JK_STATUS_ERROR
- * Recoverable, if callback didn't return with a JK_HAS_RESPONSE before.
- * JK_HAS_RESPONSE: parts of the post buffer are consumed.
- * JK_STATUS_FATAL_ERROR mostly JK_TRUE ajp_process_callback() returns JK_STATUS_FATAL_ERROR
- * Recoverable, if callback didn't return with a JK_HAS_RESPONSE before.
- * JK_HAS_RESPONSE: parts of the post buffer are consumed.
- * JK_FATAL_ERROR ? ajp_process_callback() returns JK_AJP13_ERROR
- * JK_AJP13_ERROR: protocol error, or JK_INTERNAL_ERROR: chunk size to large
- * JK_CLIENT_RD_ERROR ? ajp_process_callback() returns JK_CLIENT_RD_ERROR
- * JK_CLIENT_RD_ERROR: could not read post from client.
- * JK_CLIENT_WR_ERROR ? ajp_process_callback() returns JK_CLIENT_WR_ERROR
- * JK_CLIENT_WR_ERROR: could not write back result to client
- * JK_TRUE ? ajp_process_callback() returns JK_AJP13_END_RESPONSE
- * JK_FALSE ? Other unhandled cases (unknown return codes)
- */
-static int ajp_get_reply(jk_endpoint_t *e,
- jk_ws_service_t *s,
- jk_logger_t *l,
- ajp_endpoint_t * p, ajp_operation_t * op)
-{
- /* Don't get header from tomcat yet */
- int headeratclient = JK_FALSE;
-
- JK_TRACE_ENTER(l);
-
- p->last_errno = 0;
- /* Start read all reply message */
- while (1) {
- int rc = 0;
- /* Allow to overwrite reply_timeout on a per URL basis via service struct */
- int reply_timeout = s->extension.reply_timeout;
- if (reply_timeout < 0)
- reply_timeout = p->worker->reply_timeout;
- /* If we set a reply timeout, check if something is available */
- if (reply_timeout > 0) {
- if (jk_is_input_event(p->sd, reply_timeout, l) ==
- JK_FALSE) {
- p->last_errno = errno;
- jk_log(l, JK_LOG_ERROR,
- "(%s) Timeout with waiting reply from tomcat. "
- "Tomcat is down, stopped or network problems (errno=%d)",
- p->worker->name, p->last_errno);
- /* We can't trust this connection any more. */
- ajp_abort_endpoint(p, JK_TRUE, l);
- if (headeratclient == JK_FALSE) {
- if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
- op->recoverable = JK_FALSE;
- /*
- * We revert back to recoverable, if recovery_opts allow it for GET or HEAD
- */
- if (op->recoverable == JK_FALSE) {
- if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_HEAD) {
- if (!strcmp(s->method, "HEAD"))
- op->recoverable = JK_TRUE;
- }
- else if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_GET) {
- if (!strcmp(s->method, "GET"))
- op->recoverable = JK_TRUE;
- }
- }
- }
- else {
- if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
- op->recoverable = JK_FALSE;
- }
-
- JK_TRACE_EXIT(l);
- return JK_REPLY_TIMEOUT;
- }
- }
-
- if ((rc = ajp_connection_tcp_get_message(p, op->reply, l)) != JK_TRUE) {
- if (headeratclient == JK_FALSE) {
- jk_log(l, JK_LOG_ERROR,
- "(%s) Tomcat is down or refused connection. "
- "No response has been sent to the client (yet)",
- p->worker->name);
- /*
- * communication with tomcat has been interrupted BEFORE
- * headers have been sent to the client.
- */
-
- /*
- * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCGETREQUEST
- */
- if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
- op->recoverable = JK_FALSE;
- /*
- * We revert back to recoverable, if recovery_opts allow it for GET or HEAD
- */
- if (op->recoverable == JK_FALSE) {
- if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_HEAD) {
- if (!strcmp(s->method, "HEAD"))
- op->recoverable = JK_TRUE;
- }
- else if (p->worker->recovery_opts & RECOVER_ALWAYS_HTTP_GET) {
- if (!strcmp(s->method, "GET"))
- op->recoverable = JK_TRUE;
- }
- }
-
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "(%s) Tomcat is down or network problems. "
- "Part of the response has already been sent to the client",
- p->worker->name);
-
- /* communication with tomcat has been interrupted AFTER
- * headers have been sent to the client.
- * headers (and maybe parts of the body) have already been
- * sent, therefore the response is "complete" in a sense
- * that nobody should append any data, especially no 500 error
- * page of the webserver!
- */
-
- /*
- * We mark it unrecoverable if recovery_opts set to RECOVER_ABORT_IF_TCSENDHEADER
- */
- if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
- op->recoverable = JK_FALSE;
-
- }
-
- JK_TRACE_EXIT(l);
- return rc;
- }
-
- rc = ajp_process_callback(op->reply, op->post, p, s, l);
- p->last_op = rc;
- /* no more data to be sent, fine we have finish here */
- if (JK_AJP13_END_RESPONSE == rc) {
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- else if (JK_AJP13_SEND_HEADERS == rc) {
- if (headeratclient == JK_FALSE)
- headeratclient = JK_TRUE;
- else {
- /* Backend send headers twice?
- * This is protocol violation
- */
- jk_log(l, JK_LOG_ERROR,
- "(%s) Tomcat already send headers",
- p->worker->name);
- op->recoverable = JK_FALSE;
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- else if (JK_STATUS_ERROR == rc || JK_STATUS_FATAL_ERROR == rc) {
- jk_log(l, JK_LOG_INFO,
- "(%s) request failed%s, "
- "because of response status %d, ",
- p->worker->name,
- rc == JK_STATUS_FATAL_ERROR ? "" : " (soft)",
- s->http_response_status);
- JK_TRACE_EXIT(l);
- return rc;
- }
- else if (JK_AJP13_HAS_RESPONSE == rc) {
- /*
- * in upload-mode there is no second chance since
- * we may have already sent part of the uploaded data
- * to Tomcat.
- * In this case if Tomcat connection is broken we must
- * abort request and indicate error.
- * A possible work-around could be to store the uploaded
- * data to file and replay for it
- */
- op->recoverable = JK_FALSE;
- rc = ajp_connection_tcp_send_message(p, op->post, l);
- if (rc < 0) {
- jk_log(l, JK_LOG_ERROR,
- "(%s) Tomcat is down or network problems",
- p->worker->name);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- else if (JK_AJP13_ERROR == rc) {
- /*
- * Tomcat has send invalid AJP message.
- * Loadbalancer if present will decide if
- * failover is possible.
- */
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- else if (JK_CLIENT_RD_ERROR == rc) {
- /*
- * Client has stop sending to us, so get out.
- * We assume this isn't our fault, so just a normal exit.
- */
- JK_TRACE_EXIT(l);
- return JK_CLIENT_RD_ERROR;
- }
- else if (JK_CLIENT_WR_ERROR == rc) {
- /*
- * Client has stop receiving to us, so get out.
- * We assume this isn't our fault, so just a normal exit.
- */
- JK_TRACE_EXIT(l);
- return JK_CLIENT_WR_ERROR;
- }
- else if (JK_INTERNAL_ERROR == rc) {
- /*
- * Internal error, like memory allocation or invalid packet lengths.
- */
- JK_TRACE_EXIT(l);
- return JK_FATAL_ERROR;
- }
- else if (JK_AJP13_NO_RESPONSE == rc) {
- /*
- * This is fine, loop again, more data to send.
- */
- continue;
- }
- else if (rc < 0) {
- op->recoverable = JK_FALSE;
- jk_log(l, JK_LOG_ERROR,
- "(%s) Callback returns with unknown value %d",
- p->worker->name, rc);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- /* XXX: Not reached? */
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-static void ajp_update_stats(jk_endpoint_t *e, ajp_worker_t *aw, int rc, jk_logger_t *l)
-{
- aw->s->readed += e->rd;
- aw->s->transferred += e->wr;
- if (aw->s->busy)
- aw->s->busy--;
- if (rc == JK_TRUE) {
- aw->s->state = JK_AJP_STATE_OK;
- }
- else if (rc == JK_CLIENT_ERROR) {
- aw->s->state = JK_AJP_STATE_OK;
- aw->s->client_errors++;
- }
- else {
- aw->s->state = JK_AJP_STATE_ERROR;
- aw->s->errors++;
- aw->s->error_time = time(NULL);
- }
-}
-
-/*
- * service is now splitted in ajp_send_request and ajp_get_reply
- * much more easier to do errors recovery
- *
- * We serve here the request, using AJP13/AJP14 (e->proto)
- *
- * Return values of service() method for ajp13/ajp14 worker:
- * return value is_error e->recoverable reason
- * JK_FALSE JK_HTTP_SERVER_ERROR TRUE Invalid Parameters (null values)
- * JK_SERVER_ERROR JK_HTTP_SERVER_ERROR TRUE Error during initializing empty request, response or post body objects
- * JK_CLIENT_ERROR JK_HTTP_REQUEST_TOO_LARGE JK_TRUE Request doesn't fit into buffer (error during ajp_marshal_into_msgb())
- * JK_CLIENT_ERROR JK_HTTP_BAD_REQUEST JK_FALSE Error during reading parts of POST body from client
- * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE If ajp_send_request() returns JK_FATAL_ERROR and !op->recoverable.
- * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE If ajp_send_request() returns JK_TRUE but !op->recoverable.
- * This should never happen.
- * JK_CLIENT_ERROR JK_HTTP_BAD_REQUEST ? ajp_get_reply() returns JK_CLIENT_RD_ERROR
- * JK_CLIENT_ERROR JK_HTTP_OK ? ajp_get_reply() returns JK_CLIENT_WR_ERROR
- * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_TRUE ajp_get_reply() returns JK_FATAL_ERROR
- * JK_FATAL_ERROR: protocol error or internal error
- * JK_FATAL_ERROR JK_HTTP_SERVER_ERROR JK_FALSE ajp_get_reply() returns JK_FATAL_ERROR
- * JK_FATAL_ERROR: protocol error or internal error
- * JK_STATUS_ERROR JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_STATUS_ERROR
- * Only if op->recoverable and no more ajp13/ajp14 direct retries
- * JK_STATUS_ERROR JK_HTTP_SERVER_BUSY JK_FALSE ajp_get_reply() returns JK_STATUS_ERROR
- * Only if !op->recoverable
- * JK_STATUS_FATAL_ERROR JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_STATUS_ERROR
- * Only if op->recoverable and no more ajp13/ajp14 direct retries
- * JK_STATUS_FATAL_ERROR JK_HTTP_SERVER_BUSY JK_FALSE ajp_get_reply() returns JK_STATUS_FATAL_ERROR
- * Only if !op->recoverable
- * JK_REPLY_TIMEOUT JK_HTTP_GATEWAY_TIME_OUT JK_TRUE ajp_get_reply() returns JK_REPLY_TIMEOUT
- * JK_AJP_PROTOCOL_ERROR JK_HTTP_GATEWAY_TIME_OUT ? ajp_get_reply() returns JK_AJP_PROTOCOL_ERROR
- * ??? JK_FATAL_ERROR JK_HTTP_GATEWAY_TIME_OUT JK_FALSE ajp_get_reply() returns something else
- * Only if !op->recoverable
- * ??? JK_FALSE JK_HTTP_SERVER_BUSY JK_TRUE ajp_get_reply() returns JK_FALSE
- * Only if op->recoverable and no more ajp13/ajp14 direct retries
- * JK_TRUE JK_HTTP_OK ? OK
- */
-static int JK_METHOD ajp_service(jk_endpoint_t *e,
- jk_ws_service_t *s,
- jk_logger_t *l, int *is_error)
-{
- int i;
- int err = JK_TRUE;
- ajp_operation_t oper;
- ajp_operation_t *op = &oper;
- ajp_endpoint_t *p;
- ajp_worker_t *aw;
- int log_error;
- int rc = JK_UNSET;
- char *msg = "";
- int retry_interval;
-
- JK_TRACE_ENTER(l);
-
- if (!e || !e->endpoint_private || !s || !is_error) {
- JK_LOG_NULL_PARAMS(l);
- if (is_error)
- *is_error = JK_HTTP_SERVER_ERROR;
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- p = e->endpoint_private;
- aw = p->worker;
-
- if (aw->sequence != aw->s->h.sequence)
- jk_ajp_pull(aw, JK_FALSE, l);
-
- aw->s->used++;
-
- /* Set returned error to SERVER ERROR */
- *is_error = JK_HTTP_SERVER_ERROR;
-
- op->request = jk_b_new(&(p->pool));
- if (!op->request) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
- if (jk_b_set_buffer_size(op->request, aw->max_packet_size)) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message buffer");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
- jk_b_reset(op->request);
-
- op->reply = jk_b_new(&(p->pool));
- if (!op->reply) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
- if (jk_b_set_buffer_size(op->reply, aw->max_packet_size)) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message buffer");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
-
- op->post = jk_b_new(&(p->pool));
- if (!op->post) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
- if (jk_b_set_buffer_size(op->post, aw->max_packet_size)) {
- jk_log(l, JK_LOG_ERROR,
- "Failed allocating AJP message buffer");
- JK_TRACE_EXIT(l);
- return JK_SERVER_ERROR;
- }
- jk_b_reset(op->post);
-
- /* Set returned error to OK */
- *is_error = JK_HTTP_OK;
-
- op->recoverable = JK_TRUE;
- op->uploadfd = -1; /* not yet used, later ;) */
-
- p->left_bytes_to_send = s->content_length;
- p->reuse = JK_FALSE;
- p->hard_close = JK_FALSE;
-
- s->secret = aw->secret;
-
- /*
- * We get here initial request (in op->request)
- */
- if (!ajp_marshal_into_msgb(op->request, s, l, p)) {
- *is_error = JK_HTTP_REQUEST_TOO_LARGE;
- jk_log(l, JK_LOG_INFO,
- "Creating AJP message failed, "
- "without recovery");
- aw->s->client_errors++;
- JK_TRACE_EXIT(l);
- return JK_CLIENT_ERROR;
- }
-
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG, "processing %s with %d retries",
- aw->name, aw->retries);
- }
- aw->s->busy++;
- if (aw->s->state == JK_AJP_STATE_ERROR)
- aw->s->state = JK_AJP_STATE_PROBE;
- if (aw->s->busy > aw->s->max_busy)
- aw->s->max_busy = aw->s->busy;
- retry_interval = p->worker->retry_interval;
- for (i = 0; i < aw->retries; i++) {
- /* Reset reply message buffer for each retry */
- jk_b_reset(op->reply);
-
- /*
- * ajp_send_request() already locally handles
- * reconnecting and broken connection detection.
- * So if we already failed in it, wait a bit before
- * retrying the same backend.
- */
- if (i > 0 && retry_interval >= 0) {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "retry %d, sleeping for %d ms before retrying",
- i, retry_interval);
- jk_sleep(retry_interval);
- /* Pull shared memory if something changed during sleep */
- if (aw->sequence != aw->s->h.sequence)
- jk_ajp_pull(aw, JK_FALSE, l);
- }
- /*
- * We're using op->request which hold initial request
- * if Tomcat is stopped or restarted, we will pass op->request
- * to next valid tomcat.
- */
- log_error = JK_TRUE;
- rc = JK_UNSET;
- msg = "";
- err = ajp_send_request(e, s, l, p, op);
- e->recoverable = op->recoverable;
- if (err == JK_CLIENT_RD_ERROR) {
- *is_error = JK_HTTP_BAD_REQUEST;
- msg = "because of client read error";
- aw->s->client_errors++;
- rc = JK_CLIENT_ERROR;
- log_error = JK_FALSE;
- e->recoverable = JK_FALSE;
- /* Ajp message set reuse to TRUE in END_REQUEST message
- * However due to client bad request if the recovery
- * RECOVER_ABORT_IF_CLIENTERROR is set the physical connection
- * will be closed and application in Tomcat can catch that
- * generated exception, knowing the client aborted the
- * connection. This AJP protocol limitation, where we
- * should actually send some packet informing the backend
- * that client broke the connection in a middle of
- * request/response cycle.
- */
- if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
- /* Mark the endpoint for shutdown */
- p->reuse = JK_FALSE;
- p->hard_close = JK_TRUE;
- }
- }
- else if (err == JK_FATAL_ERROR) {
- *is_error = JK_HTTP_SERVER_BUSY;
- msg = "because of error during request sending";
- rc = err;
- if (!op->recoverable) {
- *is_error = JK_HTTP_SERVER_ERROR;
- msg = "because of protocol error during request sending";
- }
- }
- else if (err == JK_TRUE && op->recoverable) {
- /* Up to there we can recover */
-
- err = ajp_get_reply(e, s, l, p, op);
- e->recoverable = op->recoverable;
- if (err == JK_TRUE) {
- *is_error = JK_HTTP_OK;
- /* Done with the request */
- ajp_update_stats(e, aw, JK_TRUE, l);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
-
- if (err == JK_CLIENT_RD_ERROR) {
- *is_error = JK_HTTP_BAD_REQUEST;
- msg = "because of client read error";
- aw->s->client_errors++;
- rc = JK_CLIENT_ERROR;
- log_error = JK_FALSE;
- e->recoverable = JK_FALSE;
- op->recoverable = JK_FALSE;
- if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
- /* Mark the endpoint for shutdown */
- p->reuse = JK_FALSE;
- p->hard_close = JK_TRUE;
- }
- }
- else if (err == JK_CLIENT_WR_ERROR) {
- /* XXX: Is this correct to log this as 200? */
- *is_error = JK_HTTP_OK;
- msg = "because of client write error";
- aw->s->client_errors++;
- rc = JK_CLIENT_ERROR;
- log_error = JK_FALSE;
- e->recoverable = JK_FALSE;
- op->recoverable = JK_FALSE;
- if (aw->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) {
- /* Mark the endpoint for shutdown */
- p->reuse = JK_FALSE;
- p->hard_close = JK_TRUE;
- }
- }
- else if (err == JK_FATAL_ERROR) {
- *is_error = JK_HTTP_SERVER_ERROR;
- msg = "because of server error";
- rc = err;
- }
- else if (err == JK_REPLY_TIMEOUT) {
- *is_error = JK_HTTP_GATEWAY_TIME_OUT;
- msg = "because of reply timeout";
- aw->s->reply_timeouts++;
- rc = err;
- }
- else if (err == JK_STATUS_ERROR || err == JK_STATUS_FATAL_ERROR) {
- *is_error = JK_HTTP_SERVER_BUSY;
- msg = "because of response status";
- rc = err;
- }
- else if (err == JK_AJP_PROTOCOL_ERROR) {
- *is_error = JK_HTTP_BAD_GATEWAY;
- msg = "because of protocol error";
- rc = err;
- }
- /* This should only be the cases err == JK_FALSE */
- else {
- /* if we can't get reply, check if unrecoverable flag was set
- * if is_recoverable_error is cleared, we have started
- * receiving upload data and we must consider that
- * operation is no more recoverable
- */
- *is_error = JK_HTTP_BAD_GATEWAY;
- msg = "";
- rc = JK_FALSE;
- }
- }
- else {
- /* XXX: this should never happen:
- * ajp_send_request() never returns JK_TRUE if !op->recoverable.
- * and all other return values have already been handled.
- */
- e->recoverable = JK_FALSE;
- *is_error = JK_HTTP_SERVER_ERROR;
- msg = "because of an unknown reason";
- rc = JK_FATAL_ERROR;
- jk_log(l, JK_LOG_ERROR,
- "(%s) unexpected condition err=%d recoverable=%d",
- aw->name, err, op->recoverable);
- }
- if (!op->recoverable && log_error == JK_TRUE) {
- jk_log(l, JK_LOG_ERROR,
- "(%s) sending request to tomcat failed (unrecoverable), "
- "%s "
- "(attempt=%d)",
- aw->name, msg, i + 1);
- }
- else {
- jk_log(l, JK_LOG_INFO,
- "(%s) sending request to tomcat failed (%srecoverable), "
- "%s "
- "(attempt=%d)",
- aw->name,
- op->recoverable ? "" : "un",
- msg, i + 1);
- }
- if (!op->recoverable) {
- ajp_update_stats(e, aw, rc, l);
- JK_TRACE_EXIT(l);
- return rc;
- }
- /* Get another connection from the pool and try again.
- * Note: All sockets are probably closed already.
- */
- ajp_next_connection(p, l);
- }
- /* Log the error only once per failed request. */
- jk_log(l, JK_LOG_ERROR,
- "(%s) connecting to tomcat failed.",
- aw->name);
-
- ajp_update_stats(e, aw, rc, l);
- JK_TRACE_EXIT(l);
- return rc;
-}
-
-/*
- * Validate the worker (ajp13/ajp14)
- */
-
-int ajp_validate(jk_worker_t *pThis,
- jk_map_t *props,
- jk_worker_env_t *we, jk_logger_t *l, int proto)
-{
- int port;
- const char *host;
-
- JK_TRACE_ENTER(l);
-
- if (proto == AJP13_PROTO) {
- port = AJP13_DEF_PORT;
- host = AJP13_DEF_HOST;
- }
- else if (proto == AJP14_PROTO) {
- port = AJP14_DEF_PORT;
- host = AJP14_DEF_HOST;
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "unknown protocol %d", proto);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- if (pThis && pThis->worker_private) {
- ajp_worker_t *p = pThis->worker_private;
- p->port = jk_get_worker_port(props, p->name, port);
- if (!host) {
- host = "undefined";
- }
- strncpy(p->host, jk_get_worker_host(props, p->name, host), JK_SHM_STR_SIZ);
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "worker %s contact is '%s:%d'",
- p->name, p->host, p->port);
- /* Copy the contact to shm */
- strncpy(p->s->host, p->host, JK_SHM_STR_SIZ);
- p->s->port = p->port;
- p->s->addr_sequence = p->addr_sequence = 0;
- /* Resolve if port > 0.
- */
- if (p->port > 0) {
- if (jk_resolve(p->host, p->port, &p->worker_inet_addr, we->pool, l)) {
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- jk_log(l, JK_LOG_ERROR,
- "worker %s can't resolve tomcat address %s",
- p->name, p->host);
- p->s->port = p->port = 0;
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "worker %s contact is disabled",
- p->name);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- else {
- p->s->port = p->port = 0;
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "worker %s contact is disabled",
- p->name);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- }
- else {
- JK_LOG_NULL_PARAMS(l);
- }
-
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-static int ajp_create_endpoint_cache(ajp_worker_t *p, int proto, jk_logger_t *l)
-{
- unsigned int i;
- time_t now = time(NULL);
-
- JK_TRACE_ENTER(l);
-
- p->ep_cache = (ajp_endpoint_t **)calloc(1, sizeof(ajp_endpoint_t *) *
- p->ep_cache_sz);
- if (!p->ep_cache) {
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "setting connection pool size to %u with min %u and acquire timeout %d",
- p->ep_cache_sz, p->ep_mincache_sz, p->cache_acquire_timeout);
- for (i = 0; i < p->ep_cache_sz; i++) {
- p->ep_cache[i] = (ajp_endpoint_t *)calloc(1, sizeof(ajp_endpoint_t));
- if (!p->ep_cache[i]) {
- jk_log(l, JK_LOG_ERROR,
- "allocating endpoint slot %d (errno=%d)",
- i, errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- p->ep_cache[i]->sd = JK_INVALID_SOCKET;
- p->ep_cache[i]->reuse = JK_FALSE;
- p->ep_cache[i]->hard_close = JK_FALSE;
- p->ep_cache[i]->last_access = now;
- jk_open_pool(&(p->ep_cache[i]->pool), p->ep_cache[i]->buf,
- sizeof(p->ep_cache[i]->buf));
- p->ep_cache[i]->worker = p;
- p->ep_cache[i]->endpoint.endpoint_private = p->ep_cache[i];
- p->ep_cache[i]->proto = proto;
- p->ep_cache[i]->endpoint.service = ajp_service;
- p->ep_cache[i]->endpoint.done = ajp_done;
- p->ep_cache[i]->last_op = JK_AJP13_END_RESPONSE;
- p->ep_cache[i]->addr_sequence = 0;
- }
-
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-int ajp_init(jk_worker_t *pThis,
- jk_map_t *props, jk_worker_env_t *we, jk_logger_t *l, int proto)
-{
- int rc = JK_FALSE;
- int cache;
- /*
- * start the connection cache
- */
- JK_TRACE_ENTER(l);
-
- cache = jk_get_worker_def_cache_size(proto);
-
- if (pThis && pThis->worker_private) {
- ajp_worker_t *p = pThis->worker_private;
- p->worker.we = we;
- p->ep_cache_sz = jk_get_worker_cache_size(props, p->name, cache);
- p->ep_mincache_sz = jk_get_worker_cache_size_min(props, p->name,
- (p->ep_cache_sz+1) / 2);
- p->socket_timeout =
- jk_get_worker_socket_timeout(props, p->name, AJP_DEF_SOCKET_TIMEOUT);
-
- p->socket_connect_timeout =
- jk_get_worker_socket_connect_timeout(props, p->name,
- p->socket_timeout * 1000);
-
- p->keepalive =
- jk_get_worker_socket_keepalive(props, p->name, JK_FALSE);
-
- p->cache_timeout =
- jk_get_worker_cache_timeout(props, p->name,
- AJP_DEF_CACHE_TIMEOUT);
-
- p->ping_timeout =
- jk_get_worker_ping_timeout(props, p->name,
- AJP_DEF_PING_TIMEOUT);
- p->ping_mode =
- jk_get_worker_ping_mode(props, p->name,
- AJP_CPING_NONE);
-
- p->connect_timeout =
- jk_get_worker_connect_timeout(props, p->name,
- AJP_DEF_CONNECT_TIMEOUT);
-
- p->prepost_timeout =
- jk_get_worker_prepost_timeout(props, p->name,
- AJP_DEF_PREPOST_TIMEOUT);
-
- if ((p->ping_mode & AJP_CPING_CONNECT) &&
- p->connect_timeout == AJP_DEF_CONNECT_TIMEOUT)
- p->connect_timeout = p->ping_timeout;
-
- if ((p->ping_mode & AJP_CPING_PREPOST) &&
- p->prepost_timeout == AJP_DEF_PREPOST_TIMEOUT)
- p->prepost_timeout = p->ping_timeout;
-
- p->conn_ping_interval =
- jk_get_worker_conn_ping_interval(props, p->name, 0);
- if ((p->ping_mode & AJP_CPING_INTERVAL) &&
- p->conn_ping_interval == 0) {
- /* XXX: Ping timeout is in miliseconds
- * and ping_interval is in seconds.
- * Use 10 times larger value for ping interval
- * (ping_timeout / 1000) * 10
- */
- p->conn_ping_interval = p->ping_timeout / 100;
- }
- p->reply_timeout =
- jk_get_worker_reply_timeout(props, p->name,
- AJP_DEF_REPLY_TIMEOUT);
-
- p->recovery_opts =
- jk_get_worker_recovery_opts(props, p->name,
- AJP_DEF_RECOVERY_OPTS);
-
- p->retries =
- jk_get_worker_retries(props, p->name,
- JK_RETRIES);
-
- p->max_packet_size =
- jk_get_max_packet_size(props, p->name);
-
- p->socket_buf =
- jk_get_worker_socket_buffer(props, p->name, p->max_packet_size);
-
- p->retry_interval =
- jk_get_worker_retry_interval(props, p->name,
- JK_SLEEP_DEF);
- p->cache_acquire_timeout = jk_get_worker_cache_acquire_timeout(props,
- p->name, p->retries * p->retry_interval);
- p->http_status_fail_num = jk_get_worker_fail_on_status(props, p->name,
- &p->http_status_fail[0],
- JK_MAX_HTTP_STATUS_FAILS);
-
- if (p->retries < 1) {
- jk_log(l, JK_LOG_INFO,
- "number of retries must be greater then 1. Setting to default=%d",
- JK_RETRIES);
- p->retries = JK_RETRIES;
- }
-
- p->maintain_time = jk_get_worker_maintain_time(props);
- if(p->maintain_time < 0)
- p->maintain_time = 0;
- p->s->last_maintain_time = time(NULL);
- p->s->last_reset = p->s->last_maintain_time;
-
- if (JK_IS_DEBUG_LEVEL(l)) {
-
- jk_log(l, JK_LOG_DEBUG,
- "setting endpoint options:",
- p->keepalive);
- jk_log(l, JK_LOG_DEBUG,
- "keepalive: %d",
- p->keepalive);
-
- jk_log(l, JK_LOG_DEBUG,
- "socket timeout: %d",
- p->socket_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "socket connect timeout: %d",
- p->socket_connect_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "buffer size: %d",
- p->socket_buf);
-
- jk_log(l, JK_LOG_DEBUG,
- "pool timeout: %d",
- p->cache_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "ping timeout: %d",
- p->ping_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "connect timeout: %d",
- p->connect_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "reply timeout: %d",
- p->reply_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "prepost timeout: %d",
- p->prepost_timeout);
-
- jk_log(l, JK_LOG_DEBUG,
- "recovery options: %d",
- p->recovery_opts);
-
- jk_log(l, JK_LOG_DEBUG,
- "retries: %d",
- p->retries);
-
- jk_log(l, JK_LOG_DEBUG,
- "max packet size: %d",
- p->max_packet_size);
-
- jk_log(l, JK_LOG_DEBUG,
- "retry interval: %d",
- p->retry_interval);
- }
- /*
- * Need to initialize secret here since we could return from inside
- * of the following loop
- */
-
- p->secret = jk_get_worker_secret(props, p->name);
- /* Initialize cache slots */
- JK_INIT_CS(&(p->cs), rc);
- if (!rc) {
- jk_log(l, JK_LOG_ERROR,
- "creating thread lock (errno=%d)",
- errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- if (!ajp_create_endpoint_cache(p, proto, l)) {
- jk_log(l, JK_LOG_ERROR,
- "allocating connection pool of size %u",
- p->ep_cache_sz);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- rc = JK_TRUE;
- }
- else {
- JK_LOG_NULL_PARAMS(l);
- }
-
- JK_TRACE_EXIT(l);
- return rc;
-}
-
-int JK_METHOD ajp_worker_factory(jk_worker_t **w,
- const char *name, jk_logger_t *l)
-{
- ajp_worker_t *aw;
-
- JK_TRACE_ENTER(l);
- if (name == NULL || w == NULL) {
- JK_LOG_NULL_PARAMS(l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- aw = (ajp_worker_t *) calloc(1, sizeof(ajp_worker_t));
- if (!aw) {
- jk_log(l, JK_LOG_ERROR,
- "malloc of private_data failed");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- jk_open_pool(&aw->p,
- aw->buf,
- sizeof(jk_pool_atom_t) * TINY_POOL_SIZE);
-
- strncpy(aw->name, name, JK_SHM_STR_SIZ);
- aw->login = NULL;
-
- aw->ep_cache_sz = 0;
- aw->ep_cache = NULL;
- aw->connect_retry_attempts = AJP_DEF_RETRY_ATTEMPTS;
- aw->worker.worker_private = aw;
-
- aw->worker.maintain = ajp_maintain;
-
- aw->logon = NULL;
-
- *w = &aw->worker;
-
- aw->s = jk_shm_alloc_ajp_worker(&aw->p);
- if (!aw->s) {
- jk_close_pool(&aw->p);
- free(aw);
- jk_log(l, JK_LOG_ERROR,
- "allocating ajp worker record from shared memory");
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- JK_TRACE_EXIT(l);
- return JK_TRUE;
-}
-
-int ajp_destroy(jk_worker_t **pThis, jk_logger_t *l, int proto)
-{
- JK_TRACE_ENTER(l);
-
- if (pThis && *pThis && (*pThis)->worker_private) {
- unsigned int i;
- ajp_worker_t *aw = (*pThis)->worker_private;
-
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "up to %u endpoints to close",
- aw->ep_cache_sz);
-
- for (i = 0; i < aw->ep_cache_sz; i++) {
- if (aw->ep_cache[i])
- ajp_close_endpoint(aw->ep_cache[i], l);
- }
- free(aw->ep_cache);
- JK_DELETE_CS(&(aw->cs), i);
-
- if (aw->login) {
- /* take care of removing previously allocated data */
- if (aw->login->servlet_engine_name)
- free(aw->login->servlet_engine_name);
-
- free(aw->login);
- aw->login = NULL;
- }
-
- jk_close_pool(&aw->p);
- free(aw);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
-
- JK_LOG_NULL_PARAMS(l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-int JK_METHOD ajp_done(jk_endpoint_t **e, jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
-
- if (e && *e && (*e)->endpoint_private) {
- ajp_endpoint_t *p = (*e)->endpoint_private;
- int rc;
- ajp_worker_t *w = p->worker;
-
- /* set last_access only if needed */
- if (w->cache_timeout > 0)
- p->last_access = time(NULL);
- if (w->s->addr_sequence != p->addr_sequence) {
- p->reuse = JK_FALSE;
- p->addr_sequence = w->s->addr_sequence;
- }
- ajp_reset_endpoint(p, l);
- *e = NULL;
- JK_ENTER_CS(&w->cs, rc);
- if (rc) {
- int i;
-
- for (i = w->ep_cache_sz - 1; i >= 0; i--) {
- if (w->ep_cache[i] == NULL) {
- w->ep_cache[i] = p;
- break;
- }
- }
- JK_LEAVE_CS(&w->cs, rc);
-
- if (i >= 0) {
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "recycling connection pool slot=%u for worker %s",
- i, p->worker->name);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- /* This should never hapen because
- * there is always free empty cache slot
- */
- jk_log(l, JK_LOG_ERROR,
- "could not find empty connection pool slot from %u for worker %s",
- w->ep_cache_sz, w->name);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)", errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
-
- JK_LOG_NULL_PARAMS(l);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-int ajp_get_endpoint(jk_worker_t *pThis,
- jk_endpoint_t **je, jk_logger_t *l, int proto)
-{
- JK_TRACE_ENTER(l);
-
- if (pThis && pThis->worker_private && je) {
- ajp_worker_t *aw = pThis->worker_private;
- ajp_endpoint_t *ae = NULL;
- int rc;
- int retry = 0;
-
- *je = NULL;
- /* Loop until cache_acquire_timeout interval elapses */
- while ((retry * JK_SLEEP_DEF) < aw->cache_acquire_timeout) {
-
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int slot;
- /* Try to find connected socket cache entry */
- for (slot = 0; slot < aw->ep_cache_sz; slot++) {
- if (aw->ep_cache[slot] &&
- IS_VALID_SOCKET(aw->ep_cache[slot]->sd)) {
- ae = aw->ep_cache[slot];
- if (ae->reuse) {
- aw->ep_cache[slot] = NULL;
- break;
- }
- else {
- /* XXX: We shouldn't have non reusable
- * opened socket in the cache
- */
- ajp_reset_endpoint(ae, l);
- ae = NULL;
- jk_log(l, JK_LOG_WARNING,
- "closing non reusable pool slot=%d", slot);
- }
- }
- }
- if (!ae) {
- /* No connected cache entry found.
- * Use the first free one.
- */
- for (slot = 0; slot < aw->ep_cache_sz; slot++) {
- if (aw->ep_cache[slot]) {
- ae = aw->ep_cache[slot];
- aw->ep_cache[slot] = NULL;
- break;
- }
- }
- }
- JK_LEAVE_CS(&aw->cs, rc);
- if (ae) {
- if (aw->cache_timeout > 0)
- ae->last_access = time(NULL);
- *je = &ae->endpoint;
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "acquired connection pool slot=%u after %d retries",
- slot, retry);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- else {
- retry++;
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "could not get free endpoint for worker %s"
- " (retry %d, sleeping for %d ms)",
- aw->name, retry, JK_SLEEP_DEF);
- jk_sleep(JK_SLEEP_DEF);
- }
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)", errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-
- }
- }
- jk_log(l, JK_LOG_WARNING,
- "Unable to get the free endpoint for worker %s from %u slots",
- aw->name, aw->ep_cache_sz);
- }
- else {
- JK_LOG_NULL_PARAMS(l);
- }
-
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-int JK_METHOD ajp_maintain(jk_worker_t *pThis, time_t mstarted, jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
-
- if (pThis && pThis->worker_private) {
- ajp_worker_t *aw = pThis->worker_private;
- time_t now = mstarted;
- int rc;
- long delta;
-
- jk_shm_lock();
-
- /* Now we check for global maintenance (once for all processes).
- * Checking workers for idleness.
- * Therefore we globally sync and we use a global timestamp.
- * Since it's possible that we come here a few milliseconds
- * before the interval has passed, we allow a little tolerance.
- */
- delta = (long)difftime(mstarted, aw->s->last_maintain_time) + JK_AJP_MAINTAIN_TOLERANCE;
- if (delta >= aw->maintain_time) {
- aw->s->last_maintain_time = mstarted;
- if (aw->s->state == JK_AJP_STATE_OK &&
- aw->s->used == aw->s->used_snapshot)
- aw->s->state = JK_AJP_STATE_IDLE;
- aw->s->used_snapshot = aw->s->used;
- }
-
- jk_shm_unlock();
-
- /* Do connection pool maintenance only if timeouts or keepalives are set */
- if (aw->cache_timeout <= 0 &&
- aw->conn_ping_interval <= 0) {
- /* Nothing to do. */
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
-
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int n = 0, k = 0, cnt = 0;
- int i;
- unsigned int m, m_count = 0;
- jk_sock_t *m_sock;
- /* Count open slots */
- for (i = (int)aw->ep_cache_sz - 1; i >= 0; i--) {
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd))
- cnt++;
- }
- m_sock = (jk_sock_t *)malloc((cnt + 1) * sizeof(jk_sock_t));
- /* Handle worker cache timeouts */
- if (aw->cache_timeout > 0) {
- for (i = (int)aw->ep_cache_sz - 1;
- i >= 0; i--) {
- /* Skip the closed sockets */
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
- int elapsed = (int)difftime(mstarted, aw->ep_cache[i]->last_access);
- if (elapsed > aw->cache_timeout) {
- time_t rt = 0;
- n++;
- if (JK_IS_DEBUG_LEVEL(l))
- rt = time(NULL);
- aw->ep_cache[i]->reuse = JK_FALSE;
- m_sock[m_count++] = aw->ep_cache[i]->sd;
- aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
- ajp_reset_endpoint(aw->ep_cache[i], l);
- if (JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "cleaning pool slot=%d elapsed %d in %d",
- i, elapsed, (int)(difftime(time(NULL), rt)));
- }
- }
- if (cnt <= aw->ep_mincache_sz + n) {
- if (JK_IS_DEBUG_LEVEL(l)) {
- jk_log(l, JK_LOG_DEBUG,
- "reached pool min size %u from %u cache slots",
- aw->ep_mincache_sz, aw->ep_cache_sz);
- }
- break;
- }
- }
- }
- /* Handle worker connection keepalive */
- if (aw->conn_ping_interval > 0 && aw->ping_timeout > 0) {
- for (i = (int)aw->ep_cache_sz - 1; i >= 0; i--) {
- /* Skip the closed sockets */
- if (aw->ep_cache[i] && IS_VALID_SOCKET(aw->ep_cache[i]->sd)) {
- int elapsed = (int)difftime(now, aw->ep_cache[i]->last_access);
- if (elapsed > aw->conn_ping_interval) {
- k++;
- /* handle cping/cpong.
- */
- if (ajp_handle_cping_cpong(aw->ep_cache[i],
- aw->ping_timeout, l) == JK_FALSE) {
- jk_log(l, JK_LOG_INFO,
- "(%s) failed sending request, "
- "socket %d keepalive cping/cpong "
- "failure (errno=%d)",
- aw->name,
- aw->ep_cache[i]->sd,
- aw->ep_cache[i]->last_errno);
- aw->ep_cache[i]->reuse = JK_FALSE;
- m_sock[m_count++] = aw->ep_cache[i]->sd;
- aw->ep_cache[i]->sd = JK_INVALID_SOCKET;
- ajp_reset_endpoint(aw->ep_cache[i], l);
- }
- else {
- now = time(NULL);
- aw->ep_cache[i]->last_access = now;
- }
- }
- }
- }
- }
- JK_LEAVE_CS(&aw->cs, rc);
- /* Shutdown sockets outside of the lock.
- * This has benefits only if maintain was
- * called from the watchdog thread.
- */
- for (m = 0; m < m_count; m++) {
- jk_shutdown_socket(m_sock[m], l);
- }
- free(m_sock);
- if (n && JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "recycled %u sockets in %d seconds from %u pool slots",
- n, (int)(difftime(time(NULL), mstarted)),
- aw->ep_cache_sz);
- if (k && JK_IS_DEBUG_LEVEL(l))
- jk_log(l, JK_LOG_DEBUG,
- "pinged %u sockets in %d seconds from %u pool slots",
- k, (int)(difftime(time(NULL), mstarted)),
- aw->ep_cache_sz);
- JK_TRACE_EXIT(l);
- return JK_TRUE;
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)",
- errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-
- }
- }
- else {
- JK_LOG_NULL_PARAMS(l);
- }
-
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}
-
-int ajp_has_endpoint(jk_worker_t *pThis,
- jk_logger_t *l)
-{
- JK_TRACE_ENTER(l);
-
- if (pThis && pThis->worker_private) {
- ajp_worker_t *aw = pThis->worker_private;
- int rc;
-
- JK_ENTER_CS(&aw->cs, rc);
- if (rc) {
- unsigned int slot;
- /* Try to find connected socket cache entry */
- for (slot = 0; slot < aw->ep_cache_sz; slot++) {
- if (aw->ep_cache[slot]) {
- JK_LEAVE_CS(&aw->cs, rc);
- return JK_TRUE;
- }
- }
- JK_LEAVE_CS(&aw->cs, rc);
- }
- else {
- jk_log(l, JK_LOG_ERROR,
- "locking thread (errno=%d)", errno);
- JK_TRACE_EXIT(l);
- return JK_FALSE;
- }
- }
- else {
- JK_LOG_NULL_PARAMS(l);
- }
-
- JK_TRACE_EXIT(l);
- return JK_FALSE;
-}