X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=modules%2Fproxy%2Fmod_proxy_ajp.c;h=3465a97dab7f725649628c09e8212105e2194c29;hb=427c85bd2353796cbd8253854dd2fb2ccfbf8bdc;hp=4beae30b273ff967e9039b303d3ff6b1329a1557;hpb=631a73fbdc1e2db73b29b1f15f190877412027c2;p=apache diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c index 4beae30b27..3465a97dab 100644 --- a/modules/proxy/mod_proxy_ajp.c +++ b/modules/proxy/mod_proxy_ajp.c @@ -29,7 +29,8 @@ module AP_MODULE_DECLARE_DATA proxy_ajp_module; */ static int proxy_ajp_canon(request_rec *r, char *url) { - char *host, *path, *search, sport[7]; + char *host, *path, sport[7]; + char *search = NULL; const char *err; apr_port_t port = AJP13_DEF_PORT; @@ -41,7 +42,7 @@ static int proxy_ajp_canon(request_rec *r, char *url) return DECLINED; } - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, r->server, "proxy: AJP: canonicalising URL %s", url); /* @@ -57,23 +58,18 @@ static int proxy_ajp_canon(request_rec *r, char *url) } /* - * now parse path/search args, according to rfc1738 - * - * N.B. if this isn't a true proxy request, then the URL _path_ - * has already been decoded. True proxy requests have - * r->uri == r->unparsed_uri, and no others have that property. + * now parse path/search args, according to rfc1738: + * process the path. With proxy-nocanon set (by + * mod_proxy) we use the raw, unparsed uri */ - if (r->uri == r->unparsed_uri) { - search = strchr(url, '?'); - if (search != NULL) - *(search++) = '\0'; + if (apr_table_get(r->notes, "proxy-nocanon")) { + path = url; /* this is the raw path */ } - else + else { + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); search = r->args; - - /* process path */ - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + } if (path == NULL) return HTTP_BAD_REQUEST; @@ -89,6 +85,58 @@ static int proxy_ajp_canon(request_rec *r, char *url) return OK; } +#define METHOD_NON_IDEMPOTENT 0 +#define METHOD_IDEMPOTENT 1 +#define METHOD_IDEMPOTENT_WITH_ARGS 2 + +static int is_idempotent(request_rec *r) +{ + /* + * RFC2616 (9.1.2): GET, HEAD, PUT, DELETE, OPTIONS, TRACE are considered + * idempotent. Hint: HEAD requests use M_GET as method number as well. + */ + switch (r->method_number) { + case M_GET: + case M_DELETE: + case M_PUT: + case M_OPTIONS: + case M_TRACE: + /* + * If the request has arguments it might have side-effects and thus + * it might be undesirable to resend it to a backend again + * automatically. + */ + if (r->args) { + return METHOD_IDEMPOTENT_WITH_ARGS; + } + return METHOD_IDEMPOTENT; + /* Everything else is not considered idempotent. */ + default: + return METHOD_NON_IDEMPOTENT; + } +} + +static apr_off_t get_content_length(request_rec * r) +{ + apr_off_t len = 0; + + if (r->clength > 0) { + return r->clength; + } + else if (r->main == NULL) { + const char *clp = apr_table_get(r->headers_in, "Content-Length"); + + if (clp) { + char *errp; + if (apr_strtoff(&len, clp, &errp, 10) || *errp || len < 0) { + len = 0; /* parse error */ + } + } + } + + return len; +} + /* * XXX: AJP Auto Flushing * @@ -122,20 +170,29 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, apr_bucket_brigade *input_brigade; apr_bucket_brigade *output_brigade; ajp_msg_t *msg; - apr_size_t bufsiz; + apr_size_t bufsiz = 0; char *buff; + char *send_body_chunk_buff; apr_uint16_t size; + apr_byte_t conn_reuse = 0; const char *tenc; int havebody = 1; - int isok = 1; + int output_failed = 0; + int backend_failed = 0; apr_off_t bb_len; int data_sent = 0; + int request_ended = 0; + int headers_sent = 0; int rv = 0; apr_int32_t conn_poll_fd; apr_pollfd_t *conn_poll; proxy_server_conf *psf = ap_get_module_config(r->server->module_config, &proxy_module); apr_size_t maxsize = AJP_MSG_BUFFER_SZ; + int send_body = 0; + apr_off_t content_length = 0; + int original_status = r->status; + const char *original_status_line = r->status_line; if (psf->io_buffer_size_set) maxsize = psf->io_buffer_size; @@ -156,11 +213,22 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: AJP: request failed to %pI (%s)", conn->worker->cp->addr, - conn->worker->hostname); + conn->worker->s->hostname); if (status == AJP_EOVERFLOW) return HTTP_BAD_REQUEST; - else - return HTTP_SERVICE_UNAVAILABLE; + else if (status == AJP_EBAD_METHOD) { + return HTTP_NOT_IMPLEMENTED; + } else { + /* + * This is only non fatal when the method is idempotent. In this + * case we can dare to retry it with a different worker if we are + * a balancer member. + */ + if (is_idempotent(r) == METHOD_IDEMPOTENT) { + return HTTP_SERVICE_UNAVAILABLE; + } + return HTTP_INTERNAL_SERVER_ERROR; + } } /* allocate an AJP message to store the data of the buckets */ @@ -182,6 +250,8 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: request is chunked"); } else { + /* Get client provided Content-Length header */ + content_length = get_content_length(r); status = ap_get_brigade(r->input_filters, input_brigade, AP_MODE_READBYTES, APR_BLOCK_READ, maxsize - AJP_HEADER_SZ); @@ -192,7 +262,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "proxy: ap_get_brigade failed"); apr_brigade_destroy(input_brigade); - return HTTP_INTERNAL_SERVER_ERROR; + return HTTP_BAD_REQUEST; } /* have something */ @@ -221,6 +291,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, "proxy: got %" APR_SIZE_T_FMT " bytes of data", bufsiz); if (bufsiz > 0) { status = ajp_send_data_msg(conn->sock, msg, bufsiz); + ajp_msg_log(r, msg, "First ajp_send_data_msg: ajp_ilink_send packet dump"); if (status != APR_SUCCESS) { /* We had a failure: Close connection to backend */ conn->close++; @@ -228,10 +299,32 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: send failed to %pI (%s)", conn->worker->cp->addr, - conn->worker->hostname); - return HTTP_SERVICE_UNAVAILABLE; + conn->worker->s->hostname); + /* + * It is fatal when we failed to send a (part) of the request + * body. + */ + return HTTP_INTERNAL_SERVER_ERROR; } conn->worker->s->transferred += bufsiz; + send_body = 1; + } + else if (content_length > 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: read zero bytes, expecting" + " %" APR_OFF_T_FMT " bytes", + content_length); + /* + * We can only get here if the client closed the connection + * to us without sending the body. + * Now the connection is in the wrong state on the backend. + * Sending an empty data msg doesn't help either as it does + * not move this connection to the correct state on the backend + * for later resusage by the next request again. + * Close it to clean things up. + */ + conn->close++; + return HTTP_BAD_REQUEST; } } @@ -246,8 +339,26 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, "proxy: read response failed from %pI (%s)", conn->worker->cp->addr, - conn->worker->hostname); - return HTTP_SERVICE_UNAVAILABLE; + conn->worker->s->hostname); + + /* If we had a successful cping/cpong and then a timeout + * we assume it is a request that cause a back-end timeout, + * but doesn't affect the whole worker. + */ + if (APR_STATUS_IS_TIMEUP(status) && conn->worker->s->ping_timeout_set) { + return HTTP_GATEWAY_TIME_OUT; + } + + /* + * This is only non fatal when we have not sent (parts) of a possible + * request body so far (we do not store it and thus cannot send it + * again) and the method is idempotent. In this case we can dare to + * retry it with a different worker if we are a balancer member. + */ + if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) { + return HTTP_SERVICE_UNAVAILABLE; + } + return HTTP_INTERNAL_SERVER_ERROR; } /* parse the reponse */ result = ajp_parse_type(r, conn->data); @@ -264,7 +375,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, conn_poll->desc.s = conn->sock; bufsiz = maxsize; - while (isok) { + for (;;) { switch (result) { case CMD_AJP13_GET_BODY_CHUNK: if (havebody) { @@ -283,6 +394,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server, "ap_get_brigade failed"); + output_failed = 1; break; } bufsiz = maxsize; @@ -293,6 +405,7 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server, "apr_brigade_flatten failed"); + output_failed = 1; break; } } @@ -300,9 +413,11 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, ajp_msg_reset(msg); /* will go in ajp_send_data_msg */ status = ajp_send_data_msg(conn->sock, msg, bufsiz); + ajp_msg_log(r, msg, "ajp_send_data_msg after CMD_AJP13_GET_BODY_CHUNK: ajp_ilink_send packet dump"); if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server, "ajp_send_data_msg failed"); + backend_failed = 1; break; } conn->worker->s->transferred += bufsiz; @@ -313,97 +428,161 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "ap_proxy_ajp_request error read after end"); - isok = 0; + backend_failed = 1; } break; case CMD_AJP13_SEND_HEADERS: + if (headers_sent) { + /* Do not send anything to the client. + * Backend already send us the headers. + */ + backend_failed = 1; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: Backend sent headers twice."); + break; + } /* AJP13_SEND_HEADERS: process them */ status = ajp_parse_header(r, conf, conn->data); if (status != APR_SUCCESS) { - isok = 0; + backend_failed = 1; + } + else if ((r->status == 401) && conf->error_override) { + const char *buf; + const char *wa = "WWW-Authenticate"; + if ((buf = apr_table_get(r->headers_out, wa))) { + apr_table_set(r->err_headers_out, wa, buf); + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ap_proxy_ajp_request: origin server " + "sent 401 without WWW-Authenticate header"); + } } + headers_sent = 1; break; case CMD_AJP13_SEND_BODY_CHUNK: /* AJP13_SEND_BODY_CHUNK: piece of data */ - status = ajp_parse_data(r, conn->data, &size, &buff); + status = ajp_parse_data(r, conn->data, &size, &send_body_chunk_buff); if (status == APR_SUCCESS) { - if (size == 0) { + /* If we are overriding the errors, we can't put the content + * of the page into the brigade. + */ + if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) { /* AJP13_SEND_BODY_CHUNK with zero length * is explicit flush message */ - e = apr_bucket_flush_create(r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(output_brigade, e); - } - else { - e = apr_bucket_transient_create(buff, size, - r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(output_brigade, e); - - if ((conn->worker->flush_packets == flush_on) || - ((conn->worker->flush_packets == flush_auto) && - (apr_poll(conn_poll, 1, &conn_poll_fd, - conn->worker->flush_wait) - == APR_TIMEUP) ) ) { - e = apr_bucket_flush_create(r->connection->bucket_alloc); + if (size == 0) { + if (headers_sent) { + e = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Ignoring flush message received before headers"); + } + } + else { + apr_status_t rv; + + /* Handle the case where the error document is itself reverse + * proxied and was successful. We must maintain any previous + * error status so that an underlying error (eg HTTP_NOT_FOUND) + * doesn't become an HTTP_OK. + */ + if (conf->error_override && !ap_is_HTTP_ERROR(r->status) + && ap_is_HTTP_ERROR(original_status)) { + r->status = original_status; + r->status_line = original_status_line; + } + + e = apr_bucket_transient_create(send_body_chunk_buff, size, + r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(output_brigade, e); + + if ((conn->worker->s->flush_packets == flush_on) || + ((conn->worker->s->flush_packets == flush_auto) && + ((rv = apr_poll(conn_poll, 1, &conn_poll_fd, + conn->worker->s->flush_wait)) + != APR_SUCCESS) && + APR_STATUS_IS_TIMEUP(rv))) { + e = apr_bucket_flush_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + } + apr_brigade_length(output_brigade, 0, &bb_len); + if (bb_len != -1) + conn->worker->s->read += bb_len; + } + if (headers_sent) { + if (ap_pass_brigade(r->output_filters, + output_brigade) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "proxy: error processing body.%s", + r->connection->aborted ? + " Client aborted connection." : ""); + output_failed = 1; + } + data_sent = 1; + apr_brigade_cleanup(output_brigade); } - apr_brigade_length(output_brigade, 0, &bb_len); - if (bb_len != -1) - conn->worker->s->read += bb_len; - } - if (ap_pass_brigade(r->output_filters, - output_brigade) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "proxy: error processing body"); - isok = 0; } - data_sent = 1; - apr_brigade_cleanup(output_brigade); } else { - isok = 0; + backend_failed = 1; } break; case CMD_AJP13_END_RESPONSE: - e = apr_bucket_eos_create(r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(output_brigade, e); - if (ap_pass_brigade(r->output_filters, - output_brigade) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "proxy: error processing body"); - isok = 0; + /* If we are overriding the errors, we must not send anything to + * the client, especially as the brigade already contains headers. + * So do nothing here, and it will be cleaned up below. + */ + status = ajp_parse_reuse(r, conn->data, &conn_reuse); + if (status != APR_SUCCESS) { + backend_failed = 1; + } + if (!conf->error_override || !ap_is_HTTP_ERROR(r->status)) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(output_brigade, e); + if (ap_pass_brigade(r->output_filters, + output_brigade) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "proxy: error processing end"); + output_failed = 1; + } + /* XXX: what about flush here? See mod_jk */ + data_sent = 1; } - /* XXX: what about flush here? See mod_jk */ - data_sent = 1; + request_ended = 1; break; default: - isok = 0; + backend_failed = 1; break; } /* * If connection has been aborted by client: Stop working. * Nevertheless, we regard our operation so far as a success: - * So do not set isok to 0 and set result to CMD_AJP13_END_RESPONSE + * So reset output_failed to 0 and set result to CMD_AJP13_END_RESPONSE * But: Close this connection to the backend. */ if (r->connection->aborted) { conn->close++; + output_failed = 0; result = CMD_AJP13_END_RESPONSE; - break; + request_ended = 1; } - if (!isok) - break; - - if (result == CMD_AJP13_END_RESPONSE) + /* + * We either have finished successfully or we failed. + * So bail out + */ + if ((result == CMD_AJP13_END_RESPONSE) || backend_failed + || output_failed) break; /* read the response */ status = ajp_read_header(conn->sock, r, maxsize, (ajp_msg_t **)&(conn->data)); if (status != APR_SUCCESS) { - isok = 0; + backend_failed = 1; ap_log_error(APLOG_MARK, APLOG_DEBUG, status, r->server, "ajp_read_header failed"); break; @@ -418,23 +597,73 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, */ apr_brigade_cleanup(output_brigade); - if (status != APR_SUCCESS) { + if (backend_failed || output_failed) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: Processing of request failed backend: %i, " + "output: %i", backend_failed, output_failed); + /* We had a failure: Close connection to backend */ + conn->close++; + /* Return DONE to avoid error messages being added to the stream */ + if (data_sent) { + rv = DONE; + } + } + else if (!request_ended) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: Processing of request didn't terminate cleanly"); /* We had a failure: Close connection to backend */ conn->close++; + backend_failed = 1; + /* Return DONE to avoid error messages being added to the stream */ + if (data_sent) { + rv = DONE; + } + } + else if (!conn_reuse) { + /* Our backend signalled connection close */ + conn->close++; + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "proxy: got response from %pI (%s)", + conn->worker->cp->addr, + conn->worker->s->hostname); + + if (conf->error_override && ap_is_HTTP_ERROR(r->status)) { + /* clear r->status for override error, otherwise ErrorDocument + * thinks that this is a recursive error, and doesn't find the + * custom error page + */ + rv = r->status; + r->status = HTTP_OK; + } + else { + rv = OK; + } + } + + if (backend_failed) { ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, - "proxy: send body failed to %pI (%s)", + "proxy: dialog to %pI (%s) failed", conn->worker->cp->addr, - conn->worker->hostname); + conn->worker->s->hostname); /* * If we already send data, signal a broken backend connection * upwards in the chain. */ if (data_sent) { ap_proxy_backend_broke(r, output_brigade); - /* Return DONE to avoid error messages being added to the stream */ - rv = DONE; - } else + } else if (!send_body && (is_idempotent(r) == METHOD_IDEMPOTENT)) { + /* + * This is only non fatal when we have not send (parts) of a possible + * request body so far (we do not store it and thus cannot send it + * again) and the method is idempotent. In this case we can dare to + * retry it with a different worker if we are a balancer member. + */ rv = HTTP_SERVICE_UNAVAILABLE; + } else { + rv = HTTP_INTERNAL_SERVER_ERROR; + } } /* @@ -448,33 +677,17 @@ static int ap_proxy_ajp_request(apr_pool_t *p, request_rec *r, APR_BRIGADE_INSERT_TAIL(output_brigade, e); } - /* If we have added something to the brigade above, sent it */ + /* If we have added something to the brigade above, send it */ if (!APR_BRIGADE_EMPTY(output_brigade)) ap_pass_brigade(r->output_filters, output_brigade); apr_brigade_destroy(output_brigade); - if (rv) - return rv; - - /* Nice we have answer to send to the client */ - if (result == CMD_AJP13_END_RESPONSE && isok) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "proxy: got response from %pI (%s)", - conn->worker->cp->addr, - conn->worker->hostname); - return OK; + if (apr_table_get(r->subprocess_env, "proxy-nokeepalive")) { + conn->close++; } - ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, - "proxy: got bad response (%d) from %pI (%s)", - result, - conn->worker->cp->addr, - conn->worker->hostname); - - /* We had a failure: Close connection to backend */ - conn->close++; - return HTTP_SERVICE_UNAVAILABLE; + return rv; } /* @@ -490,6 +703,7 @@ static int proxy_ajp_handler(request_rec *r, proxy_worker *worker, conn_rec *origin = NULL; proxy_conn_rec *backend = NULL; const char *scheme = "AJP"; + int retry; proxy_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &proxy_module); @@ -520,58 +734,67 @@ static int proxy_ajp_handler(request_rec *r, proxy_worker *worker, "proxy: AJP: serving URL %s", url); /* create space for state information */ - if (!backend) { - status = ap_proxy_acquire_connection(scheme, &backend, worker, - r->server); - if (status != OK) { - if (backend) { - backend->close = 1; - ap_proxy_release_connection(scheme, backend, r->server); - } - return status; + status = ap_proxy_acquire_connection(scheme, &backend, worker, + r->server); + if (status != OK) { + if (backend) { + backend->close = 1; + ap_proxy_release_connection(scheme, backend, r->server); } + return status; } backend->is_ssl = 0; backend->close = 0; - /* Step One: Determine Who To Connect To */ - status = ap_proxy_determine_connection(p, r, conf, worker, backend, - uri, &url, proxyname, proxyport, - server_portstr, - sizeof(server_portstr)); - - if (status != OK) - goto cleanup; - - /* Step Two: Make the Connection */ - if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, - "proxy: AJP: failed to make connection to backend: %s", - backend->hostname); - status = HTTP_SERVICE_UNAVAILABLE; - goto cleanup; - } + retry = 0; + while (retry < 2) { + char *locurl = url; + /* Step One: Determine Who To Connect To */ + status = ap_proxy_determine_connection(p, r, conf, worker, backend, + uri, &locurl, proxyname, proxyport, + server_portstr, + sizeof(server_portstr)); - /* Handle CPING/CPONG */ - if (worker->ping_timeout_set) { - status = ajp_handle_cping_cpong(backend->sock, r, - worker->ping_timeout); - if (status != APR_SUCCESS) { - backend->close++; - ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, - "proxy: AJP: cping/cpong failed to %pI (%s)", - worker->cp->addr, - worker->hostname); + if (status != OK) + break; + + /* Step Two: Make the Connection */ + if (ap_proxy_connect_backend(scheme, backend, worker, r->server)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "proxy: AJP: failed to make connection to backend: %s", + backend->hostname); status = HTTP_SERVICE_UNAVAILABLE; - goto cleanup; + break; + } + + /* Handle CPING/CPONG */ + if (worker->s->ping_timeout_set) { + status = ajp_handle_cping_cpong(backend->sock, r, + worker->s->ping_timeout); + /* + * In case the CPING / CPONG failed for the first time we might be + * just out of luck and got a faulty backend connection, but the + * backend might be healthy nevertheless. So ensure that the backend + * TCP connection gets closed and try it once again. + */ + if (status != APR_SUCCESS) { + backend->close++; + ap_log_error(APLOG_MARK, APLOG_ERR, status, r->server, + "proxy: AJP: cping/cpong failed to %pI (%s)", + worker->cp->addr, + worker->s->hostname); + status = HTTP_SERVICE_UNAVAILABLE; + retry++; + continue; + } } + /* Step Three: Process the Request */ + status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, locurl, + server_portstr); + break; } - /* Step Three: Process the Request */ - status = ap_proxy_ajp_request(p, r, backend, origin, dconf, uri, url, - server_portstr); -cleanup: /* Do not close the socket */ ap_proxy_release_connection(scheme, backend, r->server); return status; @@ -583,7 +806,7 @@ static void ap_proxy_http_register_hook(apr_pool_t *p) proxy_hook_canon_handler(proxy_ajp_canon, NULL, NULL, APR_HOOK_FIRST); } -module AP_MODULE_DECLARE_DATA proxy_ajp_module = { +AP_DECLARE_MODULE(proxy_ajp) = { STANDARD20_MODULE_STUFF, NULL, /* create per-directory config structure */ NULL, /* merge per-directory config structures */