📄 jk_ajp_common.c
字号:
/* If we got an error or can't send data, then try to get a pooled
* connection and try again. If we are succesful, break out of this
* loop. */
if (err ||
((rc = ajp_connection_tcp_send_message(ae, op->request, l)) != JK_TRUE)) {
if (rc != JK_FATAL_ERROR) {
jk_log(l, JK_LOG_INFO,
"Error sending request. Will try another pooled connection");
ajp_next_connection(ae, l);
}
else {
op->recoverable = JK_FALSE;
jk_log(l, JK_LOG_INFO,
"Error sending request. Unrecoverable operation");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
else
break;
}
/*
* If we failed to reuse a connection, try to reconnect.
*/
if (ae->sd < 0) {
if (err) {
/* XXX: If err is set, the tomcat is either dead or disconnected */
jk_log(l, JK_LOG_INFO,
"All endpoints are disconnected or dead");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
/* no need to handle cping/cpong here since it should be at connection time */
if (ajp_connect_to_endpoint(ae, l) == JK_TRUE) {
/*
* After we are connected, each error that we are going to
* have is probably unrecoverable
*/
if (ajp_connection_tcp_send_message(ae, op->request, l) != JK_TRUE) {
jk_log(l, JK_LOG_INFO,
"Error sending request on a fresh connection");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
else {
/* Close the socket if unable to connect */
jk_close_socket(ae->sd);
ae->sd = -1;
jk_log(l, JK_LOG_INFO,
"Error connecting to the Tomcat process.");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
/*
* From now on an error means that we have an internal server error
* or Tomcat crashed. In any case we cannot recover this.
*/
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"request body to send %d - request body to resend %d",
ae->left_bytes_to_send, op->reply->len - AJP_HEADER_LEN);
/*
* 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) {
if (ajp_connection_tcp_send_message(ae, op->post, l) != JK_TRUE) {
jk_log(l, JK_LOG_ERROR, "Error resending request body (%d)",
postlen);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
else {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Resent the request body (%d)",
postlen);
}
}
else if (s->reco_status == RECO_FILLED) {
/* Recovery in LB MODE */
postlen = s->reco_buf->len;
if (postlen > AJP_HEADER_LEN) {
if (ajp_connection_tcp_send_message(ae, s->reco_buf, l) != JK_TRUE) {
jk_log(l, JK_LOG_ERROR,
"Error resending request body (lb mode) (%d)",
postlen);
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
else {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Resent the request body (lb mode) (%d)", 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 = ae->left_bytes_to_send;
if (len > AJP13_MAX_SEND_BODY_SZ) {
len = AJP13_MAX_SEND_BODY_SZ;
}
if ((len = ajp_read_into_msg_buff(ae, s, op->post, len, l)) < 0) {
/* the browser stop sending data, no need to recover */
op->recoverable = JK_FALSE;
JK_TRACE_EXIT(l);
return len;
}
/* 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;
}
s->content_read = len;
if (ajp_connection_tcp_send_message(ae, op->post, l) != JK_TRUE) {
jk_log(l, JK_LOG_ERROR, "Error sending request body");
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
}
JK_TRACE_EXIT(l);
return (JK_TRUE);
}
/*
* What to do with incoming data (dispatcher)
*/
static int ajp_process_callback(jk_msg_buf_t *msg,
jk_msg_buf_t *pmsg,
ajp_endpoint_t * ae,
jk_ws_service_t *r, jk_logger_t *l)
{
int code = (int)jk_b_get_byte(msg);
JK_TRACE_ENTER(l);
switch (code) {
case JK_AJP13_SEND_HEADERS:
{
jk_res_data_t res;
if (!ajp_unmarshal_response(msg, &res, ae, l)) {
jk_log(l, JK_LOG_ERROR,
"ajp_unmarshal_response failed");
JK_TRACE_EXIT(l);
return JK_AJP13_ERROR;
}
r->start_response(r, res.status, res.msg,
(const char *const *)res.header_names,
(const char *const *)res.header_values,
res.num_headers);
}
return JK_AJP13_SEND_HEADERS;
case JK_AJP13_SEND_BODY_CHUNK:
{
unsigned int len = (unsigned int)jk_b_get_int(msg);
/*
* Do a sanity check on len to prevent write reading beyond buffer
* boundaries and thus revealing possible sensitive memory
* contents to the client.
* len cannot be larger than msg->len - 3 because the ajp message
* contains 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 (len > (unsigned int)(msg->len - 3)) {
jk_log(l, JK_LOG_ERROR,
"Chunk length too large. Length of AJP message is %i,"
" chunk length is %i.", msg->len, len);
JK_TRACE_EXIT(l);
return JK_INTERNAL_ERROR;
}
if (!r->write(r, msg->buf + msg->pos, len)) {
jk_log(l, JK_LOG_INFO,
"Connection aborted or network problems");
JK_TRACE_EXIT(l);
return JK_CLIENT_ERROR;
}
if (r->flush && r->flush_packets)
r->flush(r);
}
break;
case JK_AJP13_GET_BODY_CHUNK:
{
int len = (int)jk_b_get_int(msg);
if (len < 0) {
len = 0;
}
if (len > AJP13_MAX_SEND_BODY_SZ) {
len = AJP13_MAX_SEND_BODY_SZ;
}
if ((unsigned int)len > ae->left_bytes_to_send) {
len = ae->left_bytes_to_send;
}
/* the right place to add file storage for upload */
if ((len = ajp_read_into_msg_buff(ae, r, pmsg, len, l)) >= 0) {
r->content_read += len;
JK_TRACE_EXIT(l);
return JK_AJP13_HAS_RESPONSE;
}
jk_log(l, JK_LOG_INFO,
"Connection aborted or network problems");
JK_TRACE_EXIT(l);
return JK_CLIENT_ERROR;
}
break;
case JK_AJP13_END_RESPONSE:
ae->reuse = (int)jk_b_get_byte(msg);
if (!ae->reuse) {
/*
* Strange protocol error.
*/
jk_log(l, JK_LOG_INFO, " Protocol error: Reuse is set to false");
}
/* Flush after the last write */
if (r->flush && !r->flush_packets)
r->flush(r);
/* Reuse in all cases */
ae->reuse = JK_TRUE;
JK_TRACE_EXIT(l);
return JK_AJP13_END_RESPONSE;
break;
default:
jk_log(l, JK_LOG_ERROR,
"Invalid code: %d", code);
JK_TRACE_EXIT(l);
return JK_AJP13_ERROR;
}
JK_TRACE_EXIT(l);
return JK_AJP13_NO_RESPONSE;
}
/*
* get replies from Tomcat via Ajp13/Ajp14
* We will know only at read time if the remote host closed
* the connection (half-closed state - FIN-WAIT2). In that case
* we must close our side of the socket and abort emission.
* We will need another connection to send the request
* There is need of refactoring here since we mix
* reply reception (tomcat -> apache) and request send (apache -> tomcat)
* and everything using the same buffer (repmsg)
* ajp13/ajp14 is async but handling read/send this way prevent nice recovery
* In fact if tomcat link is broken during upload (browser -> apache -> tomcat)
* we'll loose data and we'll have to abort the whole request.
*/
static int ajp_get_reply(jk_endpoint_t *e,
jk_ws_service_t *s,
jk_logger_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);
/* Start read all reply message */
while (1) {
int rc = 0;
/* If we set a reply timeout, check it something is available */
if (p->worker->reply_timeout != 0) {
if (ajp_is_input_event(p, p->worker->reply_timeout, l) ==
JK_FALSE) {
jk_log(l, JK_LOG_ERROR,
"Timeout with waiting reply from tomcat. "
"Tomcat is down, stopped or network problems.");
if (headeratclient == JK_FALSE) {
if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCGETREQUEST)
op->recoverable = JK_FALSE;
}
else {
if (p->worker->recovery_opts & RECOVER_ABORT_IF_TCSENDHEADER)
op->recoverable = JK_FALSE;
}
JK_TRACE_EXIT(l);
return JK_FALSE;
}
}
if (!ajp_connection_tcp_get_message(p, op->reply, l)) {
/* we just can't recover, unset recover flag */
if (headeratclient == JK_FALSE) {
jk_log(l, JK_LOG_ERROR,
"Tomcat is down or refused connection. "
"No response has been sent to the client (yet)");
/*
* communication with tomcat has been interrupted BEFORE
* headers have been sent to the client.
* DISCUSSION: As we suppose that tomcat has already started
* to process the query we think it's unrecoverable (and we
* should not retry or switch to another tomcat in the
* cluster).
*/
/*
* 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 want to display the webservers error page, therefore
* we return JK_FALSE
*/
JK_TRACE_EXIT(l);
return JK_FALSE;
}
else {
jk_log(l, JK_LOG_ERROR,
"Tomcat is down or network problems. "
"Part of the response has already been sent to the client");
/* 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!
*
* BUT if you retrun JK_TRUE you have a 200 (OK) code in your
* in your apache access.log instead of a 500 (Error).
* Therefore return FALSE/FALSE
* return JK_TRUE;
*/
/*
* 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 JK_FALSE;
}
}
rc = ajp_process_callback(op->reply, op->post, p, s, l);
/* no more data to be sent, fine we have finish here */
if (JK_AJP13_END_RESPONSE == rc) {
JK_TRACE_EXIT(l);
return JK_TRUE;
}
else if (JK_AJP13_SEND_HEADERS == rc) {
headeratclient = JK_TRUE;
}
else if (JK_AJP13_HAS_RESPONSE == rc) {
/*
* in upload-mode there is no second chance since
* we may have allready 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -