📄 jk_ajp_common.c
字号:
* 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 if 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, "(%s) Timeout with waiting reply from tomcat. " "Tomcat is down, stopped or network problems (errno=%d)", p->worker->name, p->last_errno); 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; } /* * 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; } } JK_TRACE_EXIT(l); return JK_REPLY_TIMEOUT; } } 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, "(%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. * 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 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; } } /* * 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, "(%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! * * 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; /* * 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; } } 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_STATUS_ERROR == rc || JK_STATUS_FATAL_ERROR == rc) { JK_TRACE_EXIT(l); return rc; } 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, "(%s) Tomcat is down or network problems", p->worker->name); jk_shutdown_socket(p->sd); p->sd = JK_INVALID_SOCKET; JK_TRACE_EXIT(l); return JK_FALSE; } } else 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_SERVER_ERROR; } 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_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; } else 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; } else if (JK_SERVER_ERROR == rc) { /* * Tomcat has stop talking to us, so get out. * Loadbalancer if present will decide if * failover is possible. */ JK_TRACE_EXIT(l); return JK_SERVER_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; int err = JK_TRUE; ajp_operation_t oper; ajp_operation_t *op = &oper; ajp_endpoint_t *p; JK_TRACE_ENTER(l); if (!e || !e->endpoint_private || !s || !is_error) { JK_LOG_NULL_PARAMS(l); if (is_error) *is_error = JK_HTTP_SERVER_ERROR; JK_TRACE_EXIT(l); return JK_FALSE; } p = e->endpoint_private; /* Set returned error to OK */ *is_error = JK_HTTP_OK; op->request = jk_b_new(&(p->pool)); if (!op->request) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } if (jk_b_set_buffer_size(op->request, p->worker->max_packet_size)) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message buffer"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } jk_b_reset(op->request); op->reply = jk_b_new(&(p->pool)); if (!op->reply) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } if (jk_b_set_buffer_size(op->reply, p->worker->max_packet_size)) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message buffer"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } jk_b_reset(op->reply); op->post = jk_b_new(&(p->pool)); if (!op->post) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } if (jk_b_set_buffer_size(op->post, p->worker->max_packet_size)) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "Failed allocating AJP message buffer"); JK_TRACE_EXIT(l); return JK_SERVER_ERROR; } 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)) { *is_error = JK_REQUEST_TOO_LARGE; jk_log(l, JK_LOG_INFO, "Creating AJP message failed, " "without recovery"); JK_TRACE_EXIT(l); return JK_CLIENT_ERROR; } if (JK_IS_DEBUG_LEVEL(l)) { jk_log(l, JK_LOG_DEBUG, "processing %s with %d retries", p->worker->name, p->worker->worker.retries); } /* * JK_RETRIES could be replaced by the number of workers in * a load-balancing configuration */ for (i = 0; i < p->worker->worker.retries; i++) { /* * We're using reqmsg which hold initial request * if Tomcat is stopped or restarted, we will pass reqmsg * to next valid tomcat. */ err = ajp_send_request(e, s, l, p, op); if (err == JK_TRUE) { /* If we have an unrecoverable error, it's probably because * the sender (browser) stopped sending data before the end * (certainly in a big post) */ if (!op->recoverable) { *is_error = JK_HTTP_SERVER_ERROR; jk_log(l, JK_LOG_ERROR, "(%s) sending request to tomcat failed " "without recovery in send loop %d", p->worker->name, i); JK_TRACE_EXIT(l); return JK_FALSE; } /* Up to there we can recover */ err = ajp_get_reply(e, s, l, p, op); if (err == JK_TRUE) { *is_error = JK_HTTP_OK; /* Done with the request */ JK_TRACE_EXIT(l); return JK_TRUE; } if (err == JK_CLIENT_RD_ERROR) { *is_error = JK_HTTP_BAD_REQUEST; if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) { /* Mark the endpoint for shutdown */ p->reuse = JK_FALSE; } jk_log(l, JK_LOG_INFO, "(%s) request failed, " "because of client read error " "without recovery in send loop attempt=%d", p->worker->name, i); JK_TRACE_EXIT(l); return JK_CLIENT_ERROR; } else if (err == JK_CLIENT_WR_ERROR) { /* XXX: Is this correct to log this as 200? */ *is_error = JK_HTTP_OK; if (p->worker->recovery_opts & RECOVER_ABORT_IF_CLIENTERROR) { /* Mark the endpoint for shutdown */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -