static int ajp_get_reply()

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