|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#include "mod_proxy.h"
|
|
Packit |
90a5c9 |
#include "mod_watchdog.h"
|
|
Packit |
90a5c9 |
#include "ap_slotmem.h"
|
|
Packit |
90a5c9 |
#include "ap_expr.h"
|
|
Packit |
90a5c9 |
#if APR_HAS_THREADS
|
|
Packit |
90a5c9 |
#include "apr_thread_pool.h"
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA proxy_hcheck_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define HCHECK_WATHCHDOG_NAME ("_proxy_hcheck_")
|
|
Packit |
90a5c9 |
#define HC_THREADPOOL_SIZE (16)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Why? So we can easily set/clear HC_USE_THREADS during dev testing */
|
|
Packit |
90a5c9 |
#if APR_HAS_THREADS
|
|
Packit |
90a5c9 |
#ifndef HC_USE_THREADS
|
|
Packit |
90a5c9 |
#define HC_USE_THREADS 1
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
#else
|
|
Packit |
90a5c9 |
#define HC_USE_THREADS 0
|
|
Packit |
90a5c9 |
typedef void apr_thread_pool_t;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
char *name;
|
|
Packit |
90a5c9 |
hcmethod_t method;
|
|
Packit |
90a5c9 |
int passes;
|
|
Packit |
90a5c9 |
int fails;
|
|
Packit |
90a5c9 |
apr_interval_time_t interval;
|
|
Packit |
90a5c9 |
char *hurl;
|
|
Packit |
90a5c9 |
char *hcexpr;
|
|
Packit |
90a5c9 |
} hc_template_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
char *expr;
|
|
Packit |
90a5c9 |
ap_expr_info_t *pexpr; /* parsed expression */
|
|
Packit |
90a5c9 |
} hc_condition_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
apr_pool_t *p;
|
|
Packit |
90a5c9 |
apr_array_header_t *templates;
|
|
Packit |
90a5c9 |
apr_table_t *conditions;
|
|
Packit |
90a5c9 |
apr_hash_t *hcworkers;
|
|
Packit |
90a5c9 |
server_rec *s;
|
|
Packit |
90a5c9 |
} sctx_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Used in the HC worker via the context field */
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
const char *path; /* The path of the original worker URL */
|
|
Packit |
90a5c9 |
const char *method; /* Method string for the HTTP/AJP request */
|
|
Packit |
90a5c9 |
const char *req; /* pre-formatted HTTP/AJP request */
|
|
Packit |
90a5c9 |
proxy_worker *w; /* Pointer to the actual worker */
|
|
Packit |
90a5c9 |
} wctx_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp;
|
|
Packit |
90a5c9 |
sctx_t *ctx;
|
|
Packit |
90a5c9 |
proxy_balancer *balancer;
|
|
Packit |
90a5c9 |
proxy_worker *worker;
|
|
Packit |
90a5c9 |
proxy_worker *hc;
|
|
Packit |
90a5c9 |
apr_time_t now;
|
|
Packit |
90a5c9 |
} baton_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *hc_create_config(apr_pool_t *p, server_rec *s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
sctx_t *ctx = apr_pcalloc(p, sizeof(sctx_t));
|
|
Packit |
90a5c9 |
ctx->s = s;
|
|
Packit |
90a5c9 |
apr_pool_create(&ctx->p, p);
|
|
Packit |
90a5c9 |
apr_pool_tag(ctx->p, "proxy_hcheck");
|
|
Packit |
90a5c9 |
ctx->templates = apr_array_make(p, 10, sizeof(hc_template_t));
|
|
Packit |
90a5c9 |
ctx->conditions = apr_table_make(p, 10);
|
|
Packit |
90a5c9 |
ctx->hcworkers = apr_hash_make(p);
|
|
Packit |
90a5c9 |
return ctx;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static ap_watchdog_t *watchdog;
|
|
Packit |
90a5c9 |
static int tpsize = HC_THREADPOOL_SIZE;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* This serves double duty by not only validating (and creating)
|
|
Packit |
90a5c9 |
* the health-check template, but also ties into set_worker_param()
|
|
Packit |
90a5c9 |
* which does the actual setting of worker params in shm.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static const char *set_worker_hc_param(apr_pool_t *p,
|
|
Packit |
90a5c9 |
server_rec *s,
|
|
Packit |
90a5c9 |
proxy_worker *worker,
|
|
Packit |
90a5c9 |
const char *key,
|
|
Packit |
90a5c9 |
const char *val,
|
|
Packit |
90a5c9 |
void *v)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int ival;
|
|
Packit |
90a5c9 |
hc_template_t *temp;
|
|
Packit |
90a5c9 |
sctx_t *ctx = (sctx_t *) ap_get_module_config(s->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
if (!worker && !v) {
|
|
Packit |
90a5c9 |
return "Bad call to set_worker_hc_param()";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
temp = (hc_template_t *)v;
|
|
Packit |
90a5c9 |
if (!strcasecmp(key, "hctemplate")) {
|
|
Packit |
90a5c9 |
hc_template_t *template;
|
|
Packit |
90a5c9 |
template = (hc_template_t *)ctx->templates->elts;
|
|
Packit |
90a5c9 |
for (ival = 0; ival < ctx->templates->nelts; ival++, template++) {
|
|
Packit |
90a5c9 |
if (!ap_cstr_casecmp(template->name, val)) {
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
worker->s->method = template->method;
|
|
Packit |
90a5c9 |
worker->s->interval = template->interval;
|
|
Packit |
90a5c9 |
worker->s->passes = template->passes;
|
|
Packit |
90a5c9 |
worker->s->fails = template->fails;
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(worker->s->hcuri, template->hurl);
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(worker->s->hcexpr, template->hcexpr);
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->method = template->method;
|
|
Packit |
90a5c9 |
temp->interval = template->interval;
|
|
Packit |
90a5c9 |
temp->passes = template->passes;
|
|
Packit |
90a5c9 |
temp->fails = template->fails;
|
|
Packit |
90a5c9 |
temp->hurl = apr_pstrdup(p, template->hurl);
|
|
Packit |
90a5c9 |
temp->hcexpr = apr_pstrdup(p, template->hcexpr);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Unknown ProxyHCTemplate name: %s", val);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcmethod")) {
|
|
Packit |
90a5c9 |
proxy_hcmethods_t *method = proxy_hcmethods;
|
|
Packit |
90a5c9 |
for (; method->name; method++) {
|
|
Packit |
90a5c9 |
if (!ap_cstr_casecmp(val, method->name)) {
|
|
Packit |
90a5c9 |
if (!method->implemented) {
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Health check method %s not (yet) implemented",
|
|
Packit |
90a5c9 |
val);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
worker->s->method = method->method;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->method = method->method;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return "Unknown method";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcinterval")) {
|
|
Packit |
90a5c9 |
apr_interval_time_t hci;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
rv = ap_timeout_parameter_parse(val, &hci, "s");
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return "Unparse-able hcinterval setting";
|
|
Packit |
90a5c9 |
if (hci < AP_WD_TM_SLICE)
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Interval must be a positive value greater than %"
|
|
Packit |
90a5c9 |
APR_TIME_T_FMT "ms", apr_time_as_msec(AP_WD_TM_SLICE));
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
worker->s->interval = hci;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->interval = hci;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcpasses")) {
|
|
Packit |
90a5c9 |
ival = atoi(val);
|
|
Packit |
90a5c9 |
if (ival < 0)
|
|
Packit |
90a5c9 |
return "Passes must be a positive value";
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
worker->s->passes = ival;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->passes = ival;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcfails")) {
|
|
Packit |
90a5c9 |
ival = atoi(val);
|
|
Packit |
90a5c9 |
if (ival < 0)
|
|
Packit |
90a5c9 |
return "Fails must be a positive value";
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
worker->s->fails = ival;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->fails = ival;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcuri")) {
|
|
Packit |
90a5c9 |
if (strlen(val) >= sizeof(worker->s->hcuri))
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Health check uri length must be < %d characters",
|
|
Packit |
90a5c9 |
(int)sizeof(worker->s->hcuri));
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(worker->s->hcuri, val);
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->hurl = apr_pstrdup(p, val);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(key, "hcexpr")) {
|
|
Packit |
90a5c9 |
hc_condition_t *cond;
|
|
Packit |
90a5c9 |
cond = (hc_condition_t *)apr_table_get(ctx->conditions, val);
|
|
Packit |
90a5c9 |
if (!cond) {
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Unknown health check condition expr: %s", val);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* This check is wonky... a known expr can't be this big. Check anyway */
|
|
Packit |
90a5c9 |
if (strlen(val) >= sizeof(worker->s->hcexpr))
|
|
Packit |
90a5c9 |
return apr_psprintf(p, "Health check uri length must be < %d characters",
|
|
Packit |
90a5c9 |
(int)sizeof(worker->s->hcexpr));
|
|
Packit |
90a5c9 |
if (worker) {
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(worker->s->hcexpr, val);
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
temp->hcexpr = apr_pstrdup(p, val);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return "unknown Worker hcheck parameter";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_hc_condition(cmd_parms *cmd, void *dummy, const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *name = NULL;
|
|
Packit |
90a5c9 |
char *expr;
|
|
Packit |
90a5c9 |
sctx_t *ctx;
|
|
Packit |
90a5c9 |
hc_condition_t *cond;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
|
|
Packit |
90a5c9 |
if (err)
|
|
Packit |
90a5c9 |
return err;
|
|
Packit |
90a5c9 |
ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
name = ap_getword_conf(cmd->pool, &arg;;
|
|
Packit |
90a5c9 |
if (!*name) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->temp_pool, "Missing expression name for ",
|
|
Packit |
90a5c9 |
cmd->cmd->name, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (strlen(name) > (PROXY_WORKER_MAX_SCHEME_SIZE - 1)) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->temp_pool, "Expression name limited to %d characters",
|
|
Packit |
90a5c9 |
(PROXY_WORKER_MAX_SCHEME_SIZE - 1));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* get expr. Allow fancy new {...} quoting style */
|
|
Packit |
90a5c9 |
expr = ap_getword_conf2(cmd->temp_pool, &arg;;
|
|
Packit |
90a5c9 |
if (!*expr) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->temp_pool, "Missing expression for ",
|
|
Packit |
90a5c9 |
cmd->cmd->name, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
cond = apr_palloc(cmd->pool, sizeof(hc_condition_t));
|
|
Packit |
90a5c9 |
cond->pexpr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->temp_pool, "Could not parse expression \"%s\": %s",
|
|
Packit |
90a5c9 |
expr, err);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
cond->expr = apr_pstrdup(cmd->pool, expr);
|
|
Packit |
90a5c9 |
apr_table_setn(ctx->conditions, name, (void *)cond);
|
|
Packit |
90a5c9 |
expr = ap_getword_conf(cmd->temp_pool, &arg;;
|
|
Packit |
90a5c9 |
if (*expr) {
|
|
Packit |
90a5c9 |
return "error: extra parameter(s)";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *set_hc_template(cmd_parms *cmd, void *dummy, const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *name = NULL;
|
|
Packit |
90a5c9 |
char *word, *val;
|
|
Packit |
90a5c9 |
hc_template_t *template;
|
|
Packit |
90a5c9 |
sctx_t *ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
|
|
Packit |
90a5c9 |
if (err)
|
|
Packit |
90a5c9 |
return err;
|
|
Packit |
90a5c9 |
ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
name = ap_getword_conf(cmd->temp_pool, &arg;;
|
|
Packit |
90a5c9 |
if (!*name) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->temp_pool, "Missing template name for ",
|
|
Packit |
90a5c9 |
cmd->cmd->name, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
template = (hc_template_t *)apr_array_push(ctx->templates);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
template->name = apr_pstrdup(cmd->pool, name);
|
|
Packit |
90a5c9 |
template->method = template->passes = template->fails = 1;
|
|
Packit |
90a5c9 |
template->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL);
|
|
Packit |
90a5c9 |
template->hurl = NULL;
|
|
Packit |
90a5c9 |
template->hcexpr = NULL;
|
|
Packit |
90a5c9 |
while (*arg) {
|
|
Packit |
90a5c9 |
word = ap_getword_conf(cmd->pool, &arg;;
|
|
Packit |
90a5c9 |
val = strchr(word, '=');
|
|
Packit |
90a5c9 |
if (!val) {
|
|
Packit |
90a5c9 |
return "Invalid ProxyHCTemplate parameter. Parameter must be "
|
|
Packit |
90a5c9 |
"in the form 'key=value'";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else
|
|
Packit |
90a5c9 |
*val++ = '\0';
|
|
Packit |
90a5c9 |
err = set_worker_hc_param(cmd->pool, ctx->s, NULL, word, val, template);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
/* get rid of recently pushed (bad) template */
|
|
Packit |
90a5c9 |
apr_array_pop(ctx->templates);
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->temp_pool, "ProxyHCTemplate: ", err, " ", word, "=", val, "; ", name, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* No error means we have a valid template */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if HC_USE_THREADS
|
|
Packit |
90a5c9 |
static const char *set_hc_tpsize (cmd_parms *cmd, void *dummy, const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
|
|
Packit |
90a5c9 |
if (err)
|
|
Packit |
90a5c9 |
return err;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
tpsize = atoi(arg);
|
|
Packit |
90a5c9 |
if (tpsize < 0)
|
|
Packit |
90a5c9 |
return "Invalid ProxyHCTPsize parameter. Parameter must be "
|
|
Packit |
90a5c9 |
">= 0";
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Create a dummy request rec, simply so we can use ap_expr.
|
|
Packit |
90a5c9 |
* Use our short-lived pool for bucket_alloc so that we can simply move
|
|
Packit |
90a5c9 |
* buckets and use them after the backend connection is released.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static request_rec *create_request_rec(apr_pool_t *p, server_rec *s,
|
|
Packit |
90a5c9 |
proxy_balancer *balancer,
|
|
Packit |
90a5c9 |
const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
request_rec *r;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r = apr_pcalloc(p, sizeof(request_rec));
|
|
Packit |
90a5c9 |
r->pool = p;
|
|
Packit |
90a5c9 |
r->server = s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->per_dir_config = r->server->lookup_defaults;
|
|
Packit |
90a5c9 |
if (balancer->section_config) {
|
|
Packit |
90a5c9 |
r->per_dir_config = ap_merge_per_dir_configs(r->pool,
|
|
Packit |
90a5c9 |
r->per_dir_config,
|
|
Packit |
90a5c9 |
balancer->section_config);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->proxyreq = PROXYREQ_RESPONSE;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->user = NULL;
|
|
Packit |
90a5c9 |
r->ap_auth_type = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->allowed_methods = ap_make_method_list(p, 2);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->headers_in = apr_table_make(r->pool, 1);
|
|
Packit |
90a5c9 |
r->trailers_in = apr_table_make(r->pool, 1);
|
|
Packit |
90a5c9 |
r->subprocess_env = apr_table_make(r->pool, 25);
|
|
Packit |
90a5c9 |
r->headers_out = apr_table_make(r->pool, 12);
|
|
Packit |
90a5c9 |
r->err_headers_out = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
r->trailers_out = apr_table_make(r->pool, 1);
|
|
Packit |
90a5c9 |
r->notes = apr_table_make(r->pool, 5);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->request_config = ap_create_request_config(r->pool);
|
|
Packit |
90a5c9 |
/* Must be set before we run create request hook */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->sent_bodyct = 0; /* bytect isn't for body */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->read_length = 0;
|
|
Packit |
90a5c9 |
r->read_body = REQUEST_NO_BODY;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->status = HTTP_OK; /* Until further notice */
|
|
Packit |
90a5c9 |
r->the_request = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Begin by presuming any module can make its own path_info assumptions,
|
|
Packit |
90a5c9 |
* until some module interjects and changes the value.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Time to populate r with the data we have. */
|
|
Packit |
90a5c9 |
r->method = method;
|
|
Packit |
90a5c9 |
/* Provide quick information about the request method as soon as known */
|
|
Packit |
90a5c9 |
r->method_number = ap_method_number_of(r->method);
|
|
Packit |
90a5c9 |
if (r->method_number == M_OPTIONS
|
|
Packit |
90a5c9 |
|| (r->method_number == M_GET && r->method[0] == 'H')) {
|
|
Packit |
90a5c9 |
r->header_only = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
r->header_only = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->protocol = "HTTP/1.0";
|
|
Packit |
90a5c9 |
r->proto_num = HTTP_VERSION(1, 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->hostname = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return r;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void set_request_connection(request_rec *r, conn_rec *conn)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
conn->bucket_alloc = apr_bucket_alloc_create(r->pool);
|
|
Packit |
90a5c9 |
r->connection = conn;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->kept_body = apr_brigade_create(r->pool, conn->bucket_alloc);
|
|
Packit |
90a5c9 |
r->output_filters = r->proto_output_filters = conn->output_filters;
|
|
Packit |
90a5c9 |
r->input_filters = r->proto_input_filters = conn->input_filters;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->useragent_addr = conn->client_addr;
|
|
Packit |
90a5c9 |
r->useragent_ip = conn->client_ip;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void create_hcheck_req(wctx_t *wctx, proxy_worker *hc,
|
|
Packit |
90a5c9 |
apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *req = NULL;
|
|
Packit |
90a5c9 |
const char *method = NULL;
|
|
Packit |
90a5c9 |
switch (hc->s->method) {
|
|
Packit |
90a5c9 |
case OPTIONS:
|
|
Packit |
90a5c9 |
method = "OPTIONS";
|
|
Packit |
90a5c9 |
req = apr_psprintf(p,
|
|
Packit |
90a5c9 |
"OPTIONS * HTTP/1.0\r\n"
|
|
Packit |
90a5c9 |
"Host: %s:%d\r\n"
|
|
Packit |
90a5c9 |
"\r\n",
|
|
Packit |
90a5c9 |
hc->s->hostname_ex, (int)hc->s->port);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case HEAD:
|
|
Packit |
90a5c9 |
method = "HEAD";
|
|
Packit |
90a5c9 |
/* fallthru */
|
|
Packit |
90a5c9 |
case GET:
|
|
Packit |
90a5c9 |
if (!method) { /* did we fall thru? If not, we are GET */
|
|
Packit |
90a5c9 |
method = "GET";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
req = apr_psprintf(p,
|
|
Packit |
90a5c9 |
"%s %s%s%s HTTP/1.0\r\n"
|
|
Packit |
90a5c9 |
"Host: %s:%d\r\n"
|
|
Packit |
90a5c9 |
"\r\n",
|
|
Packit |
90a5c9 |
method,
|
|
Packit |
90a5c9 |
(wctx->path ? wctx->path : ""),
|
|
Packit |
90a5c9 |
(wctx->path && *hc->s->hcuri ? "/" : "" ),
|
|
Packit |
90a5c9 |
(*hc->s->hcuri ? hc->s->hcuri : ""),
|
|
Packit |
90a5c9 |
hc->s->hostname_ex, (int)hc->s->port);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
wctx->req = req;
|
|
Packit |
90a5c9 |
wctx->method = method;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker,
|
|
Packit |
90a5c9 |
apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
proxy_worker *hc = NULL;
|
|
Packit |
90a5c9 |
apr_port_t port;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, &worker, sizeof worker);
|
|
Packit |
90a5c9 |
if (!hc) {
|
|
Packit |
90a5c9 |
apr_uri_t uri;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
const char *url = worker->s->name;
|
|
Packit |
90a5c9 |
wctx_t *wctx = apr_pcalloc(ctx->p, sizeof(wctx_t));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
port = (worker->s->port ? worker->s->port
|
|
Packit |
90a5c9 |
: ap_proxy_port_of_scheme(worker->s->scheme));
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03248)
|
|
Packit |
90a5c9 |
"Creating hc worker %pp for %s://%s:%d",
|
|
Packit |
90a5c9 |
worker, worker->s->scheme, worker->s->hostname_ex,
|
|
Packit |
90a5c9 |
(int)port);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name, 0);
|
|
Packit |
90a5c9 |
apr_snprintf(hc->s->name, sizeof hc->s->name, "%pp", worker);
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(hc->s->hostname, worker->s->hostname); /* for compatibility */
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(hc->s->hostname_ex, worker->s->hostname_ex);
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(hc->s->scheme, worker->s->scheme);
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(hc->s->hcuri, worker->s->hcuri);
|
|
Packit |
90a5c9 |
PROXY_STRNCPY(hc->s->hcexpr, worker->s->hcexpr);
|
|
Packit |
90a5c9 |
hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_DEFAULT);
|
|
Packit |
90a5c9 |
hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name, PROXY_HASHFUNC_FNV);
|
|
Packit |
90a5c9 |
hc->s->port = port;
|
|
Packit |
90a5c9 |
/* Do not disable worker in case of errors */
|
|
Packit |
90a5c9 |
hc->s->status |= PROXY_WORKER_IGNORE_ERRORS;
|
|
Packit |
90a5c9 |
/* Mark as the "generic" worker */
|
|
Packit |
90a5c9 |
hc->s->status |= PROXY_WORKER_GENERIC;
|
|
Packit |
90a5c9 |
ap_proxy_initialize_worker(hc, ctx->s, ctx->p);
|
|
Packit |
90a5c9 |
hc->s->is_address_reusable = worker->s->is_address_reusable;
|
|
Packit |
90a5c9 |
hc->s->disablereuse = worker->s->disablereuse;
|
|
Packit |
90a5c9 |
hc->s->method = worker->s->method;
|
|
Packit |
90a5c9 |
rv = apr_uri_parse(p, url, &uri);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
wctx->path = apr_pstrdup(ctx->p, uri.path);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
wctx->w = worker;
|
|
Packit |
90a5c9 |
create_hcheck_req(wctx, hc, ctx->p);
|
|
Packit |
90a5c9 |
hc->context = wctx;
|
|
Packit |
90a5c9 |
apr_hash_set(ctx->hcworkers, &worker, sizeof worker, hc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* This *could* have changed via the Balancer Manager */
|
|
Packit |
90a5c9 |
/* TODO */
|
|
Packit |
90a5c9 |
if (hc->s->method != worker->s->method) {
|
|
Packit |
90a5c9 |
wctx_t *wctx = hc->context;
|
|
Packit |
90a5c9 |
port = (worker->s->port ? worker->s->port
|
|
Packit |
90a5c9 |
: ap_proxy_port_of_scheme(worker->s->scheme));
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03311)
|
|
Packit |
90a5c9 |
"Updating hc worker %pp for %s://%s:%d",
|
|
Packit |
90a5c9 |
worker, worker->s->scheme, worker->s->hostname_ex,
|
|
Packit |
90a5c9 |
(int)port);
|
|
Packit |
90a5c9 |
hc->s->method = worker->s->method;
|
|
Packit |
90a5c9 |
create_hcheck_req(wctx, hc, ctx->p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return hc;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_determine_connection(sctx_t *ctx, proxy_worker *worker,
|
|
Packit |
90a5c9 |
apr_sockaddr_t **addr, apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* normally, this is done in ap_proxy_determine_connection().
|
|
Packit |
90a5c9 |
* TODO: Look at using ap_proxy_determine_connection() with a
|
|
Packit |
90a5c9 |
* fake request_rec
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (worker->cp->addr) {
|
|
Packit |
90a5c9 |
*addr = worker->cp->addr;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = apr_sockaddr_info_get(addr, worker->s->hostname_ex,
|
|
Packit |
90a5c9 |
APR_UNSPEC, worker->s->port, 0, p);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03249)
|
|
Packit |
90a5c9 |
"DNS lookup failure for: %s:%d",
|
|
Packit |
90a5c9 |
worker->s->hostname_ex, (int)worker->s->port);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return (rv == APR_SUCCESS ? OK : !OK);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Since this is the watchdog, workers never actually handle a
|
|
Packit |
90a5c9 |
* request here, and so the local data isn't initialized (of
|
|
Packit |
90a5c9 |
* course, the shared memory is). So we need to bootstrap
|
|
Packit |
90a5c9 |
* worker->cp. Note, we only need do this once.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!worker->cp) {
|
|
Packit |
90a5c9 |
rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO(03250) "Cannot init worker");
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (worker->s->is_address_reusable && !worker->s->disablereuse &&
|
|
Packit |
90a5c9 |
hc_determine_connection(ctx, worker, &worker->cp->addr,
|
|
Packit |
90a5c9 |
worker->cp->pool) != OK) {
|
|
Packit |
90a5c9 |
rv = APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *backend,
|
|
Packit |
90a5c9 |
server_rec *s, int status)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (backend) {
|
|
Packit |
90a5c9 |
backend->close = 1;
|
|
Packit |
90a5c9 |
ap_proxy_release_connection(proxy_function, backend, s);
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03251)
|
|
Packit |
90a5c9 |
"Health check %s Status (%d) for %s.",
|
|
Packit |
90a5c9 |
ap_proxy_show_hcmethod(backend->worker->s->method),
|
|
Packit |
90a5c9 |
status,
|
|
Packit |
90a5c9 |
backend->worker->s->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (status != OK) {
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_get_backend(const char *proxy_function, proxy_conn_rec **backend,
|
|
Packit |
90a5c9 |
proxy_worker *hc, sctx_t *ctx, apr_pool_t *ptemp)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int status;
|
|
Packit |
90a5c9 |
status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s);
|
|
Packit |
90a5c9 |
if (status == OK) {
|
|
Packit |
90a5c9 |
(*backend)->addr = hc->cp->addr;
|
|
Packit |
90a5c9 |
(*backend)->hostname = hc->s->hostname_ex;
|
|
Packit |
90a5c9 |
if (strcmp(hc->s->scheme, "https") == 0) {
|
|
Packit |
90a5c9 |
if (!ap_proxy_ssl_enable(NULL)) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252)
|
|
Packit |
90a5c9 |
"mod_ssl not configured?");
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
(*backend)->is_ssl = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return hc_determine_connection(ctx, hc, &(*backend)->addr, ptemp);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t hc_check_tcp(baton_t *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int status;
|
|
Packit |
90a5c9 |
sctx_t *ctx = baton->ctx;
|
|
Packit |
90a5c9 |
proxy_worker *hc = baton->hc;
|
|
Packit |
90a5c9 |
proxy_conn_rec *backend = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
status = hc_get_backend("HCTCP", &backend, hc, ctx, baton->ptemp);
|
|
Packit |
90a5c9 |
if (status == OK) {
|
|
Packit |
90a5c9 |
status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s);
|
|
Packit |
90a5c9 |
/* does an unconditional ap_proxy_is_socket_connected() */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return backend_cleanup("HCTCP", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_send(request_rec *r, const char *out, apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
conn_rec *c = r->connection;
|
|
Packit |
90a5c9 |
apr_bucket_alloc_t *ba = c->bucket_alloc;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%s", out);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(out, strlen(out),
|
|
Packit |
90a5c9 |
r->pool, ba));
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(ba));
|
|
Packit |
90a5c9 |
rv = ap_pass_brigade(c->output_filters, bb);
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
return (rv) ? !OK : OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_read_headers(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char buffer[HUGE_STRING_LEN];
|
|
Packit |
90a5c9 |
int len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
len = ap_getline(buffer, sizeof(buffer), r, 1);
|
|
Packit |
90a5c9 |
if (len <= 0) {
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(03254)
|
|
Packit |
90a5c9 |
"%.*s", len, buffer);
|
|
Packit |
90a5c9 |
/* for the below, see ap_proxy_http_process_response() */
|
|
Packit |
90a5c9 |
if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
|
|
Packit |
90a5c9 |
int major;
|
|
Packit |
90a5c9 |
char keepchar;
|
|
Packit |
90a5c9 |
int proxy_status = OK;
|
|
Packit |
90a5c9 |
const char *proxy_status_line = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
major = buffer[5] - '0';
|
|
Packit |
90a5c9 |
if ((major != 1) || (len >= sizeof(buffer)-1)) {
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
keepchar = buffer[12];
|
|
Packit |
90a5c9 |
buffer[12] = '\0';
|
|
Packit |
90a5c9 |
proxy_status = atoi(&buffer[9]);
|
|
Packit |
90a5c9 |
if (keepchar != '\0') {
|
|
Packit |
90a5c9 |
buffer[12] = keepchar;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
buffer[12] = ' ';
|
|
Packit |
90a5c9 |
buffer[13] = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
proxy_status_line = apr_pstrdup(r->pool, &buffer[9]);
|
|
Packit |
90a5c9 |
r->status = proxy_status;
|
|
Packit |
90a5c9 |
r->status_line = proxy_status_line;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* OK, 1st line is OK... scarf in the headers */
|
|
Packit |
90a5c9 |
while ((len = ap_getline(buffer, sizeof(buffer), r, 1)) > 0) {
|
|
Packit |
90a5c9 |
char *value, *end;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%.*s",
|
|
Packit |
90a5c9 |
len, buffer);
|
|
Packit |
90a5c9 |
if (!(value = strchr(buffer, ':'))) {
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*value = '\0';
|
|
Packit |
90a5c9 |
++value;
|
|
Packit |
90a5c9 |
while (apr_isspace(*value))
|
|
Packit |
90a5c9 |
++value; /* Skip to start of value */
|
|
Packit |
90a5c9 |
for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
|
|
Packit |
90a5c9 |
*end = '\0';
|
|
Packit |
90a5c9 |
apr_table_add(r->headers_out, buffer, value);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_read_body(request_rec *r, apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
int seen_eos = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
apr_size_t len = HUGE_STRING_LEN;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_READBYTES,
|
|
Packit |
90a5c9 |
APR_BLOCK_READ, len);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
if (APR_STATUS_IS_EOF(rv)) {
|
|
Packit |
90a5c9 |
rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server, APLOGNO(03300)
|
|
Packit |
90a5c9 |
"Error reading response body");
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (!APR_BRIGADE_EMPTY(bb)) {
|
|
Packit |
90a5c9 |
apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_EOS(bucket)) {
|
|
Packit |
90a5c9 |
seen_eos = 1;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_FLUSH(bucket)) {
|
|
Packit |
90a5c9 |
apr_bucket_delete(bucket);
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(bucket);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(r->kept_body, bucket);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
while (!seen_eos);
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
return (rv == APR_SUCCESS ? OK : !OK);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Send the HTTP OPTIONS, HEAD or GET request to the backend
|
|
Packit |
90a5c9 |
* server associated w/ worker. If we have Conditions,
|
|
Packit |
90a5c9 |
* then apply those to the resulting response, otherwise
|
|
Packit |
90a5c9 |
* any status code 2xx or 3xx is considered "passing"
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t hc_check_http(baton_t *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int status;
|
|
Packit |
90a5c9 |
proxy_conn_rec *backend = NULL;
|
|
Packit |
90a5c9 |
sctx_t *ctx = baton->ctx;
|
|
Packit |
90a5c9 |
proxy_worker *hc = baton->hc;
|
|
Packit |
90a5c9 |
proxy_worker *worker = baton->worker;
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp = baton->ptemp;
|
|
Packit |
90a5c9 |
request_rec *r;
|
|
Packit |
90a5c9 |
wctx_t *wctx;
|
|
Packit |
90a5c9 |
hc_condition_t *cond;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
wctx = (wctx_t *)hc->context;
|
|
Packit |
90a5c9 |
if (!wctx->req || !wctx->method) {
|
|
Packit |
90a5c9 |
return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((status = hc_get_backend("HCOH", &backend, hc, ctx, ptemp)) != OK) {
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((status = ap_proxy_connect_backend("HCOH", backend, hc, ctx->s)) != OK) {
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r = create_request_rec(ptemp, ctx->s, baton->balancer, wctx->method);
|
|
Packit |
f02de7 |
if ((status = ap_proxy_connection_create_ex("HCOH", backend, r)) != OK) {
|
|
Packit |
f02de7 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
set_request_connection(r, backend->connection);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((status = hc_send(r, wctx->req, bb)) != OK) {
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if ((status = hc_read_headers(r)) != OK) {
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!r->header_only) {
|
|
Packit |
90a5c9 |
apr_table_t *saved_headers_in = r->headers_in;
|
|
Packit |
90a5c9 |
r->headers_in = apr_table_copy(r->pool, r->headers_out);
|
|
Packit |
90a5c9 |
ap_proxy_pre_http_request(backend->connection, r);
|
|
Packit |
90a5c9 |
status = hc_read_body(r, bb);
|
|
Packit |
90a5c9 |
r->headers_in = saved_headers_in;
|
|
Packit |
90a5c9 |
if (status != OK) {
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
r->trailers_out = apr_table_copy(r->pool, r->trailers_in);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (*worker->s->hcexpr &&
|
|
Packit |
90a5c9 |
(cond = (hc_condition_t *)apr_table_get(ctx->conditions, worker->s->hcexpr)) != NULL) {
|
|
Packit |
90a5c9 |
const char *err;
|
|
Packit |
90a5c9 |
int ok = ap_expr_exec(r, cond->pexpr, &err;;
|
|
Packit |
90a5c9 |
if (ok > 0) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
|
|
Packit |
90a5c9 |
"Condition %s for %s (%s): passed", worker->s->hcexpr,
|
|
Packit |
90a5c9 |
hc->s->name, worker->s->name);
|
|
Packit |
90a5c9 |
} else if (ok < 0 || err) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctx->s, APLOGNO(03301)
|
|
Packit |
90a5c9 |
"Error on checking condition %s for %s (%s): %s", worker->s->hcexpr,
|
|
Packit |
90a5c9 |
hc->s->name, worker->s->name, err);
|
|
Packit |
90a5c9 |
status = !OK;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
|
|
Packit |
90a5c9 |
"Condition %s for %s (%s) : failed", worker->s->hcexpr,
|
|
Packit |
90a5c9 |
hc->s->name, worker->s->name);
|
|
Packit |
90a5c9 |
status = !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
} else if (r->status < 200 || r->status > 399) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
|
|
Packit |
90a5c9 |
"Response status %i for %s (%s): failed", r->status,
|
|
Packit |
90a5c9 |
hc->s->name, worker->s->name);
|
|
Packit |
90a5c9 |
status = !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return backend_cleanup("HCOH", backend, ctx->s, status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
baton_t *baton = (baton_t *)b;
|
|
Packit |
90a5c9 |
server_rec *s = baton->ctx->s;
|
|
Packit |
90a5c9 |
proxy_worker *worker = baton->worker;
|
|
Packit |
90a5c9 |
proxy_worker *hc = baton->hc;
|
|
Packit |
90a5c9 |
apr_time_t now = baton->now;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03256)
|
|
Packit |
90a5c9 |
"%sHealth checking %s", (thread ? "Threaded " : ""),
|
|
Packit |
90a5c9 |
worker->s->name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
worker->s->updated = now;
|
|
Packit |
90a5c9 |
if (hc->s->method == TCP) {
|
|
Packit |
90a5c9 |
rv = hc_check_tcp(baton);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = hc_check_http(baton);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (rv == APR_ENOTIMPL) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(03257)
|
|
Packit |
90a5c9 |
"Somehow tried to use unimplemented hcheck method: %d",
|
|
Packit |
90a5c9 |
(int)hc->s->method);
|
|
Packit |
90a5c9 |
apr_pool_destroy(baton->ptemp);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* what state are we in ? */
|
|
Packit |
90a5c9 |
if (PROXY_WORKER_IS_HCFAILED(worker)) {
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
worker->s->pcount += 1;
|
|
Packit |
90a5c9 |
if (worker->s->pcount >= worker->s->passes) {
|
|
Packit |
90a5c9 |
ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 0, worker);
|
|
Packit |
90a5c9 |
ap_proxy_set_wstatus(PROXY_WORKER_IN_ERROR_FLAG, 0, worker);
|
|
Packit |
90a5c9 |
worker->s->pcount = 0;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03302)
|
|
Packit |
90a5c9 |
"%sHealth check ENABLING %s", (thread ? "Threaded " : ""),
|
|
Packit |
90a5c9 |
worker->s->name);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
worker->s->error_time = now;
|
|
Packit |
90a5c9 |
worker->s->fcount += 1;
|
|
Packit |
90a5c9 |
if (worker->s->fcount >= worker->s->fails) {
|
|
Packit |
90a5c9 |
ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 1, worker);
|
|
Packit |
90a5c9 |
worker->s->fcount = 0;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03303)
|
|
Packit |
90a5c9 |
"%sHealth check DISABLING %s", (thread ? "Threaded " : ""),
|
|
Packit |
90a5c9 |
worker->s->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_pool_destroy(baton->ptemp);
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t hc_watchdog_callback(int state, void *data,
|
|
Packit |
90a5c9 |
apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_time_t now = apr_time_now();
|
|
Packit |
90a5c9 |
proxy_balancer *balancer;
|
|
Packit |
90a5c9 |
sctx_t *ctx = (sctx_t *)data;
|
|
Packit |
90a5c9 |
server_rec *s = ctx->s;
|
|
Packit |
90a5c9 |
proxy_server_conf *conf;
|
|
Packit |
90a5c9 |
static apr_thread_pool_t *hctp = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (state) {
|
|
Packit |
90a5c9 |
case AP_WATCHDOG_STATE_STARTING:
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03258)
|
|
Packit |
90a5c9 |
"%s watchdog started.",
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME);
|
|
Packit |
90a5c9 |
#if HC_USE_THREADS
|
|
Packit |
90a5c9 |
if (tpsize && hctp == NULL) {
|
|
Packit |
90a5c9 |
rv = apr_thread_pool_create(&hctp, tpsize,
|
|
Packit |
90a5c9 |
tpsize, ctx->p);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03312)
|
|
Packit |
90a5c9 |
"apr_thread_pool_create() with %d threads failed",
|
|
Packit |
90a5c9 |
tpsize);
|
|
Packit |
90a5c9 |
/* we can continue on without the threadpools */
|
|
Packit |
90a5c9 |
hctp = NULL;
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03313)
|
|
Packit |
90a5c9 |
"apr_thread_pool_create() with %d threads succeeded",
|
|
Packit |
90a5c9 |
tpsize);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
} else {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03314)
|
|
Packit |
90a5c9 |
"Skipping apr_thread_pool_create()");
|
|
Packit |
90a5c9 |
hctp = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case AP_WATCHDOG_STATE_RUNNING:
|
|
Packit |
90a5c9 |
/* loop thru all workers */
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, s,
|
|
Packit |
90a5c9 |
"Run of %s watchdog.",
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME);
|
|
Packit |
90a5c9 |
if (s) {
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
conf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
|
|
Packit |
90a5c9 |
balancer = (proxy_balancer *)conf->balancers->elts;
|
|
Packit |
90a5c9 |
ctx->s = s;
|
|
Packit |
90a5c9 |
for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
|
|
Packit |
90a5c9 |
int n;
|
|
Packit |
90a5c9 |
proxy_worker **workers;
|
|
Packit |
90a5c9 |
proxy_worker *worker;
|
|
Packit |
90a5c9 |
/* Have any new balancers or workers been added dynamically? */
|
|
Packit |
90a5c9 |
ap_proxy_sync_balancer(balancer, s, conf);
|
|
Packit |
90a5c9 |
workers = (proxy_worker **)balancer->workers->elts;
|
|
Packit |
90a5c9 |
for (n = 0; n < balancer->workers->nelts; n++) {
|
|
Packit |
90a5c9 |
worker = *workers;
|
|
Packit |
90a5c9 |
if (!PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED) &&
|
|
Packit |
90a5c9 |
(worker->s->method != NONE) &&
|
|
Packit |
90a5c9 |
(now > worker->s->updated + worker->s->interval)) {
|
|
Packit |
90a5c9 |
baton_t *baton;
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp;
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
|
|
Packit |
90a5c9 |
"Checking %s worker: %s [%d] (%pp)", balancer->s->name,
|
|
Packit |
90a5c9 |
worker->s->name, worker->s->method, worker);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* This pool must last the lifetime of the (possible) thread */
|
|
Packit |
90a5c9 |
apr_pool_create(&ptemp, ctx->p);
|
|
Packit |
90a5c9 |
apr_pool_tag(ptemp, "hc_request");
|
|
Packit |
90a5c9 |
baton = apr_palloc(ptemp, sizeof(baton_t));
|
|
Packit |
90a5c9 |
baton->ctx = ctx;
|
|
Packit |
90a5c9 |
baton->now = now;
|
|
Packit |
90a5c9 |
baton->balancer = balancer;
|
|
Packit |
90a5c9 |
baton->worker = worker;
|
|
Packit |
90a5c9 |
baton->ptemp = ptemp;
|
|
Packit |
90a5c9 |
baton->hc = hc_get_hcworker(ctx, worker, ptemp);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!hctp) {
|
|
Packit |
90a5c9 |
hc_check(NULL, baton);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#if HC_USE_THREADS
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = apr_thread_pool_push(hctp, hc_check, (void *)baton,
|
|
Packit |
90a5c9 |
APR_THREAD_TASK_PRIORITY_NORMAL, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
workers++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case AP_WATCHDOG_STATE_STOPPING:
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03261)
|
|
Packit |
90a5c9 |
"stopping %s watchdog.",
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME);
|
|
Packit |
90a5c9 |
#if HC_USE_THREADS
|
|
Packit |
90a5c9 |
if (hctp) {
|
|
Packit |
90a5c9 |
rv = apr_thread_pool_destroy(hctp);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03315)
|
|
Packit |
90a5c9 |
"apr_thread_pool_destroy() failed");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
hctp = NULL;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
static int hc_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
tpsize = HC_THREADPOOL_SIZE;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
static int hc_post_config(apr_pool_t *p, apr_pool_t *plog,
|
|
Packit |
90a5c9 |
apr_pool_t *ptemp, server_rec *main_s)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
server_rec *s = main_s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *hc_watchdog_get_instance;
|
|
Packit |
90a5c9 |
APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *hc_watchdog_register_callback;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
hc_watchdog_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
|
|
Packit |
90a5c9 |
hc_watchdog_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback);
|
|
Packit |
90a5c9 |
if (!hc_watchdog_get_instance || !hc_watchdog_register_callback) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(03262)
|
|
Packit |
90a5c9 |
"mod_watchdog is required");
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = hc_watchdog_get_instance(&watchdog,
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME,
|
|
Packit |
90a5c9 |
0, 1, p);
|
|
Packit |
90a5c9 |
if (rv) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03263)
|
|
Packit |
90a5c9 |
"Failed to create watchdog instance (%s)",
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME);
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
while (s) {
|
|
Packit |
90a5c9 |
sctx_t *ctx = ap_get_module_config(s->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (s != ctx->s) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, APLOGNO(10019)
|
|
Packit |
90a5c9 |
"Missing unique per-server context: %s (%pp:%pp) (no hchecks)",
|
|
Packit |
90a5c9 |
s->server_hostname, s, ctx->s);
|
|
Packit |
90a5c9 |
s = s->next;
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = hc_watchdog_register_callback(watchdog,
|
|
Packit |
90a5c9 |
AP_WD_TM_SLICE,
|
|
Packit |
90a5c9 |
ctx,
|
|
Packit |
90a5c9 |
hc_watchdog_callback);
|
|
Packit |
90a5c9 |
if (rv) {
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03264)
|
|
Packit |
90a5c9 |
"Failed to register watchdog callback (%s)",
|
|
Packit |
90a5c9 |
HCHECK_WATHCHDOG_NAME);
|
|
Packit |
90a5c9 |
return !OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03265)
|
|
Packit |
90a5c9 |
"watchdog callback registered (%s for %s)", HCHECK_WATHCHDOG_NAME, s->server_hostname);
|
|
Packit |
90a5c9 |
s = s->next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void hc_show_exprs(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const apr_table_entry_t *elts;
|
|
Packit |
90a5c9 |
const apr_array_header_t *hdr;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
if (apr_is_empty_table(ctx->conditions))
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_rputs("\n\n"
|
|
Packit |
90a5c9 |
"Health check cond. expressions:\n"
|
|
Packit |
90a5c9 |
"Expr nameExpression\n", r);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
hdr = apr_table_elts(ctx->conditions);
|
|
Packit |
90a5c9 |
elts = (const apr_table_entry_t *) hdr->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < hdr->nelts; ++i) {
|
|
Packit |
90a5c9 |
hc_condition_t *cond;
|
|
Packit |
90a5c9 |
if (!elts[i].key) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
cond = (hc_condition_t *)elts[i].val;
|
|
Packit |
90a5c9 |
ap_rprintf(r, "%s%s\n",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, elts[i].key),
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, cond->expr));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_rputs(" \n", r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void hc_select_exprs(request_rec *r, const char *expr)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const apr_table_entry_t *elts;
|
|
Packit |
90a5c9 |
const apr_array_header_t *hdr;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
if (apr_is_empty_table(ctx->conditions))
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
hdr = apr_table_elts(ctx->conditions);
|
|
Packit |
90a5c9 |
elts = (const apr_table_entry_t *) hdr->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < hdr->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (!elts[i].key) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_rprintf(r, "<option value='%s' %s >%s</option>\n",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, elts[i].key),
|
|
Packit |
90a5c9 |
(!strcmp(elts[i].key, expr)) ? "selected" : "",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, elts[i].key));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_valid_expr(request_rec *r, const char *expr)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const apr_table_entry_t *elts;
|
|
Packit |
90a5c9 |
const apr_array_header_t *hdr;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
|
|
Packit |
90a5c9 |
&proxy_hcheck_module);
|
|
Packit |
90a5c9 |
if (apr_is_empty_table(ctx->conditions))
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
hdr = apr_table_elts(ctx->conditions);
|
|
Packit |
90a5c9 |
elts = (const apr_table_entry_t *) hdr->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < hdr->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (!elts[i].key) {
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!strcmp(elts[i].key, expr))
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *hc_get_body(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_off_t length;
|
|
Packit |
90a5c9 |
apr_size_t len;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
char *buf;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!r || !r->kept_body)
|
|
Packit |
90a5c9 |
return "";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = apr_brigade_length(r->kept_body, 1, &length);
|
|
Packit |
90a5c9 |
len = (apr_size_t)length;
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS || len == 0)
|
|
Packit |
90a5c9 |
return "";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
buf = apr_palloc(r->pool, len + 1);
|
|
Packit |
90a5c9 |
rv = apr_brigade_flatten(r->kept_body, buf, &len;;
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS)
|
|
Packit |
90a5c9 |
return "";
|
|
Packit |
90a5c9 |
buf[len] = '\0'; /* ensure */
|
|
Packit |
90a5c9 |
return (const char*)buf;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *hc_expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *var = (char *)data;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (var && *var && ctx->r && ap_cstr_casecmp(var, "BODY") == 0) {
|
|
Packit |
90a5c9 |
return hc_get_body(ctx->r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *hc_expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data,
|
|
Packit |
90a5c9 |
const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *var = (char *)arg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (var && *var && ctx->r && ap_cstr_casecmp(var, "BODY") == 0) {
|
|
Packit |
90a5c9 |
return hc_get_body(ctx->r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int hc_expr_lookup(ap_expr_lookup_parms *parms)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
switch (parms->type) {
|
|
Packit |
90a5c9 |
case AP_EXPR_FUNC_VAR:
|
|
Packit |
90a5c9 |
/* for now, we just handle everything that starts with HC_.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (strncasecmp(parms->name, "HC_", 3) == 0) {
|
|
Packit |
90a5c9 |
*parms->func = hc_expr_var_fn;
|
|
Packit |
90a5c9 |
*parms->data = parms->name + 3;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
case AP_EXPR_FUNC_STRING:
|
|
Packit |
90a5c9 |
/* Function HC() is implemented by us.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (strcasecmp(parms->name, "HC") == 0) {
|
|
Packit |
90a5c9 |
*parms->func = hc_expr_func_fn;
|
|
Packit |
90a5c9 |
*parms->data = parms->arg;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const command_rec command_table[] = {
|
|
Packit |
90a5c9 |
AP_INIT_RAW_ARGS("ProxyHCTemplate", set_hc_template, NULL, OR_FILEINFO,
|
|
Packit |
90a5c9 |
"Health check template"),
|
|
Packit |
90a5c9 |
AP_INIT_RAW_ARGS("ProxyHCExpr", set_hc_condition, NULL, OR_FILEINFO,
|
|
Packit |
90a5c9 |
"Define a health check condition ruleset expression"),
|
|
Packit |
90a5c9 |
#if HC_USE_THREADS
|
|
Packit |
90a5c9 |
AP_INIT_TAKE1("ProxyHCTPsize", set_hc_tpsize, NULL, RSRC_CONF,
|
|
Packit |
90a5c9 |
"Set size of health check thread pool"),
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
{ NULL }
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void hc_register_hooks(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
static const char *const aszPre[] = { "mod_proxy_balancer.c", "mod_proxy.c", NULL};
|
|
Packit |
90a5c9 |
static const char *const aszSucc[] = { "mod_watchdog.c", NULL};
|
|
Packit |
90a5c9 |
APR_REGISTER_OPTIONAL_FN(set_worker_hc_param);
|
|
Packit |
90a5c9 |
APR_REGISTER_OPTIONAL_FN(hc_show_exprs);
|
|
Packit |
90a5c9 |
APR_REGISTER_OPTIONAL_FN(hc_select_exprs);
|
|
Packit |
90a5c9 |
APR_REGISTER_OPTIONAL_FN(hc_valid_expr);
|
|
Packit |
90a5c9 |
ap_hook_pre_config(hc_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
ap_hook_post_config(hc_post_config, aszPre, aszSucc, APR_HOOK_LAST);
|
|
Packit |
90a5c9 |
ap_hook_expr_lookup(hc_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* the main config structure */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(proxy_hcheck) =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
NULL, /* create per-dir config structures */
|
|
Packit |
90a5c9 |
NULL, /* merge per-dir config structures */
|
|
Packit |
90a5c9 |
hc_create_config, /* create per-server config structures */
|
|
Packit |
90a5c9 |
NULL, /* merge per-server config structures */
|
|
Packit |
90a5c9 |
command_table, /* table of config file commands */
|
|
Packit |
90a5c9 |
hc_register_hooks /* register hooks */
|
|
Packit |
90a5c9 |
};
|