mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 14:21:25 +02:00
[MEDIUM] appsession: add "len", "prefix" and "mode" options
To sum up : - len : it's now the max number of characters for the value, preventing garbaged results. - a new option "prefix" is added, this allows to use dynamic cookie names (e.g. ASPSESSIONIDXXX). Previously in the thread, I wanted to use the value found with "capture cookie" but when i started to update the documentation, I found this solution quite weird. I've made a small rework to not depend on "capture cookie". - There's the posssiblity to define the URL parser mode (path parameters or query string).
This commit is contained in:
parent
fa355d4a51
commit
b21570ae0f
@ -861,7 +861,8 @@ acl <aclname> <criterion> [flags] [operator] <value> ...
|
||||
See section 7 about ACL usage.
|
||||
|
||||
|
||||
appsession <cookie> len <length> timeout <holdtime> [request-learn]
|
||||
appsession <cookie> len <length> timeout <holdtime>
|
||||
[request-learn] [prefix] [mode <path-parameters|query-string>]
|
||||
Define session stickiness on an existing application cookie.
|
||||
May be used in sections : defaults | frontend | listen | backend
|
||||
no | no | yes | yes
|
||||
@ -869,7 +870,7 @@ appsession <cookie> len <length> timeout <holdtime> [request-learn]
|
||||
<cookie> this is the name of the cookie used by the application and which
|
||||
HAProxy will have to learn for each new session.
|
||||
|
||||
<length> this is the number of characters that will be memorized and
|
||||
<length> this is the max number of characters that will be memorized and
|
||||
checked in each cookie value.
|
||||
|
||||
<holdtime> this is the time after which the cookie will be removed from
|
||||
@ -884,13 +885,34 @@ appsession <cookie> len <length> timeout <holdtime> [request-learn]
|
||||
the application's session and the correct server is selected.
|
||||
It is recommended to specify this option to improve reliability.
|
||||
|
||||
prefix When this option is specified, haproxy will match on the cookie
|
||||
prefix (or URL parameter prefix). The appsession value is the
|
||||
data following this prefix.
|
||||
|
||||
Example :
|
||||
appsession ASPSESSIONID len 64 timeout 3h prefix
|
||||
|
||||
This will match the cookie ASPSESSIONIDXXXX=XXXXX,
|
||||
the appsession value will be XXXX=XXXXX.
|
||||
|
||||
mode This option allows to change the URL parser mode.
|
||||
2 modes are currently supported :
|
||||
- path-parameters :
|
||||
The parser looks for the appsession in the path parameters
|
||||
part (each parameter is separated by a semi-colon), which is
|
||||
convenient for JSESSIONID for example.
|
||||
This is the default mode if the option is not set.
|
||||
- query-string :
|
||||
In this mode, the parser will look for the appsession in the
|
||||
query string.
|
||||
|
||||
When an application cookie is defined in a backend, HAProxy will check when
|
||||
the server sets such a cookie, and will store its value in a table, and
|
||||
associate it with the server's identifier. Up to <length> characters from
|
||||
the value will be retained. On each connection, haproxy will look for this
|
||||
cookie both in the "Cookie:" headers, and as a URL parameter in the query
|
||||
string. If a known value is found, the client will be directed to the server
|
||||
associated with this value. Otherwise, the load balancing algorithm is
|
||||
cookie both in the "Cookie:" headers, and as a URL parameter (depending on
|
||||
the mode used). If a known value is found, the client will be directed to the
|
||||
server associated with this value. Otherwise, the load balancing algorithm is
|
||||
applied. Cookies are automatically removed from memory when they have been
|
||||
unused for a duration longer than <holdtime>.
|
||||
|
||||
|
@ -75,7 +75,7 @@ int apply_filter_to_req_headers(struct session *t, struct buffer *req, struct hd
|
||||
int apply_filter_to_req_line(struct session *t, struct buffer *req, struct hdr_exp *exp);
|
||||
int apply_filters_to_request(struct session *t, struct buffer *req, struct hdr_exp *exp);
|
||||
int apply_filters_to_response(struct session *t, struct buffer *rtr, struct hdr_exp *exp);
|
||||
void manage_client_side_appsession(struct session *t, const char *buf);
|
||||
void manage_client_side_appsession(struct session *t, const char *buf, int len);
|
||||
void manage_client_side_cookies(struct session *t, struct buffer *req);
|
||||
void manage_server_side_cookies(struct session *t, struct buffer *rtr);
|
||||
void check_response_for_cacheability(struct session *t, struct buffer *rtr);
|
||||
|
@ -123,7 +123,15 @@
|
||||
#define PR_O2_LOGHCHKS 0x00000800 /* log health checks */
|
||||
#define PR_O2_INDEPSTR 0x00001000 /* independant streams, don't update rex on write */
|
||||
#define PR_O2_SOCKSTAT 0x00002000 /* collect & provide separate statistics for sockets */
|
||||
#define PR_O2_AS_REQL 0x00004000 /* appsession: learn the session id from the request */
|
||||
|
||||
/* appsession */
|
||||
#define PR_O2_AS_REQL 0x00004000 /* learn the session id from the request */
|
||||
#define PR_O2_AS_PFX 0x00008000 /* match on the cookie prefix */
|
||||
|
||||
/* Encoding of appsession cookie matching modes : 2 possible values => 1 bit */
|
||||
#define PR_O2_AS_M_PP 0x00000000 /* path-parameters mode (the default mode) */
|
||||
#define PR_O2_AS_M_QS 0x00010000 /* query-string mode */
|
||||
#define PR_O2_AS_M_ANY 0x00010000 /* mask covering all PR_O2_AS_M_* values */
|
||||
|
||||
struct error_snapshot {
|
||||
struct timeval when; /* date of this event, (tv_sec == 0) means "never" */
|
||||
|
@ -1541,7 +1541,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
err_code |= ERR_WARN;
|
||||
|
||||
if (*(args[5]) == 0) {
|
||||
Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout>.\n",
|
||||
Alert("parsing [%s:%d] : '%s' expects 'appsession' <cookie_name> 'len' <len> 'timeout' <timeout> [options*].\n",
|
||||
file, linenum, args[0]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
@ -1568,9 +1568,34 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
||||
|
||||
cur_arg = 6;
|
||||
curproxy->options2 &= ~PR_O2_AS_REQL;
|
||||
curproxy->options2 &= ~PR_O2_AS_M_ANY;
|
||||
curproxy->options2 |= PR_O2_AS_M_PP;
|
||||
while (*(args[cur_arg])) {
|
||||
if (!strcmp(args[cur_arg], "request-learn"))
|
||||
if (!strcmp(args[cur_arg], "request-learn")) {
|
||||
curproxy->options2 |= PR_O2_AS_REQL;
|
||||
} else if (!strcmp(args[cur_arg], "prefix")) {
|
||||
curproxy->options2 |= PR_O2_AS_PFX;
|
||||
} else if (!strcmp(args[cur_arg], "mode")) {
|
||||
if (!*args[cur_arg + 1]) {
|
||||
Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
|
||||
file, linenum, args[0], args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cur_arg++;
|
||||
if (!strcmp(args[cur_arg], "query-string")) {
|
||||
curproxy->options2 &= ~PR_O2_AS_M_ANY;
|
||||
curproxy->options2 |= PR_O2_AS_M_QS;
|
||||
} else if (!strcmp(args[cur_arg], "path-parameters")) {
|
||||
curproxy->options2 &= ~PR_O2_AS_M_ANY;
|
||||
curproxy->options2 |= PR_O2_AS_M_PP;
|
||||
} else {
|
||||
Alert("parsing [%s:%d] : unknown mode '%s'\n", file, linenum, args[cur_arg]);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
cur_arg++;
|
||||
}
|
||||
} /* Url App Session */
|
||||
|
144
src/proto_http.c
144
src/proto_http.c
@ -2479,17 +2479,7 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
|
||||
}
|
||||
|
||||
/*
|
||||
* 7: the appsession cookie was looked up very early in 1.2,
|
||||
* so let's do the same now.
|
||||
*/
|
||||
|
||||
/* It needs to look into the URI */
|
||||
if (s->be->appsession_name) {
|
||||
get_srv_from_appsession(s, &req->data[msg->som], msg->sl.rq.l);
|
||||
}
|
||||
|
||||
/*
|
||||
* 8: Now we can work with the cookies.
|
||||
* 7: Now we can work with the cookies.
|
||||
* Note that doing so might move headers in the request, but
|
||||
* the fields will stay coherent and the URI will not move.
|
||||
* This should only be performed in the backend.
|
||||
@ -2498,6 +2488,16 @@ int http_process_request(struct session *s, struct buffer *req, int an_bit)
|
||||
&& !(txn->flags & (TX_CLDENY|TX_CLTARPIT)))
|
||||
manage_client_side_cookies(s, req);
|
||||
|
||||
/*
|
||||
* 8: the appsession cookie was looked up very early in 1.2,
|
||||
* so let's do the same now.
|
||||
*/
|
||||
|
||||
/* It needs to look into the URI */
|
||||
if ((s->sessid == NULL) && s->be->appsession_name) {
|
||||
get_srv_from_appsession(s, &req->data[msg->som + msg->sl.rq.u], msg->sl.rq.u_l);
|
||||
}
|
||||
|
||||
/*
|
||||
* 9: add X-Forwarded-For if either the frontend or the backend
|
||||
* asks for it.
|
||||
@ -3783,11 +3783,15 @@ int apply_filters_to_request(struct session *t, struct buffer *req, struct hdr_e
|
||||
* Try to retrieve the server associated to the appsession.
|
||||
* If the server is found, it's assigned to the session.
|
||||
*/
|
||||
void manage_client_side_appsession(struct session *t, const char *buf) {
|
||||
void manage_client_side_appsession(struct session *t, const char *buf, int len) {
|
||||
struct http_txn *txn = &t->txn;
|
||||
appsess *asession = NULL;
|
||||
char *sessid_temp = NULL;
|
||||
|
||||
if (len > t->be->appsession_len) {
|
||||
len = t->be->appsession_len;
|
||||
}
|
||||
|
||||
if (t->be->options2 & PR_O2_AS_REQL) {
|
||||
/* request-learn option is enabled : store the sessid in the session for future use */
|
||||
if (t->sessid != NULL) {
|
||||
@ -3801,8 +3805,8 @@ void manage_client_side_appsession(struct session *t, const char *buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(t->sessid, buf, t->be->appsession_len);
|
||||
t->sessid[t->be->appsession_len] = 0;
|
||||
memcpy(t->sessid, buf, len);
|
||||
t->sessid[len] = 0;
|
||||
}
|
||||
|
||||
if ((sessid_temp = pool_alloc2(apools.sessid)) == NULL) {
|
||||
@ -3811,8 +3815,8 @@ void manage_client_side_appsession(struct session *t, const char *buf) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(sessid_temp, buf, t->be->appsession_len);
|
||||
sessid_temp[t->be->appsession_len] = 0;
|
||||
memcpy(sessid_temp, buf, len);
|
||||
sessid_temp[len] = 0;
|
||||
|
||||
asession = appsession_hash_lookup(&(t->be->htbl_proxy), sessid_temp);
|
||||
/* free previously allocated memory */
|
||||
@ -4080,12 +4084,25 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
}
|
||||
}
|
||||
|
||||
if ((t->be->appsession_name != NULL) &&
|
||||
(memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
|
||||
/* first, let's see if the cookie is our appcookie*/
|
||||
if (t->be->appsession_name != NULL) {
|
||||
int cmp_len, value_len;
|
||||
char *value_begin;
|
||||
|
||||
if (t->be->options2 & PR_O2_AS_PFX) {
|
||||
cmp_len = MIN(p4 - p1, t->be->appsession_name_len);
|
||||
value_begin = p1 + t->be->appsession_name_len;
|
||||
value_len = p4 - p1 - t->be->appsession_name_len;
|
||||
} else {
|
||||
cmp_len = p2 - p1;
|
||||
value_begin = p3;
|
||||
value_len = p4 - p3;
|
||||
}
|
||||
|
||||
/* let's see if the cookie is our appcookie */
|
||||
if (memcmp(p1, t->be->appsession_name, cmp_len) == 0) {
|
||||
/* Cool... it's the right one */
|
||||
manage_client_side_appsession(t, p3);
|
||||
manage_client_side_appsession(t, value_begin, value_len);
|
||||
}
|
||||
#if defined(DEBUG_HASH)
|
||||
Alert("manage_client_side_cookies\n");
|
||||
appsession_hash_dump(&(t->be->htbl_proxy));
|
||||
@ -4497,11 +4514,22 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
}
|
||||
}
|
||||
/* next, let's see if the cookie is our appcookie */
|
||||
else if ((t->be->appsession_name != NULL) &&
|
||||
(memcmp(p1, t->be->appsession_name, p2 - p1) == 0)) {
|
||||
else if (t->be->appsession_name != NULL) {
|
||||
int cmp_len, value_len;
|
||||
char *value_begin;
|
||||
|
||||
if (t->be->options2 & PR_O2_AS_PFX) {
|
||||
cmp_len = MIN(p4 - p1, t->be->appsession_name_len);
|
||||
value_begin = p1 + t->be->appsession_name_len;
|
||||
value_len = MIN(t->be->appsession_len, p4 - p1 - t->be->appsession_name_len);
|
||||
} else {
|
||||
cmp_len = p2 - p1;
|
||||
value_begin = p3;
|
||||
value_len = MIN(t->be->appsession_len, p4 - p3);
|
||||
}
|
||||
|
||||
if (memcmp(p1, t->be->appsession_name, cmp_len) == 0) {
|
||||
/* Cool... it's the right one */
|
||||
|
||||
if (t->sessid != NULL) {
|
||||
/* free previously allocated memory as we don't need it anymore */
|
||||
pool_free2(apools.sessid, t->sessid);
|
||||
@ -4512,9 +4540,10 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
|
||||
return;
|
||||
}
|
||||
memcpy(t->sessid, p3, t->be->appsession_len);
|
||||
t->sessid[t->be->appsession_len] = 0;
|
||||
}/* end if ((t->proxy->appsession_name != NULL) ... */
|
||||
memcpy(t->sessid, value_begin, value_len);
|
||||
t->sessid[value_len] = 0;
|
||||
}
|
||||
} /* end if ((t->be->appsession_name != NULL) ... */
|
||||
break; /* we don't want to loop again since there cannot be another cookie on the same line */
|
||||
} /* we're now at the end of the cookie value */
|
||||
/* keep the link from this header to next one */
|
||||
@ -4657,25 +4686,66 @@ void check_response_for_cacheability(struct session *t, struct buffer *rtr)
|
||||
*/
|
||||
void get_srv_from_appsession(struct session *t, const char *begin, int len)
|
||||
{
|
||||
char *request_line;
|
||||
char *end_params, *first_param, *cur_param, *next_param;
|
||||
char separator;
|
||||
int value_len;
|
||||
|
||||
int mode = t->be->options2 & PR_O2_AS_M_ANY;
|
||||
|
||||
if (t->be->appsession_name == NULL ||
|
||||
(t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST) ||
|
||||
(request_line = memchr(begin, ';', len)) == NULL ||
|
||||
((1 + t->be->appsession_name_len + 1 + t->be->appsession_len) > (begin + len - request_line)))
|
||||
(t->txn.meth != HTTP_METH_GET && t->txn.meth != HTTP_METH_POST)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip ';' */
|
||||
request_line++;
|
||||
first_param = NULL;
|
||||
switch (mode) {
|
||||
case PR_O2_AS_M_PP:
|
||||
first_param = memchr(begin, ';', len);
|
||||
break;
|
||||
case PR_O2_AS_M_QS:
|
||||
first_param = memchr(begin, '?', len);
|
||||
break;
|
||||
}
|
||||
|
||||
/* look if we have a jsessionid */
|
||||
if (strncasecmp(request_line, t->be->appsession_name, t->be->appsession_name_len) != 0)
|
||||
if (first_param == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* skip jsessionid= */
|
||||
request_line += t->be->appsession_name_len + 1;
|
||||
switch (mode) {
|
||||
case PR_O2_AS_M_PP:
|
||||
if ((end_params = memchr(first_param, '?', len - (begin - first_param))) == NULL) {
|
||||
end_params = (char *) begin + len;
|
||||
}
|
||||
separator = ';';
|
||||
break;
|
||||
case PR_O2_AS_M_QS:
|
||||
end_params = (char *) begin + len;
|
||||
separator = '&';
|
||||
break;
|
||||
default:
|
||||
/* unknown mode, shouldn't happen */
|
||||
return;
|
||||
}
|
||||
|
||||
manage_client_side_appsession(t, request_line);
|
||||
cur_param = next_param = end_params;
|
||||
while (cur_param > first_param) {
|
||||
cur_param--;
|
||||
if ((cur_param[0] == separator) || (cur_param == first_param)) {
|
||||
/* let's see if this is the appsession parameter */
|
||||
if ((cur_param + t->be->appsession_name_len + 1 < next_param) &&
|
||||
((t->be->options2 & PR_O2_AS_PFX) || cur_param[t->be->appsession_name_len + 1] == '=') &&
|
||||
(strncasecmp(cur_param + 1, t->be->appsession_name, t->be->appsession_name_len) == 0)) {
|
||||
/* Cool... it's the right one */
|
||||
cur_param += t->be->appsession_name_len + (t->be->options2 & PR_O2_AS_PFX ? 1 : 2);
|
||||
value_len = MIN(t->be->appsession_len, next_param - cur_param);
|
||||
if (value_len > 0) {
|
||||
manage_client_side_appsession(t, cur_param, value_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
next_param = cur_param;
|
||||
}
|
||||
}
|
||||
#if defined(DEBUG_HASH)
|
||||
Alert("get_srv_from_appsession\n");
|
||||
appsession_hash_dump(&(t->be->htbl_proxy));
|
||||
|
Loading…
x
Reference in New Issue
Block a user