1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "mod_proxy.h"
17 #include "mod_watchdog.h"
18 #include "ap_slotmem.h"
19 #include "ap_expr.h"
20 #if APR_HAS_THREADS
21 #include "apr_thread_pool.h"
22 #endif
23 #include "http_ssl.h"
24
25 module AP_MODULE_DECLARE_DATA proxy_hcheck_module;
26
27 #define HCHECK_WATHCHDOG_NAME ("_proxy_hcheck_")
28 #define HC_THREADPOOL_SIZE (16)
29
30 /* Why? So we can easily set/clear HC_USE_THREADS during dev testing */
31 #if APR_HAS_THREADS
32 #ifndef HC_USE_THREADS
33 #define HC_USE_THREADS 1
34 #endif
35 #else
36 #define HC_USE_THREADS 0
37 #endif
38
39 typedef struct {
40 char *name;
41 hcmethod_t method;
42 int passes;
43 int fails;
44 apr_interval_time_t interval;
45 char *hurl;
46 char *hcexpr;
47 } hc_template_t;
48
49 typedef struct {
50 char *expr;
51 ap_expr_info_t *pexpr; /* parsed expression */
52 } hc_condition_t;
53
54 typedef struct {
55 apr_pool_t *p;
56 apr_array_header_t *templates;
57 apr_table_t *conditions;
58 apr_hash_t *hcworkers;
59 server_rec *s;
60 } sctx_t;
61
62 /* Used in the HC worker via the context field */
63 typedef struct {
64 const char *path; /* The path of the original worker URL */
65 const char *method; /* Method string for the HTTP/AJP request */
66 const char *req; /* pre-formatted HTTP/AJP request */
67 proxy_worker *w; /* Pointer to the actual worker */
68 } wctx_t;
69
70 typedef struct {
71 apr_pool_t *ptemp;
72 sctx_t *ctx;
73 proxy_balancer *balancer;
74 proxy_worker *worker;
75 proxy_worker *hc;
76 apr_time_t *now;
77 } baton_t;
78
79 static APR_OPTIONAL_FN_TYPE(ajp_handle_cping_cpong) *ajp_handle_cping_cpong = NULL;
80
hc_create_config(apr_pool_t * p,server_rec * s)81 static void *hc_create_config(apr_pool_t *p, server_rec *s)
82 {
83 sctx_t *ctx = apr_pcalloc(p, sizeof(sctx_t));
84 ctx->s = s;
85 apr_pool_create(&ctx->p, p);
86 apr_pool_tag(ctx->p, "proxy_hcheck");
87 ctx->templates = apr_array_make(p, 10, sizeof(hc_template_t));
88 ctx->conditions = apr_table_make(p, 10);
89 ctx->hcworkers = apr_hash_make(p);
90 return ctx;
91 }
92
93 static ap_watchdog_t *watchdog;
94 #if HC_USE_THREADS
95 static apr_thread_pool_t *hctp;
96 static int tpsize;
97 #endif
98
99 /*
100 * This serves double duty by not only validating (and creating)
101 * the health-check template, but also ties into set_worker_param()
102 * which does the actual setting of worker params in shm.
103 */
set_worker_hc_param(apr_pool_t * p,server_rec * s,proxy_worker * worker,const char * key,const char * val,void * v)104 static const char *set_worker_hc_param(apr_pool_t *p,
105 server_rec *s,
106 proxy_worker *worker,
107 const char *key,
108 const char *val,
109 void *v)
110 {
111 int ival;
112 hc_template_t *temp;
113 sctx_t *ctx = (sctx_t *) ap_get_module_config(s->module_config,
114 &proxy_hcheck_module);
115 if (!worker && !v) {
116 return "Bad call to set_worker_hc_param()";
117 }
118 if (!ctx) {
119 ctx = hc_create_config(p, s);
120 ap_set_module_config(s->module_config, &proxy_hcheck_module, ctx);
121 }
122 temp = (hc_template_t *)v;
123 if (!strcasecmp(key, "hctemplate")) {
124 hc_template_t *template;
125 template = (hc_template_t *)ctx->templates->elts;
126 for (ival = 0; ival < ctx->templates->nelts; ival++, template++) {
127 if (!ap_cstr_casecmp(template->name, val)) {
128 if (worker) {
129 worker->s->method = template->method;
130 worker->s->interval = template->interval;
131 worker->s->passes = template->passes;
132 worker->s->fails = template->fails;
133 PROXY_STRNCPY(worker->s->hcuri, template->hurl);
134 PROXY_STRNCPY(worker->s->hcexpr, template->hcexpr);
135 } else {
136 temp->method = template->method;
137 temp->interval = template->interval;
138 temp->passes = template->passes;
139 temp->fails = template->fails;
140 temp->hurl = apr_pstrdup(p, template->hurl);
141 temp->hcexpr = apr_pstrdup(p, template->hcexpr);
142 }
143 return NULL;
144 }
145 }
146 return apr_psprintf(p, "Unknown ProxyHCTemplate name: %s", val);
147 }
148 else if (!strcasecmp(key, "hcmethod")) {
149 proxy_hcmethods_t *method = proxy_hcmethods;
150 for (; method->name; method++) {
151 if (!ap_cstr_casecmp(val, method->name)) {
152 if (!method->implemented) {
153 return apr_psprintf(p, "Health check method %s not (yet) implemented",
154 val);
155 }
156 if (worker) {
157 worker->s->method = method->method;
158 } else {
159 temp->method = method->method;
160 }
161 return NULL;
162 }
163 }
164 return "Unknown method";
165 }
166 else if (!strcasecmp(key, "hcinterval")) {
167 apr_interval_time_t hci;
168 apr_status_t rv;
169 rv = ap_timeout_parameter_parse(val, &hci, "s");
170 if (rv != APR_SUCCESS)
171 return "Unparse-able hcinterval setting";
172 if (hci < AP_WD_TM_SLICE)
173 return apr_psprintf(p, "Interval must be a positive value greater than %"
174 APR_TIME_T_FMT "ms", apr_time_as_msec(AP_WD_TM_SLICE));
175 if (worker) {
176 worker->s->interval = hci;
177 } else {
178 temp->interval = hci;
179 }
180 }
181 else if (!strcasecmp(key, "hcpasses")) {
182 ival = atoi(val);
183 if (ival < 0)
184 return "Passes must be a positive value";
185 if (worker) {
186 worker->s->passes = ival;
187 } else {
188 temp->passes = ival;
189 }
190 }
191 else if (!strcasecmp(key, "hcfails")) {
192 ival = atoi(val);
193 if (ival < 0)
194 return "Fails must be a positive value";
195 if (worker) {
196 worker->s->fails = ival;
197 } else {
198 temp->fails = ival;
199 }
200 }
201 else if (!strcasecmp(key, "hcuri")) {
202 if (strlen(val) >= sizeof(worker->s->hcuri))
203 return apr_psprintf(p, "Health check uri length must be < %d characters",
204 (int)sizeof(worker->s->hcuri));
205 if (worker) {
206 PROXY_STRNCPY(worker->s->hcuri, val);
207 } else {
208 temp->hurl = apr_pstrdup(p, val);
209 }
210 }
211 else if (!strcasecmp(key, "hcexpr")) {
212 hc_condition_t *cond;
213 cond = (hc_condition_t *)apr_table_get(ctx->conditions, val);
214 if (!cond) {
215 return apr_psprintf(p, "Unknown health check condition expr: %s", val);
216 }
217 /* This check is wonky... a known expr can't be this big. Check anyway */
218 if (strlen(val) >= sizeof(worker->s->hcexpr))
219 return apr_psprintf(p, "Health check uri length must be < %d characters",
220 (int)sizeof(worker->s->hcexpr));
221 if (worker) {
222 PROXY_STRNCPY(worker->s->hcexpr, val);
223 } else {
224 temp->hcexpr = apr_pstrdup(p, val);
225 }
226 }
227 else {
228 return "unknown Worker hcheck parameter";
229 }
230 return NULL;
231 }
232
set_hc_condition(cmd_parms * cmd,void * dummy,const char * arg)233 static const char *set_hc_condition(cmd_parms *cmd, void *dummy, const char *arg)
234 {
235 char *name = NULL;
236 char *expr;
237 sctx_t *ctx;
238 hc_condition_t *cond;
239
240 const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
241 if (err)
242 return err;
243 ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config,
244 &proxy_hcheck_module);
245
246 name = ap_getword_conf(cmd->pool, &arg);
247 if (!*name) {
248 return apr_pstrcat(cmd->temp_pool, "Missing expression name for ",
249 cmd->cmd->name, NULL);
250 }
251 if (strlen(name) > (PROXY_WORKER_MAX_SCHEME_SIZE - 1)) {
252 return apr_psprintf(cmd->temp_pool, "Expression name limited to %d characters",
253 (PROXY_WORKER_MAX_SCHEME_SIZE - 1));
254 }
255 /* get expr. Allow fancy new {...} quoting style */
256 expr = ap_getword_conf2(cmd->temp_pool, &arg);
257 if (!*expr) {
258 return apr_pstrcat(cmd->temp_pool, "Missing expression for ",
259 cmd->cmd->name, NULL);
260 }
261 cond = apr_palloc(cmd->pool, sizeof(hc_condition_t));
262 cond->pexpr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
263 if (err) {
264 return apr_psprintf(cmd->temp_pool, "Could not parse expression \"%s\": %s",
265 expr, err);
266 }
267 cond->expr = apr_pstrdup(cmd->pool, expr);
268 apr_table_setn(ctx->conditions, name, (void *)cond);
269 expr = ap_getword_conf(cmd->temp_pool, &arg);
270 if (*expr) {
271 return "error: extra parameter(s)";
272 }
273 return NULL;
274 }
275
set_hc_template(cmd_parms * cmd,void * dummy,const char * arg)276 static const char *set_hc_template(cmd_parms *cmd, void *dummy, const char *arg)
277 {
278 char *name = NULL;
279 char *word, *val;
280 hc_template_t *template;
281 sctx_t *ctx;
282
283 const char *err = ap_check_cmd_context(cmd, NOT_IN_HTACCESS);
284 if (err)
285 return err;
286 ctx = (sctx_t *) ap_get_module_config(cmd->server->module_config,
287 &proxy_hcheck_module);
288
289 name = ap_getword_conf(cmd->temp_pool, &arg);
290 if (!*name) {
291 return apr_pstrcat(cmd->temp_pool, "Missing template name for ",
292 cmd->cmd->name, NULL);
293 }
294
295 template = (hc_template_t *)apr_array_push(ctx->templates);
296
297 template->name = apr_pstrdup(cmd->pool, name);
298 template->method = template->passes = template->fails = 1;
299 template->interval = apr_time_from_sec(HCHECK_WATHCHDOG_DEFAULT_INTERVAL);
300 template->hurl = NULL;
301 template->hcexpr = NULL;
302 while (*arg) {
303 word = ap_getword_conf(cmd->pool, &arg);
304 val = strchr(word, '=');
305 if (!val) {
306 return "Invalid ProxyHCTemplate parameter. Parameter must be "
307 "in the form 'key=value'";
308 }
309 else
310 *val++ = '\0';
311 err = set_worker_hc_param(cmd->pool, ctx->s, NULL, word, val, template);
312
313 if (err) {
314 /* get rid of recently pushed (bad) template */
315 apr_array_pop(ctx->templates);
316 return apr_pstrcat(cmd->temp_pool, "ProxyHCTemplate: ", err, " ", word, "=", val, "; ", name, NULL);
317 }
318 /* No error means we have a valid template */
319 }
320 return NULL;
321 }
322
323 #if HC_USE_THREADS
set_hc_tpsize(cmd_parms * cmd,void * dummy,const char * arg)324 static const char *set_hc_tpsize (cmd_parms *cmd, void *dummy, const char *arg)
325 {
326 const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
327 if (err)
328 return err;
329
330 tpsize = atoi(arg);
331 if (tpsize < 0)
332 return "Invalid ProxyHCTPsize parameter. Parameter must be "
333 ">= 0";
334 return NULL;
335 }
336 #endif
337
338 /*
339 * Create a dummy request rec, simply so we can use ap_expr.
340 * Use our short-lived pool for bucket_alloc so that we can simply move
341 * buckets and use them after the backend connection is released.
342 */
create_request_rec(apr_pool_t * p,server_rec * s,proxy_balancer * balancer,const char * method)343 static request_rec *create_request_rec(apr_pool_t *p, server_rec *s,
344 proxy_balancer *balancer,
345 const char *method)
346 {
347 request_rec *r;
348
349 r = apr_pcalloc(p, sizeof(request_rec));
350 r->pool = p;
351 r->server = s;
352
353 r->per_dir_config = r->server->lookup_defaults;
354 if (balancer->section_config) {
355 r->per_dir_config = ap_merge_per_dir_configs(r->pool,
356 r->per_dir_config,
357 balancer->section_config);
358 }
359
360 r->proxyreq = PROXYREQ_RESPONSE;
361
362 r->user = NULL;
363 r->ap_auth_type = NULL;
364
365 r->allowed_methods = ap_make_method_list(p, 2);
366
367 r->headers_in = apr_table_make(r->pool, 1);
368 r->trailers_in = apr_table_make(r->pool, 1);
369 r->subprocess_env = apr_table_make(r->pool, 25);
370 r->headers_out = apr_table_make(r->pool, 12);
371 r->err_headers_out = apr_table_make(r->pool, 5);
372 r->trailers_out = apr_table_make(r->pool, 1);
373 r->notes = apr_table_make(r->pool, 5);
374
375 r->request_config = ap_create_request_config(r->pool);
376 /* Must be set before we run create request hook */
377
378 r->sent_bodyct = 0; /* bytect isn't for body */
379
380 r->read_length = 0;
381 r->read_body = REQUEST_NO_BODY;
382
383 r->status = HTTP_OK; /* Until further notice */
384 r->the_request = NULL;
385
386 /* Begin by presuming any module can make its own path_info assumptions,
387 * until some module interjects and changes the value.
388 */
389 r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
390
391
392 /* Time to populate r with the data we have. */
393 r->method = method;
394 /* Provide quick information about the request method as soon as known */
395 r->method_number = ap_method_number_of(r->method);
396 if (r->method_number == M_OPTIONS
397 || (r->method_number == M_GET && r->method[0] == 'H')) {
398 r->header_only = 1;
399 }
400 else {
401 r->header_only = 0;
402 }
403
404 r->protocol = "HTTP/1.0";
405 r->proto_num = HTTP_VERSION(1, 0);
406
407 r->hostname = NULL;
408
409 return r;
410 }
411
set_request_connection(request_rec * r,conn_rec * conn)412 static void set_request_connection(request_rec *r, conn_rec *conn)
413 {
414 conn->bucket_alloc = apr_bucket_alloc_create(r->pool);
415 r->connection = conn;
416
417 r->kept_body = apr_brigade_create(r->pool, conn->bucket_alloc);
418 r->output_filters = r->proto_output_filters = conn->output_filters;
419 r->input_filters = r->proto_input_filters = conn->input_filters;
420
421 r->useragent_addr = conn->client_addr;
422 r->useragent_ip = conn->client_ip;
423 }
424
create_hcheck_req(wctx_t * wctx,proxy_worker * hc,apr_pool_t * p)425 static void create_hcheck_req(wctx_t *wctx, proxy_worker *hc,
426 apr_pool_t *p)
427 {
428 char *req = NULL;
429 const char *method = NULL;
430 switch (hc->s->method) {
431 case OPTIONS:
432 method = "OPTIONS";
433 req = apr_psprintf(p,
434 "OPTIONS * HTTP/1.0\r\n"
435 "Host: %s:%d\r\n"
436 "\r\n",
437 hc->s->hostname_ex, (int)hc->s->port);
438 break;
439
440 case HEAD:
441 method = "HEAD";
442 /* fallthru */
443 case GET:
444 if (!method) { /* did we fall thru? If not, we are GET */
445 method = "GET";
446 }
447 req = apr_psprintf(p,
448 "%s %s%s%s HTTP/1.0\r\n"
449 "Host: %s:%d\r\n"
450 "\r\n",
451 method,
452 (wctx->path ? wctx->path : ""),
453 (wctx->path && *hc->s->hcuri ? "/" : "" ),
454 (*hc->s->hcuri ? hc->s->hcuri : ""),
455 hc->s->hostname_ex, (int)hc->s->port);
456 break;
457
458 default:
459 break;
460 }
461 wctx->req = req;
462 wctx->method = method;
463 }
464
hc_get_hcworker(sctx_t * ctx,proxy_worker * worker,apr_pool_t * p)465 static proxy_worker *hc_get_hcworker(sctx_t *ctx, proxy_worker *worker,
466 apr_pool_t *p)
467 {
468 proxy_worker *hc = NULL;
469 apr_port_t port;
470
471 hc = (proxy_worker *)apr_hash_get(ctx->hcworkers, &worker, sizeof worker);
472 if (!hc) {
473 apr_uri_t uri;
474 apr_status_t rv;
475 const char *url = worker->s->name_ex;
476 wctx_t *wctx = apr_pcalloc(ctx->p, sizeof(wctx_t));
477
478 port = (worker->s->port ? worker->s->port
479 : ap_proxy_port_of_scheme(worker->s->scheme));
480 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03248)
481 "Creating hc worker %pp for %s://%s:%d",
482 worker, worker->s->scheme, worker->s->hostname_ex,
483 (int)port);
484
485 ap_proxy_define_worker(ctx->p, &hc, NULL, NULL, worker->s->name_ex, 0);
486 apr_snprintf(hc->s->name, sizeof hc->s->name, "%pp", worker);
487 apr_snprintf(hc->s->name_ex, sizeof hc->s->name_ex, "%pp", worker);
488 PROXY_STRNCPY(hc->s->hostname, worker->s->hostname); /* for compatibility */
489 PROXY_STRNCPY(hc->s->hostname_ex, worker->s->hostname_ex);
490 PROXY_STRNCPY(hc->s->scheme, worker->s->scheme);
491 PROXY_STRNCPY(hc->s->hcuri, worker->s->hcuri);
492 PROXY_STRNCPY(hc->s->hcexpr, worker->s->hcexpr);
493 hc->hash.def = hc->s->hash.def = ap_proxy_hashfunc(hc->s->name_ex,
494 PROXY_HASHFUNC_DEFAULT);
495 hc->hash.fnv = hc->s->hash.fnv = ap_proxy_hashfunc(hc->s->name_ex,
496 PROXY_HASHFUNC_FNV);
497 hc->s->port = port;
498 if (worker->s->conn_timeout_set) {
499 hc->s->conn_timeout_set = worker->s->conn_timeout_set;
500 hc->s->conn_timeout = worker->s->conn_timeout;
501 }
502 /* Do not disable worker in case of errors */
503 hc->s->status |= PROXY_WORKER_IGNORE_ERRORS;
504 /* Mark as the "generic" worker */
505 hc->s->status |= PROXY_WORKER_GENERIC;
506 ap_proxy_initialize_worker(hc, ctx->s, ctx->p);
507 hc->s->is_address_reusable = worker->s->is_address_reusable;
508 hc->s->disablereuse = worker->s->disablereuse;
509 hc->s->method = worker->s->method;
510 rv = apr_uri_parse(p, url, &uri);
511 if (rv == APR_SUCCESS) {
512 wctx->path = apr_pstrdup(ctx->p, uri.path);
513 }
514 wctx->w = worker;
515 create_hcheck_req(wctx, hc, ctx->p);
516 hc->context = wctx;
517 apr_hash_set(ctx->hcworkers, &worker, sizeof worker, hc);
518 }
519 /* This *could* have changed via the Balancer Manager */
520 /* TODO */
521 if (hc->s->method != worker->s->method) {
522 wctx_t *wctx = hc->context;
523 port = (worker->s->port ? worker->s->port
524 : ap_proxy_port_of_scheme(worker->s->scheme));
525 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03311)
526 "Updating hc worker %pp for %s://%s:%d",
527 worker, worker->s->scheme, worker->s->hostname_ex,
528 (int)port);
529 hc->s->method = worker->s->method;
530 create_hcheck_req(wctx, hc, ctx->p);
531 }
532 return hc;
533 }
534
hc_determine_connection(sctx_t * ctx,proxy_worker * worker,apr_sockaddr_t ** addr,apr_pool_t * p)535 static int hc_determine_connection(sctx_t *ctx, proxy_worker *worker,
536 apr_sockaddr_t **addr, apr_pool_t *p)
537 {
538 apr_status_t rv = APR_SUCCESS;
539 /*
540 * normally, this is done in ap_proxy_determine_connection().
541 * TODO: Look at using ap_proxy_determine_connection() with a
542 * fake request_rec
543 */
544 if (worker->cp->addr) {
545 *addr = worker->cp->addr;
546 }
547 else {
548 rv = apr_sockaddr_info_get(addr, worker->s->hostname_ex,
549 APR_UNSPEC, worker->s->port, 0, p);
550 if (rv != APR_SUCCESS) {
551 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ctx->s, APLOGNO(03249)
552 "DNS lookup failure for: %s:%d",
553 worker->s->hostname_ex, (int)worker->s->port);
554 }
555 }
556 return (rv == APR_SUCCESS ? OK : !OK);
557 }
558
hc_init_worker(sctx_t * ctx,proxy_worker * worker)559 static apr_status_t hc_init_worker(sctx_t *ctx, proxy_worker *worker)
560 {
561 apr_status_t rv = APR_SUCCESS;
562 /*
563 * Since this is the watchdog, workers never actually handle a
564 * request here, and so the local data isn't initialized (of
565 * course, the shared memory is). So we need to bootstrap
566 * worker->cp. Note, we only need do this once.
567 */
568 if (!worker->cp) {
569 rv = ap_proxy_initialize_worker(worker, ctx->s, ctx->p);
570 if (rv != APR_SUCCESS) {
571 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ctx->s, APLOGNO(03250) "Cannot init worker");
572 return rv;
573 }
574 if (worker->s->is_address_reusable && !worker->s->disablereuse &&
575 hc_determine_connection(ctx, worker, &worker->cp->addr,
576 worker->cp->pool) != OK) {
577 rv = APR_EGENERAL;
578 }
579 }
580 return rv;
581 }
582
backend_cleanup(const char * proxy_function,proxy_conn_rec * backend,server_rec * s,int status)583 static apr_status_t backend_cleanup(const char *proxy_function, proxy_conn_rec *backend,
584 server_rec *s, int status)
585 {
586 if (backend) {
587 backend->close = 1;
588 ap_proxy_release_connection(proxy_function, backend, s);
589 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03251)
590 "Health check %s Status (%d) for %s.",
591 ap_proxy_show_hcmethod(backend->worker->s->method),
592 status,
593 backend->worker->s->name_ex);
594 }
595 if (status != OK) {
596 return APR_EGENERAL;
597 }
598 return APR_SUCCESS;
599 }
600
hc_get_backend(const char * proxy_function,proxy_conn_rec ** backend,proxy_worker * hc,sctx_t * ctx,apr_pool_t * ptemp)601 static int hc_get_backend(const char *proxy_function, proxy_conn_rec **backend,
602 proxy_worker *hc, sctx_t *ctx, apr_pool_t *ptemp)
603 {
604 int status;
605 status = ap_proxy_acquire_connection(proxy_function, backend, hc, ctx->s);
606 if (status == OK) {
607 (*backend)->addr = hc->cp->addr;
608 (*backend)->hostname = hc->s->hostname_ex;
609 if (strcmp(hc->s->scheme, "https") == 0 || strcmp(hc->s->scheme, "wss") == 0 ) {
610 if (!ap_ssl_has_outgoing_handlers()) {
611 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctx->s, APLOGNO(03252)
612 "mod_ssl not configured?");
613 return !OK;
614 }
615 (*backend)->is_ssl = 1;
616 }
617
618 }
619 return hc_determine_connection(ctx, hc, &(*backend)->addr, ptemp);
620 }
621
hc_check_cping(baton_t * baton,apr_thread_t * thread)622 static apr_status_t hc_check_cping(baton_t *baton, apr_thread_t *thread)
623 {
624 int status;
625 sctx_t *ctx = baton->ctx;
626 proxy_worker *hc = baton->hc;
627 proxy_conn_rec *backend = NULL;
628 apr_pool_t *ptemp = baton->ptemp;
629 request_rec *r;
630 apr_interval_time_t timeout;
631
632 if (!ajp_handle_cping_cpong) {
633 return APR_ENOTIMPL;
634 }
635
636 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, baton->ctx->s, "HCCPING starting");
637 if ((status = hc_get_backend("HCCPING", &backend, hc, ctx, baton->ptemp)) != OK) {
638 return backend_cleanup("HCCPING", backend, ctx->s, status);
639 }
640 if ((status = ap_proxy_connect_backend("HCCPING", backend, hc, ctx->s)) != OK) {
641 return backend_cleanup("HCCPING", backend, ctx->s, status);
642 }
643 r = create_request_rec(ptemp, ctx->s, baton->balancer, "CPING");
644 if ((status = ap_proxy_connection_create_ex("HCCPING", backend, r)) != OK) {
645 return backend_cleanup("HCCPING", backend, ctx->s, status);
646 }
647 set_request_connection(r, backend->connection);
648 backend->connection->current_thread = thread;
649
650 if (hc->s->ping_timeout_set) {
651 timeout = hc->s->ping_timeout;
652 } else if ( hc->s->conn_timeout_set) {
653 timeout = hc->s->conn_timeout;
654 } else if ( hc->s->timeout_set) {
655 timeout = hc->s->timeout;
656 } else {
657 /* default to socket timeout */
658 apr_socket_timeout_get(backend->sock, &timeout);
659 }
660 status = ajp_handle_cping_cpong(backend->sock, r, timeout);
661 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, baton->ctx->s, "HCCPING done %d", status);
662 return backend_cleanup("HCCPING", backend, ctx->s, status);
663 }
664
hc_check_tcp(baton_t * baton)665 static apr_status_t hc_check_tcp(baton_t *baton)
666 {
667 int status;
668 sctx_t *ctx = baton->ctx;
669 proxy_worker *hc = baton->hc;
670 proxy_conn_rec *backend = NULL;
671
672 status = hc_get_backend("HCTCP", &backend, hc, ctx, baton->ptemp);
673 if (status == OK) {
674 status = ap_proxy_connect_backend("HCTCP", backend, hc, ctx->s);
675 /* does an unconditional ap_proxy_is_socket_connected() */
676 }
677 return backend_cleanup("HCTCP", backend, ctx->s, status);
678 }
679
hc_send(request_rec * r,const char * out,apr_bucket_brigade * bb)680 static int hc_send(request_rec *r, const char *out, apr_bucket_brigade *bb)
681 {
682 apr_status_t rv;
683 conn_rec *c = r->connection;
684 apr_bucket_alloc_t *ba = c->bucket_alloc;
685 ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%s", out);
686 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(out, strlen(out),
687 r->pool, ba));
688 APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(ba));
689 rv = ap_pass_brigade(c->output_filters, bb);
690 apr_brigade_cleanup(bb);
691 return (rv) ? !OK : OK;
692 }
693
hc_read_headers(request_rec * r)694 static int hc_read_headers(request_rec *r)
695 {
696 char buffer[HUGE_STRING_LEN];
697 int len;
698 const char *ct;
699
700 len = ap_getline(buffer, sizeof(buffer), r, 1);
701 if (len <= 0) {
702 return !OK;
703 }
704 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, APLOGNO(03254)
705 "%.*s", len, buffer);
706 /* for the below, see ap_proxy_http_process_response() */
707 if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) {
708 int major;
709 char keepchar;
710 int proxy_status = OK;
711 const char *proxy_status_line = NULL;
712
713 major = buffer[5] - '0';
714 if ((major != 1) || (len >= sizeof(buffer)-1)) {
715 return !OK;
716 }
717
718 keepchar = buffer[12];
719 buffer[12] = '\0';
720 proxy_status = atoi(&buffer[9]);
721 if (keepchar != '\0') {
722 buffer[12] = keepchar;
723 } else {
724 buffer[12] = ' ';
725 buffer[13] = '\0';
726 }
727 proxy_status_line = apr_pstrdup(r->pool, &buffer[9]);
728 r->status = proxy_status;
729 r->status_line = proxy_status_line;
730 } else {
731 return !OK;
732 }
733
734 /* OK, 1st line is OK... scarf in the headers */
735 while ((len = ap_getline(buffer, sizeof(buffer), r, 1)) > 0) {
736 char *value, *end;
737 ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, r->server, "%.*s",
738 len, buffer);
739 if (!(value = strchr(buffer, ':'))) {
740 return !OK;
741 }
742 *value = '\0';
743 ++value;
744 while (apr_isspace(*value))
745 ++value; /* Skip to start of value */
746 for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
747 *end = '\0';
748 apr_table_add(r->headers_out, buffer, value);
749 }
750
751 /* Set the Content-Type for the request if set */
752 if ((ct = apr_table_get(r->headers_out, "Content-Type")) != NULL)
753 ap_set_content_type(r, ct);
754
755 return OK;
756 }
757
hc_read_body(request_rec * r,apr_bucket_brigade * bb)758 static int hc_read_body(request_rec *r, apr_bucket_brigade *bb)
759 {
760 apr_status_t rv = APR_SUCCESS;
761 int seen_eos = 0;
762
763 do {
764 apr_size_t len = HUGE_STRING_LEN;
765
766 apr_brigade_cleanup(bb);
767 rv = ap_get_brigade(r->proto_input_filters, bb, AP_MODE_READBYTES,
768 APR_BLOCK_READ, len);
769
770 if (rv != APR_SUCCESS) {
771 if (APR_STATUS_IS_EOF(rv)) {
772 rv = APR_SUCCESS;
773 break;
774 }
775 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server, APLOGNO(03300)
776 "Error reading response body");
777 break;
778 }
779
780 while (!APR_BRIGADE_EMPTY(bb)) {
781 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
782 if (APR_BUCKET_IS_EOS(bucket)) {
783 seen_eos = 1;
784 break;
785 }
786 if (APR_BUCKET_IS_FLUSH(bucket)) {
787 apr_bucket_delete(bucket);
788 continue;
789 }
790 APR_BUCKET_REMOVE(bucket);
791 APR_BRIGADE_INSERT_TAIL(r->kept_body, bucket);
792 }
793 }
794 while (!seen_eos);
795 apr_brigade_cleanup(bb);
796 return (rv == APR_SUCCESS ? OK : !OK);
797 }
798
799 /*
800 * Send the HTTP OPTIONS, HEAD or GET request to the backend
801 * server associated w/ worker. If we have Conditions,
802 * then apply those to the resulting response, otherwise
803 * any status code 2xx or 3xx is considered "passing"
804 */
hc_check_http(baton_t * baton,apr_thread_t * thread)805 static apr_status_t hc_check_http(baton_t *baton, apr_thread_t *thread)
806 {
807 int status;
808 proxy_conn_rec *backend = NULL;
809 sctx_t *ctx = baton->ctx;
810 proxy_worker *hc = baton->hc;
811 proxy_worker *worker = baton->worker;
812 apr_pool_t *ptemp = baton->ptemp;
813 request_rec *r;
814 wctx_t *wctx;
815 hc_condition_t *cond;
816 apr_bucket_brigade *bb;
817
818 wctx = (wctx_t *)hc->context;
819 if (!wctx->req || !wctx->method) {
820 return APR_ENOTIMPL;
821 }
822
823 if ((status = hc_get_backend("HCOH", &backend, hc, ctx, ptemp)) != OK) {
824 return backend_cleanup("HCOH", backend, ctx->s, status);
825 }
826 if ((status = ap_proxy_connect_backend("HCOH", backend, hc, ctx->s)) != OK) {
827 return backend_cleanup("HCOH", backend, ctx->s, status);
828 }
829
830 r = create_request_rec(ptemp, ctx->s, baton->balancer, wctx->method);
831 if ((status = ap_proxy_connection_create_ex("HCOH", backend, r)) != OK) {
832 return backend_cleanup("HCOH", backend, ctx->s, status);
833 }
834 set_request_connection(r, backend->connection);
835 backend->connection->current_thread = thread;
836
837 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
838
839 if ((status = hc_send(r, wctx->req, bb)) != OK) {
840 return backend_cleanup("HCOH", backend, ctx->s, status);
841 }
842 if ((status = hc_read_headers(r)) != OK) {
843 return backend_cleanup("HCOH", backend, ctx->s, status);
844 }
845 if (!r->header_only) {
846 apr_table_t *saved_headers_in = r->headers_in;
847 r->headers_in = apr_table_copy(r->pool, r->headers_out);
848 ap_proxy_pre_http_request(backend->connection, r);
849 status = hc_read_body(r, bb);
850 r->headers_in = saved_headers_in;
851 if (status != OK) {
852 return backend_cleanup("HCOH", backend, ctx->s, status);
853 }
854 r->trailers_out = apr_table_copy(r->pool, r->trailers_in);
855 }
856
857 if (*worker->s->hcexpr &&
858 (cond = (hc_condition_t *)apr_table_get(ctx->conditions, worker->s->hcexpr)) != NULL) {
859 const char *err;
860 int ok = ap_expr_exec(r, cond->pexpr, &err);
861 if (ok > 0) {
862 ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
863 "Condition %s for %s (%s): passed", worker->s->hcexpr,
864 hc->s->name_ex, worker->s->name_ex);
865 } else if (ok < 0 || err) {
866 ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctx->s, APLOGNO(03301)
867 "Error on checking condition %s for %s (%s): %s", worker->s->hcexpr,
868 hc->s->name_ex, worker->s->name_ex, err);
869 status = !OK;
870 } else {
871 ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
872 "Condition %s for %s (%s) : failed", worker->s->hcexpr,
873 hc->s->name_ex, worker->s->name_ex);
874 status = !OK;
875 }
876 } else if (r->status < 200 || r->status > 399) {
877 ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, ctx->s,
878 "Response status %i for %s (%s): failed", r->status,
879 hc->s->name_ex, worker->s->name_ex);
880 status = !OK;
881 }
882 return backend_cleanup("HCOH", backend, ctx->s, status);
883 }
884
hc_check(apr_thread_t * thread,void * b)885 static void * APR_THREAD_FUNC hc_check(apr_thread_t *thread, void *b)
886 {
887 baton_t *baton = (baton_t *)b;
888 server_rec *s = baton->ctx->s;
889 proxy_worker *worker = baton->worker;
890 proxy_worker *hc = baton->hc;
891 apr_time_t now;
892 apr_status_t rv;
893
894 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03256)
895 "%sHealth checking %s", (thread ? "Threaded " : ""),
896 worker->s->name_ex);
897
898 if (hc->s->method == TCP) {
899 rv = hc_check_tcp(baton);
900 }
901 else if (hc->s->method == CPING) {
902 rv = hc_check_cping(baton, thread);
903 }
904 else {
905 rv = hc_check_http(baton, thread);
906 }
907
908 now = apr_time_now();
909 if (rv == APR_ENOTIMPL) {
910 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(03257)
911 "Somehow tried to use unimplemented hcheck method: %d",
912 (int)hc->s->method);
913 }
914 /* what state are we in ? */
915 else if (PROXY_WORKER_IS_HCFAILED(worker)) {
916 if (rv == APR_SUCCESS) {
917 worker->s->pcount += 1;
918 if (worker->s->pcount >= worker->s->passes) {
919 ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 0, worker);
920 ap_proxy_set_wstatus(PROXY_WORKER_IN_ERROR_FLAG, 0, worker);
921 worker->s->pcount = 0;
922 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03302)
923 "%sHealth check ENABLING %s", (thread ? "Threaded " : ""),
924 worker->s->name_ex);
925
926 }
927 }
928 }
929 else {
930 if (rv != APR_SUCCESS) {
931 worker->s->error_time = now;
932 worker->s->fcount += 1;
933 if (worker->s->fcount >= worker->s->fails) {
934 ap_proxy_set_wstatus(PROXY_WORKER_HC_FAIL_FLAG, 1, worker);
935 worker->s->fcount = 0;
936 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, APLOGNO(03303)
937 "%sHealth check DISABLING %s", (thread ? "Threaded " : ""),
938 worker->s->name_ex);
939 }
940 }
941 }
942 if (baton->now) {
943 *baton->now = now;
944 }
945 apr_pool_destroy(baton->ptemp);
946 worker->s->updated = now;
947
948 return NULL;
949 }
950
hc_watchdog_callback(int state,void * data,apr_pool_t * pool)951 static apr_status_t hc_watchdog_callback(int state, void *data,
952 apr_pool_t *pool)
953 {
954 apr_status_t rv = APR_SUCCESS;
955 proxy_balancer *balancer;
956 sctx_t *ctx = (sctx_t *)data;
957 server_rec *s = ctx->s;
958 proxy_server_conf *conf;
959
960 switch (state) {
961 case AP_WATCHDOG_STATE_STARTING:
962 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03258)
963 "%s watchdog started.",
964 HCHECK_WATHCHDOG_NAME);
965 #if HC_USE_THREADS
966 if (tpsize && hctp == NULL) {
967 rv = apr_thread_pool_create(&hctp, tpsize,
968 tpsize, ctx->p);
969 if (rv != APR_SUCCESS) {
970 ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03312)
971 "apr_thread_pool_create() with %d threads failed",
972 tpsize);
973 /* we can continue on without the threadpools */
974 hctp = NULL;
975 } else {
976 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03313)
977 "apr_thread_pool_create() with %d threads succeeded",
978 tpsize);
979 }
980 } else {
981 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO(03314)
982 "Skipping apr_thread_pool_create()");
983 hctp = NULL;
984 }
985 #endif
986 break;
987
988 case AP_WATCHDOG_STATE_RUNNING:
989 /* loop thru all workers */
990 if (s) {
991 int i;
992 conf = (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
993 balancer = (proxy_balancer *)conf->balancers->elts;
994 ctx->s = s;
995 for (i = 0; i < conf->balancers->nelts; i++, balancer++) {
996 int n;
997 apr_time_t now;
998 proxy_worker **workers;
999 proxy_worker *worker;
1000 /* Have any new balancers or workers been added dynamically? */
1001 ap_proxy_sync_balancer(balancer, s, conf);
1002 workers = (proxy_worker **)balancer->workers->elts;
1003 now = apr_time_now();
1004 for (n = 0; n < balancer->workers->nelts; n++) {
1005 worker = *workers;
1006 if (!PROXY_WORKER_IS(worker, PROXY_WORKER_STOPPED) &&
1007 (worker->s->method != NONE) &&
1008 (worker->s->updated != 0) &&
1009 (now > worker->s->updated + worker->s->interval)) {
1010 baton_t *baton;
1011 apr_pool_t *ptemp;
1012
1013 ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
1014 "Checking %s worker: %s [%d] (%pp)", balancer->s->name,
1015 worker->s->name_ex, worker->s->method, worker);
1016
1017 if ((rv = hc_init_worker(ctx, worker)) != APR_SUCCESS) {
1018 worker->s->updated = now;
1019 return rv;
1020 }
1021 worker->s->updated = 0;
1022
1023 /* This pool has the lifetime of the check */
1024 apr_pool_create(&ptemp, ctx->p);
1025 apr_pool_tag(ptemp, "hc_request");
1026 baton = apr_pcalloc(ptemp, sizeof(baton_t));
1027 baton->ctx = ctx;
1028 baton->balancer = balancer;
1029 baton->worker = worker;
1030 baton->ptemp = ptemp;
1031 baton->hc = hc_get_hcworker(ctx, worker, ptemp);
1032 #if HC_USE_THREADS
1033 if (hctp) {
1034 apr_thread_pool_push(hctp, hc_check, (void *)baton,
1035 APR_THREAD_TASK_PRIORITY_NORMAL,
1036 NULL);
1037 }
1038 else
1039 #endif
1040 {
1041 baton->now = &now;
1042 hc_check(NULL, baton);
1043 }
1044 }
1045 workers++;
1046 }
1047 }
1048 }
1049 break;
1050
1051 case AP_WATCHDOG_STATE_STOPPING:
1052 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03261)
1053 "stopping %s watchdog.",
1054 HCHECK_WATHCHDOG_NAME);
1055 #if HC_USE_THREADS
1056 if (hctp) {
1057 rv = apr_thread_pool_destroy(hctp);
1058 if (rv != APR_SUCCESS) {
1059 ap_log_error(APLOG_MARK, APLOG_INFO, rv, s, APLOGNO(03315)
1060 "apr_thread_pool_destroy() failed");
1061 }
1062 hctp = NULL;
1063 }
1064 #endif
1065 break;
1066 }
1067 return rv;
1068 }
hc_pre_config(apr_pool_t * pconf,apr_pool_t * plog,apr_pool_t * ptemp)1069 static int hc_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
1070 apr_pool_t *ptemp)
1071 {
1072 #if HC_USE_THREADS
1073 hctp = NULL;
1074 tpsize = HC_THREADPOOL_SIZE;
1075 #endif
1076 return OK;
1077 }
hc_post_config(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * main_s)1078 static int hc_post_config(apr_pool_t *p, apr_pool_t *plog,
1079 apr_pool_t *ptemp, server_rec *main_s)
1080 {
1081 apr_status_t rv;
1082 server_rec *s = main_s;
1083
1084 APR_OPTIONAL_FN_TYPE(ap_watchdog_get_instance) *hc_watchdog_get_instance;
1085 APR_OPTIONAL_FN_TYPE(ap_watchdog_register_callback) *hc_watchdog_register_callback;
1086
1087 if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
1088 return OK;
1089 }
1090 hc_watchdog_get_instance = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_get_instance);
1091 hc_watchdog_register_callback = APR_RETRIEVE_OPTIONAL_FN(ap_watchdog_register_callback);
1092 if (!hc_watchdog_get_instance || !hc_watchdog_register_callback) {
1093 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, APLOGNO(03262)
1094 "mod_watchdog is required");
1095 return !OK;
1096 }
1097 rv = hc_watchdog_get_instance(&watchdog,
1098 HCHECK_WATHCHDOG_NAME,
1099 0, 1, p);
1100 if (rv) {
1101 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03263)
1102 "Failed to create watchdog instance (%s)",
1103 HCHECK_WATHCHDOG_NAME);
1104 return !OK;
1105 }
1106 while (s) {
1107 sctx_t *ctx = ap_get_module_config(s->module_config,
1108 &proxy_hcheck_module);
1109
1110 if (s != ctx->s) {
1111 ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, s, APLOGNO(10019)
1112 "Missing unique per-server context: %s (%pp:%pp) (no hchecks)",
1113 s->server_hostname, s, ctx->s);
1114 s = s->next;
1115 continue;
1116 }
1117 rv = hc_watchdog_register_callback(watchdog,
1118 AP_WD_TM_SLICE,
1119 ctx,
1120 hc_watchdog_callback);
1121 if (rv) {
1122 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(03264)
1123 "Failed to register watchdog callback (%s)",
1124 HCHECK_WATHCHDOG_NAME);
1125 return !OK;
1126 }
1127 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(03265)
1128 "watchdog callback registered (%s for %s)", HCHECK_WATHCHDOG_NAME, s->server_hostname);
1129 s = s->next;
1130 }
1131
1132 ajp_handle_cping_cpong = APR_RETRIEVE_OPTIONAL_FN(ajp_handle_cping_cpong);
1133 if (ajp_handle_cping_cpong) {
1134 proxy_hcmethods_t *method = proxy_hcmethods;
1135 for (; method->name; method++) {
1136 if (method->method == CPING) {
1137 method->implemented = 1;
1138 break;
1139 }
1140 }
1141 }
1142
1143 return OK;
1144 }
1145
hc_show_exprs(request_rec * r)1146 static void hc_show_exprs(request_rec *r)
1147 {
1148 const apr_table_entry_t *elts;
1149 const apr_array_header_t *hdr;
1150 int i;
1151 sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
1152 &proxy_hcheck_module);
1153 if (!ctx)
1154 return;
1155 if (apr_is_empty_table(ctx->conditions))
1156 return;
1157
1158 ap_rputs("\n\n<table>"
1159 "<tr><th colspan='2'>Health check cond. expressions:</th></tr>\n"
1160 "<tr><th>Expr name</th><th>Expression</th></tr>\n", r);
1161
1162 hdr = apr_table_elts(ctx->conditions);
1163 elts = (const apr_table_entry_t *) hdr->elts;
1164 for (i = 0; i < hdr->nelts; ++i) {
1165 hc_condition_t *cond;
1166 if (!elts[i].key) {
1167 continue;
1168 }
1169 cond = (hc_condition_t *)elts[i].val;
1170 ap_rprintf(r, "<tr><td>%s</td><td>%s</td></tr>\n",
1171 ap_escape_html(r->pool, elts[i].key),
1172 ap_escape_html(r->pool, cond->expr));
1173 }
1174 ap_rputs("</table><hr/>\n", r);
1175 }
1176
hc_select_exprs(request_rec * r,const char * expr)1177 static void hc_select_exprs(request_rec *r, const char *expr)
1178 {
1179 const apr_table_entry_t *elts;
1180 const apr_array_header_t *hdr;
1181 int i;
1182 sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
1183 &proxy_hcheck_module);
1184 if (!ctx)
1185 return;
1186 if (apr_is_empty_table(ctx->conditions))
1187 return;
1188
1189 hdr = apr_table_elts(ctx->conditions);
1190 elts = (const apr_table_entry_t *) hdr->elts;
1191 for (i = 0; i < hdr->nelts; ++i) {
1192 if (!elts[i].key) {
1193 continue;
1194 }
1195 ap_rprintf(r, "<option value='%s' %s >%s</option>\n",
1196 ap_escape_html(r->pool, elts[i].key),
1197 (!strcmp(elts[i].key, expr)) ? "selected" : "",
1198 ap_escape_html(r->pool, elts[i].key));
1199 }
1200 }
1201
hc_valid_expr(request_rec * r,const char * expr)1202 static int hc_valid_expr(request_rec *r, const char *expr)
1203 {
1204 const apr_table_entry_t *elts;
1205 const apr_array_header_t *hdr;
1206 int i;
1207 sctx_t *ctx = (sctx_t *) ap_get_module_config(r->server->module_config,
1208 &proxy_hcheck_module);
1209 if (!ctx)
1210 return 0;
1211 if (apr_is_empty_table(ctx->conditions))
1212 return 0;
1213
1214 hdr = apr_table_elts(ctx->conditions);
1215 elts = (const apr_table_entry_t *) hdr->elts;
1216 for (i = 0; i < hdr->nelts; ++i) {
1217 if (!elts[i].key) {
1218 continue;
1219 }
1220 if (!strcmp(elts[i].key, expr))
1221 return 1;
1222 }
1223 return 0;
1224 }
1225
hc_get_body(request_rec * r)1226 static const char *hc_get_body(request_rec *r)
1227 {
1228 apr_off_t length;
1229 apr_size_t len;
1230 apr_status_t rv;
1231 char *buf;
1232
1233 if (!r || !r->kept_body)
1234 return "";
1235
1236 rv = apr_brigade_length(r->kept_body, 1, &length);
1237 len = (apr_size_t)length;
1238 if (rv != APR_SUCCESS || len == 0)
1239 return "";
1240
1241 buf = apr_palloc(r->pool, len + 1);
1242 rv = apr_brigade_flatten(r->kept_body, buf, &len);
1243 if (rv != APR_SUCCESS)
1244 return "";
1245 buf[len] = '\0'; /* ensure */
1246 return (const char*)buf;
1247 }
1248
hc_expr_var_fn(ap_expr_eval_ctx_t * ctx,const void * data)1249 static const char *hc_expr_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
1250 {
1251 char *var = (char *)data;
1252
1253 if (var && *var && ctx->r && ap_cstr_casecmp(var, "BODY") == 0) {
1254 return hc_get_body(ctx->r);
1255 }
1256 return NULL;
1257 }
1258
hc_expr_func_fn(ap_expr_eval_ctx_t * ctx,const void * data,const char * arg)1259 static const char *hc_expr_func_fn(ap_expr_eval_ctx_t *ctx, const void *data,
1260 const char *arg)
1261 {
1262 char *var = (char *)arg;
1263
1264 if (var && *var && ctx->r && ap_cstr_casecmp(var, "BODY") == 0) {
1265 return hc_get_body(ctx->r);
1266 }
1267 return NULL;
1268 }
1269
hc_expr_lookup(ap_expr_lookup_parms * parms)1270 static int hc_expr_lookup(ap_expr_lookup_parms *parms)
1271 {
1272 switch (parms->type) {
1273 case AP_EXPR_FUNC_VAR:
1274 /* for now, we just handle everything that starts with HC_.
1275 */
1276 if (strncasecmp(parms->name, "HC_", 3) == 0) {
1277 *parms->func = hc_expr_var_fn;
1278 *parms->data = parms->name + 3;
1279 return OK;
1280 }
1281 break;
1282 case AP_EXPR_FUNC_STRING:
1283 /* Function HC() is implemented by us.
1284 */
1285 if (strcasecmp(parms->name, "HC") == 0) {
1286 *parms->func = hc_expr_func_fn;
1287 *parms->data = parms->arg;
1288 return OK;
1289 }
1290 break;
1291 }
1292 return DECLINED;
1293 }
1294
1295 static const command_rec command_table[] = {
1296 AP_INIT_RAW_ARGS("ProxyHCTemplate", set_hc_template, NULL, OR_FILEINFO,
1297 "Health check template"),
1298 AP_INIT_RAW_ARGS("ProxyHCExpr", set_hc_condition, NULL, OR_FILEINFO,
1299 "Define a health check condition ruleset expression"),
1300 #if HC_USE_THREADS
1301 AP_INIT_TAKE1("ProxyHCTPsize", set_hc_tpsize, NULL, RSRC_CONF,
1302 "Set size of health check thread pool"),
1303 #endif
1304 { NULL }
1305 };
1306
hc_register_hooks(apr_pool_t * p)1307 static void hc_register_hooks(apr_pool_t *p)
1308 {
1309 static const char *const aszPre[] = { "mod_proxy_balancer.c", "mod_proxy.c", NULL};
1310 static const char *const aszSucc[] = { "mod_watchdog.c", NULL};
1311 APR_REGISTER_OPTIONAL_FN(set_worker_hc_param);
1312 APR_REGISTER_OPTIONAL_FN(hc_show_exprs);
1313 APR_REGISTER_OPTIONAL_FN(hc_select_exprs);
1314 APR_REGISTER_OPTIONAL_FN(hc_valid_expr);
1315 ap_hook_pre_config(hc_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
1316 ap_hook_post_config(hc_post_config, aszPre, aszSucc, APR_HOOK_LAST);
1317 ap_hook_expr_lookup(hc_expr_lookup, NULL, NULL, APR_HOOK_MIDDLE);
1318 }
1319
1320 /* the main config structure */
1321
1322 AP_DECLARE_MODULE(proxy_hcheck) =
1323 {
1324 STANDARD20_MODULE_STUFF,
1325 NULL, /* create per-dir config structures */
1326 NULL, /* merge per-dir config structures */
1327 hc_create_config, /* create per-server config structures */
1328 NULL, /* merge per-server config structures */
1329 command_table, /* table of config file commands */
1330 hc_register_hooks /* register hooks */
1331 };
1332