ngx_borderpatrol/contrib/nginx-sticky-module/ngx_http_sticky_module.c

692 lines
23 KiB
C

/*
* Copyright (C) Jerome Loyet <jerome at loyet dot net>
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_http_sticky_misc.h"
/* define a peer */
typedef struct {
ngx_http_upstream_rr_peer_t *rr_peer;
ngx_str_t digest;
} ngx_http_sticky_peer_t;
/* the configuration structure */
typedef struct {
ngx_http_upstream_srv_conf_t uscf;
ngx_str_t cookie_name;
ngx_str_t cookie_domain;
ngx_str_t cookie_path;
time_t cookie_expires;
ngx_str_t hmac_key;
ngx_http_sticky_misc_hash_pt hash;
ngx_http_sticky_misc_hmac_pt hmac;
ngx_http_sticky_misc_text_pt text;
ngx_uint_t no_fallback;
ngx_http_sticky_peer_t *peers;
} ngx_http_sticky_srv_conf_t;
/* the custom sticky struct used on each request */
typedef struct {
/* the round robin data must be first */
ngx_http_upstream_rr_peer_data_t rrp;
ngx_event_get_peer_pt get_rr_peer;
int selected_peer;
int no_fallback;
ngx_http_sticky_srv_conf_t *sticky_conf;
ngx_http_request_t *request;
} ngx_http_sticky_peer_data_t;
static ngx_int_t ngx_http_init_sticky_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us);
static ngx_int_t ngx_http_get_sticky_peer(ngx_peer_connection_t *pc, void *data);
static char *ngx_http_sticky_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_sticky_create_conf(ngx_conf_t *cf);
static ngx_command_t ngx_http_sticky_commands[] = {
{ ngx_string("sticky"),
NGX_HTTP_UPS_CONF|NGX_CONF_ANY,
ngx_http_sticky_set,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_sticky_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
ngx_http_sticky_create_conf, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_sticky_module = {
NGX_MODULE_V1,
&ngx_http_sticky_module_ctx, /* module context */
ngx_http_sticky_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
/*
* function called by the upstream module to init itself
* it's called once per instance
*/
ngx_int_t ngx_http_init_upstream_sticky(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
ngx_http_upstream_rr_peers_t *rr_peers;
ngx_http_sticky_srv_conf_t *conf;
ngx_uint_t i;
/* call the rr module on wich the sticky module is based on */
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
return NGX_ERROR;
}
/* calculate each peer digest once and save */
rr_peers = us->peer.data;
/* do nothing there's only one peer */
if (rr_peers->number <= 1 || rr_peers->single) {
return NGX_OK;
}
/* tell the upstream module to call ngx_http_init_sticky_peer when it inits peer */
us->peer.init = ngx_http_init_sticky_peer;
conf = ngx_http_conf_upstream_srv_conf(us, ngx_http_sticky_module);
/* if 'index', no need to alloc and generate digest */
if (!conf->hash && !conf->hmac && !conf->text) {
conf->peers = NULL;
return NGX_OK;
}
/* create our own upstream indexes */
conf->peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_sticky_peer_t) * rr_peers->number);
if (conf->peers == NULL) {
return NGX_ERROR;
}
/* parse each peer and generate digest if necessary */
for (i = 0; i < rr_peers->number; i++) {
conf->peers[i].rr_peer = &rr_peers->peer[i];
if (conf->hmac) {
/* generate hmac */
conf->hmac(cf->pool, rr_peers->peer[i].sockaddr, rr_peers->peer[i].socklen, &conf->hmac_key, &conf->peers[i].digest);
} else if (conf->text) {
/* generate text */
conf->text(cf->pool, rr_peers->peer[i].sockaddr, &conf->peers[i].digest);
} else {
/* generate hash */
conf->hash(cf->pool, rr_peers->peer[i].sockaddr, rr_peers->peer[i].socklen, &conf->peers[i].digest);
}
#if 0
/* FIXME: is it possible to log to debug level when at configuration stage ? */
ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "[sticky/ngx_http_init_upstream_sticky] generated digest \"%V\" for upstream at index %d", &conf->peers[i].digest, i);
#endif
}
return NGX_OK;
}
/*
* function called by the upstream module when it inits each peer
* it's called once per request
*/
static ngx_int_t ngx_http_init_sticky_peer(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us)
{
ngx_http_sticky_peer_data_t *iphp;
ngx_str_t route;
ngx_uint_t i;
ngx_int_t n;
/* alloc custom sticky struct */
iphp = ngx_palloc(r->pool, sizeof(ngx_http_sticky_peer_data_t));
if (iphp == NULL) {
return NGX_ERROR;
}
/* attach it to the request upstream data */
r->upstream->peer.data = &iphp->rrp;
/* call the rr module on which the sticky is based on */
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
return NGX_ERROR;
}
/* set the callback to select the next peer to use */
r->upstream->peer.get = ngx_http_get_sticky_peer;
/* init the custom sticky struct */
iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
iphp->selected_peer = -1;
iphp->no_fallback = 0;
iphp->sticky_conf = ngx_http_conf_upstream_srv_conf(us, ngx_http_sticky_module);
iphp->request = r;
/* check weather a cookie is present or not and save it */
if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &iphp->sticky_conf->cookie_name, &route) != NGX_DECLINED) {
/* a route cookie has been found. Let's give it a try */
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[sticky/init_sticky_peer] got cookie route=%V, let's try to find a matching peer", &route);
/* hash, hmac or text, just compare digest */
if (iphp->sticky_conf->hash || iphp->sticky_conf->hmac || iphp->sticky_conf->text) {
/* check internal struct has been set */
if (!iphp->sticky_conf->peers) {
/* log a warning, as it will continue without the sticky */
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "[sticky/init_sticky_peer] internal peers struct has not been set");
return NGX_OK; /* return OK, in order to continue */
}
/* search the digest found in the cookie in the peer digest list */
for (i = 0; i < iphp->rrp.peers->number; i++) {
/* ensure the both len are equal and > 0 */
if (iphp->sticky_conf->peers[i].digest.len != route.len || route.len <= 0) {
continue;
}
if (!ngx_strncmp(iphp->sticky_conf->peers[i].digest.data, route.data, route.len)) {
/* we found a match */
iphp->selected_peer = i;
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[sticky/init_sticky_peer] the route \"%V\" matches peer at index %ui", &route, i);
return NGX_OK;
}
}
} else {
/* switch back to index, just convert to integer and ensure it corresponds to a valid peer */
n = ngx_atoi(route.data, route.len);
if (n == NGX_ERROR) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "[sticky/init_sticky_peer] unable to convert the route \"%V\" to an integer value", &route);
} else if (n >= 0 && n < (ngx_int_t)iphp->rrp.peers->number) {
/* found one */
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[sticky/init_sticky_peer] the route \"%V\" matches peer at index %i", &route, n);
iphp->selected_peer = n;
return NGX_OK;
}
}
/* nothing was found, just continue with rr */
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[sticky/init_sticky_peer] the route \"%V\" does not match any peer. Just ignoring it ...", &route);
return NGX_OK;
}
/* nothing found */
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "[sticky/init_sticky_peer] route cookie not found", &route);
return NGX_OK; /* return OK, in order to continue */
}
/*
* function called by the upstream module to choose the next peer to use
* called at least one time per request
*/
static ngx_int_t ngx_http_get_sticky_peer(ngx_peer_connection_t *pc, void *data)
{
ngx_http_sticky_peer_data_t *iphp = data;
ngx_http_sticky_srv_conf_t *conf = iphp->sticky_conf;
ngx_int_t selected_peer = -1;
time_t now = ngx_time();
uintptr_t m;
ngx_uint_t n, i;
ngx_http_upstream_rr_peer_t *peer = NULL;
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] get sticky peer, try: %ui, n_peers: %ui, no_fallback: %ui/%ui", pc->tries, iphp->rrp.peers->number, conf->no_fallback, iphp->no_fallback);
/* TODO: cached */
/* has the sticky module already choosen a peer to connect to and is it a valid peer */
/* is there more than one peer (otherwise, no choices to make) */
if (iphp->selected_peer >= 0 && iphp->selected_peer < (ngx_int_t)iphp->rrp.peers->number && !iphp->rrp.peers->single) {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] let's try the selected peer (%i)", iphp->selected_peer);
n = iphp->selected_peer / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << iphp->selected_peer % (8 * sizeof(uintptr_t));
/* has the peer not already been tried ? */
if (!(iphp->rrp.tried[n] & m)) {
peer = &iphp->rrp.peers->peer[iphp->selected_peer];
/* if the no_fallback flag is set */
if (conf->no_fallback) {
iphp->no_fallback = 1;
/* if peer is down */
if (peer->down) {
ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "[sticky/get_sticky_peer] the selected peer is down and no_fallback is flagged");
return NGX_BUSY;
}
/* if it's been ignored for long enought (fail_timeout), reset timeout */
/* do this check before testing peer->fails ! :) */
if (now - peer->accessed > peer->fail_timeout) {
peer->fails = 0;
}
/* if peer is failed */
if (peer->max_fails > 0 && peer->fails >= peer->max_fails) {
ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "[sticky/get_sticky_peer] the selected peer is maked as failed and no_fallback is flagged");
return NGX_BUSY;
}
}
/* ensure the peer is not marked as down */
if (!peer->down) {
/* if it's not failedi, use it */
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
selected_peer = (ngx_int_t)n;
/* if it's been ignored for long enought (fail_timeout), reset timeout and use it */
} else if (now - peer->accessed > peer->fail_timeout) {
peer->fails = 0;
selected_peer = (ngx_int_t)n;
/* it's failed or timeout did not expire yet */
} else {
/* mark the peer as tried */
iphp->rrp.tried[n] |= m;
}
}
}
}
/* we have a valid peer, tell the upstream module to use it */
if (peer && selected_peer >= 0) {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] peer found at index %i", selected_peer);
iphp->rrp.current = iphp->selected_peer;
pc->cached = 0;
pc->connection = NULL;
pc->sockaddr = peer->sockaddr;
pc->socklen = peer->socklen;
pc->name = &peer->name;
iphp->rrp.tried[n] |= m;
} else {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] no sticky peer selected, switch back to classic rr");
if (iphp->no_fallback) {
ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "[sticky/get_sticky_peer] No fallback in action !");
return NGX_BUSY;
}
ngx_int_t ret = iphp->get_rr_peer(pc, &iphp->rrp);
if (ret != NGX_OK) {
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] ngx_http_upstream_get_round_robin_peer returned %i", ret);
return ret;
}
/* search for the choosen peer in order to set the cookie */
for (i = 0; i < iphp->rrp.peers->number; i++) {
if (iphp->rrp.peers->peer[i].sockaddr == pc->sockaddr && iphp->rrp.peers->peer[i].socklen == pc->socklen) {
if (conf->hash || conf->hmac || conf->text) {
ngx_http_sticky_misc_set_cookie(iphp->request, &conf->cookie_name, &conf->peers[i].digest, &conf->cookie_domain, &conf->cookie_path, conf->cookie_expires);
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] set cookie \"%V\" value=\"%V\" index=%ui", &conf->cookie_name, &conf->peers[i].digest, i);
} else {
ngx_str_t route;
ngx_uint_t tmp = i;
route.len = 0;
do {
route.len++;
} while (tmp /= 10);
route.data = ngx_pcalloc(iphp->request->pool, sizeof(u_char) * (route.len + 1));
if (route.data == NULL) {
break;
}
ngx_snprintf(route.data, route.len, "%d", i);
route.len = ngx_strlen(route.data);
ngx_http_sticky_misc_set_cookie(iphp->request, &conf->cookie_name, &route, &conf->cookie_domain, &conf->cookie_path, conf->cookie_expires);
ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[sticky/get_sticky_peer] set cookie \"%V\" value=\"%V\" index=%ui", &conf->cookie_name, &tmp, i);
}
break; /* found and hopefully the cookie have been set */
}
}
}
/* reset the selection in order to bypass the sticky module when the upstream module will try another peers if necessary */
iphp->selected_peer = -1;
return NGX_OK;
}
/*
* Function called when the sticky command is parsed on the conf file
*/
static char *ngx_http_sticky_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_upstream_srv_conf_t *upstream_conf;
ngx_http_sticky_srv_conf_t *sticky_conf;
ngx_uint_t i;
ngx_str_t tmp;
ngx_str_t name = ngx_string("route");
ngx_str_t domain = ngx_string("");
ngx_str_t path = ngx_string("");
ngx_str_t hmac_key = ngx_string("");
time_t expires = NGX_CONF_UNSET;
ngx_http_sticky_misc_hash_pt hash = NGX_CONF_UNSET_PTR;
ngx_http_sticky_misc_hmac_pt hmac = NULL;
ngx_http_sticky_misc_text_pt text = NULL;
ngx_uint_t no_fallback = 0;
/* parse all elements */
for (i = 1; i < cf->args->nelts; i++) {
ngx_str_t *value = cf->args->elts;
/* is "name=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "name=") == value[i].data) {
/* do we have at least on char after "name=" ? */
if (value[i].len <= sizeof("name=") - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"name=\"");
return NGX_CONF_ERROR;
}
/* save what's after "name=" */
name.len = value[i].len - ngx_strlen("name=");
name.data = (u_char *)(value[i].data + sizeof("name=") - 1);
continue;
}
/* is "domain=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "domain=") == value[i].data) {
/* do we have at least on char after "domain=" ? */
if (value[i].len <= ngx_strlen("domain=")) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"domain=\"");
return NGX_CONF_ERROR;
}
/* save what's after "domain=" */
domain.len = value[i].len - ngx_strlen("domain=");
domain.data = (u_char *)(value[i].data + sizeof("domain=") - 1);
continue;
}
/* is "path=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "path=") == value[i].data) {
/* do we have at least on char after "path=" ? */
if (value[i].len <= ngx_strlen("path=")) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"path=\"");
return NGX_CONF_ERROR;
}
/* save what's after "domain=" */
path.len = value[i].len - ngx_strlen("path=");
path.data = (u_char *)(value[i].data + sizeof("path=") - 1);
continue;
}
/* is "expires=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "expires=") == value[i].data) {
/* do we have at least on char after "expires=" ? */
if (value[i].len <= sizeof("expires=") - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"expires=\"");
return NGX_CONF_ERROR;
}
/* extract value */
tmp.len = value[i].len - ngx_strlen("expires=");
tmp.data = (u_char *)(value[i].data + sizeof("expires=") - 1);
/* convert to time, save and validate */
expires = ngx_parse_time(&tmp, 1);
if (expires == NGX_ERROR || expires < 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value for \"expires=\"");
return NGX_CONF_ERROR;
}
continue;
}
/* is "text=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "text=") == value[i].data) {
/* only hash or hmac can be used, not both */
if (hmac || hash != NGX_CONF_UNSET_PTR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "please choose between \"hash=\", \"hmac=\" and \"text\"");
return NGX_CONF_ERROR;
}
/* do we have at least on char after "name=" ? */
if (value[i].len <= sizeof("text=") - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"text=\"");
return NGX_CONF_ERROR;
}
/* extract value to temp */
tmp.len = value[i].len - ngx_strlen("text=");
tmp.data = (u_char *)(value[i].data + sizeof("text=") - 1);
/* is name=raw */
if (ngx_strncmp(tmp.data, "raw", sizeof("raw") - 1) == 0 ) {
text = ngx_http_sticky_misc_text_raw;
continue;
}
/* is name=md5 */
if (ngx_strncmp(tmp.data, "md5", sizeof("md5") - 1) == 0 ) {
text = ngx_http_sticky_misc_text_md5;
continue;
}
/* is name=sha1 */
if (ngx_strncmp(tmp.data, "sha1", sizeof("sha1") - 1) == 0 ) {
text = ngx_http_sticky_misc_text_sha1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "wrong value for \"text=\": raw, md5 or sha1");
return NGX_CONF_ERROR;
}
/* is "hash=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "hash=") == value[i].data) {
/* only hash or hmac can be used, not both */
if (hmac || text) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "please choose between \"hash=\", \"hmac=\" and \"text=\"");
return NGX_CONF_ERROR;
}
/* do we have at least on char after "hash=" ? */
if (value[i].len <= sizeof("hash=") - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"hash=\"");
return NGX_CONF_ERROR;
}
/* extract value to temp */
tmp.len = value[i].len - ngx_strlen("hash=");
tmp.data = (u_char *)(value[i].data + sizeof("hash=") - 1);
/* is hash=index */
if (ngx_strncmp(tmp.data, "index", sizeof("index") - 1) == 0 ) {
hash = NULL;
continue;
}
/* is hash=md5 */
if (ngx_strncmp(tmp.data, "md5", sizeof("md5") - 1) == 0 ) {
hash = ngx_http_sticky_misc_md5;
continue;
}
/* is hash=sha1 */
if (ngx_strncmp(tmp.data, "sha1", sizeof("sha1") - 1) == 0 ) {
hash = ngx_http_sticky_misc_sha1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "wrong value for \"hash=\": index, md5 or sha1");
return NGX_CONF_ERROR;
}
/* is "hmac=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "hmac=") == value[i].data) {
/* only hash or hmac can be used, not both */
if (hash != NGX_CONF_UNSET_PTR || text) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "please choose between \"hash=\", \"hmac=\" and \"text\"");
return NGX_CONF_ERROR;
}
/* do we have at least on char after "hmac=" ? */
if (value[i].len <= sizeof("hmac=") - 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"hmac=\"");
return NGX_CONF_ERROR;
}
/* extract value */
tmp.len = value[i].len - ngx_strlen("hmac=");
tmp.data = (u_char *)(value[i].data + sizeof("hmac=") - 1);
/* is hmac=md5 ? */
if (ngx_strncmp(tmp.data, "md5", sizeof("md5") - 1) == 0 ) {
hmac = ngx_http_sticky_misc_hmac_md5;
continue;
}
/* is hmac=sha1 ? */
if (ngx_strncmp(tmp.data, "sha1", sizeof("sha1") - 1) == 0 ) {
hmac = ngx_http_sticky_misc_hmac_sha1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "wrong value for \"hmac=\": md5 or sha1");
return NGX_CONF_ERROR;
}
/* is "hmac_key=" is starting the argument ? */
if ((u_char *)ngx_strstr(value[i].data, "hmac_key=") == value[i].data) {
/* do we have at least on char after "hmac_key=" ? */
if (value[i].len <= ngx_strlen("hmac_key=")) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "a value must be provided to \"hmac_key=\"");
return NGX_CONF_ERROR;
}
/* save what's after "hmac_key=" */
hmac_key.len = value[i].len - ngx_strlen("hmac_key=");
hmac_key.data = (u_char *)(value[i].data + sizeof("hmac_key=") - 1);
continue;
}
/* is "no_fallback" flag present ? */
if (ngx_strncmp(value[i].data, "no_fallback", sizeof("no_fallback") - 1) == 0 ) {
no_fallback = 1;
continue;
}
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid arguement (%V)", &value[i]);
return NGX_CONF_ERROR;
}
/* if has and hmac and name have not been set, default to md5 */
if (hash == NGX_CONF_UNSET_PTR && hmac == NULL && text == NULL) {
hash = ngx_http_sticky_misc_md5;
}
/* don't allow meaning less parameters */
if (hmac_key.len > 0 && hash != NGX_CONF_UNSET_PTR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"hmac_key=\" is meaningless when \"hmac\" is used. Please remove it.");
return NGX_CONF_ERROR;
}
/* ensure we have an hmac key if hmac's been set */
if (hmac_key.len == 0 && hmac != NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "please specify \"hmac_key=\" when using \"hmac\"");
return NGX_CONF_ERROR;
}
/* ensure hash is NULL to avoid conflicts later */
if (hash == NGX_CONF_UNSET_PTR) {
hash = NULL;
}
/* save the sticky parameters */
sticky_conf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_sticky_module);
sticky_conf->cookie_name = name;
sticky_conf->cookie_domain = domain;
sticky_conf->cookie_path = path;
sticky_conf->cookie_expires = expires;
sticky_conf->hash = hash;
sticky_conf->hmac = hmac;
sticky_conf->text = text;
sticky_conf->hmac_key = hmac_key;
sticky_conf->no_fallback = no_fallback;
sticky_conf->peers = NULL; /* ensure it's null before running */
upstream_conf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
/*
* ensure another upstream module has not been already loaded
* peer.init_upstream is set to null and the upstream module use RR if not set
* But this check only works when the other module is declared before sticky
*/
if (upstream_conf->peer.init_upstream) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "You can't use sticky with another upstream module");
return NGX_CONF_ERROR;
}
/* configure the upstream to get back to this module */
upstream_conf->peer.init_upstream = ngx_http_init_upstream_sticky;
upstream_conf->flags = NGX_HTTP_UPSTREAM_CREATE
| NGX_HTTP_UPSTREAM_MAX_FAILS
| NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
| NGX_HTTP_UPSTREAM_DOWN
| NGX_HTTP_UPSTREAM_WEIGHT;
return NGX_CONF_OK;
}
/*
* alloc stick configuration
*/
static void *ngx_http_sticky_create_conf(ngx_conf_t *cf)
{
ngx_http_sticky_srv_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sticky_srv_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
return conf;
}