in native/common/jk_ajp_common.c [1645:1951]
static int ajp_send_request(jk_endpoint_t *e,
jk_ws_service_t *s,
jk_log_context_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 or all connections are in use
*/
if (err_conn + err_cping + err_send > 0)
if (err_cping + err_send > 0)
jk_log(l, JK_LOG_INFO,
"(%s) no usable connection found, will create a new one, "
"detected by connect check (%d), cping (%d), send (%d).",
ae->worker->name, err_conn, err_cping, err_send);
else
jk_log(l, JK_LOG_DEBUG,
"(%s) no usable connection found, will create a new one, "
"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) no usable connection found, will create a new one.",
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->post->len > AJP_HEADER_LEN ?
op->post->len - AJP_HEADER_LEN : 0);
/*
* 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, "(%s) Resent the request body (%d)",
ae->worker->name, 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,
"(%s) Resent the request body (lb mode) (%d)",
ae->worker->name, 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;
if ((len = ajp_read_into_msg_buff(ae, s, op->post, -1, 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;
}