/* * This function tries to find a running server for the proxy <px> following * the source hash method. Depending on the number of active/backup servers, * it will either look for active servers, or for backup servers. * If any server is found, it will be returned. If no valid server is found, * NULL is returned. */ struct server *get_server_sh(struct proxy *px, const char *addr, int len) { unsigned int h, l; if (px->lbprm.tot_weight == 0) return NULL; l = h = 0; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; while ((l + sizeof (int)) <= len) { h ^= ntohl(*(unsigned int *)(&addr[l])); l += sizeof (int); } if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) h = full_hash(h); hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, h); else return map_get_server_hash(px, h); }
/* * This function tries to find a running server for the proxy <px> following * the URI hash method. In order to optimize cache hits, the hash computation * ends at the question mark. Depending on the number of active/backup servers, * it will either look for active servers, or for backup servers. * If any server is found, it will be returned. If no valid server is found, * NULL is returned. * * This code was contributed by Guillaume Dallaire, who also selected this hash * algorithm out of a tens because it gave him the best results. * */ struct server *get_server_uh(struct proxy *px, char *uri, int uri_len) { unsigned long hash = 0; int c; int slashes = 0; if (px->lbprm.tot_weight == 0) return NULL; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; if (px->uri_len_limit) uri_len = MIN(uri_len, px->uri_len_limit); while (uri_len--) { c = *uri++; if (c == '/') { slashes++; if (slashes == px->uri_dirs_depth1) /* depth+1 */ break; } else if (c == '?') break; hash = c + (hash << 6) + (hash << 16) - hash; } hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* * This function tries to find a running server for the proxy <px> following * the URL 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_ph(struct proxy *px, const char *uri, int uri_len) { unsigned int hash = 0; const char *start, *end; const char *p; const char *params; int plen; /* when tot_weight is 0 then so is srv_count */ if (px->lbprm.tot_weight == 0) return NULL; if ((p = memchr(uri, '?', uri_len)) == NULL) return NULL; p++; uri_len -= (p - uri); plen = px->url_param_len; params = p; while (uri_len > plen) { /* Look for the parameter name followed by an equal symbol */ if (params[plen] == '=') { if (memcmp(params, px->url_param_name, plen) == 0) { /* OK, we have the parameter here at <params>, and * the value after the equal sign, at <p> * skip the equal symbol */ p += plen + 1; start = end = p; uri_len -= plen + 1; while (uri_len && *end != '&') { uri_len--; end++; } hash = gen_hash(px, start, (end - start)); if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) hash = full_hash(hash); if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); } } /* skip to next parameter */ p = memchr(params, '&', uri_len); if (!p) return NULL; p++; uri_len -= (p - params); params = p; } return NULL; }
/* * This function tries to find a running server for the proxy <px> following * the URL 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_ph(struct proxy *px, const char *uri, int uri_len) { unsigned long hash = 0; const char *p; const char *params; int plen; /* when tot_weight is 0 then so is srv_count */ if (px->lbprm.tot_weight == 0) return NULL; if ((p = memchr(uri, '?', uri_len)) == NULL) return NULL; p++; uri_len -= (p - uri); plen = px->url_param_len; params = p; while (uri_len > plen) { /* Look for the parameter name followed by an equal symbol */ if (params[plen] == '=') { if (memcmp(params, px->url_param_name, plen) == 0) { /* OK, we have the parameter here at <params>, and * the value after the equal sign, at <p> * skip the equal symbol */ p += plen + 1; uri_len -= plen + 1; while (uri_len && *p != '&') { hash = *p + (hash << 6) + (hash << 16) - hash; uri_len--; p++; } if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); } } /* skip to next parameter */ p = memchr(params, '&', uri_len); if (!p) return NULL; p++; uri_len -= (p - params); params = p; } return NULL; }
struct server *get_server_rch(struct session *s) { unsigned long hash = 0; struct proxy *px = s->be; unsigned long len; const char *p; int ret; struct acl_expr expr; struct acl_test test; /* tot_weight appears to mean srv_count */ if (px->lbprm.tot_weight == 0) return NULL; memset(&expr, 0, sizeof(expr)); memset(&test, 0, sizeof(test)); expr.arg.str = px->hh_name; expr.arg_len = px->hh_len; ret = acl_fetch_rdp_cookie(px, s, NULL, ACL_DIR_REQ, &expr, &test); if (ret == 0 || (test.flags & ACL_TEST_F_MAY_CHANGE) || test.len == 0) return NULL; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; /* Found a the hh_name in the headers. * we will compute the hash based on this value ctx.val. */ len = test.len; p = (char *)test.ptr; while (len) { hash = *p + (hash << 6) + (hash << 16) - hash; len--; p++; } hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* RDP Cookie HASH. */ struct server *get_server_rch(struct session *s) { unsigned int hash = 0; struct proxy *px = s->be; unsigned long len; int ret; struct sample smp; int rewind; /* tot_weight appears to mean srv_count */ if (px->lbprm.tot_weight == 0) return NULL; memset(&smp, 0, sizeof(smp)); b_rew(s->req.buf, rewind = s->req.buf->o); ret = fetch_rdp_cookie_name(s, &smp, px->hh_name, px->hh_len); len = smp.data.str.len; b_adv(s->req.buf, rewind); if (ret == 0 || (smp.flags & SMP_F_MAY_CHANGE) || len == 0) return NULL; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; /* Found a the hh_name in the headers. * we will compute the hash based on this value ctx.val. */ hash = gen_hash(px, smp.data.str.str, len); if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) hash = full_hash(hash); hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* * This function tries to find a running server for the proxy <px> following * the URI hash method. In order to optimize cache hits, the hash computation * ends at the question mark. Depending on the number of active/backup servers, * it will either look for active servers, or for backup servers. * If any server is found, it will be returned. If no valid server is found, * NULL is returned. * * This code was contributed by Guillaume Dallaire, who also selected this hash * algorithm out of a tens because it gave him the best results. * */ struct server *get_server_uh(struct proxy *px, char *uri, int uri_len) { unsigned int hash = 0; int c; int slashes = 0; const char *start, *end; if (px->lbprm.tot_weight == 0) return NULL; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; if (px->uri_len_limit) uri_len = MIN(uri_len, px->uri_len_limit); start = end = uri; while (uri_len--) { c = *end; if (c == '/') { slashes++; if (slashes == px->uri_dirs_depth1) /* depth+1 */ break; } else if (c == '?' && !px->uri_whole) break; end++; } hash = gen_hash(px, start, (end - start)); if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) hash = full_hash(hash); hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* * 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 int hash = 0; struct http_txn *txn = &s->txn; struct proxy *px = s->be; unsigned int plen = px->hh_len; unsigned long len; struct hdr_ctx ctx; const char *p; const char *start, *end; /* tot_weight appears to mean srv_count */ if (px->lbprm.tot_weight == 0) return NULL; 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, b_ptr(s->req.buf, -http_hdr_rewind(&txn->req)), &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; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; /* 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) { hash = gen_hash(px, p, len); } else { int dohash = 0; p += len; /* 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. */ end = p; while (len) { if (dohash) { /* Rewind the pointer until the previous char * is a dot, this will allow to set the start * position of the domain. */ if (*(p - 1) == '.') break; } else if (*p == '.') { /* The pointer is rewinded to the dot before the * tld, we memorize the end of the domain and * can enter the domain processing. */ end = p; dohash = 1; } p--; len--; } start = p; hash = gen_hash(px, start, (end - start)); } if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) hash = full_hash(hash); hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* * this does the same as the previous server_ph, but check the body contents */ struct server *get_server_ph_post(struct session *s) { unsigned int hash = 0; struct http_txn *txn = &s->txn; struct channel *req = &s->req; struct http_msg *msg = &txn->req; struct proxy *px = s->be; unsigned int plen = px->url_param_len; unsigned long len = http_body_bytes(msg); const char *params = b_ptr(req->buf, -http_data_rewind(msg)); const char *p = params; const char *start, *end; if (len == 0) return NULL; if (px->lbprm.tot_weight == 0) return NULL; while (len > plen) { /* Look for the parameter name followed by an equal symbol */ if (params[plen] == '=') { if (memcmp(params, px->url_param_name, plen) == 0) { /* OK, we have the parameter here at <params>, and * the value after the equal sign, at <p> * skip the equal symbol */ p += plen + 1; start = end = p; len -= plen + 1; while (len && *end != '&') { if (unlikely(!HTTP_IS_TOKEN(*p))) { /* if in a POST, body must be URI encoded or it's not a URI. * Do not interpret any possible binary data as a parameter. */ if (likely(HTTP_IS_LWS(*p))) /* eol, uncertain uri len */ break; return NULL; /* oh, no; this is not uri-encoded. * This body does not contain parameters. */ } len--; end++; /* should we break if vlen exceeds limit? */ } hash = gen_hash(px, start, (end - start)); if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL) hash = full_hash(hash); if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); } } /* skip to next parameter */ p = memchr(params, '&', len); if (!p) return NULL; p++; len -= (p - params); params = p; } return NULL; }
/* * 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; 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; /* note: we won't hash if there's only one server left */ if (px->lbprm.tot_used == 1) goto hash_done; /* 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--; } } hash_done: if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); }
/* * this does the same as the previous server_ph, but check the body contents */ struct server *get_server_ph_post(struct session *s) { unsigned long hash = 0; struct http_txn *txn = &s->txn; struct buffer *req = s->req; struct http_msg *msg = &txn->req; struct proxy *px = s->be; unsigned int plen = px->url_param_len; unsigned long len = msg->body_len; const char *params = req->data + msg->sov; const char *p = params; if (len > req->l - (msg->sov - msg->som)) len = req->l - (msg->sov - msg->som); if (len == 0) return NULL; if (px->lbprm.tot_weight == 0) return NULL; while (len > plen) { /* Look for the parameter name followed by an equal symbol */ if (params[plen] == '=') { if (memcmp(params, px->url_param_name, plen) == 0) { /* OK, we have the parameter here at <params>, and * the value after the equal sign, at <p> * skip the equal symbol */ p += plen + 1; len -= plen + 1; while (len && *p != '&') { if (unlikely(!HTTP_IS_TOKEN(*p))) { /* if in a POST, body must be URI encoded or it's not a URI. * Do not interprete any possible binary data as a parameter. */ if (likely(HTTP_IS_LWS(*p))) /* eol, uncertain uri len */ break; return NULL; /* oh, no; this is not uri-encoded. * This body does not contain parameters. */ } hash = *p + (hash << 6) + (hash << 16) - hash; len--; p++; /* should we break if vlen exceeds limit? */ } if (px->lbprm.algo & BE_LB_LKUP_CHTREE) return chash_get_server_hash(px, hash); else return map_get_server_hash(px, hash); } } /* skip to next parameter */ p = memchr(params, '&', len); if (!p) return NULL; p++; len -= (p - params); params = p; } return NULL; }