in native/common/jk_ajp_common.c [2590:2920]
static int JK_METHOD ajp_service(jk_endpoint_t *e,
jk_ws_service_t *s,
jk_log_context_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;
int busy;
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;
}
/* Reset endpoint read and write sizes for
* this request.
*/
e->rd = e->wr = 0;
e->recoverable = JK_TRUE;
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,
"(%s) Failed allocating AJP request message", aw->name);
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,
"(%s) Failed allocating AJP request message buffer", aw->name);
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,
"(%s) Failed allocating AJP reply message", aw->name);
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,
"(%s) Failed allocating AJP reply message buffer", aw->name);
JK_TRACE_EXIT(l);
return JK_SERVER_ERROR;
}
op->post = jk_b_new(&(p->pool));
if (!op->post) {
jk_log(l, JK_LOG_ERROR,
"(%s) Failed allocating AJP post message", aw->name);
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,
"(%s) Failed allocating AJP post message buffer", aw->name);
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,
"(%s) Creating AJP message failed "
"without recovery - check max_packet_size", aw->name);
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);
}
busy = JK_ATOMIC_INCREMENT(&(aw->s->busy));
if (aw->busy_limit > 0 && busy > aw->busy_limit) {
JK_ATOMIC_DECREMENT(&(aw->s->busy));
e->recoverable = JK_TRUE;
aw->s->errors++;
aw->s->error_time = time(NULL);
*is_error = JK_HTTP_SERVER_BUSY;
rc = JK_BUSY_ERROR;
jk_log(l, JK_LOG_ERROR,
"(%s) sending request to tomcat failed (recoverable), "
"busy limit %d reached (rc=%d, errors=%d, client_errors=%d).",
aw->name, aw->busy_limit, rc, aw->s->errors,
aw->s->client_errors);
JK_TRACE_EXIT(l);
return rc;
}
if (aw->s->state == JK_AJP_STATE_ERROR)
aw->s->state = JK_AJP_STATE_PROBE;
if (busy > aw->s->max_busy)
aw->s->max_busy = 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,
"(%s) retry %d, sleeping for %d ms before retrying",
aw->name, 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 (%srecoverable)",
aw->name, err, op->recoverable ? "" : "un");
}
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);
}
ajp_update_stats(e, aw, rc, l);
/* Log the error only once per failed request.
*/
jk_log(l, JK_LOG_ERROR,
"(%s) connecting to tomcat failed "
"(rc=%d, errors=%d, client_errors=%d).",
aw->name, rc, aw->s->errors, aw->s->client_errors);
JK_TRACE_EXIT(l);
return rc;
}