diff --exclude .project --exclude .cproject -Naur tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.c tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.c --- tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.c 2012-05-28 12:23:40.000000000 +0100 +++ tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.c 2013-02-28 12:31:55.000000000 +0000 @@ -76,6 +76,12 @@ #define UNKNOWN_METHOD (-1) +#define JK_CUSTOM_CHECK_PROTOCOL "HTTP/1.1" +#define JK_CUSTOM_CHECK_REMOTE_HOST "127.0.0.1" +#define JK_CUSTOM_CHECK_REMOTE_PORT "65535" +#define JK_CUSTOM_CHECK_SERVER_NAME "localhost" +#define JK_CUSTOM_CHECK_METHOD "GET" + static int sc_for_req_method(const char *method, size_t len) { /* Note: the following code was generated by the "shilka" tool from @@ -862,6 +868,211 @@ return ret; } +/* The below set of functions provide a replacement to ajp_handle_cping_cpong. + * (An alternative way to check if the tomcat is ready to receive requests). + * Instead of sending a CPING message, send a GET request to a configurable + * URL and check the response code. The advantage of this is that we can + * check if the app is deployed and ready. + * + * worker.tomcat_default.custom_check_expected_status=200 + * worker.tomcat_default.custom_check_uri=/app/emptyfile + * + * The ping_mode setting to specify when checks happen are still effective. + * These checks are enabled by specifying custom_check_uri. + */ + +/* The mod_jk internals don't appear to check these start/read/write callbacks for NULL pointers. + * So provide dummy functions. */ + +static int JK_METHOD check_start_response(jk_ws_service_t *s, int status, + const char *reason, const char * const *header_names, + const char * const *header_values, unsigned num_of_headers) +{ + return JK_TRUE; +} + +static int JK_METHOD check_write(jk_ws_service_t *s, const void *b, + unsigned int l) +{ + return JK_TRUE; +} + +/* Callback which just tells the engine we read the whole response from AJP. */ + +static int JK_METHOD check_read(jk_ws_service_t *s, void *b, unsigned len, + unsigned *actually_read) +{ + *actually_read = len; + return JK_TRUE; +} + +/* Create our dummy request in the jk_ws_service_t */ + +static int ajp_custom_check_init_service(jk_ws_service_t *s, ajp_worker_t *aw) +{ + s->start_response = check_start_response; + s->read = check_read; + s->write = check_write; + + s->req_uri = (char*)aw->custom_check_uri; + s->remote_host = s->remote_addr = JK_CUSTOM_CHECK_REMOTE_HOST; + s->remote_port = JK_CUSTOM_CHECK_REMOTE_PORT; + s->server_name = JK_CUSTOM_CHECK_SERVER_NAME; + + s->protocol = JK_CUSTOM_CHECK_PROTOCOL; + s->method = JK_CUSTOM_CHECK_METHOD; + s->content_length = 0; + s->is_chunked = JK_FALSE; + + return JK_TRUE; +} + +static int ajp_custom_check(ajp_endpoint_t * ae, int timeout, jk_logger_t *l) +{ + jk_ws_service_t s; // the dummy request + ajp_worker_t *aw; // AJP worker, contains config + jk_msg_buf_t *msg; // message buffer, for request and response + jk_res_data_t res; // unmarshaled response + unsigned char msgType; + + JK_TRACE_ENTER(l); + + jk_log(l, JK_LOG_DEBUG, "Running a mod_jk custom check"); + + aw = ae->worker; + ae->last_errno = 0; + + /* Pull config from shared mem config to worker. */ + + if (aw->sequence != aw->s->h.sequence) + jk_ajp_pull(aw, JK_FALSE, l); + + /* Create a message buffer. */ + + msg = jk_b_new(&ae->pool); + + if (!msg) + { + jk_log(l, JK_LOG_ERROR, + "Failed allocating AJP message for custom check"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* Set the buffer size to worker config (probably 8K). */ + + if (jk_b_set_buffer_size(msg, aw->max_packet_size)) + { + jk_log(l, JK_LOG_ERROR, + "Failed allocating AJP message buffer for custom check"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* Clear the buffer and reset its position. */ + + jk_b_reset(msg); + + /* Zero all the bytes in the jk_ws_service struct and do some basic init. */ + + jk_init_ws_service(&s); + + /* Create our dummy request. */ + + ajp_custom_check_init_service(&s, aw); + + if (!ajp_marshal_into_msgb(msg, &s, l, ae)) + { + jk_log(l, JK_LOG_ERROR, "Creating AJP message for custom check failed"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + jk_log(l, JK_LOG_DEBUG, "sending custom check request..."); + + if (ajp_connection_tcp_send_message(ae, msg, l) != JK_TRUE) + { + jk_log(l, JK_LOG_ERROR, "can't send custom check request"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* wait for reply for timeout milliseconds */ + + jk_log(l, JK_LOG_DEBUG, "waiting for custom check response for %d ms...", timeout); + + if (jk_is_input_event(ae->sd, timeout, l) == JK_FALSE) + { + ae->last_errno = errno; + jk_log(l, JK_LOG_INFO, "timeout in custom check response"); + ajp_abort_endpoint(ae, JK_TRUE, l); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* 1) Unmarshal the SEND_HEADERS message, which contains the HTTP status code we need. + * 2) Throw away any SEND_BODY_CHUNK messages. + * 3) Stop when we get END_RESPONSE + * Anything else is considered an unexpected and an error. */ + + while (1) + { + jk_b_reset(msg); + if (ajp_connection_tcp_get_message(ae, msg, l) != JK_TRUE) + { + jk_log(l, JK_LOG_ERROR, "failed to get responses to custom check"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + + /* We need to consume the first byte so the buffer position is correct for unmarshaling the message. */ + + msgType = jk_b_get_byte(msg); + if (msgType == JK_AJP13_SEND_HEADERS) + { + if (!ajp_unmarshal_response(msg, &res, ae, l)) + { + jk_log(l, JK_LOG_ERROR, + "ajp_unmarshal_response on custom check failed"); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + } + else if (msgType == JK_AJP13_SEND_BODY_CHUNK) + { + jk_log(l, JK_LOG_DEBUG, "got BODY_CHUNK to custom check"); + continue; + } + else if (msgType == JK_AJP13_END_RESPONSE) + { + jk_log(l, JK_LOG_DEBUG, "got END_RESPONSE to custom check"); + break; + } + else + { + jk_log(l, JK_LOG_ERROR, "unexpected response check message type %d", + msgType); + JK_TRACE_EXIT(l); + return JK_FALSE; + } + } + + if (res.status == aw->custom_check_expected_code) + { + jk_log(l, JK_LOG_DEBUG, "mod_jk custom health check succeeded"); + JK_TRACE_EXIT(l); + return JK_TRUE; + } + else + { + jk_log(l, JK_LOG_ERROR, "Custom check request got HTTP status %d, expected %d", res.status, aw->custom_check_expected_code); + } + + JK_TRACE_EXIT(l); + + return JK_FALSE; +} + /** Handle the cping/cpong query * @param ae endpoint * @param timeout wait timeout in milliseconds @@ -879,6 +1090,12 @@ JK_TRACE_ENTER(l); + /* If a custom check URI and expected code are defined, delegate to the custom check function. */ + if (ae->worker->custom_check_uri && ae->worker->custom_check_expected_code > 0) { + JK_TRACE_EXIT(l); + return ajp_custom_check(ae, timeout, l); + } + ae->last_errno = 0; msg = jk_b_new(&ae->pool); if (!msg) { @@ -1057,6 +1274,9 @@ aw->retries = aw->s->retries; aw->retry_interval = aw->s->retry_interval; aw->max_packet_size = aw->s->max_packet_size; + aw->custom_check_expected_code = aw->s->custom_check_expected_code; + aw->custom_check_uri = aw->s->custom_check_uri; + aw->sequence = aw->s->h.sequence; if (aw->addr_sequence != aw->s->addr_sequence) { address_change = JK_TRUE; @@ -1125,6 +1345,9 @@ aw->s->retries = aw->retries; aw->s->retry_interval = aw->retry_interval; aw->s->max_packet_size = aw->max_packet_size; + aw->s->custom_check_expected_code = aw->custom_check_expected_code; + aw->s->custom_check_uri = aw->custom_check_uri; + /* Force squence update on push */ ++aw->s->h.sequence; @@ -2826,6 +3049,11 @@ jk_get_worker_prepost_timeout(props, p->name, AJP_DEF_PREPOST_TIMEOUT); + p->custom_check_expected_code = + jk_get_worker_custom_check_expected_status(props, p->name, JK_HTTP_OK); + + p->custom_check_uri = jk_get_worker_custom_check_uri(props, p->name); + if ((p->ping_mode & AJP_CPING_CONNECT) && p->connect_timeout == AJP_DEF_CONNECT_TIMEOUT) p->connect_timeout = p->ping_timeout; diff --exclude .project --exclude .cproject -Naur tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.h tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.h --- tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.h 2011-11-09 17:39:01.000000000 +0000 +++ tomcat-connectors-1.2.37-src/native/common/jk_ajp_common.h 2013-02-27 16:33:49.000000000 +0000 @@ -344,6 +344,11 @@ * as default for boolean valued connect and prepost timeouts. */ unsigned int ping_mode; /* Ping mode flags (which types of cpings should be used) */ + + /* Custom check settings. */ + const char* custom_check_uri; + int custom_check_expected_code; + /* * Recovery options */ diff --exclude .project --exclude .cproject -Naur tomcat-connectors-1.2.37-src/native/common/jk_shm.h tomcat-connectors-1.2.37-src/native/common/jk_shm.h --- tomcat-connectors-1.2.37-src/native/common/jk_shm.h 2012-05-05 09:50:28.000000000 +0100 +++ tomcat-connectors-1.2.37-src/native/common/jk_shm.h 2013-02-27 16:33:19.000000000 +0000 @@ -90,6 +90,8 @@ int ping_timeout; int reply_timeout; int prepost_timeout; + const char* custom_check_uri; + int custom_check_expected_code; unsigned int recovery_opts; int retries; int retry_interval; diff --exclude .project --exclude .cproject -Naur tomcat-connectors-1.2.37-src/native/common/jk_util.c tomcat-connectors-1.2.37-src/native/common/jk_util.c --- tomcat-connectors-1.2.37-src/native/common/jk_util.c 2012-05-15 18:19:03.000000000 +0100 +++ tomcat-connectors-1.2.37-src/native/common/jk_util.c 2013-02-27 14:53:00.000000000 +0000 @@ -56,6 +56,8 @@ #define RECOVERY_OPTS_OF_WORKER "recovery_options" #define CONNECT_TIMEOUT_OF_WORKER "connect_timeout" #define PREPOST_TIMEOUT_OF_WORKER "prepost_timeout" +#define CUSTOM_CHECK_EXPECTED_STATUS_OF_WORKER "custom_check_expected_status" +#define CUSTOM_CHECK_URI_OF_WORKER "custom_check_uri" #define REPLY_TIMEOUT_OF_WORKER "reply_timeout" #define SOCKET_TIMEOUT_OF_WORKER "socket_timeout" #define SOCKET_CONNECT_TIMEOUT_OF_WORKER "socket_connect_timeout" @@ -180,6 +182,8 @@ RECOVERY_OPTS_OF_WORKER, CONNECT_TIMEOUT_OF_WORKER, PREPOST_TIMEOUT_OF_WORKER, + CUSTOM_CHECK_EXPECTED_STATUS_OF_WORKER, + CUSTOM_CHECK_URI_OF_WORKER, PING_TIMEOUT_OF_WORKER, PING_MODE_OF_WORKER, REPLY_TIMEOUT_OF_WORKER, @@ -272,6 +276,8 @@ RECOVERY_OPTS_OF_WORKER, CONNECT_TIMEOUT_OF_WORKER, PREPOST_TIMEOUT_OF_WORKER, + CUSTOM_CHECK_EXPECTED_STATUS_OF_WORKER, + CUSTOM_CHECK_URI_OF_WORKER, PING_TIMEOUT_OF_WORKER, PING_MODE_OF_WORKER, REPLY_TIMEOUT_OF_WORKER, @@ -1124,6 +1130,31 @@ return jk_map_get_int(m, buf, def); } +int jk_get_worker_custom_check_expected_status(jk_map_t *m, const char *wname, int def) +{ + char buf[1024]; + + if (!m || !wname) { + return -1; + } + + MAKE_WORKER_PARAM(CUSTOM_CHECK_EXPECTED_STATUS_OF_WORKER); + + return jk_map_get_int(m, buf, def); +} + +const char *jk_get_worker_custom_check_uri(jk_map_t *m, const char *wname) +{ + char buf[1024]; + + if (!m || !wname) { + return NULL; + } + + MAKE_WORKER_PARAM(CUSTOM_CHECK_URI_OF_WORKER); + return jk_map_get_string(m, buf, NULL); +} + int jk_get_worker_ping_timeout(jk_map_t *m, const char *wname, int def) { char buf[1024]; diff --exclude .project --exclude .cproject -Naur tomcat-connectors-1.2.37-src/native/common/jk_util.h tomcat-connectors-1.2.37-src/native/common/jk_util.h --- tomcat-connectors-1.2.37-src/native/common/jk_util.h 2012-03-27 09:16:32.000000000 +0100 +++ tomcat-connectors-1.2.37-src/native/common/jk_util.h 2013-02-27 14:53:05.000000000 +0000 @@ -97,6 +97,10 @@ int jk_get_worker_prepost_timeout(jk_map_t *m, const char *wname, int def); +int jk_get_worker_custom_check_expected_status(jk_map_t *m, const char *wname, int def); + +const char *jk_get_worker_custom_check_uri(jk_map_t *m, const char *wname); + int jk_get_worker_ping_timeout(jk_map_t *m, const char *wname, int def); int jk_get_worker_ping_mode(jk_map_t *m, const char *wname, int def);