--- ./haproxy.c~ Sat Nov 27 15:04:19 2004 +++ ./haproxy.c Sat Nov 27 16:27:15 2004 @@ -103,9 +103,13 @@ // max # of matches per regexp #define MAX_MATCH 10 -/* FIXME: serverid_len and cookiename_len are no longer checked in configuration file */ -#define COOKIENAME_LEN 16 -#define SERVERID_LEN 16 +// cookie delimitor in "prefix" mode. This character is inserted between the +// persistence cookie and the original value. The '~' is allowed by RFC2965, +// and should not be too common in server names. +#ifndef COOKIE_DELIM +#define COOKIE_DELIM '~' +#endif + #define CONN_RETRIES 3 #define CHK_CONNTIME 2000 @@ -142,6 +146,9 @@ /* if a < min, then bound to . The macro returns the new */ #define LBOUND(a, min) ({ typeof(a) b = (min); if ((a) < b) (a) = b; (a); }) +/* returns 1 only if only zero or one bit is set in X, which means that X is a + * power of 2, and 0 otherwise */ +#define POWEROF2(x) (((x) & ((x)-1)) == 0) /* * copies at most chars from to . Last char is always * set to 0, unless is 0. The number of chars copied is returned @@ -255,25 +262,26 @@ #define PR_MODE_HEALTH 2 /* bits for proxy->options */ -#define PR_O_REDISP 1 /* allow reconnection to dispatch in case of errors */ -#define PR_O_TRANSP 2 /* transparent mode : use original DEST as dispatch */ -#define PR_O_COOK_RW 4 /* rewrite all direct cookies with the right serverid */ -#define PR_O_COOK_IND 8 /* keep only indirect cookies */ -#define PR_O_COOK_INS 16 /* insert cookies when not accessing a server directly */ -#define PR_O_COOK_ANY (PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS) -#define PR_O_BALANCE_RR 32 /* balance in round-robin mode */ +#define PR_O_REDISP 0x00000001 /* allow reconnection to dispatch in case of errors */ +#define PR_O_TRANSP 0x00000002 /* transparent mode : use original DEST as dispatch */ +#define PR_O_COOK_RW 0x00000004 /* rewrite all direct cookies with the right serverid */ +#define PR_O_COOK_IND 0x00000008 /* keep only indirect cookies */ +#define PR_O_COOK_INS 0x00000010 /* insert cookies when not accessing a server directly */ +#define PR_O_COOK_PFX 0x00000020 /* rewrite all cookies by prefixing the right serverid */ +#define PR_O_COOK_ANY (PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS | PR_O_COOK_PFX) +#define PR_O_BALANCE_RR 0x00000040 /* balance in round-robin mode */ #define PR_O_BALANCE (PR_O_BALANCE_RR) -#define PR_O_KEEPALIVE 64 /* follow keep-alive sessions */ -#define PR_O_FWDFOR 128 /* insert x-forwarded-for with client address */ -#define PR_O_BIND_SRC 256 /* bind to a specific source address when connect()ing */ -#define PR_O_NULLNOLOG 512 /* a connect without request will not be logged */ -#define PR_O_COOK_NOC 1024 /* add a 'Cache-control' header with the cookie */ -#define PR_O_COOK_POST 2048 /* don't insert cookies for requests other than a POST */ -#define PR_O_HTTP_CHK 4096 /* use HTTP 'OPTIONS' method to check server health */ -#define PR_O_PERSIST 8192 /* server persistence stays effective even when server is down */ -#define PR_O_LOGASAP 16384 /* log as soon as possible, without waiting for the session to complete */ -#define PR_O_HTTP_CLOSE 32768 /* force 'connection: close' in both directions */ -#define PR_O_CHK_CACHE 65536 /* require examination of cacheability of the 'set-cookie' field */ +#define PR_O_KEEPALIVE 0x00000080 /* follow keep-alive sessions */ +#define PR_O_FWDFOR 0x00000100 /* insert x-forwarded-for with client address */ +#define PR_O_BIND_SRC 0x00000200 /* bind to a specific source address when connect()ing */ +#define PR_O_NULLNOLOG 0x00000400 /* a connect without request will not be logged */ +#define PR_O_COOK_NOC 0x00000800 /* add a 'Cache-control' header with the cookie */ +#define PR_O_COOK_POST 0x00001000 /* don't insert cookies for requests other than a POST */ +#define PR_O_HTTP_CHK 0x00002000 /* use HTTP 'OPTIONS' method to check server health */ +#define PR_O_PERSIST 0x00004000 /* server persistence stays effective even when server is down */ +#define PR_O_LOGASAP 0x00008000 /* log as soon as possible, without waiting for the session to complete */ +#define PR_O_HTTP_CLOSE 0x00010000 /* force 'connection: close' in both directions */ +#define PR_O_CHK_CACHE 0x00020000 /* require examination of cacheability of the 'set-cookie' field */ /* various session flags */ #define SN_DIRECT 0x00000001 /* connection made on the server matching the client cookie */ @@ -2895,7 +2903,16 @@ /* here, we have the cookie name between p1 and p2, * and its value between p3 and p4. - * we can process it. + * we can process it : + * + * Cookie: NAME=VALUE; + * | || || | + * | || || +--> p4 + * | || |+-------> p3 + * | || +--------> p2 + * | |+------------> p1 + * | +-------------> colon + * +--------------------> req->h */ if (*p1 == '$') { @@ -2923,13 +2940,38 @@ (memcmp(p1, t->proxy->cookie_name, p2 - p1) == 0)) { /* Cool... it's the right one */ struct server *srv = t->proxy->srv; + char *delim; + + /* if we're in cookie prefix mode, we'll search the delimitor so that we + * have the server ID betweek p3 and delim, and the original cookie between + * delim+1 and p4. Otherwise, delim==p4 : + * + * Cookie: NAME=SRV~VALUE; + * | || || | | + * | || || | +--> p4 + * | || || +--------> delim + * | || |+-----------> p3 + * | || +------------> p2 + * | |+----------------> p1 + * | +-----------------> colon + * +------------------------> req->h + */ + + if (t->proxy->options & PR_O_COOK_PFX) { + for (delim = p3; delim < p4; delim++) + if (*delim == COOKIE_DELIM) + break; + } + else + delim = p4; + /* Here, we'll look for the first running server which supports the cookie. * This allows to share a same cookie between several servers, for example * to dedicate backup servers to specific servers only. */ while (srv) { - if ((srv->cklen == p4 - p3) && !memcmp(p3, srv->cookie, p4 - p3)) { + if ((srv->cklen == delim - p3) && !memcmp(p3, srv->cookie, delim - p3)) { if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { /* we found the server and it's usable */ t->flags &= ~SN_CK_MASK; @@ -2952,10 +2994,21 @@ t->flags |= SN_CK_INVALID; } - /* if this cookie was set in insert+indirect mode, then it's better that the - * server never sees it. + /* depending on the cookie mode, we may have to either : + * - delete the complete cookie if we're in insert+indirect mode, so that + * the server never sees it ; + * - remove the server id from the cookie value, and tag the cookie as an + * application cookie so that it does not get accidentely removed later, + * if we're in cookie prefix mode */ - if (del_cookie == NULL && + if ((t->proxy->options & PR_O_COOK_PFX) && (delim != p4)) { + buffer_replace2(req, p3, delim + 1, NULL, 0); + p4 -= (delim + 1 - p3); + ptr -= (delim + 1 - p3); + del_cookie = del_colon = NULL; + app_cookies++; /* protect the header from deletion */ + } + else if (del_cookie == NULL && (t->proxy->options & (PR_O_COOK_INS | PR_O_COOK_IND)) == (PR_O_COOK_INS | PR_O_COOK_IND)) { del_cookie = p1; del_colon = colon; @@ -3779,6 +3832,14 @@ buffer_replace2(rep, p3, p4, t->srv->cookie, t->srv->cklen); t->flags |= SN_SCK_INSERTED | SN_SCK_DELETED; } + else if ((t->srv) && (t->proxy->options & PR_O_COOK_PFX)) { + /* insert the cookie name associated with this server + * before existing cookie, and insert a delimitor between them.. + */ + buffer_replace2(rep, p3, p3, t->srv->cookie, t->srv->cklen + 1); + p3[t->srv->cklen] = COOKIE_DELIM; + t->flags |= SN_SCK_INSERTED | SN_SCK_DELETED; + } break; } else { @@ -5040,15 +5101,24 @@ else if (!strcmp(args[cur_arg], "postonly")) { curproxy->options |= PR_O_COOK_POST; } + else if (!strcmp(args[cur_arg], "prefix")) { + curproxy->options |= PR_O_COOK_PFX; + } else { - Alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'indirect', 'nocache' and 'postonly' options.\n", + Alert("parsing [%s:%d] : '%s' supports 'rewrite', 'insert', 'prefix', 'indirect', 'nocache' and 'postonly' options.\n", file, linenum, args[0]); return -1; } cur_arg++; } - if ((curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND)) == (PR_O_COOK_RW|PR_O_COOK_IND)) { - Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' mode are incompatible.\n", + if (!POWEROF2(curproxy->options & (PR_O_COOK_RW|PR_O_COOK_IND))) { + Alert("parsing [%s:%d] : cookie 'rewrite' and 'indirect' modes are incompatible.\n", + file, linenum); + return -1; + } + + if (!POWEROF2(curproxy->options & (PR_O_COOK_RW|PR_O_COOK_INS|PR_O_COOK_PFX))) { + Alert("parsing [%s:%d] : cookie 'rewrite', 'insert' and 'prefix' modes are incompatible.\n", file, linenum); return -1; }