📄 jk_ajp_common.c
字号:
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); 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; rc = ajp_connection_tcp_send_message(p, op->post, l); if (rc < 0) { jk_log(l, JK_LOG_ERROR, "Tomcat is down or network problems.", rc); JK_TRACE_EXIT(l); return JK_FALSE; } } else if (JK_FATAL_ERROR == rc) { /* * we won't be able to gracefully recover from this so * set recoverable to false and get out. */ op->recoverable = JK_FALSE; JK_TRACE_EXIT(l); return JK_FALSE; } else if (JK_CLIENT_ERROR == rc) { /* * Client has stop talking to us, so get out. * We assume this isn't our fault, so just a normal exit. * In most (all?) cases, the ajp13_endpoint::reuse will still be * false here, so this will be functionally the same as an * un-recoverable error. We just won't log it as such. */ JK_TRACE_EXIT(l); return JK_CLIENT_ERROR; } else if (rc < 0) { JK_TRACE_EXIT(l); return (JK_FALSE); /* XXX error */ } } /* XXX: Not reached? */ JK_TRACE_EXIT(l); return JK_FALSE;}/* * service is now splitted in ajp_send_request and ajp_get_reply * much more easier to do errors recovery * * We serve here the request, using AJP13/AJP14 (e->proto) * */static int JK_METHOD ajp_service(jk_endpoint_t *e, jk_ws_service_t *s, jk_logger_t *l, int *is_error){ int i, err; ajp_operation_t oper; ajp_operation_t *op = &oper; JK_TRACE_ENTER(l); if (is_error) *is_error = JK_HTTP_SERVER_ERROR; if (e && e->endpoint_private && s && is_error) { ajp_endpoint_t *p = e->endpoint_private; op->request = jk_b_new(&(p->pool)); jk_b_set_buffer_size(op->request, DEF_BUFFER_SZ); jk_b_reset(op->request); op->reply = jk_b_new(&(p->pool)); jk_b_set_buffer_size(op->reply, DEF_BUFFER_SZ); jk_b_reset(op->reply); op->post = jk_b_new(&(p->pool)); jk_b_set_buffer_size(op->post, DEF_BUFFER_SZ); jk_b_reset(op->post); op->recoverable = JK_TRUE; op->uploadfd = -1; /* not yet used, later ;) */ p->left_bytes_to_send = s->content_length; p->reuse = JK_FALSE; s->secret = p->worker->secret; /* * We get here initial request (in reqmsg) */ if (!ajp_marshal_into_msgb(op->request, s, l, p)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -