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