in native/common/jk_ajp_common.c [2280:2506]
static int ajp_get_reply(jk_endpoint_t *e,
jk_ws_service_t *s,
jk_log_context_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;
}
if (JK_AJP13_SEND_HEADERS == rc) {
if (headeratclient == JK_FALSE) {
headeratclient = JK_TRUE;
continue;
}
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;
}
}
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;
}
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 != JK_TRUE) {
jk_log(l, JK_LOG_ERROR,
"(%s) Tomcat is down or network problems",
p->worker->name);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
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;
}
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;
}
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;
}
if (JK_INTERNAL_ERROR == rc) {
/*
* Internal error, like memory allocation or invalid packet lengths.
*/
JK_TRACE_EXIT(l);
return JK_FATAL_ERROR;
}
if (JK_AJP13_NO_RESPONSE == rc) {
/*
* This is fine, loop again, more data to send.
*/
continue;
}
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;
}