1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-08 15:02:10 +03:00

mod_proxy: Handle backend address renewal with address_ttl= parameter.

Define a new proxy_address struct holding the current/latest sockaddr in use
by each proxy worker and conn. Since backend addresses can be updated when
their TTL expires and while connections are being processed, each address is
refcounted and freed only when the last worker (or conn) using it grabs the
new one.

The lifetime of the addresses is handled at a single place by the new
ap_proxy_determine_address() function. It guarantees to bind the current/latest
backend address to the passed in conn (or do nothing if it's up to date already).
The function is called indirectly by ap_proxy_determine_connection() for the
proxy modules that use it, or directly by mod_proxy_ftp and mod_proxy_hcheck.
It also is called eventually by ap_proxy_connect_backend() when connect()ing all
the current addresses fails, to check (PROXY_DETERMINE_ADDRESS_CHECK) if some
new addrs are available.

This commit is also a rework of the lifetime of conn->addr, conn->hostname
and conn->forward, using the conn->uds_pool and conn->fwd_pool for the cases
where the backend is connected through a UDS socket and a remote CONNECT proxy
respectively.

* include/ap_mmn.h:
  Minor bump for new function/fields.

* modules/proxy/mod_proxy.h (struct proxy_address,
                             ap_proxy_determine_addresss()):
  Declare ap_proxy_determine_addresss() and opaque struct proxy_address,
  new fields to structs proxy_conn_rec/proxy_worker_shared/proxy_worker.

* modules/proxy/mod_proxy.c (set_worker_param):
  Parse/set the new worker->address_ttl parameter.

* modules/proxy/proxy_util.c (proxy_util_register_hooks(),
                              ap_proxy_initialize_worker(),
                              ap_proxy_connection_reusable(),
                              ap_proxyerror(), proxyerror_core(),
                              init_conn_pool(), make_conn_subpool(),
                              connection_make(), connection_cleanup(),
                              connection_constructor()):
 Initialize *proxy_start_time in proxy_util_register_hooks() as the epoch
 from which expiration times are relative (i.e. seconds stored in an uint32_t
 for atomic changes).
 Make sure worker->s->is_address_reusable and worker->s->disablereuse are
 consistant in ap_proxy_initialize_worker(), thus no need to check for both
 in ap_proxy_connection_reusable().
 New proxyerror_core() helper taking an apr_status_t to log, wrap in
 ap_proxyerror().
 New make_conn_subpool() to create worker->cp->{pool,dns} with their own
 allocator.
 New connection_make() helper to factorize code in connection_cleanup() and
 connection_constructor().

* modules/proxy/proxy_util.c (proxy_address_inc(), proxy_address_dec(),
                              proxy_address_cleanup(), proxy_address_set_expired(),
                              worker_address_get(), worker_address_set(),
                              worker_address_resolve(), proxy_addrs_equal(),
                              ap_proxy_determine_address(),
                              ap_proxy_determine_connection(),
                              ap_proxy_connect_backend()):
 Implement ap_proxy_determine_address() using the above helpers for atomic changes,
 and call it from ap_proxy_determine_connection() and ap_proxy_connect_backend().

* modules/proxy/mod_proxy_ftp.c (proxy_ftp_handler):
  Use ap_proxy_determine_address() and use the returned backend->addr.

* modules/proxy/mod_proxy_hcheck.c (hc_determine_connection, hc_get_backend,
                                    hc_init_worker, hc_watchdog_callback):
  Use ap_proxy_determine_address() in hc_determine_connection() and call the
  latter from hc_get_backend(), replace hc_init_worker() by hc_init_baton()
  which now calls hc_get_hcworker() and hc_get_backend() to resolve the first
  address at init time.

* modules/proxy/mod_proxy_http.c (proxy_http_handler):
  Use backend->addr and ->hostname instead of worker->cp->addr and
  worker->s->hostname_ex respectively.

* modules/proxy/mod_proxy_ajp.c (ap_proxy_ajp_request):
  Use backend->addr and ->hostname instead of worker->cp->addr and
  worker->s->hostname_ex respectively.


Closes #367



git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1912459 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yann Ylavic
2023-09-21 13:15:35 +00:00
parent 850f1a5d42
commit 3c7f67fa2a
8 changed files with 839 additions and 413 deletions

View File

@@ -548,52 +548,29 @@ static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker,
return hc;
}
static int hc_determine_connection(sctx_t *ctx, proxy_worker *worker,
apr_sockaddr_t **addr, apr_pool_t *p)
static int hc_determine_connection(const char *proxy_function,
proxy_conn_rec *backend,
server_rec *s)
{
apr_status_t rv = APR_SUCCESS;
proxy_worker *worker = backend->worker;
apr_status_t rv;
/*
* normally, this is done in ap_proxy_determine_connection().
* TODO: Look at using ap_proxy_determine_connection() with a
* fake request_rec
*/
if (worker->cp->addr) {
*addr = worker->cp->addr;
rv = ap_proxy_determine_address(proxy_function, backend,
worker->s->hostname_ex, worker->s->port,
0, NULL, s);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03249)
"DNS lookup failure for: %s:%hu",
worker->s->hostname_ex, worker->s->port);
return !OK;
}
else {
rv = apr_sockaddr_info_get(addr, worker->s->hostname_ex,
APR_UNSPEC, worker->s->port, 0, p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03249)
"DNS lookup failure for: %s:%d",
worker->s->hostname_ex, (int)worker->s->port);
}
}
return (rv == APR_SUCCESS ? OK : !OK);
}
static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker)
{
apr_status_t rv = APR_SUCCESS;
/*
* Since this is the watchdog, workers never actually handle a
* request here, and so the local data isn't initialized (of
* course, the shared memory is). So we need to bootstrap
* worker->cp. Note, we only need do this once.
*/
if (!worker->cp) {
rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO(03250) "Cannot init worker");
return rv;
}
if (worker->s->is_address_reusable && !worker->s->disablereuse &&
hc_determine_connection(ctx, worker, &worker->cp->addr,
worker->cp->pool) != OK) {
rv = APR_EGENERAL;
}
}
return rv;
return OK;
}
static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *backend,
@@ -615,24 +592,64 @@ static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *
}
static int hc_get_backend(const char *proxy_function, proxy_conn_rec **backend,
proxy_worker *hc, sctx_t *ctx, apr_pool_t *ptemp)
proxy_worker *hc, sctx_t *ctx)
{
int status;
status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s);
if (status == OK) {
(*backend)->addr = hc->cp->addr;
(*backend)->hostname = hc->s->hostname_ex;
if (strcmp(hc->s->scheme, "https") == 0 || strcmp(hc->s->scheme, "wss") == 0 ) {
if (!ap_ssl_has_outgoing_handlers()) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252)
"mod_ssl not configured?");
return !OK;
}
(*backend)->is_ssl = 1;
}
status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s);
if (status != OK) {
return status;
}
return hc_determine_connection(ctx, hc, &(*backend)->addr, ptemp);
if (strcmp(hc->s->scheme, "https") == 0 || strcmp(hc->s->scheme, "wss") == 0 ) {
if (!ap_ssl_has_outgoing_handlers()) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252)
"mod_ssl not configured?");
return !OK;
}
(*backend)->is_ssl = 1;
}
return hc_determine_connection(proxy_function, *backend, ctx->s);
}
static apr_status_t hc_init_baton(baton_t *baton)
{
sctx_t *ctx = baton->ctx;
proxy_worker *worker = baton->worker, *hc;
apr_status_t rv = APR_SUCCESS;
int once = 0;
/*
* Since this is the watchdog, workers never actually handle a
* request here, and so the local data isn't initialized (of
* course, the shared memory is). So we need to bootstrap
* worker->cp. Note, we only need do this once.
*/
if (!worker->cp) {
rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO(03250) "Cannot init worker");
return rv;
}
once = 1;
}
baton->hc = hc = hc_get_hcworker(ctx, worker, baton->ptemp);
/* Try to resolve the worker address once if it's reusable */
if (once && worker->s->is_address_reusable) {
proxy_conn_rec *backend = NULL;
if (hc_get_backend("HCHECK", &backend, hc, ctx)) {
rv = APR_EGENERAL;
}
if (backend) {
backend->close = 1;
ap_proxy_release_connection("HCHECK", backend, ctx->s);
}
}
return rv;
}
static apr_status_t hc_check_cping(baton_t *baton, apr_thread_t *thread)
@@ -650,7 +667,7 @@ static apr_status_t hc_check_cping(baton_t *baton, apr_thread_t *thread)
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, baton->ctx->s, "HCCPING starting");
if ((status = hc_get_backend("HCCPING", &backend, hc, ctx, baton->ptemp)) != OK) {
if ((status = hc_get_backend("HCCPING", &backend, hc, ctx)) != OK) {
return backend_cleanup("HCCPING", backend, ctx->s, status);
}
if ((status = ap_proxy_connect_backend("HCCPING", backend, hc, ctx->s)) != OK) {
@@ -685,7 +702,7 @@ static apr_status_t hc_check_tcp(baton_t *baton)
proxy_worker *hc = baton->hc;
proxy_conn_rec *backend = NULL;
status = hc_get_backend("HCTCP", &backend, hc, ctx, baton->ptemp);
status = hc_get_backend("HCTCP", &backend, hc, ctx);
if (status == OK) {
status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s);
/* does an unconditional ap_proxy_is_socket_connected() */
@@ -836,7 +853,7 @@ static apr_status_t hc_check_http(baton_t *baton, apr_thread_t *thread)
return APR_ENOTIMPL;
}
if ((status = hc_get_backend("HCOH", &backend, hc, ctx, ptemp)) != OK) {
if ((status = hc_get_backend("HCOH", &backend, hc, ctx)) != OK) {
return backend_cleanup("HCOH", backend, ctx->s, status);
}
if ((status = ap_proxy_connect_backend("HCOH", backend, hc, ctx->s)) != OK) {
@@ -1030,12 +1047,6 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
"Checking %s worker: %s [%d] (%pp)", balancer->s->name,
worker->s->name, worker->s->method, worker);
if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) {
worker->s->updated = now;
return rv;
}
worker->s->updated = 0;
/* This pool has the lifetime of the check */
apr_pool_create(&ptemp, ctx->p);
apr_pool_tag(ptemp, "hc_request");
@@ -1044,7 +1055,12 @@ static apr_status_t hc_watchdog_callback(int state, void *data,
baton->balancer = balancer;
baton->worker = worker;
baton->ptemp = ptemp;
baton->hc = hc_get_hcworker(ctx, worker, ptemp);
if ((rv = hc_init_baton(baton))) {
worker->s->updated = now;
apr_pool_destroy(ptemp);
return rv;
}
worker->s->updated = 0;
#if HC_USE_THREADS
if (hctp) {
apr_thread_pool_push(hctp, hc_check, (void *)baton,