mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 15:17:01 +02:00
[MEDIUM] add support for "balance hdr(name)"
There is a patch made by me that allow for balancing on any http header field. [WT: made minor changes: - turned 'balance header name' into 'balance hdr(name)' to match more closely the ACL syntax for easier future convergence - renamed the proxy structure fields header_* => hh_* - made it possible to use the domain name reduction to any header, not only "host" since it makes sense to do it with other ones. Otherwise patch looks good. /WT]
This commit is contained in:
parent
946ba59190
commit
affb481f1a
@ -937,6 +937,17 @@ balance url_param <param> [check_post [<max_wait>]]
|
||||
backend. This algorithm is static, which means that changing a
|
||||
server's weight on the fly will have no effect.
|
||||
|
||||
hdr(name) The HTTP header <name> will be looked up in each HTTP request.
|
||||
Just as with the equivalent ACL 'hdr()' function, the header
|
||||
name in parenthesis is not case sensitive. If the header is
|
||||
absent or if it does not contain any value, the round-robin
|
||||
algorithm is applied instead.
|
||||
|
||||
An optionnal 'use_domain_only' parameter is available, for
|
||||
reducing the hash algorithm to the main domain part with some
|
||||
specific headers such as 'Host'. For instance, in the Host
|
||||
value "haproxy.1wt.eu", only "1wt" will be considered.
|
||||
|
||||
<arguments> is an optional list of arguments which may be needed by some
|
||||
algorithms. Right now, only "url_param" and "uri" support an
|
||||
optional argument.
|
||||
@ -952,6 +963,9 @@ balance url_param <param> [check_post [<max_wait>]]
|
||||
balance roundrobin
|
||||
balance url_param userid
|
||||
balance url_param session_id check_post 64
|
||||
balance hdr(User-Agent)
|
||||
balance hdr(host)
|
||||
balance hdr(Host) use_domain_only
|
||||
|
||||
Note: the following caveats and limitations on using the "check_post"
|
||||
extension with "url_param" must be considered :
|
||||
|
@ -42,7 +42,8 @@
|
||||
#define BE_LB_ALGO_SH (BE_LB_PROP_L4 | 0x02) /* balance on source IP hash */
|
||||
#define BE_LB_ALGO_UH (BE_LB_PROP_L7 | 0x03) /* balance on URI hash */
|
||||
#define BE_LB_ALGO_PH (BE_LB_PROP_L7 | 0x04) /* balance on URL parameter hash */
|
||||
#define BE_LB_ALGO_LC (BE_LB_PROP_DYN | 0x05) /* fast weighted round-robin mode (dynamic) */
|
||||
#define BE_LB_ALGO_LC (BE_LB_PROP_DYN | 0x05) /* fast weighted leastconn mode (dynamic) */
|
||||
#define BE_LB_ALGO_HH (BE_LB_PROP_L7 | 0x06) /* balance on Http Header value */
|
||||
|
||||
/* various constants */
|
||||
|
||||
|
@ -199,6 +199,9 @@ struct proxy {
|
||||
unsigned url_param_post_limit; /* if checking POST body for URI parameter, max body to wait for */
|
||||
int uri_len_limit; /* character limit for uri balancing algorithm */
|
||||
int uri_dirs_depth1; /* directories+1 (slashes) limit for uri balancing algorithm */
|
||||
char *hh_name; /* name of the header parameter used for hashing */
|
||||
int hh_len; /* strlen(hh_name), computed only once */
|
||||
int hh_match_domain; /* toggle use of special match function */
|
||||
char *appsession_name; /* name of the cookie to look for */
|
||||
int appsession_name_len; /* strlen(appsession_name), computed only once */
|
||||
int appsession_len; /* length of the appsession cookie value to be used */
|
||||
|
117
src/backend.c
117
src/backend.c
@ -1278,6 +1278,80 @@ struct server *get_server_ph_post(struct session *s)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function tries to find a running server for the proxy <px> following
|
||||
* the Header parameter hash method. It looks for a specific parameter in the
|
||||
* URL and hashes it to compute the server ID. This is useful to optimize
|
||||
* performance by avoiding bounces between servers in contexts where sessions
|
||||
* are shared but cookies are not usable. If the parameter is not found, NULL
|
||||
* is returned. If any server is found, it will be returned. If no valid server
|
||||
* is found, NULL is returned.
|
||||
*/
|
||||
struct server *get_server_hh(struct session *s)
|
||||
{
|
||||
unsigned long hash = 0;
|
||||
struct http_txn *txn = &s->txn;
|
||||
struct http_msg *msg = &txn->req;
|
||||
struct proxy *px = s->be;
|
||||
unsigned int plen = px->hh_len;
|
||||
unsigned long len;
|
||||
struct hdr_ctx ctx;
|
||||
const char *p;
|
||||
|
||||
/* tot_weight appears to mean srv_count */
|
||||
if (px->lbprm.tot_weight == 0)
|
||||
return NULL;
|
||||
|
||||
if (px->lbprm.map.state & PR_MAP_RECALC)
|
||||
recalc_server_map(px);
|
||||
|
||||
ctx.idx = 0;
|
||||
|
||||
/* if the message is chunked, we skip the chunk size, but use the value as len */
|
||||
http_find_header2(px->hh_name, plen, msg->sol, &txn->hdr_idx, &ctx);
|
||||
|
||||
/* if the header is not found or empty, let's fallback to round robin */
|
||||
if (!ctx.idx || !ctx.vlen)
|
||||
return NULL;
|
||||
|
||||
/* Found a the hh_name in the headers.
|
||||
* we will compute the hash based on this value ctx.val.
|
||||
*/
|
||||
len = ctx.vlen;
|
||||
p = (char *)ctx.line + ctx.val;
|
||||
if (!px->hh_match_domain) {
|
||||
while (len) {
|
||||
hash = *p + (hash << 6) + (hash << 16) - hash;
|
||||
len--;
|
||||
p++;
|
||||
}
|
||||
} else {
|
||||
int dohash = 0;
|
||||
p += len - 1;
|
||||
/* special computation, use only main domain name, not tld/host
|
||||
* going back from the end of string, start hashing at first
|
||||
* dot stop at next.
|
||||
* This is designed to work with the 'Host' header, and requires
|
||||
* a special option to activate this.
|
||||
*/
|
||||
while (len) {
|
||||
if (*p == '.') {
|
||||
if (!dohash)
|
||||
dohash = 1;
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
if (dohash)
|
||||
hash = *p + (hash << 6) + (hash << 16) - hash;
|
||||
}
|
||||
len--;
|
||||
p--;
|
||||
}
|
||||
}
|
||||
return px->lbprm.map.srv[hash % px->lbprm.tot_weight];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function applies the load-balancing algorithm to the session, as
|
||||
* defined by the backend it is assigned to. The session is then marked as
|
||||
@ -1387,6 +1461,19 @@ int assign_server(struct session *s)
|
||||
s->txn.req.sol + s->txn.req.sl.rq.u,
|
||||
s->txn.req.sl.rq.u_l);
|
||||
|
||||
if (!s->srv) {
|
||||
/* parameter not found, fall back to round robin on the map */
|
||||
s->srv = get_server_rr_with_conns(s->be, s->prev_srv);
|
||||
if (!s->srv) {
|
||||
err = SRV_STATUS_FULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BE_LB_ALGO_HH:
|
||||
/* Header Parameter hashing */
|
||||
s->srv = get_server_hh(s);
|
||||
|
||||
if (!s->srv) {
|
||||
/* parameter not found, fall back to round robin on the map */
|
||||
s->srv = get_server_rr_with_conns(s->be, s->prev_srv);
|
||||
@ -2023,8 +2110,36 @@ int backend_parse_balance(const char **args, char *err, int errlen, struct proxy
|
||||
curproxy->url_param_post_limit = 3; /* minimum example: S=3 or \r\nS=6& */
|
||||
}
|
||||
}
|
||||
else if (!strncmp(args[0], "hdr(", 4)) {
|
||||
const char *beg, *end;
|
||||
|
||||
beg = args[0] + 4;
|
||||
end = strchr(beg, ')');
|
||||
|
||||
if (!end || end == beg) {
|
||||
snprintf(err, errlen, "'balance hdr(name)' requires an http header field name.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
curproxy->lbprm.algo &= ~BE_LB_ALGO;
|
||||
curproxy->lbprm.algo |= BE_LB_ALGO_HH;
|
||||
|
||||
free(curproxy->hh_name);
|
||||
curproxy->hh_len = end - beg;
|
||||
curproxy->hh_name = my_strndup(beg, end - beg);
|
||||
curproxy->hh_match_domain = 0;
|
||||
|
||||
if (*args[1]) {
|
||||
if (strcmp(args[1], "use_domain_only")) {
|
||||
snprintf(err, errlen, "'balance hdr(name)' only accepts 'use_domain_only' modifier.");
|
||||
return -1;
|
||||
}
|
||||
curproxy->hh_match_domain = 1;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
snprintf(err, errlen, "'balance' only supports 'roundrobin', 'leastconn', 'source', 'uri' and 'url_param' options.");
|
||||
snprintf(err, errlen, "'balance' only supports 'roundrobin', 'leastconn', 'source', 'uri', 'url_param' and 'hdr(name)' options.");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -803,6 +803,11 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
||||
curproxy->url_param_name = strdup(defproxy.url_param_name);
|
||||
curproxy->url_param_len = defproxy.url_param_len;
|
||||
|
||||
if (defproxy.hh_name)
|
||||
curproxy->hh_name = strdup(defproxy.hh_name);
|
||||
curproxy->hh_len = defproxy.hh_len;
|
||||
curproxy->hh_match_domain = defproxy.hh_match_domain;
|
||||
|
||||
if (defproxy.iface_name)
|
||||
curproxy->iface_name = strdup(defproxy.iface_name);
|
||||
curproxy->iface_len = defproxy.iface_len;
|
||||
@ -859,6 +864,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
|
||||
free(defproxy.check_req);
|
||||
free(defproxy.cookie_name);
|
||||
free(defproxy.url_param_name);
|
||||
free(defproxy.hh_name);
|
||||
free(defproxy.capture_name);
|
||||
free(defproxy.monitor_uri);
|
||||
free(defproxy.defbe.name);
|
||||
|
Loading…
Reference in New Issue
Block a user