mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-02-19 04:07:04 +00:00
MEDIUM: session: implement the "use-server" directive
Sometimes it is desirable to forward a particular request to a specific server without having to declare a dedicated backend for this server. This can be achieved using the "use-server" rules. These rules are evaluated after the "redirect" rules and before evaluating cookies, and they have precedence on them. There may be as many "use-server" rules as desired. All of these rules are evaluated in their declaration order, and the first one which matches will assign the server.
This commit is contained in:
parent
ceafb4aa92
commit
4a5cadea40
@ -1118,6 +1118,7 @@ timeout srvtimeout (deprecated) X - X X
|
||||
timeout tarpit X X X X
|
||||
transparent (deprecated) X - X X
|
||||
use_backend - X X -
|
||||
use-server - - X X
|
||||
------------------------------------+----------+----------+---------+---------
|
||||
keyword defaults frontend listen backend
|
||||
|
||||
@ -6597,6 +6598,60 @@ use_backend <backend> unless <condition>
|
||||
See also: "default_backend", "tcp-request", and section 7 about ACLs.
|
||||
|
||||
|
||||
use-server <server> if <condition>
|
||||
use-server <server> unless <condition>
|
||||
Only use a specific server if/unless an ACL-based condition is matched.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
no | no | yes | yes
|
||||
Arguments :
|
||||
<server> is the name of a valid server in the same backend section.
|
||||
|
||||
<condition> is a condition composed of ACLs, as described in section 7.
|
||||
|
||||
By default, connections which arrive to a backend are load-balanced across
|
||||
the available servers according to the configured algorithm, unless a
|
||||
persistence mechanism such as a cookie is used and found in the request.
|
||||
|
||||
Sometimes it is desirable to forward a particular request to a specific
|
||||
server without having to declare a dedicated backend for this server. This
|
||||
can be achieved using the "use-server" rules. These rules are evaluated after
|
||||
the "redirect" rules and before evaluating cookies, and they have precedence
|
||||
on them. There may be as many "use-server" rules as desired. All of these
|
||||
rules are evaluated in their declaration order, and the first one which
|
||||
matches will assign the server.
|
||||
|
||||
If a rule designates a server which is down, and "option persist" is not used
|
||||
and no force-persist rule was validated, it is ignored and evaluation goes on
|
||||
with the next rules until one matches.
|
||||
|
||||
In the first form, the server will be used if the condition is met. In the
|
||||
second form, the server will be used if the condition is not met. If no
|
||||
condition is valid, the processing continues and the server will be assigned
|
||||
according to other persistence mechanisms.
|
||||
|
||||
Note that even if a rule is matched, cookie processing is still performed but
|
||||
does not assign the server. This allows prefixed cookies to have their prefix
|
||||
stripped.
|
||||
|
||||
The "use-server" statement works both in HTTP and TCP mode. This makes it
|
||||
suitable for use with content-based inspection. For instance, a server could
|
||||
be selected in a farm according to the TLS SNI field. And if these servers
|
||||
have their weight set to zero, they will not be used for other traffic.
|
||||
|
||||
Example :
|
||||
# intercept incoming TLS requests based on the SNI field
|
||||
use-server www if { req_ssl_sni -i www.example.com }
|
||||
server www 192.168.0.1:443 weight 0
|
||||
use-server mail if { req_ssl_sni -i mail.example.com }
|
||||
server mail 192.168.0.1:587 weight 0
|
||||
use-server imap if { req_ssl_sni -i imap.example.com }
|
||||
server mail 192.168.0.1:993 weight 0
|
||||
# all the rest is forwarded to this server
|
||||
server default 192.168.0.2:443 check
|
||||
|
||||
See also: "use_backend", serction 5 about server and section 7 about ACLs.
|
||||
|
||||
|
||||
5. Server and default-server options
|
||||
------------------------------------
|
||||
|
||||
|
@ -144,12 +144,13 @@
|
||||
#define AN_REQ_SWITCHING_RULES 0x00000010 /* apply the switching rules */
|
||||
#define AN_REQ_INSPECT_BE 0x00000020 /* inspect request contents in the backend */
|
||||
#define AN_REQ_HTTP_PROCESS_BE 0x00000040 /* process the backend's HTTP part */
|
||||
#define AN_REQ_HTTP_INNER 0x00000080 /* inner processing of HTTP request */
|
||||
#define AN_REQ_HTTP_TARPIT 0x00000100 /* wait for end of HTTP tarpit */
|
||||
#define AN_REQ_HTTP_BODY 0x00000200 /* inspect HTTP request body */
|
||||
#define AN_REQ_STICKING_RULES 0x00000400 /* table persistence matching */
|
||||
#define AN_REQ_PRST_RDP_COOKIE 0x00000800 /* persistence on rdp cookie */
|
||||
#define AN_REQ_HTTP_XFER_BODY 0x00001000 /* forward request body */
|
||||
#define AN_REQ_SRV_RULES 0x00000080 /* use-server rules */
|
||||
#define AN_REQ_HTTP_INNER 0x00000100 /* inner processing of HTTP request */
|
||||
#define AN_REQ_HTTP_TARPIT 0x00000200 /* wait for end of HTTP tarpit */
|
||||
#define AN_REQ_HTTP_BODY 0x00000400 /* inspect HTTP request body */
|
||||
#define AN_REQ_STICKING_RULES 0x00000800 /* table persistence matching */
|
||||
#define AN_REQ_PRST_RDP_COOKIE 0x00001000 /* persistence on rdp cookie */
|
||||
#define AN_REQ_HTTP_XFER_BODY 0x00002000 /* forward request body */
|
||||
|
||||
/* response analysers */
|
||||
#define AN_RES_INSPECT 0x00010000 /* content inspection */
|
||||
|
@ -205,6 +205,7 @@ struct proxy {
|
||||
struct list persist_rules; /* 'force-persist' and 'ignore-persist' rules (chained) */
|
||||
struct list sticking_rules; /* content sticking rules (chained) */
|
||||
struct list storersp_rules; /* content store response rules (chained) */
|
||||
struct list server_rules; /* server switching rules (chained) */
|
||||
struct { /* TCP request processing */
|
||||
unsigned int inspect_delay; /* inspection delay */
|
||||
struct list inspect_rules; /* inspection rules */
|
||||
@ -350,6 +351,15 @@ struct switching_rule {
|
||||
} be;
|
||||
};
|
||||
|
||||
struct server_rule {
|
||||
struct list list; /* list linked to from the proxy */
|
||||
struct acl_cond *cond; /* acl condition to meet */
|
||||
union {
|
||||
struct server *ptr; /* target server */
|
||||
char *name; /* target server name during config parsing */
|
||||
} srv;
|
||||
};
|
||||
|
||||
struct persist_rule {
|
||||
struct list list; /* list linked to from the proxy */
|
||||
struct acl_cond *cond; /* acl condition to meet */
|
||||
|
@ -2702,6 +2702,47 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
LIST_INIT(&rule->list);
|
||||
LIST_ADDQ(&curproxy->switching_rules, &rule->list);
|
||||
}
|
||||
else if (strcmp(args[0], "use-server") == 0) {
|
||||
struct server_rule *rule;
|
||||
|
||||
if (curproxy == &defproxy) {
|
||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (*(args[1]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects a server name.\n", file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(args[2], "if") != 0 && strcmp(args[2], "unless") != 0) {
|
||||
Alert("parsing [%s:%d] : '%s' requires either 'if' or 'unless' followed by a condition.\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((cond = build_acl_cond(file, linenum, curproxy, (const char **)args + 2)) == NULL) {
|
||||
Alert("parsing [%s:%d] : error detected while parsing switching rule.\n",
|
||||
file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err_code |= warnif_cond_requires_resp(cond, file, linenum);
|
||||
|
||||
rule = (struct server_rule *)calloc(1, sizeof(*rule));
|
||||
rule->cond = cond;
|
||||
rule->srv.name = strdup(args[1]);
|
||||
LIST_INIT(&rule->list);
|
||||
LIST_ADDQ(&curproxy->server_rules, &rule->list);
|
||||
curproxy->be_req_ana |= AN_REQ_SRV_RULES;
|
||||
}
|
||||
else if ((!strcmp(args[0], "force-persist")) ||
|
||||
(!strcmp(args[0], "ignore-persist"))) {
|
||||
struct persist_rule *rule;
|
||||
@ -5603,6 +5644,7 @@ int check_config_validity()
|
||||
|
||||
while (curproxy != NULL) {
|
||||
struct switching_rule *rule;
|
||||
struct server_rule *srule;
|
||||
struct sticking_rule *mrule;
|
||||
struct tcp_rule *trule;
|
||||
struct listener *listener;
|
||||
@ -5794,6 +5836,20 @@ int check_config_validity()
|
||||
}
|
||||
}
|
||||
|
||||
/* find the target proxy for 'use_backend' rules */
|
||||
list_for_each_entry(srule, &curproxy->server_rules, list) {
|
||||
struct server *target = findserver(curproxy, srule->srv.name);
|
||||
|
||||
if (!target) {
|
||||
Alert("config : %s '%s' : unable to find server '%s' referenced in a 'use-server' rule.\n",
|
||||
proxy_type_str(curproxy), curproxy->id, srule->srv.name);
|
||||
cfgerr++;
|
||||
continue;
|
||||
}
|
||||
free((void *)srule->srv.name);
|
||||
srule->srv.ptr = target;
|
||||
}
|
||||
|
||||
/* find the target table for 'stick' rules */
|
||||
list_for_each_entry(mrule, &curproxy->sticking_rules, list) {
|
||||
struct proxy *target;
|
||||
|
@ -797,6 +797,7 @@ void deinit(void)
|
||||
struct hdr_exp *exp, *expb;
|
||||
struct acl *acl, *aclb;
|
||||
struct switching_rule *rule, *ruleb;
|
||||
struct server_rule *srule, *sruleb;
|
||||
struct redirect_rule *rdr, *rdrb;
|
||||
struct wordlist *wl, *wlb;
|
||||
struct cond_wordlist *cwl, *cwlb;
|
||||
@ -891,6 +892,13 @@ void deinit(void)
|
||||
free(acl);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(srule, sruleb, &p->server_rules, list) {
|
||||
LIST_DEL(&srule->list);
|
||||
prune_acl_cond(srule->cond);
|
||||
free(srule->cond);
|
||||
free(srule);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(rule, ruleb, &p->switching_rules, list) {
|
||||
LIST_DEL(&rule->list);
|
||||
prune_acl_cond(rule->cond);
|
||||
|
@ -6221,7 +6221,7 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
* empty cookies and mark them as invalid.
|
||||
* The same behaviour is applied when persistence must be ignored.
|
||||
*/
|
||||
if ((delim == val_beg) || (t->flags & SN_IGNORE_PRST))
|
||||
if ((delim == val_beg) || (t->flags & (SN_IGNORE_PRST | SN_ASSIGNED)))
|
||||
srv = NULL;
|
||||
|
||||
while (srv) {
|
||||
|
@ -427,6 +427,7 @@ void init_new_proxy(struct proxy *p)
|
||||
LIST_INIT(&p->redirect_rules);
|
||||
LIST_INIT(&p->mon_fail_cond);
|
||||
LIST_INIT(&p->switching_rules);
|
||||
LIST_INIT(&p->server_rules);
|
||||
LIST_INIT(&p->persist_rules);
|
||||
LIST_INIT(&p->sticking_rules);
|
||||
LIST_INIT(&p->storersp_rules);
|
||||
|
@ -1041,6 +1041,55 @@ static int process_switching_rules(struct session *s, struct buffer *req, int an
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This stream analyser works on a request. It applies all use-server rules on
|
||||
* it then returns 1. The data must already be present in the buffer otherwise
|
||||
* they won't match. It always returns 1.
|
||||
*/
|
||||
static int process_server_rules(struct session *s, struct buffer *req, int an_bit)
|
||||
{
|
||||
struct proxy *px = s->be;
|
||||
struct server_rule *rule;
|
||||
|
||||
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
||||
now_ms, __FUNCTION__,
|
||||
s,
|
||||
req,
|
||||
req->rex, req->wex,
|
||||
req->flags,
|
||||
req->l,
|
||||
req->analysers);
|
||||
|
||||
if (!(s->flags & SN_ASSIGNED)) {
|
||||
list_for_each_entry(rule, &px->server_rules, list) {
|
||||
int ret;
|
||||
|
||||
ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, ACL_DIR_REQ);
|
||||
ret = acl_pass(ret);
|
||||
if (rule->cond->pol == ACL_COND_UNLESS)
|
||||
ret = !ret;
|
||||
|
||||
if (ret) {
|
||||
struct server *srv = rule->srv.ptr;
|
||||
|
||||
if ((srv->state & SRV_RUNNING) ||
|
||||
(px->options & PR_O_PERSIST) ||
|
||||
(s->flags & SN_FORCE_PRST)) {
|
||||
s->flags |= SN_DIRECT | SN_ASSIGNED;
|
||||
set_target_server(&s->target, srv);
|
||||
break;
|
||||
}
|
||||
/* if the server is not UP, let's go on with next rules
|
||||
* just in case another one is suited.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req->analysers &= ~an_bit;
|
||||
req->analyse_exp = TICK_ETERNITY;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This stream analyser works on a request. It applies all sticking rules on
|
||||
* it then returns 1. The data must already be present in the buffer otherwise
|
||||
* they won't match. It always returns 1.
|
||||
@ -1526,6 +1575,12 @@ struct task *process_session(struct task *t)
|
||||
UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_HTTP_TARPIT);
|
||||
}
|
||||
|
||||
if (ana_list & AN_REQ_SRV_RULES) {
|
||||
if (!process_server_rules(s, s->req, AN_REQ_SRV_RULES))
|
||||
break;
|
||||
UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_SRV_RULES);
|
||||
}
|
||||
|
||||
if (ana_list & AN_REQ_HTTP_INNER) {
|
||||
if (!http_process_request(s, s->req, AN_REQ_HTTP_INNER))
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user