static int JK_METHOD ajp_service()

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