Index: server/util_cookies.c
===================================================================
--- server/util_cookies.c (Revision 1417803)
+++ server/util_cookies.c (Arbeitskopie)
@@ -274,7 +274,83 @@
}
+/* Iterate through the cookies, isolate our cookie and then remove it.
+ *
+ * If our cookie appears two or more times, but with different values,
+ * remove it twice and set the duplicated flag to true. Remove any
+ * $path or other attributes following our cookie if present. If we end
+ * up with an empty cookie, remove the whole header.
+ *
+ * Only RfC 6265 based cookies are supported. But most other cookie works, too.
+ * TODO: support RfC 2109 & 2965 styled cookie
+ */
+static int extract_set_cookie_value(ap_cookie_do * v, const char *key, const char *val)
+{
+ char *cookie;
+ char *cookie_pair, *pair_next;
+ char *name, *value;
+
+ /* breaks, when already duplicated */
+ if (v->duplicated)
+ return 1;
+
+ /* get first part of cookie, must be NAME=VALUE */
+ cookie = apr_pstrdup(v->r->pool, val);
+ cookie_pair = apr_strtok(cookie, ";", &pair_next);
+ if (!cookie_pair)
+ return 1;
+
+ /* get name */
+ name = apr_strtok(cookie_pair, "=", &value);
+ while (*name == ' ')
+ name++;
+ if (*name == '$')
+ name++;
+ if (!name || strcasecmp(v->name, name))
+ return 1;
+
+ /* check duplication */
+ if (v->encoded && strcmp(v->encoded, value) != 0) {
+ v->duplicated = TRUE;
+ return 1;
+ }
+
+ /* save cookie value */
+ v->encoded = value;
+
+ return 1;
+}
+
/**
+ * Read a set cookie called name, placing its value in val. This function implent
+ * the RfC 6265, only.
+ * The Set-Cookie2 Headers are scanned, too. (Should valid in most cases)
+ *
+ * If the cookie is duplicated, this function returns APR_EGENERAL.
+ */
+AP_DECLARE(apr_status_t) ap_set_cookie_read(request_rec * r, const char *name, const char **val)
+{
+ ap_cookie_do v;
+ v.r = r;
+ v.encoded = NULL;
+ v.new_cookies = apr_table_make(r->pool, 10);
+ v.duplicated = 0;
+ v.name = name;
+
+ apr_table_do((int (*) (void *, const char *, const char *))
+ extract_set_cookie_value, (void *) &v, r->headers_out,
+ "Set-Cookie", "Set-Cookie2", NULL);
+ if (v.duplicated) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() LOG_PREFIX
+ "server setted cookie '%s' more than once: %s", v.name, r->uri);
+ return APR_EGENERAL;
+ }
+
+ *val = v.encoded;
+ return APR_SUCCESS;
+}
+
+/**
* Sanity check a given string that it exists, is not empty,
* and does not contain the special characters '=', ';' and '&'.
*
Index: modules/proxy/proxy_util.c
===================================================================
--- modules/proxy/proxy_util.c (Revision 1417803)
+++ modules/proxy/proxy_util.c (Arbeitskopie)
@@ -1185,6 +1185,9 @@
bshared->sticky_separator = '.';
*bshared->nonce = PROXY_UNSET_NONCE; /* impossible valid input */
+ bshared->autoroute_type = PROXY_AUTOROUTE_TYPE_DISABLE;
+ *bshared->autoroute_field = '\0';
+
(*balancer)->s = bshared;
(*balancer)->sconf = conf;
Index: modules/proxy/mod_proxy.c
===================================================================
--- modules/proxy/mod_proxy.c (Revision 1417803)
+++ modules/proxy/mod_proxy.c (Arbeitskopie)
@@ -263,6 +263,42 @@
return NULL;
}
+PROXY_DECLARE(char *) ap_set_balancer_autoroute(proxy_balancer *balancer,
+ const char *val)
+{
+ if (!strncasecmp(val, "header:", 7)) {
+ val = val + 7;
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_HEADER;
+ }
+ else if (!strncasecmp(val, "header-remove:", 14)) {
+ val = val + 14;
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_HEADER_REMOVE;
+ }
+ else if (!strncasecmp(val, "notes:", 6)) {
+ val = val + 6;
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_NOTES;
+ }
+ else if (!strncasecmp(val, "cookie:", 7)) {
+ val = val + 7;
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_COOKIE;
+ }
+ else if (!strncasecmp(val, "env:", 4)) {
+ val = val + 4;
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_ENV;
+ }
+ else {
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_HEADER_REMOVE;
+ }
+
+ if (strlen(val) >= PROXY_BALANCER_MAX_STICKY_SIZE) {
+ balancer->s->autoroute_type = PROXY_AUTOROUTE_TYPE_DISABLE;
+ return "autoroute field length must be < 64 characters";
+ }
+ PROXY_STRNCPY(balancer->s->autoroute_field, val);
+
+ return NULL;
+}
+
static const char *set_balancer_param(proxy_server_conf *conf,
apr_pool_t *p,
proxy_balancer *balancer,
@@ -271,6 +307,7 @@
{
int ival;
+ char *ret;
if (!strcasecmp(key, "stickysession")) {
char *path;
/* Balancer sticky session name.
@@ -407,6 +444,10 @@
else
return "forcerecovery must be On|Off";
}
+ else if (!strcasecmp(key, "autoroute")) {
+ if ((ret = ap_set_balancer_autoroute( balancer, val)))
+ return ret;
+ }
else {
return "unknown Balancer parameter";
}
Index: modules/proxy/mod_proxy.h
===================================================================
--- modules/proxy/mod_proxy.h (Revision 1417803)
+++ modules/proxy/mod_proxy.h (Arbeitskopie)
@@ -312,6 +312,15 @@
#define PROXY_MAX_PROVIDER_NAME_SIZE 16
+/* autoroute types */
+#define PROXY_AUTOROUTE_TYPE_DISABLE 0
+#define PROXY_AUTOROUTE_TYPE_HEADER 1
+#define PROXY_AUTOROUTE_TYPE_NOTES 2
+#define PROXY_AUTOROUTE_TYPE_COOKIE 3
+#define PROXY_AUTOROUTE_TYPE_ENV 4
+#define PROXY_AUTOROUTE_TYPE_HEADER_REMOVE 5
+
+
#define PROXY_STRNCPY(dst, src) ap_proxy_strncpy((dst), (src), (sizeof(dst)))
#define PROXY_COPY_CONF_PARAMS(w, c) \
@@ -428,6 +437,8 @@
unsigned int vhosted:1;
unsigned int inactive:1;
unsigned int forcerecovery:1;
+ unsigned int autoroute_type;
+ char autoroute_field[PROXY_BALANCER_MAX_STICKY_SIZE];
} proxy_balancer_shared;
#define ALIGNED_PROXY_BALANCER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_balancer_shared)))
@@ -903,6 +914,15 @@
*/
int ap_proxy_lb_workers(void);
+/**
+ * set the autoroute of balancer
+ * @param balancer balancer
+ * @param val string with new config
+ * @return error message, or NULL
+ */
+PROXY_DECLARE(char *) ap_set_balancer_autoroute(proxy_balancer *balancer,
+ const char *val);
+
extern module PROXY_DECLARE_DATA proxy_module;
#endif /*MOD_PROXY_H*/
Index: modules/proxy/mod_proxy_balancer.c
===================================================================
--- modules/proxy/mod_proxy_balancer.c (Revision 1417803)
+++ modules/proxy/mod_proxy_balancer.c (Arbeitskopie)
@@ -22,6 +22,7 @@
#include "apr_version.h"
#include "ap_hooks.h"
#include "apr_date.h"
+#include "util_cookies.h"
static const char *balancer_mutex_type = "proxy-balancer-shm";
ap_slotmem_provider_t *storage = NULL;
@@ -614,6 +615,12 @@
"%s: worker (%s) rewritten to %s",
(*balancer)->s->name, (*worker)->s->name, *url);
+ /* store autoroute fieldname in notes for the output filter */
+ if ((*balancer)->s->autoroute_field == PROXY_AUTOROUTE_TYPE_HEADER_REMOVE &&
+ (*balancer)->s->autoroute_field[0] != '\0') {
+ apr_table_set(r->notes, "proxy-balancer-ar-field", (*balancer)->s->autoroute_field);
+ }
+
return access_status;
}
@@ -624,6 +631,8 @@
{
apr_status_t rv;
+ const char *new_route = NULL;
+ char *session = NULL;
if ((rv = PROXY_THREAD_LOCK(balancer)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01173)
@@ -632,6 +641,74 @@
return HTTP_INTERNAL_SERVER_ERROR;
}
+ /* set new route id */
+ if (balancer->s->autoroute_type != PROXY_AUTOROUTE_TYPE_DISABLE && *(balancer->s->autoroute_field) != '\0') {
+ switch (balancer->s->autoroute_type) {
+ case PROXY_AUTOROUTE_TYPE_HEADER:
+ new_route = apr_table_get(r->headers_out, balancer->s->autoroute_field);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "%s %s: get route (%s) from header (%s)",
+ balancer->s->name, worker->s->name, new_route, balancer->s->autoroute_field);
+ break;
+ case PROXY_AUTOROUTE_TYPE_HEADER_REMOVE:
+ new_route = apr_table_get(r->notes, "proxy-balancer-ar-newroute");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "%s %s: get route (%s) from removed header (%s)",
+ balancer->s->name, worker->s->name, new_route, balancer->s->autoroute_field);
+ break;
+ case PROXY_AUTOROUTE_TYPE_NOTES:
+ new_route = apr_table_get(r->notes, balancer->s->autoroute_field);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "%s %s: get route (%s) from notes (%s)",
+ balancer->s->name, worker->s->name, new_route, balancer->s->autoroute_field);
+ break;
+ case PROXY_AUTOROUTE_TYPE_ENV:
+ new_route = apr_table_get(r->subprocess_env, balancer->s->autoroute_field);
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "%s %s: get route (%s) from enviroment variable (%s)",
+ balancer->s->name, worker->s->name, new_route, balancer->s->autoroute_field);
+ break;
+ case PROXY_AUTOROUTE_TYPE_COOKIE:
+ if (*(balancer->s->autoroute_field) != '\0') {
+ if ((rv = ap_set_cookie_read(r, balancer->s->autoroute_field, (const char**)&session)) != APR_SUCCESS)
+ session = NULL;
+ }
+ else {
+ if ((rv = ap_set_cookie_read(r, balancer->s->sticky, (const char**)&session)) == APR_SUCCESS) {
+ /* look for session seperators */
+ if (balancer->s->sticky_separator && (session = ap_strchr(session, balancer->s->sticky_separator)))
+ session++;
+ if (session && balancer->s->sticky_separator && (session = ap_strchr(session, balancer->s->sticky_separator)))
+ *session = '\0';
+ }
+ else
+ session = NULL;
+ }
+ new_route = session;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "%s %s: get route (%s) from cookie (%s)",
+ balancer->s->name, worker->s->name, new_route, balancer->s->autoroute_field);
+ break;
+ }
+
+ if (new_route != NULL) {
+ /* update route id */
+ if (strcmp(worker->s->route, new_route) != 0) {
+ if (strlen(new_route) >= PROXY_WORKER_MAX_ROUTE_SIZE) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
+ "%s %s: route length must be < 64 characters",
+ balancer->s->name, worker->s->name);
+ }
+ else {
+ PROXY_STRNCPY(worker->s->route, new_route);
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO()
+ "%s: new route id (%s) for worker (%s)",
+ balancer->s->name, worker->s->name, new_route);
+ }
+ }
+ }
+ }
+
if (!apr_is_empty_array(balancer->errstatuses)) {
int i;
for (i = 0; i < balancer->errstatuses->nelts; i++) {
@@ -1136,6 +1213,15 @@
}
}
}
+ if ((val = apr_table_get(params, "b_autoroute")) && *val) {
+ if (strcasecmp(val, "-")) {
+ ap_set_balancer_autoroute(bsel, val);
+ }
+ else {
+ bsel->s->autoroute_type = PROXY_AUTOROUTE_TYPE_DISABLE;
+ *bsel->s->autoroute_field = '\0';
+ }
+ }
if ((val = apr_table_get(params, "b_wyes")) &&
(*val == '1' && *(val+1) == '\0') &&
(val = apr_table_get(params, "b_nwrkr"))) {
@@ -1233,6 +1319,16 @@
ap_rprintf(r,
"
MaxMembers | StickySession | DisableFailover | Timeout | FailoverAttempts | Method | " + "MaxMembers | StickySession | DisableFailover | Timeout | Autoroute | FailoverAttempts | Method | " "Path | Active |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
%" APR_TIME_T_FMT " | ", apr_time_sec(balancer->s->timeout)); + if (balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_DISABLE) { + ap_rprintf(r, "(None) | \n"); + } + else { + ap_rprintf(r, + "%s:%s | ", + balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_COOKIE ? "cookie" : + balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_ENV ? "env" : + balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_HEADER ? "header" : + balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_HEADER_REMOVE ? "header-remove" : + balancer->s->autoroute_type == PROXY_AUTOROUTE_TYPE_NOTES ? "notes" : "unknown", + balancer->s->autoroute_field); + } ap_rprintf(r, "%d | \n", balancer->s->max_attempts); ap_rprintf(r, "%s | \n", balancer->s->lbpname); @@ -1588,6 +1697,19 @@ ap_rvputs(r, "value ='", bsel->s->sticky, NULL); } ap_rputs("'> (Use '-' to delete)||||||||||
AutoRoute | ", r); + if (bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_DISABLE) { + ap_rprintf(r, "(Use '-' to delete) | (Use '-' to delete) | ", + bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_COOKIE ? "cookie" : + bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_ENV ? "env" : + bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_HEADER ? "header" : + bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_HEADER_REMOVE ? "header-remove" : + bsel->s->autoroute_type == PROXY_AUTOROUTE_TYPE_NOTES ? "notes" : "unknown", + bsel->s->autoroute_field); + } if (storage->num_free_slots(bsel->wslot) != 0) { ap_rputs("||||||||||||
Add New Worker: | "
" Are you sure? "
@@ -1645,6 +1767,30 @@
}
+static apr_status_t proxy_balancer_fixup_headers_out(ap_filter_t *f,
+ apr_bucket_brigade *in)
+{
+ /* copy new route id to notes, and remove it from response */
+ const char *field;
+ if ((field = apr_table_get(f->r->notes, "proxy-balancer-ar-field")) != NULL) {
+ const char *value;
+ if ((value = apr_table_get(f->r->headers_out, field)) != NULL) {
+ apr_table_set( f->r->notes, "proxy-balancer-ar-newroute", value);
+ apr_table_unset( f->r->headers_out, field);
+ }
+ }
+
+ /* remove ourselves from the filter chain */
+ /* send the data up the stack */
+ ap_remove_output_filter(f);
+ return ap_pass_brigade(f->next,in);
+}
+
+static void proxy_balancer_insert_output_filter(request_rec *r)
+{
+ ap_add_output_filter("FIXUP_PROXY_BALANCER_OUT", NULL, r, r->connection);
+}
+
static void ap_proxy_balancer_register_hook(apr_pool_t *p)
{
/* Only the mpm_winnt has child init hook handler.
@@ -1657,9 +1803,12 @@
ap_hook_pre_config(balancer_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(balancer_handler, NULL, NULL, APR_HOOK_FIRST);
ap_hook_child_init(balancer_child_init, aszPred, NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_filter(proxy_balancer_insert_output_filter, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_pre_request(proxy_balancer_pre_request, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_post_request(proxy_balancer_post_request, NULL, NULL, APR_HOOK_FIRST);
proxy_hook_canon_handler(proxy_balancer_canon, NULL, NULL, APR_HOOK_FIRST);
+ ap_register_output_filter("FIXUP_PROXY_BALANCER_OUT", proxy_balancer_fixup_headers_out,
+ NULL, AP_FTYPE_CONTENT_SET);
}
AP_DECLARE_MODULE(proxy_balancer) = {
Index: include/util_cookies.h
===================================================================
--- include/util_cookies.h (Revision 1417803)
+++ include/util_cookies.h (Arbeitskopie)
@@ -128,6 +128,15 @@
int remove);
/**
+ * Read a set cookie called name, placing its value in val.
+ *
+ * Both the Set-Cookie and Set-Cookie2 headers are scanned for the cookie.
+ *
+ * If the cookie is duplicated, this function returns APR_EGENERAL.
+ */
+AP_DECLARE(apr_status_t) ap_set_cookie_read(request_rec * r, const char *name, const char **val);
+
+/**
* Sanity check a given string that it exists, is not empty,
* and does not contain the special characters '=', ';' and '&'.
*
Index: docs/manual/mod/mod_proxy.xml
===================================================================
--- docs/manual/mod/mod_proxy.xml (Revision 1417803)
+++ docs/manual/mod/mod_proxy.xml (Arbeitskopie)
@@ -1143,6 +1143,22 @@
without considering the retry parameter of each worker. In this case
set to Off .
| |||||||||||||
autoroute | ++ | Set the route id automatically. The format of the value is
+ [method]:[field name]. The autoroute support different methods to get the
+ new route id.
+
|
A sample balancer setup