static int ajp_send_request()

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;
}