static int ajp_process_callback()

in native/common/jk_ajp_common.c [1957:2239]


static int ajp_process_callback(jk_msg_buf_t *msg,
                                jk_msg_buf_t *pmsg,
                                ajp_endpoint_t * ae,
                                jk_ws_service_t *s, jk_log_context_t *l)
{
    int code = (int)jk_b_get_byte(msg);

    JK_TRACE_ENTER(l);

    switch (code) {
    case JK_AJP13_SEND_HEADERS:
        {
            int rc;
            jk_res_data_t res;
            if (ae->last_op == JK_AJP13_SEND_HEADERS) {
                /* Do not send anything to the client.
                 * Backend already send us the headers.
                 */
                if (JK_IS_DEBUG_LEVEL(l)) {
                    jk_log(l, JK_LOG_DEBUG,
                           "(%s) Already received AJP13_SEND HEADERS",
                           ae->worker->name);
                }
                JK_TRACE_EXIT(l);
                return JK_AJP13_ERROR;
            }
            if (!ajp_unmarshal_response(msg, &res, ae, l)) {
                jk_log(l, JK_LOG_ERROR,
                       "(%s) ajp_unmarshal_response failed", ae->worker->name);
                JK_TRACE_EXIT(l);
                return JK_AJP13_ERROR;
            }
            if (s->num_resp_headers > 0) {
                char **old_names = res.header_names;
                char **old_values = res.header_values;
                if (JK_IS_DEBUG_LEVEL(l))
                    jk_log(l, JK_LOG_DEBUG, "(%s) Adding %d response headers "
                           "to %d headers received from tomcat",
                           ae->worker->name, s->num_resp_headers,
                           res.num_headers);
                res.header_names  = jk_pool_alloc(s->pool,
                                                  (s->num_resp_headers +
                                                   res.num_headers) *
                                                   sizeof(char *));
                res.header_values = jk_pool_alloc(s->pool,
                                                  (s->num_resp_headers +
                                                   res.num_headers) *
                                                   sizeof(char *));
                if (!res.header_names || !res.header_values) {
                    jk_log(l, JK_LOG_ERROR,
                           "(%s) Failed allocating one %d response headers.",
                           ae->worker->name,
                           s->num_resp_headers + res.num_headers);
                    res.header_names = old_names;
                    res.header_values = old_values;
                }
                else {
                    if (res.num_headers) {
                        memcpy(res.header_names, old_names,
                               res.num_headers * sizeof(char *));
                        memcpy(res.header_values, old_values,
                               res.num_headers * sizeof(char *));
                    }
                    if (s->num_resp_headers) {
                        memcpy(res.header_names + res.num_headers,
                               s->resp_headers_names,
                               s->num_resp_headers * sizeof(char *));
                        memcpy(res.header_values + res.num_headers,
                               s->resp_headers_values,
                               s->num_resp_headers * sizeof(char *));
                    }
                    res.num_headers = res.num_headers + s->num_resp_headers;
                }
            }
            s->http_response_status = res.status;
            if (s->extension.fail_on_status_size > 0)
                rc = is_http_status_fail(s->extension.fail_on_status_size,
                                         s->extension.fail_on_status,
                                         res.status);
            else
                rc = is_http_status_fail(ae->worker->http_status_fail_num,
                                         ae->worker->http_status_fail,
                                         res.status);
            if (rc > 0) {
                JK_TRACE_EXIT(l);
                return JK_STATUS_FATAL_ERROR;
            }
            if (rc < 0) {
                JK_TRACE_EXIT(l);
                return JK_STATUS_ERROR;
            }

            if (s->extension.use_server_error_pages &&
                s->http_response_status >= s->extension.use_server_error_pages)
                s->response_blocked = JK_TRUE;

            /*
             * Call even if response is blocked, since it also handles
             * forwarding some headers for special http status codes
             * even if the server uses an own error page.
             * Example: The WWW-Authenticate header in case of
             * HTTP_UNAUTHORIZED (401).
             */
            s->start_response(s, res.status, res.msg,
                              (const char *const *)res.header_names,
                              (const char *const *)res.header_values,
                              res.num_headers);

            if (!s->response_blocked) {
                if (s->flush && s->flush_header)
                    s->flush(s);
            }
        }
        return JK_AJP13_SEND_HEADERS;

    case JK_AJP13_SEND_BODY_CHUNK:
        if (ae->last_op == JK_AJP13_FORWARD_REQUEST) {
            /* AJP13_SEND_BODY_CHUNK with length 0 is
             * explicit flush packet message.
             * Ignore those if they are left over from previous responses.
             * Reportedly some versions of JBoss suffer from that problem.
             */
            if (jk_b_get_int(msg) == 0) {
                jk_log(l, JK_LOG_DEBUG,
                       "(%s) Ignoring flush message received while sending the "
                       "request", ae->worker->name);
                return ae->last_op;
            }
            /* We have just send a request but received something
             * that probably originates from buffered response.
             */
            if (JK_IS_DEBUG_LEVEL(l)) {
                jk_log(l, JK_LOG_DEBUG,
                       "(%s) Unexpected AJP13_SEND_BODY_CHUNK",
                       ae->worker->name);
            }
            JK_TRACE_EXIT(l);
            return JK_AJP13_ERROR;
        }
        if (!s->response_blocked) {
            unsigned int len;
            /* Check there is sufficient data in the ajp message to read a valid
             * length. It must be at least 3 bytes - the magic byte for
             * JK_AJP13_SEND_BODY_CHUNK (1 byte) and the length of the chunk
             * (2 bytes). The remaining part of the message is the chunk.
             */
            if (msg->len < 3) {
                jk_log(l, JK_LOG_ERROR,
                       "(%s) Invalid AJP message. Length of AJP message "
                       "is %d, but it should be at least 3.",
                       ae->worker->name, msg->len);
                JK_TRACE_EXIT(l);
                return JK_INTERNAL_ERROR;
            }
            /*
             * Once we have len, do a sanity check to prevent reading beyond
             * buffer boundaries and thus revealing possible sensitive memory
             * contents to the client.
             */
            len = (unsigned int)jk_b_get_int(msg);
            if (len > (unsigned int)(msg->len - 3)) {
                jk_log(l, JK_LOG_ERROR,
                       "(%s) Chunk length too large. Length of AJP message "
                       "is %d, chunk length is %d.",
                       ae->worker->name, msg->len, len);
                JK_TRACE_EXIT(l);
                return JK_INTERNAL_ERROR;
            }
            if (len == 0) {
                /* AJP13_SEND_BODY_CHUNK with length 0 is
                 * explicit flush packet message.
                 */
                if (s->response_started) {
                    if (s->flush) {
                        s->flush(s);
                    }
                }
                else {
                    jk_log(l, JK_LOG_DEBUG,
                           "(%s) Ignoring flush message received before headers",
                           ae->worker->name);
                }
            }
            else {
                if (!s->write(s, msg->buf + msg->pos, len)) {
                    jk_log(l, JK_LOG_INFO,
                           "(%s) Writing to client aborted or client network "
                           "problems", ae->worker->name);
                    JK_TRACE_EXIT(l);
                    return JK_CLIENT_WR_ERROR;
                }
                if (s->flush && s->flush_packets)
                    s->flush(s);
            }
        }
        break;

    case JK_AJP13_GET_BODY_CHUNK:
        {
            int len = (int)jk_b_get_int(msg);
            if (len == 0xFFFF) {
                jk_log(l, JK_LOG_ERROR,
                       "(%s) Invalid AJP message: Not enough bytes available to read chunk length",
                       ae->worker->name);
                JK_TRACE_EXIT(l);
                return JK_AJP13_ERROR;
            }

            /* the right place to add file storage for upload
             */
            if ((len = ajp_read_into_msg_buff(ae, s, pmsg, len, l)) >= 0) {
                s->content_read += (jk_uint64_t)len;
                JK_TRACE_EXIT(l);
                return JK_AJP13_HAS_RESPONSE;
            }

            jk_log(l, JK_LOG_INFO,
                   "(%s) Reading from client aborted or client network problems",
                   ae->worker->name);

            JK_TRACE_EXIT(l);
            return JK_CLIENT_RD_ERROR;
        }
        break;

    case JK_AJP13_END_RESPONSE:
        ae->reuse = (int)jk_b_get_byte(msg);
        if (ae->reuse == 0xFF) {
            jk_log(l, JK_LOG_ERROR,
                   "(%s) Not enough bytes available to read reuse flag",
                   ae->worker->name);
            JK_TRACE_EXIT(l);
            return JK_AJP13_ERROR;
        } else  if (!ae->reuse) {
            /*
             * AJP13 protocol reuse flag set to false.
             * Tomcat will close its side of the connection.
             */
            jk_log(l, JK_LOG_WARNING, "(%s) AJP13 protocol: Reuse is set to false",
                   ae->worker->name);
        }
        else if (s->disable_reuse) {
            if (JK_IS_DEBUG_LEVEL(l)) {
                jk_log(l, JK_LOG_DEBUG, "(%s) AJP13 protocol: Reuse is disabled",
                       ae->worker->name);
            }
            ae->reuse = JK_FALSE;
        }
        else {
            /* Reuse in all cases
             */
            if (JK_IS_DEBUG_LEVEL(l)) {
                jk_log(l, JK_LOG_DEBUG, "(%s) AJP13 protocol: Reuse is OK",
                       ae->worker->name);
            }
            ae->reuse = JK_TRUE;
        }
        if (!s->response_blocked) {
            if (s->done) {
                /* Done with response
                 */
                s->done(s);
            }
            else if (s->flush && !s->flush_packets) {
                /* Flush after the last write
                 */
                s->flush(s);
            }
        }
        JK_TRACE_EXIT(l);
        return JK_AJP13_END_RESPONSE;
        break;

    default:
        jk_log(l, JK_LOG_ERROR,
               "(%s) Unknown AJP protocol code: %02X", ae->worker->name, code);
        JK_TRACE_EXIT(l);
        return JK_AJP13_ERROR;
    }

    JK_TRACE_EXIT(l);
    return JK_AJP13_NO_RESPONSE;
}