Initial Commit
This commit is contained in:
683
src/stream/ngx_stream.c
Normal file
683
src/stream/ngx_stream.c
Normal file
@@ -0,0 +1,683 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static char *ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
static ngx_int_t ngx_stream_init_phases(ngx_conf_t *cf,
|
||||
ngx_stream_core_main_conf_t *cmcf);
|
||||
static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf,
|
||||
ngx_stream_core_main_conf_t *cmcf);
|
||||
static ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
|
||||
ngx_stream_listen_t *listen);
|
||||
static char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
|
||||
static ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
|
||||
ngx_stream_conf_addr_t *addr);
|
||||
#if (NGX_HAVE_INET6)
|
||||
static ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,
|
||||
ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);
|
||||
#endif
|
||||
static ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);
|
||||
|
||||
|
||||
ngx_uint_t ngx_stream_max_module;
|
||||
|
||||
|
||||
ngx_stream_filter_pt ngx_stream_top_filter;
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_commands[] = {
|
||||
|
||||
{ ngx_string("stream"),
|
||||
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
|
||||
ngx_stream_block,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_core_module_t ngx_stream_module_ctx = {
|
||||
ngx_string("stream"),
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_module_ctx, /* module context */
|
||||
ngx_stream_commands, /* module directives */
|
||||
NGX_CORE_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
|
||||
};
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
char *rv;
|
||||
ngx_uint_t i, m, mi, s;
|
||||
ngx_conf_t pcf;
|
||||
ngx_array_t ports;
|
||||
ngx_stream_listen_t *listen;
|
||||
ngx_stream_module_t *module;
|
||||
ngx_stream_conf_ctx_t *ctx;
|
||||
ngx_stream_core_srv_conf_t **cscfp;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
if (*(ngx_stream_conf_ctx_t **) conf) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
/* the main stream context */
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*(ngx_stream_conf_ctx_t **) conf = ctx;
|
||||
|
||||
/* count the number of the stream modules and set up their indices */
|
||||
|
||||
ngx_stream_max_module = ngx_count_modules(cf->cycle, NGX_STREAM_MODULE);
|
||||
|
||||
|
||||
/* the stream main_conf context, it's the same in the all stream contexts */
|
||||
|
||||
ctx->main_conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(void *) * ngx_stream_max_module);
|
||||
if (ctx->main_conf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* the stream null srv_conf context, it is used to merge
|
||||
* the server{}s' srv_conf's
|
||||
*/
|
||||
|
||||
ctx->srv_conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(void *) * ngx_stream_max_module);
|
||||
if (ctx->srv_conf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create the main_conf's and the null srv_conf's of the all stream modules
|
||||
*/
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
mi = cf->cycle->modules[m]->ctx_index;
|
||||
|
||||
if (module->create_main_conf) {
|
||||
ctx->main_conf[mi] = module->create_main_conf(cf);
|
||||
if (ctx->main_conf[mi] == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (module->create_srv_conf) {
|
||||
ctx->srv_conf[mi] = module->create_srv_conf(cf);
|
||||
if (ctx->srv_conf[mi] == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pcf = *cf;
|
||||
cf->ctx = ctx;
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
|
||||
if (module->preconfiguration) {
|
||||
if (module->preconfiguration(cf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* parse inside the stream{} block */
|
||||
|
||||
cf->module_type = NGX_STREAM_MODULE;
|
||||
cf->cmd_type = NGX_STREAM_MAIN_CONF;
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
*cf = pcf;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/* init stream{} main_conf's, merge the server{}s' srv_conf's */
|
||||
|
||||
cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];
|
||||
cscfp = cmcf->servers.elts;
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
mi = cf->cycle->modules[m]->ctx_index;
|
||||
|
||||
/* init stream{} main_conf's */
|
||||
|
||||
cf->ctx = ctx;
|
||||
|
||||
if (module->init_main_conf) {
|
||||
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
|
||||
if (rv != NGX_CONF_OK) {
|
||||
*cf = pcf;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
for (s = 0; s < cmcf->servers.nelts; s++) {
|
||||
|
||||
/* merge the server{}s' srv_conf's */
|
||||
|
||||
cf->ctx = cscfp[s]->ctx;
|
||||
|
||||
if (module->merge_srv_conf) {
|
||||
rv = module->merge_srv_conf(cf,
|
||||
ctx->srv_conf[mi],
|
||||
cscfp[s]->ctx->srv_conf[mi]);
|
||||
if (rv != NGX_CONF_OK) {
|
||||
*cf = pcf;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_stream_init_phases(cf, cmcf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
|
||||
if (module->postconfiguration) {
|
||||
if (module->postconfiguration(cf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_stream_variables_init_vars(cf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*cf = pcf;
|
||||
|
||||
if (ngx_stream_init_phase_handlers(cf, cmcf) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
listen = cmcf->listen.elts;
|
||||
|
||||
for (i = 0; i < cmcf->listen.nelts; i++) {
|
||||
if (ngx_stream_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_stream_optimize_servers(cf, &ports);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_init_phases(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf)
|
||||
{
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers,
|
||||
cf->pool, 1, sizeof(ngx_stream_handler_pt))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_init_phase_handlers(ngx_conf_t *cf,
|
||||
ngx_stream_core_main_conf_t *cmcf)
|
||||
{
|
||||
ngx_int_t j;
|
||||
ngx_uint_t i, n;
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_phase_handler_t *ph;
|
||||
ngx_stream_phase_handler_pt checker;
|
||||
|
||||
n = 1 /* content phase */;
|
||||
|
||||
for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) {
|
||||
n += cmcf->phases[i].handlers.nelts;
|
||||
}
|
||||
|
||||
ph = ngx_pcalloc(cf->pool,
|
||||
n * sizeof(ngx_stream_phase_handler_t) + sizeof(void *));
|
||||
if (ph == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cmcf->phase_engine.handlers = ph;
|
||||
n = 0;
|
||||
|
||||
for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) {
|
||||
h = cmcf->phases[i].handlers.elts;
|
||||
|
||||
switch (i) {
|
||||
|
||||
case NGX_STREAM_PREREAD_PHASE:
|
||||
checker = ngx_stream_core_preread_phase;
|
||||
break;
|
||||
|
||||
case NGX_STREAM_CONTENT_PHASE:
|
||||
ph->checker = ngx_stream_core_content_phase;
|
||||
n++;
|
||||
ph++;
|
||||
|
||||
continue;
|
||||
|
||||
default:
|
||||
checker = ngx_stream_core_generic_phase;
|
||||
}
|
||||
|
||||
n += cmcf->phases[i].handlers.nelts;
|
||||
|
||||
for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
|
||||
ph->checker = checker;
|
||||
ph->handler = h[j];
|
||||
ph->next = n;
|
||||
ph++;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
|
||||
ngx_stream_listen_t *listen)
|
||||
{
|
||||
in_port_t p;
|
||||
ngx_uint_t i;
|
||||
struct sockaddr *sa;
|
||||
ngx_stream_conf_port_t *port;
|
||||
ngx_stream_conf_addr_t *addr;
|
||||
|
||||
sa = &listen->sockaddr.sockaddr;
|
||||
p = ngx_inet_get_port(sa);
|
||||
|
||||
port = ports->elts;
|
||||
for (i = 0; i < ports->nelts; i++) {
|
||||
|
||||
if (p == port[i].port
|
||||
&& listen->type == port[i].type
|
||||
&& sa->sa_family == port[i].family)
|
||||
{
|
||||
/* a port is already in the port list */
|
||||
|
||||
port = &port[i];
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
/* add a port to the port list */
|
||||
|
||||
port = ngx_array_push(ports);
|
||||
if (port == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
port->family = sa->sa_family;
|
||||
port->type = listen->type;
|
||||
port->port = p;
|
||||
|
||||
if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
|
||||
sizeof(ngx_stream_conf_addr_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
found:
|
||||
|
||||
addr = ngx_array_push(&port->addrs);
|
||||
if (addr == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
addr->opt = *listen;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
|
||||
{
|
||||
ngx_uint_t i, p, last, bind_wildcard;
|
||||
ngx_listening_t *ls;
|
||||
ngx_stream_port_t *stport;
|
||||
ngx_stream_conf_port_t *port;
|
||||
ngx_stream_conf_addr_t *addr;
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
port = ports->elts;
|
||||
for (p = 0; p < ports->nelts; p++) {
|
||||
|
||||
ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
|
||||
sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);
|
||||
|
||||
addr = port[p].addrs.elts;
|
||||
last = port[p].addrs.nelts;
|
||||
|
||||
/*
|
||||
* if there is the binding to the "*:port" then we need to bind()
|
||||
* to the "*:port" only and ignore the other bindings
|
||||
*/
|
||||
|
||||
if (addr[last - 1].opt.wildcard) {
|
||||
addr[last - 1].opt.bind = 1;
|
||||
bind_wildcard = 1;
|
||||
|
||||
} else {
|
||||
bind_wildcard = 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < last) {
|
||||
|
||||
if (bind_wildcard && !addr[i].opt.bind) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
ls = ngx_create_listening(cf, &addr[i].opt.sockaddr.sockaddr,
|
||||
addr[i].opt.socklen);
|
||||
if (ls == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ls->addr_ntop = 1;
|
||||
ls->handler = ngx_stream_init_connection;
|
||||
ls->pool_size = 256;
|
||||
ls->type = addr[i].opt.type;
|
||||
|
||||
cscf = addr->opt.ctx->srv_conf[ngx_stream_core_module.ctx_index];
|
||||
|
||||
ls->logp = cscf->error_log;
|
||||
ls->log.data = &ls->addr_text;
|
||||
ls->log.handler = ngx_accept_log_error;
|
||||
|
||||
ls->backlog = addr[i].opt.backlog;
|
||||
|
||||
ls->wildcard = addr[i].opt.wildcard;
|
||||
|
||||
ls->keepalive = addr[i].opt.so_keepalive;
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
ls->keepidle = addr[i].opt.tcp_keepidle;
|
||||
ls->keepintvl = addr[i].opt.tcp_keepintvl;
|
||||
ls->keepcnt = addr[i].opt.tcp_keepcnt;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
ls->ipv6only = addr[i].opt.ipv6only;
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_REUSEPORT)
|
||||
ls->reuseport = addr[i].opt.reuseport;
|
||||
#endif
|
||||
|
||||
stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
|
||||
if (stport == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ls->servers = stport;
|
||||
|
||||
stport->naddrs = i + 1;
|
||||
|
||||
switch (ls->sockaddr->sa_family) {
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default: /* AF_INET */
|
||||
if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ngx_clone_listening(cf, ls) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
addr++;
|
||||
last--;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,
|
||||
ngx_stream_conf_addr_t *addr)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
struct sockaddr_in *sin;
|
||||
ngx_stream_in_addr_t *addrs;
|
||||
u_char buf[NGX_SOCKADDR_STRLEN];
|
||||
|
||||
stport->addrs = ngx_pcalloc(cf->pool,
|
||||
stport->naddrs * sizeof(ngx_stream_in_addr_t));
|
||||
if (stport->addrs == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
addrs = stport->addrs;
|
||||
|
||||
for (i = 0; i < stport->naddrs; i++) {
|
||||
|
||||
sin = &addr[i].opt.sockaddr.sockaddr_in;
|
||||
addrs[i].addr = sin->sin_addr.s_addr;
|
||||
|
||||
addrs[i].conf.ctx = addr[i].opt.ctx;
|
||||
#if (NGX_STREAM_SSL)
|
||||
addrs[i].conf.ssl = addr[i].opt.ssl;
|
||||
#endif
|
||||
addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
|
||||
|
||||
len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen,
|
||||
buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
p = ngx_pnalloc(cf->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(p, buf, len);
|
||||
|
||||
addrs[i].conf.addr_text.len = len;
|
||||
addrs[i].conf.addr_text.data = p;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
|
||||
ngx_stream_conf_addr_t *addr)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
struct sockaddr_in6 *sin6;
|
||||
ngx_stream_in6_addr_t *addrs6;
|
||||
u_char buf[NGX_SOCKADDR_STRLEN];
|
||||
|
||||
stport->addrs = ngx_pcalloc(cf->pool,
|
||||
stport->naddrs * sizeof(ngx_stream_in6_addr_t));
|
||||
if (stport->addrs == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
addrs6 = stport->addrs;
|
||||
|
||||
for (i = 0; i < stport->naddrs; i++) {
|
||||
|
||||
sin6 = &addr[i].opt.sockaddr.sockaddr_in6;
|
||||
addrs6[i].addr6 = sin6->sin6_addr;
|
||||
|
||||
addrs6[i].conf.ctx = addr[i].opt.ctx;
|
||||
#if (NGX_STREAM_SSL)
|
||||
addrs6[i].conf.ssl = addr[i].opt.ssl;
|
||||
#endif
|
||||
addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
|
||||
|
||||
len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen,
|
||||
buf, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
p = ngx_pnalloc(cf->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(p, buf, len);
|
||||
|
||||
addrs6[i].conf.addr_text.len = len;
|
||||
addrs6[i].conf.addr_text.data = p;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_cmp_conf_addrs(const void *one, const void *two)
|
||||
{
|
||||
ngx_stream_conf_addr_t *first, *second;
|
||||
|
||||
first = (ngx_stream_conf_addr_t *) one;
|
||||
second = (ngx_stream_conf_addr_t *) two;
|
||||
|
||||
if (first->opt.wildcard) {
|
||||
/* a wildcard must be the last resort, shift it to the end */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (second->opt.wildcard) {
|
||||
/* a wildcard must be the last resort, shift it to the end */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first->opt.bind && !second->opt.bind) {
|
||||
/* shift explicit bind()ed addresses to the start */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!first->opt.bind && second->opt.bind) {
|
||||
/* shift explicit bind()ed addresses to the start */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* do not sort by default */
|
||||
|
||||
return 0;
|
||||
}
|
||||
303
src/stream/ngx_stream.h
Normal file
303
src/stream/ngx_stream.h
Normal file
@@ -0,0 +1,303 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_H_INCLUDED_
|
||||
#define _NGX_STREAM_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
#include <ngx_stream_ssl_module.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct ngx_stream_session_s ngx_stream_session_t;
|
||||
|
||||
|
||||
#include <ngx_stream_variables.h>
|
||||
#include <ngx_stream_script.h>
|
||||
#include <ngx_stream_upstream.h>
|
||||
#include <ngx_stream_upstream_round_robin.h>
|
||||
|
||||
|
||||
#define NGX_STREAM_OK 200
|
||||
#define NGX_STREAM_BAD_REQUEST 400
|
||||
#define NGX_STREAM_FORBIDDEN 403
|
||||
#define NGX_STREAM_INTERNAL_SERVER_ERROR 500
|
||||
#define NGX_STREAM_BAD_GATEWAY 502
|
||||
#define NGX_STREAM_SERVICE_UNAVAILABLE 503
|
||||
|
||||
|
||||
typedef struct {
|
||||
void **main_conf;
|
||||
void **srv_conf;
|
||||
} ngx_stream_conf_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_sockaddr_t sockaddr;
|
||||
socklen_t socklen;
|
||||
|
||||
/* server ctx */
|
||||
ngx_stream_conf_ctx_t *ctx;
|
||||
|
||||
unsigned bind:1;
|
||||
unsigned wildcard:1;
|
||||
unsigned ssl:1;
|
||||
#if (NGX_HAVE_INET6)
|
||||
unsigned ipv6only:1;
|
||||
#endif
|
||||
unsigned reuseport:1;
|
||||
unsigned so_keepalive:2;
|
||||
unsigned proxy_protocol:1;
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
int tcp_keepidle;
|
||||
int tcp_keepintvl;
|
||||
int tcp_keepcnt;
|
||||
#endif
|
||||
int backlog;
|
||||
int type;
|
||||
} ngx_stream_listen_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_conf_ctx_t *ctx;
|
||||
ngx_str_t addr_text;
|
||||
unsigned ssl:1;
|
||||
unsigned proxy_protocol:1;
|
||||
} ngx_stream_addr_conf_t;
|
||||
|
||||
typedef struct {
|
||||
in_addr_t addr;
|
||||
ngx_stream_addr_conf_t conf;
|
||||
} ngx_stream_in_addr_t;
|
||||
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
typedef struct {
|
||||
struct in6_addr addr6;
|
||||
ngx_stream_addr_conf_t conf;
|
||||
} ngx_stream_in6_addr_t;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */
|
||||
void *addrs;
|
||||
ngx_uint_t naddrs;
|
||||
} ngx_stream_port_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int family;
|
||||
int type;
|
||||
in_port_t port;
|
||||
ngx_array_t addrs; /* array of ngx_stream_conf_addr_t */
|
||||
} ngx_stream_conf_port_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_listen_t opt;
|
||||
} ngx_stream_conf_addr_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
NGX_STREAM_POST_ACCEPT_PHASE = 0,
|
||||
NGX_STREAM_PREACCESS_PHASE,
|
||||
NGX_STREAM_ACCESS_PHASE,
|
||||
NGX_STREAM_SSL_PHASE,
|
||||
NGX_STREAM_PREREAD_PHASE,
|
||||
NGX_STREAM_CONTENT_PHASE,
|
||||
NGX_STREAM_LOG_PHASE
|
||||
} ngx_stream_phases;
|
||||
|
||||
|
||||
typedef struct ngx_stream_phase_handler_s ngx_stream_phase_handler_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_stream_phase_handler_pt)(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph);
|
||||
typedef ngx_int_t (*ngx_stream_handler_pt)(ngx_stream_session_t *s);
|
||||
typedef void (*ngx_stream_content_handler_pt)(ngx_stream_session_t *s);
|
||||
|
||||
|
||||
struct ngx_stream_phase_handler_s {
|
||||
ngx_stream_phase_handler_pt checker;
|
||||
ngx_stream_handler_pt handler;
|
||||
ngx_uint_t next;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_phase_handler_t *handlers;
|
||||
} ngx_stream_phase_engine_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t handlers;
|
||||
} ngx_stream_phase_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t servers; /* ngx_stream_core_srv_conf_t */
|
||||
ngx_array_t listen; /* ngx_stream_listen_t */
|
||||
|
||||
ngx_stream_phase_engine_t phase_engine;
|
||||
|
||||
ngx_hash_t variables_hash;
|
||||
|
||||
ngx_array_t variables; /* ngx_stream_variable_t */
|
||||
ngx_uint_t ncaptures;
|
||||
|
||||
ngx_uint_t variables_hash_max_size;
|
||||
ngx_uint_t variables_hash_bucket_size;
|
||||
|
||||
ngx_hash_keys_arrays_t *variables_keys;
|
||||
|
||||
ngx_stream_phase_t phases[NGX_STREAM_LOG_PHASE + 1];
|
||||
} ngx_stream_core_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_content_handler_pt handler;
|
||||
|
||||
ngx_stream_conf_ctx_t *ctx;
|
||||
|
||||
u_char *file_name;
|
||||
ngx_uint_t line;
|
||||
|
||||
ngx_flag_t tcp_nodelay;
|
||||
size_t preread_buffer_size;
|
||||
ngx_msec_t preread_timeout;
|
||||
|
||||
ngx_log_t *error_log;
|
||||
|
||||
ngx_msec_t resolver_timeout;
|
||||
ngx_resolver_t *resolver;
|
||||
|
||||
ngx_msec_t proxy_protocol_timeout;
|
||||
|
||||
ngx_uint_t listen; /* unsigned listen:1; */
|
||||
} ngx_stream_core_srv_conf_t;
|
||||
|
||||
|
||||
struct ngx_stream_session_s {
|
||||
uint32_t signature; /* "STRM" */
|
||||
|
||||
ngx_connection_t *connection;
|
||||
|
||||
off_t received;
|
||||
time_t start_sec;
|
||||
ngx_msec_t start_msec;
|
||||
|
||||
ngx_log_handler_pt log_handler;
|
||||
|
||||
void **ctx;
|
||||
void **main_conf;
|
||||
void **srv_conf;
|
||||
|
||||
ngx_stream_upstream_t *upstream;
|
||||
ngx_array_t *upstream_states;
|
||||
/* of ngx_stream_upstream_state_t */
|
||||
ngx_stream_variable_value_t *variables;
|
||||
|
||||
#if (NGX_PCRE)
|
||||
ngx_uint_t ncaptures;
|
||||
int *captures;
|
||||
u_char *captures_data;
|
||||
#endif
|
||||
|
||||
ngx_int_t phase_handler;
|
||||
ngx_uint_t status;
|
||||
|
||||
unsigned ssl:1;
|
||||
|
||||
unsigned stat_processing:1;
|
||||
|
||||
unsigned health_check:1;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
|
||||
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
|
||||
|
||||
void *(*create_main_conf)(ngx_conf_t *cf);
|
||||
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
|
||||
|
||||
void *(*create_srv_conf)(ngx_conf_t *cf);
|
||||
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
|
||||
void *conf);
|
||||
} ngx_stream_module_t;
|
||||
|
||||
|
||||
#define NGX_STREAM_MODULE 0x4d525453 /* "STRM" */
|
||||
|
||||
#define NGX_STREAM_MAIN_CONF 0x02000000
|
||||
#define NGX_STREAM_SRV_CONF 0x04000000
|
||||
#define NGX_STREAM_UPS_CONF 0x08000000
|
||||
|
||||
|
||||
#define NGX_STREAM_MAIN_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, main_conf)
|
||||
#define NGX_STREAM_SRV_CONF_OFFSET offsetof(ngx_stream_conf_ctx_t, srv_conf)
|
||||
|
||||
|
||||
#define ngx_stream_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
|
||||
#define ngx_stream_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
|
||||
#define ngx_stream_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
|
||||
|
||||
|
||||
#define ngx_stream_get_module_main_conf(s, module) \
|
||||
(s)->main_conf[module.ctx_index]
|
||||
#define ngx_stream_get_module_srv_conf(s, module) \
|
||||
(s)->srv_conf[module.ctx_index]
|
||||
|
||||
#define ngx_stream_conf_get_module_main_conf(cf, module) \
|
||||
((ngx_stream_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
|
||||
#define ngx_stream_conf_get_module_srv_conf(cf, module) \
|
||||
((ngx_stream_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
|
||||
|
||||
#define ngx_stream_cycle_get_module_main_conf(cycle, module) \
|
||||
(cycle->conf_ctx[ngx_stream_module.index] ? \
|
||||
((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index]) \
|
||||
->main_conf[module.ctx_index]: \
|
||||
NULL)
|
||||
|
||||
|
||||
#define NGX_STREAM_WRITE_BUFFERED 0x10
|
||||
|
||||
|
||||
void ngx_stream_core_run_phases(ngx_stream_session_t *s);
|
||||
ngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph);
|
||||
ngx_int_t ngx_stream_core_preread_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph);
|
||||
ngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph);
|
||||
|
||||
|
||||
void ngx_stream_init_connection(ngx_connection_t *c);
|
||||
void ngx_stream_session_handler(ngx_event_t *rev);
|
||||
void ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc);
|
||||
|
||||
|
||||
extern ngx_module_t ngx_stream_module;
|
||||
extern ngx_uint_t ngx_stream_max_module;
|
||||
extern ngx_module_t ngx_stream_core_module;
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_stream_filter_pt)(ngx_stream_session_t *s,
|
||||
ngx_chain_t *chain, ngx_uint_t from_upstream);
|
||||
|
||||
|
||||
extern ngx_stream_filter_pt ngx_stream_top_filter;
|
||||
|
||||
|
||||
#endif /* _NGX_STREAM_H_INCLUDED_ */
|
||||
459
src/stream/ngx_stream_access_module.c
Normal file
459
src/stream/ngx_stream_access_module.c
Normal file
@@ -0,0 +1,459 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
in_addr_t mask;
|
||||
in_addr_t addr;
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_stream_access_rule_t;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
typedef struct {
|
||||
struct in6_addr addr;
|
||||
struct in6_addr mask;
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_stream_access_rule6_t;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t deny; /* unsigned deny:1; */
|
||||
} ngx_stream_access_rule_un_t;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *rules; /* array of ngx_stream_access_rule_t */
|
||||
#if (NGX_HAVE_INET6)
|
||||
ngx_array_t *rules6; /* array of ngx_stream_access_rule6_t */
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
ngx_array_t *rules_un; /* array of ngx_stream_access_rule_un_t */
|
||||
#endif
|
||||
} ngx_stream_access_srv_conf_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_access_handler(ngx_stream_session_t *s);
|
||||
static ngx_int_t ngx_stream_access_inet(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf, in_addr_t addr);
|
||||
#if (NGX_HAVE_INET6)
|
||||
static ngx_int_t ngx_stream_access_inet6(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf, u_char *p);
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
static ngx_int_t ngx_stream_access_unix(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf);
|
||||
#endif
|
||||
static ngx_int_t ngx_stream_access_found(ngx_stream_session_t *s,
|
||||
ngx_uint_t deny);
|
||||
static char *ngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void *ngx_stream_access_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_access_merge_srv_conf(ngx_conf_t *cf,
|
||||
void *parent, void *child);
|
||||
static ngx_int_t ngx_stream_access_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_access_commands[] = {
|
||||
|
||||
{ ngx_string("allow"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_stream_access_rule,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("deny"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_stream_access_rule,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_access_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_stream_access_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_access_create_srv_conf, /* create server configuration */
|
||||
ngx_stream_access_merge_srv_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_access_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_access_module_ctx, /* module context */
|
||||
ngx_stream_access_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
ngx_stream_access_srv_conf_t *ascf;
|
||||
#if (NGX_HAVE_INET6)
|
||||
u_char *p;
|
||||
in_addr_t addr;
|
||||
struct sockaddr_in6 *sin6;
|
||||
#endif
|
||||
|
||||
ascf = ngx_stream_get_module_srv_conf(s, ngx_stream_access_module);
|
||||
|
||||
switch (s->connection->sockaddr->sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
if (ascf->rules) {
|
||||
sin = (struct sockaddr_in *) s->connection->sockaddr;
|
||||
return ngx_stream_access_inet(s, ascf, sin->sin_addr.s_addr);
|
||||
}
|
||||
break;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) s->connection->sockaddr;
|
||||
p = sin6->sin6_addr.s6_addr;
|
||||
|
||||
if (ascf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
|
||||
addr = p[12] << 24;
|
||||
addr += p[13] << 16;
|
||||
addr += p[14] << 8;
|
||||
addr += p[15];
|
||||
return ngx_stream_access_inet(s, ascf, htonl(addr));
|
||||
}
|
||||
|
||||
if (ascf->rules6) {
|
||||
return ngx_stream_access_inet6(s, ascf, p);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
case AF_UNIX:
|
||||
if (ascf->rules_un) {
|
||||
return ngx_stream_access_unix(s, ascf);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_inet(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf, in_addr_t addr)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_stream_access_rule_t *rule;
|
||||
|
||||
rule = ascf->rules->elts;
|
||||
for (i = 0; i < ascf->rules->nelts; i++) {
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"access: %08XD %08XD %08XD",
|
||||
addr, rule[i].mask, rule[i].addr);
|
||||
|
||||
if ((addr & rule[i].mask) == rule[i].addr) {
|
||||
return ngx_stream_access_found(s, rule[i].deny);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_inet6(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf, u_char *p)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
ngx_uint_t i;
|
||||
ngx_stream_access_rule6_t *rule6;
|
||||
|
||||
rule6 = ascf->rules6->elts;
|
||||
for (i = 0; i < ascf->rules6->nelts; i++) {
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
{
|
||||
size_t cl, ml, al;
|
||||
u_char ct[NGX_INET6_ADDRSTRLEN];
|
||||
u_char mt[NGX_INET6_ADDRSTRLEN];
|
||||
u_char at[NGX_INET6_ADDRSTRLEN];
|
||||
|
||||
cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);
|
||||
ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);
|
||||
al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);
|
||||
|
||||
ngx_log_debug6(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"access: %*s %*s %*s", cl, ct, ml, mt, al, at);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (n = 0; n < 16; n++) {
|
||||
if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_stream_access_found(s, rule6[i].deny);
|
||||
|
||||
next:
|
||||
continue;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_unix(ngx_stream_session_t *s,
|
||||
ngx_stream_access_srv_conf_t *ascf)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_stream_access_rule_un_t *rule_un;
|
||||
|
||||
rule_un = ascf->rules_un->elts;
|
||||
for (i = 0; i < ascf->rules_un->nelts; i++) {
|
||||
|
||||
/* TODO: check path */
|
||||
if (1) {
|
||||
return ngx_stream_access_found(s, rule_un[i].deny);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_found(ngx_stream_session_t *s, ngx_uint_t deny)
|
||||
{
|
||||
if (deny) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"access forbidden by rule");
|
||||
return NGX_STREAM_FORBIDDEN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_access_srv_conf_t *ascf = conf;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t all;
|
||||
ngx_str_t *value;
|
||||
ngx_cidr_t cidr;
|
||||
ngx_stream_access_rule_t *rule;
|
||||
#if (NGX_HAVE_INET6)
|
||||
ngx_stream_access_rule6_t *rule6;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
ngx_stream_access_rule_un_t *rule_un;
|
||||
#endif
|
||||
|
||||
ngx_memzero(&cidr, sizeof(ngx_cidr_t));
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);
|
||||
|
||||
if (!all) {
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) {
|
||||
cidr.family = AF_UNIX;
|
||||
rc = NGX_OK;
|
||||
|
||||
} else {
|
||||
rc = ngx_ptocidr(&value[1], &cidr);
|
||||
}
|
||||
|
||||
#else
|
||||
rc = ngx_ptocidr(&value[1], &cidr);
|
||||
#endif
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"low address bits of %V are meaningless", &value[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (cidr.family == AF_INET || all) {
|
||||
|
||||
if (ascf->rules == NULL) {
|
||||
ascf->rules = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_stream_access_rule_t));
|
||||
if (ascf->rules == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule = ngx_array_push(ascf->rules);
|
||||
if (rule == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule->mask = cidr.u.in.mask;
|
||||
rule->addr = cidr.u.in.addr;
|
||||
rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
if (cidr.family == AF_INET6 || all) {
|
||||
|
||||
if (ascf->rules6 == NULL) {
|
||||
ascf->rules6 = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_stream_access_rule6_t));
|
||||
if (ascf->rules6 == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule6 = ngx_array_push(ascf->rules6);
|
||||
if (rule6 == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule6->mask = cidr.u.in6.mask;
|
||||
rule6->addr = cidr.u.in6.addr;
|
||||
rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
if (cidr.family == AF_UNIX || all) {
|
||||
|
||||
if (ascf->rules_un == NULL) {
|
||||
ascf->rules_un = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_stream_access_rule_un_t));
|
||||
if (ascf->rules_un == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
rule_un = ngx_array_push(ascf->rules_un);
|
||||
if (rule_un == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_access_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_access_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_access_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_access_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_access_srv_conf_t *prev = parent;
|
||||
ngx_stream_access_srv_conf_t *conf = child;
|
||||
|
||||
if (conf->rules == NULL
|
||||
#if (NGX_HAVE_INET6)
|
||||
&& conf->rules6 == NULL
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
&& conf->rules_un == NULL
|
||||
#endif
|
||||
) {
|
||||
conf->rules = prev->rules;
|
||||
#if (NGX_HAVE_INET6)
|
||||
conf->rules6 = prev->rules6;
|
||||
#endif
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
conf->rules_un = prev->rules_un;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_access_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_stream_access_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
888
src/stream/ngx_stream_core_module.c
Normal file
888
src/stream/ngx_stream_core_module.c
Normal file
@@ -0,0 +1,888 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf);
|
||||
static void *ngx_stream_core_create_main_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf);
|
||||
static void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_core_commands[] = {
|
||||
|
||||
{ ngx_string("variables_hash_max_size"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_main_conf_t, variables_hash_max_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("variables_hash_bucket_size"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("server"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
|
||||
ngx_stream_core_server,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("listen"),
|
||||
NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_stream_core_listen,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("error_log"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_stream_core_error_log,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("resolver"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_stream_core_resolver,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("resolver_timeout"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_srv_conf_t, resolver_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("proxy_protocol_timeout"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_srv_conf_t, proxy_protocol_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("tcp_nodelay"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_srv_conf_t, tcp_nodelay),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("preread_buffer_size"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_srv_conf_t, preread_buffer_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("preread_timeout"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_core_srv_conf_t, preread_timeout),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_core_module_ctx = {
|
||||
ngx_stream_core_preconfiguration, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_stream_core_create_main_conf, /* create main configuration */
|
||||
ngx_stream_core_init_main_conf, /* init main configuration */
|
||||
|
||||
ngx_stream_core_create_srv_conf, /* create server configuration */
|
||||
ngx_stream_core_merge_srv_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_core_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_core_module_ctx, /* module context */
|
||||
ngx_stream_core_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_core_run_phases(ngx_stream_session_t *s)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_stream_phase_handler_t *ph;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
||||
|
||||
ph = cmcf->phase_engine.handlers;
|
||||
|
||||
while (ph[s->phase_handler].checker) {
|
||||
|
||||
rc = ph[s->phase_handler].checker(s, &ph[s->phase_handler]);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_core_generic_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
|
||||
/*
|
||||
* generic phase checker,
|
||||
* used by all phases, except for preread and content
|
||||
*/
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"generic phase: %ui", s->phase_handler);
|
||||
|
||||
rc = ph->handler(s);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
s->phase_handler = ph->next;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
s->phase_handler++;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN || rc == NGX_DONE) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
rc = NGX_STREAM_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_stream_finalize_session(s, rc);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_core_preread_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph)
|
||||
{
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
ngx_int_t rc;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
c->log->action = "prereading client data";
|
||||
|
||||
cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
|
||||
|
||||
if (c->read->timedout) {
|
||||
rc = NGX_STREAM_OK;
|
||||
|
||||
} else if (c->read->timer_set) {
|
||||
rc = NGX_AGAIN;
|
||||
|
||||
} else {
|
||||
rc = ph->handler(s);
|
||||
}
|
||||
|
||||
while (rc == NGX_AGAIN) {
|
||||
|
||||
if (c->buffer == NULL) {
|
||||
c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size);
|
||||
if (c->buffer == NULL) {
|
||||
rc = NGX_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size = c->buffer->end - c->buffer->last;
|
||||
|
||||
if (size == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, c->log, 0, "preread buffer full");
|
||||
rc = NGX_STREAM_BAD_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c->read->eof) {
|
||||
rc = NGX_STREAM_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c->read->ready) {
|
||||
if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
||||
rc = NGX_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c->read->timer_set) {
|
||||
ngx_add_timer(c->read, cscf->preread_timeout);
|
||||
}
|
||||
|
||||
c->read->handler = ngx_stream_session_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
n = c->recv(c, c->buffer->last, size);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
rc = NGX_STREAM_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
c->buffer->last += n;
|
||||
}
|
||||
|
||||
rc = ph->handler(s);
|
||||
}
|
||||
|
||||
if (c->read->timer_set) {
|
||||
ngx_del_timer(c->read);
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
s->phase_handler = ph->next;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
s->phase_handler++;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
rc = NGX_STREAM_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_stream_finalize_session(s, rc);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_core_content_phase(ngx_stream_session_t *s,
|
||||
ngx_stream_phase_handler_t *ph)
|
||||
{
|
||||
int tcp_nodelay;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
c->log->action = NULL;
|
||||
|
||||
cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
|
||||
|
||||
if (c->type == SOCK_STREAM
|
||||
&& cscf->tcp_nodelay
|
||||
&& c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "tcp_nodelay");
|
||||
|
||||
tcp_nodelay = 1;
|
||||
|
||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
|
||||
(const void *) &tcp_nodelay, sizeof(int)) == -1)
|
||||
{
|
||||
ngx_connection_error(c, ngx_socket_errno,
|
||||
"setsockopt(TCP_NODELAY) failed");
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
c->tcp_nodelay = NGX_TCP_NODELAY_SET;
|
||||
}
|
||||
|
||||
cscf->handler(s);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_core_preconfiguration(ngx_conf_t *cf)
|
||||
{
|
||||
return ngx_stream_variables_add_core_vars(cf);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_core_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t));
|
||||
if (cmcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->servers, cf->pool, 4,
|
||||
sizeof(ngx_stream_core_srv_conf_t *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;
|
||||
cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return cmcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf)
|
||||
{
|
||||
ngx_stream_core_main_conf_t *cmcf = conf;
|
||||
|
||||
ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
|
||||
ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
|
||||
|
||||
cmcf->variables_hash_bucket_size =
|
||||
ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);
|
||||
|
||||
if (cmcf->ncaptures) {
|
||||
cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_core_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t));
|
||||
if (cscf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* cscf->handler = NULL;
|
||||
* cscf->error_log = NULL;
|
||||
*/
|
||||
|
||||
cscf->file_name = cf->conf_file->file.name.data;
|
||||
cscf->line = cf->conf_file->line;
|
||||
cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
|
||||
cscf->proxy_protocol_timeout = NGX_CONF_UNSET_MSEC;
|
||||
cscf->tcp_nodelay = NGX_CONF_UNSET;
|
||||
cscf->preread_buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
cscf->preread_timeout = NGX_CONF_UNSET_MSEC;
|
||||
|
||||
return cscf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *prev = parent;
|
||||
ngx_stream_core_srv_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_msec_value(conf->resolver_timeout,
|
||||
prev->resolver_timeout, 30000);
|
||||
|
||||
if (conf->resolver == NULL) {
|
||||
|
||||
if (prev->resolver == NULL) {
|
||||
|
||||
/*
|
||||
* create dummy resolver in stream {} context
|
||||
* to inherit it in all servers
|
||||
*/
|
||||
|
||||
prev->resolver = ngx_resolver_create(cf, NULL, 0);
|
||||
if (prev->resolver == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
conf->resolver = prev->resolver;
|
||||
}
|
||||
|
||||
if (conf->handler == NULL) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no handler for server in %s:%ui",
|
||||
conf->file_name, conf->line);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->error_log == NULL) {
|
||||
if (prev->error_log) {
|
||||
conf->error_log = prev->error_log;
|
||||
} else {
|
||||
conf->error_log = &cf->cycle->new_log;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_conf_merge_msec_value(conf->proxy_protocol_timeout,
|
||||
prev->proxy_protocol_timeout, 30000);
|
||||
|
||||
ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);
|
||||
|
||||
ngx_conf_merge_size_value(conf->preread_buffer_size,
|
||||
prev->preread_buffer_size, 16384);
|
||||
|
||||
ngx_conf_merge_msec_value(conf->preread_timeout,
|
||||
prev->preread_timeout, 30000);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *cscf = conf;
|
||||
|
||||
return ngx_log_set_log(cf, &cscf->error_log);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
char *rv;
|
||||
void *mconf;
|
||||
ngx_uint_t m;
|
||||
ngx_conf_t pcf;
|
||||
ngx_stream_module_t *module;
|
||||
ngx_stream_conf_ctx_t *ctx, *stream_ctx;
|
||||
ngx_stream_core_srv_conf_t *cscf, **cscfp;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
stream_ctx = cf->ctx;
|
||||
ctx->main_conf = stream_ctx->main_conf;
|
||||
|
||||
/* the server{}'s srv_conf */
|
||||
|
||||
ctx->srv_conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(void *) * ngx_stream_max_module);
|
||||
if (ctx->srv_conf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
|
||||
if (module->create_srv_conf) {
|
||||
mconf = module->create_srv_conf(cf);
|
||||
if (mconf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
|
||||
}
|
||||
}
|
||||
|
||||
/* the server configuration context */
|
||||
|
||||
cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index];
|
||||
cscf->ctx = ctx;
|
||||
|
||||
cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];
|
||||
|
||||
cscfp = ngx_array_push(&cmcf->servers);
|
||||
if (cscfp == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*cscfp = cscf;
|
||||
|
||||
|
||||
/* parse inside server{} */
|
||||
|
||||
pcf = *cf;
|
||||
cf->ctx = ctx;
|
||||
cf->cmd_type = NGX_STREAM_SRV_CONF;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = pcf;
|
||||
|
||||
if (rv == NGX_CONF_OK && !cscf->listen) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no \"listen\" is defined for server in %s:%ui",
|
||||
cscf->file_name, cscf->line);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *cscf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_url_t u;
|
||||
ngx_uint_t i, backlog;
|
||||
ngx_stream_listen_t *ls, *als;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cscf->listen = 1;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
u.url = value[1];
|
||||
u.listen = 1;
|
||||
|
||||
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
|
||||
if (u.err) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%s in \"%V\" of the \"listen\" directive",
|
||||
u.err, &u.url);
|
||||
}
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
ls = ngx_array_push(&cmcf->listen);
|
||||
if (ls == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(ls, sizeof(ngx_stream_listen_t));
|
||||
|
||||
ngx_memcpy(&ls->sockaddr.sockaddr, &u.sockaddr, u.socklen);
|
||||
|
||||
ls->socklen = u.socklen;
|
||||
ls->backlog = NGX_LISTEN_BACKLOG;
|
||||
ls->type = SOCK_STREAM;
|
||||
ls->wildcard = u.wildcard;
|
||||
ls->ctx = cf->ctx;
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
ls->ipv6only = 1;
|
||||
#endif
|
||||
|
||||
backlog = 0;
|
||||
|
||||
for (i = 2; i < cf->args->nelts; i++) {
|
||||
|
||||
#if !(NGX_WIN32)
|
||||
if (ngx_strcmp(value[i].data, "udp") == 0) {
|
||||
ls->type = SOCK_DGRAM;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ngx_strcmp(value[i].data, "bind") == 0) {
|
||||
ls->bind = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "backlog=", 8) == 0) {
|
||||
ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);
|
||||
ls->bind = 1;
|
||||
|
||||
if (ls->backlog == NGX_ERROR || ls->backlog == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid backlog \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
backlog = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
|
||||
#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
|
||||
size_t len;
|
||||
u_char buf[NGX_SOCKADDR_STRLEN];
|
||||
|
||||
if (ls->sockaddr.sockaddr.sa_family == AF_INET6) {
|
||||
|
||||
if (ngx_strcmp(&value[i].data[10], "n") == 0) {
|
||||
ls->ipv6only = 1;
|
||||
|
||||
} else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
|
||||
ls->ipv6only = 0;
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid ipv6only flags \"%s\"",
|
||||
&value[i].data[9]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ls->bind = 1;
|
||||
|
||||
} else {
|
||||
len = ngx_sock_ntop(&ls->sockaddr.sockaddr, ls->socklen, buf,
|
||||
NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"ipv6only is not supported "
|
||||
"on addr \"%*s\", ignored", len, buf);
|
||||
}
|
||||
|
||||
continue;
|
||||
#else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"bind ipv6only is not supported "
|
||||
"on this platform");
|
||||
return NGX_CONF_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "reuseport") == 0) {
|
||||
#if (NGX_HAVE_REUSEPORT)
|
||||
ls->reuseport = 1;
|
||||
ls->bind = 1;
|
||||
#else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"reuseport is not supported "
|
||||
"on this platform, ignored");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "ssl") == 0) {
|
||||
#if (NGX_STREAM_SSL)
|
||||
ls->ssl = 1;
|
||||
continue;
|
||||
#else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the \"ssl\" parameter requires "
|
||||
"ngx_stream_ssl_module");
|
||||
return NGX_CONF_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
|
||||
|
||||
if (ngx_strcmp(&value[i].data[13], "on") == 0) {
|
||||
ls->so_keepalive = 1;
|
||||
|
||||
} else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
|
||||
ls->so_keepalive = 2;
|
||||
|
||||
} else {
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
u_char *p, *end;
|
||||
ngx_str_t s;
|
||||
|
||||
end = value[i].data + value[i].len;
|
||||
s.data = value[i].data + 13;
|
||||
|
||||
p = ngx_strlchr(s.data, end, ':');
|
||||
if (p == NULL) {
|
||||
p = end;
|
||||
}
|
||||
|
||||
if (p > s.data) {
|
||||
s.len = p - s.data;
|
||||
|
||||
ls->tcp_keepidle = ngx_parse_time(&s, 1);
|
||||
if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
|
||||
goto invalid_so_keepalive;
|
||||
}
|
||||
}
|
||||
|
||||
s.data = (p < end) ? (p + 1) : end;
|
||||
|
||||
p = ngx_strlchr(s.data, end, ':');
|
||||
if (p == NULL) {
|
||||
p = end;
|
||||
}
|
||||
|
||||
if (p > s.data) {
|
||||
s.len = p - s.data;
|
||||
|
||||
ls->tcp_keepintvl = ngx_parse_time(&s, 1);
|
||||
if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
|
||||
goto invalid_so_keepalive;
|
||||
}
|
||||
}
|
||||
|
||||
s.data = (p < end) ? (p + 1) : end;
|
||||
|
||||
if (s.data < end) {
|
||||
s.len = end - s.data;
|
||||
|
||||
ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
|
||||
if (ls->tcp_keepcnt == NGX_ERROR) {
|
||||
goto invalid_so_keepalive;
|
||||
}
|
||||
}
|
||||
|
||||
if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
|
||||
&& ls->tcp_keepcnt == 0)
|
||||
{
|
||||
goto invalid_so_keepalive;
|
||||
}
|
||||
|
||||
ls->so_keepalive = 1;
|
||||
|
||||
#else
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the \"so_keepalive\" parameter accepts "
|
||||
"only \"on\" or \"off\" on this platform");
|
||||
return NGX_CONF_ERROR;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
ls->bind = 1;
|
||||
|
||||
continue;
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
invalid_so_keepalive:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid so_keepalive value: \"%s\"",
|
||||
&value[i].data[13]);
|
||||
return NGX_CONF_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
|
||||
ls->proxy_protocol = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"the invalid \"%V\" parameter", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ls->type == SOCK_DGRAM) {
|
||||
if (backlog) {
|
||||
return "\"backlog\" parameter is incompatible with \"udp\"";
|
||||
}
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
if (ls->ssl) {
|
||||
return "\"ssl\" parameter is incompatible with \"udp\"";
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ls->so_keepalive) {
|
||||
return "\"so_keepalive\" parameter is incompatible with \"udp\"";
|
||||
}
|
||||
|
||||
if (ls->proxy_protocol) {
|
||||
return "\"proxy_protocol\" parameter is incompatible with \"udp\"";
|
||||
}
|
||||
}
|
||||
|
||||
als = cmcf->listen.elts;
|
||||
|
||||
for (i = 0; i < cmcf->listen.nelts - 1; i++) {
|
||||
if (ls->type != als[i].type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_cmp_sockaddr(&als[i].sockaddr.sockaddr, als[i].socklen,
|
||||
&ls->sockaddr.sockaddr, ls->socklen, 1)
|
||||
!= NGX_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"duplicate \"%V\" address and port pair", &u.url);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_core_srv_conf_t *cscf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (cscf->resolver) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);
|
||||
if (cscf->resolver == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
1586
src/stream/ngx_stream_geo_module.c
Normal file
1586
src/stream/ngx_stream_geo_module.c
Normal file
File diff suppressed because it is too large
Load Diff
814
src/stream/ngx_stream_geoip_module.c
Normal file
814
src/stream/ngx_stream_geoip_module.c
Normal file
@@ -0,0 +1,814 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
#include <GeoIP.h>
|
||||
#include <GeoIPCity.h>
|
||||
|
||||
|
||||
#define NGX_GEOIP_COUNTRY_CODE 0
|
||||
#define NGX_GEOIP_COUNTRY_CODE3 1
|
||||
#define NGX_GEOIP_COUNTRY_NAME 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
GeoIP *country;
|
||||
GeoIP *org;
|
||||
GeoIP *city;
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
unsigned country_v6:1;
|
||||
unsigned org_v6:1;
|
||||
unsigned city_v6:1;
|
||||
#endif
|
||||
} ngx_stream_geoip_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t *name;
|
||||
uintptr_t data;
|
||||
} ngx_stream_geoip_var_t;
|
||||
|
||||
|
||||
typedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *,
|
||||
u_long addr);
|
||||
|
||||
|
||||
ngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = {
|
||||
GeoIP_country_code_by_ipnum,
|
||||
GeoIP_country_code3_by_ipnum,
|
||||
GeoIP_country_name_by_ipnum,
|
||||
};
|
||||
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
|
||||
typedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *,
|
||||
geoipv6_t addr);
|
||||
|
||||
|
||||
ngx_stream_geoip_variable_handler_v6_pt
|
||||
ngx_stream_geoip_country_v6_functions[] =
|
||||
{
|
||||
GeoIP_country_code_by_ipnum_v6,
|
||||
GeoIP_country_code3_by_ipnum_v6,
|
||||
GeoIP_country_name_by_ipnum_v6,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s);
|
||||
|
||||
static ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf);
|
||||
static void *ngx_stream_geoip_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void ngx_stream_geoip_cleanup(void *data);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_geoip_commands[] = {
|
||||
|
||||
{ ngx_string("geoip_country"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_geoip_country,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_org"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_geoip_org,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("geoip_city"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_geoip_city,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_geoip_module_ctx = {
|
||||
ngx_stream_geoip_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_stream_geoip_create_conf, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_geoip_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_geoip_module_ctx, /* module context */
|
||||
ngx_stream_geoip_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_variable_t ngx_stream_geoip_vars[] = {
|
||||
|
||||
{ ngx_string("geoip_country_code"), NULL,
|
||||
ngx_stream_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_CODE, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_country_code3"), NULL,
|
||||
ngx_stream_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_country_name"), NULL,
|
||||
ngx_stream_geoip_country_variable,
|
||||
NGX_GEOIP_COUNTRY_NAME, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_org"), NULL,
|
||||
ngx_stream_geoip_org_variable,
|
||||
0, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_continent_code"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, continent_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_code"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_code3"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_code3), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city_country_name"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, country_name), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_region"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, region), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_region_name"), NULL,
|
||||
ngx_stream_geoip_region_name_variable,
|
||||
0, 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_city"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, city), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_postal_code"), NULL,
|
||||
ngx_stream_geoip_city_variable,
|
||||
offsetof(GeoIPRecord, postal_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_latitude"), NULL,
|
||||
ngx_stream_geoip_city_float_variable,
|
||||
offsetof(GeoIPRecord, latitude), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_longitude"), NULL,
|
||||
ngx_stream_geoip_city_float_variable,
|
||||
offsetof(GeoIPRecord, longitude), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_dma_code"), NULL,
|
||||
ngx_stream_geoip_city_int_variable,
|
||||
offsetof(GeoIPRecord, dma_code), 0, 0 },
|
||||
|
||||
{ ngx_string("geoip_area_code"), NULL,
|
||||
ngx_stream_geoip_city_int_variable,
|
||||
offsetof(GeoIPRecord, area_code), 0, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static u_long
|
||||
ngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
|
||||
{
|
||||
ngx_addr_t addr;
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
addr.sockaddr = s->connection->sockaddr;
|
||||
addr.socklen = s->connection->socklen;
|
||||
/* addr.name = s->connection->addr_text; */
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
|
||||
if (addr.sockaddr->sa_family == AF_INET6) {
|
||||
u_char *p;
|
||||
in_addr_t inaddr;
|
||||
struct in6_addr *inaddr6;
|
||||
|
||||
inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
|
||||
|
||||
if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
|
||||
p = inaddr6->s6_addr;
|
||||
|
||||
inaddr = p[12] << 24;
|
||||
inaddr += p[13] << 16;
|
||||
inaddr += p[14] << 8;
|
||||
inaddr += p[15];
|
||||
|
||||
return inaddr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (addr.sockaddr->sa_family != AF_INET) {
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
sin = (struct sockaddr_in *) addr.sockaddr;
|
||||
return ntohl(sin->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
|
||||
static geoipv6_t
|
||||
ngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)
|
||||
{
|
||||
ngx_addr_t addr;
|
||||
in_addr_t addr4;
|
||||
struct in6_addr addr6;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
addr.sockaddr = s->connection->sockaddr;
|
||||
addr.socklen = s->connection->socklen;
|
||||
/* addr.name = s->connection->addr_text; */
|
||||
|
||||
switch (addr.sockaddr->sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
/* Produce IPv4-mapped IPv6 address. */
|
||||
sin = (struct sockaddr_in *) addr.sockaddr;
|
||||
addr4 = ntohl(sin->sin_addr.s_addr);
|
||||
|
||||
ngx_memzero(&addr6, sizeof(struct in6_addr));
|
||||
addr6.s6_addr[10] = 0xff;
|
||||
addr6.s6_addr[11] = 0xff;
|
||||
addr6.s6_addr[12] = addr4 >> 24;
|
||||
addr6.s6_addr[13] = addr4 >> 16;
|
||||
addr6.s6_addr[14] = addr4 >> 8;
|
||||
addr6.s6_addr[15] = addr4;
|
||||
return addr6;
|
||||
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) addr.sockaddr;
|
||||
return sin6->sin6_addr;
|
||||
|
||||
default:
|
||||
return in6addr_any;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_country_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_stream_geoip_variable_handler_pt handler =
|
||||
ngx_stream_geoip_country_functions[data];
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
ngx_stream_geoip_variable_handler_v6_pt handler_v6 =
|
||||
ngx_stream_geoip_country_v6_functions[data];
|
||||
#endif
|
||||
|
||||
const char *val;
|
||||
ngx_stream_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
|
||||
|
||||
if (gcf->country == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
val = gcf->country_v6
|
||||
? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf))
|
||||
: handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
|
||||
#else
|
||||
val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf));
|
||||
#endif
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
v->len = ngx_strlen(val);
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = (u_char *) val;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_org_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
size_t len;
|
||||
char *val;
|
||||
ngx_stream_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
|
||||
|
||||
if (gcf->org == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
val = gcf->org_v6
|
||||
? GeoIP_name_by_ipnum_v6(gcf->org,
|
||||
ngx_stream_geoip_addr_v6(s, gcf))
|
||||
: GeoIP_name_by_ipnum(gcf->org,
|
||||
ngx_stream_geoip_addr(s, gcf));
|
||||
#else
|
||||
val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf));
|
||||
#endif
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(s->connection->pool, len);
|
||||
if (v->data == NULL) {
|
||||
ngx_free(val);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
ngx_free(val);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_city_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
char *val;
|
||||
size_t len;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_stream_geoip_get_city_record(s);
|
||||
if (gr == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
val = *(char **) ((char *) gr + data);
|
||||
if (val == NULL) {
|
||||
goto no_value;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(s->connection->pool, len);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
no_value:
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
size_t len;
|
||||
const char *val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_stream_geoip_get_city_record(s);
|
||||
if (gr == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
val = GeoIP_region_name_by_code(gr->country_code, gr->region);
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
if (val == NULL) {
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
len = ngx_strlen(val);
|
||||
v->data = ngx_pnalloc(s->connection->pool, len);
|
||||
if (v->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(v->data, val, len);
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
not_found:
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
float val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_stream_geoip_get_city_record(s);
|
||||
if (gr == NULL) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
val = *(float *) ((char *) gr + data);
|
||||
|
||||
v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
int val;
|
||||
GeoIPRecord *gr;
|
||||
|
||||
gr = ngx_stream_geoip_get_city_record(s);
|
||||
if (gr == NULL) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);
|
||||
if (v->data == NULL) {
|
||||
GeoIPRecord_delete(gr);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
val = *(int *) ((char *) gr + data);
|
||||
|
||||
v->len = ngx_sprintf(v->data, "%d", val) - v->data;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
GeoIPRecord_delete(gr);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static GeoIPRecord *
|
||||
ngx_stream_geoip_get_city_record(ngx_stream_session_t *s)
|
||||
{
|
||||
ngx_stream_geoip_conf_t *gcf;
|
||||
|
||||
gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);
|
||||
|
||||
if (gcf->city) {
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
return gcf->city_v6
|
||||
? GeoIP_record_by_ipnum_v6(gcf->city,
|
||||
ngx_stream_geoip_addr_v6(s, gcf))
|
||||
: GeoIP_record_by_ipnum(gcf->city,
|
||||
ngx_stream_geoip_addr(s, gcf));
|
||||
#else
|
||||
return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf));
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_geoip_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_stream_geoip_vars; v->name.len; v++) {
|
||||
var = ngx_stream_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_geoip_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_pool_cleanup_t *cln;
|
||||
ngx_stream_geoip_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cln = ngx_pool_cleanup_add(cf->pool, 0);
|
||||
if (cln == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cln->handler = ngx_stream_geoip_cleanup;
|
||||
cln->data = conf;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->country) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->country == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->country->databaseType) {
|
||||
|
||||
case GEOIP_COUNTRY_EDITION:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_COUNTRY_EDITION_V6:
|
||||
|
||||
gcf->country_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP database \"%V\" type:%d",
|
||||
&value[1], gcf->country->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->org) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->org == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->org->databaseType) {
|
||||
|
||||
case GEOIP_ISP_EDITION:
|
||||
case GEOIP_ORG_EDITION:
|
||||
case GEOIP_DOMAIN_EDITION:
|
||||
case GEOIP_ASNUM_EDITION:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_ISP_EDITION_V6:
|
||||
case GEOIP_ORG_EDITION_V6:
|
||||
case GEOIP_DOMAIN_EDITION_V6:
|
||||
case GEOIP_ASNUM_EDITION_V6:
|
||||
|
||||
gcf->org_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP database \"%V\" type:%d",
|
||||
&value[1], gcf->org->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_geoip_conf_t *gcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (gcf->city) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
|
||||
|
||||
if (gcf->city == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"GeoIP_open(\"%V\") failed", &value[1]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
if (ngx_strcmp(value[2].data, "utf8") == 0) {
|
||||
GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gcf->city->databaseType) {
|
||||
|
||||
case GEOIP_CITY_EDITION_REV0:
|
||||
case GEOIP_CITY_EDITION_REV1:
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
#if (NGX_HAVE_GEOIP_V6)
|
||||
case GEOIP_CITY_EDITION_REV0_V6:
|
||||
case GEOIP_CITY_EDITION_REV1_V6:
|
||||
|
||||
gcf->city_v6 = 1;
|
||||
return NGX_CONF_OK;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid GeoIP City database \"%V\" type:%d",
|
||||
&value[1], gcf->city->databaseType);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_geoip_cleanup(void *data)
|
||||
{
|
||||
ngx_stream_geoip_conf_t *gcf = data;
|
||||
|
||||
if (gcf->country) {
|
||||
GeoIP_delete(gcf->country);
|
||||
}
|
||||
|
||||
if (gcf->org) {
|
||||
GeoIP_delete(gcf->org);
|
||||
}
|
||||
|
||||
if (gcf->city) {
|
||||
GeoIP_delete(gcf->city);
|
||||
}
|
||||
}
|
||||
385
src/stream/ngx_stream_handler.c
Normal file
385
src/stream/ngx_stream_handler.c
Normal file
@@ -0,0 +1,385 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static void ngx_stream_log_session(ngx_stream_session_t *s);
|
||||
static void ngx_stream_close_connection(ngx_connection_t *c);
|
||||
static u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len);
|
||||
static void ngx_stream_proxy_protocol_handler(ngx_event_t *rev);
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_init_connection(ngx_connection_t *c)
|
||||
{
|
||||
u_char text[NGX_SOCKADDR_STRLEN];
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_time_t *tp;
|
||||
ngx_event_t *rev;
|
||||
struct sockaddr *sa;
|
||||
ngx_stream_port_t *port;
|
||||
struct sockaddr_in *sin;
|
||||
ngx_stream_in_addr_t *addr;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_addr_conf_t *addr_conf;
|
||||
#if (NGX_HAVE_INET6)
|
||||
struct sockaddr_in6 *sin6;
|
||||
ngx_stream_in6_addr_t *addr6;
|
||||
#endif
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
/* find the server configuration for the address:port */
|
||||
|
||||
port = c->listening->servers;
|
||||
|
||||
if (port->naddrs > 1) {
|
||||
|
||||
/*
|
||||
* There are several addresses on this port and one of them
|
||||
* is the "*:port" wildcard so getsockname() is needed to determine
|
||||
* the server address.
|
||||
*
|
||||
* AcceptEx() and recvmsg() already gave this address.
|
||||
*/
|
||||
|
||||
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
|
||||
ngx_stream_close_connection(c);
|
||||
return;
|
||||
}
|
||||
|
||||
sa = c->local_sockaddr;
|
||||
|
||||
switch (sa->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
sin6 = (struct sockaddr_in6 *) sa;
|
||||
|
||||
addr6 = port->addrs;
|
||||
|
||||
/* the last address is "*" */
|
||||
|
||||
for (i = 0; i < port->naddrs - 1; i++) {
|
||||
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
addr_conf = &addr6[i].conf;
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* AF_INET */
|
||||
sin = (struct sockaddr_in *) sa;
|
||||
|
||||
addr = port->addrs;
|
||||
|
||||
/* the last address is "*" */
|
||||
|
||||
for (i = 0; i < port->naddrs - 1; i++) {
|
||||
if (addr[i].addr == sin->sin_addr.s_addr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
addr_conf = &addr[i].conf;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (c->local_sockaddr->sa_family) {
|
||||
|
||||
#if (NGX_HAVE_INET6)
|
||||
case AF_INET6:
|
||||
addr6 = port->addrs;
|
||||
addr_conf = &addr6[0].conf;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: /* AF_INET */
|
||||
addr = port->addrs;
|
||||
addr_conf = &addr[0].conf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t));
|
||||
if (s == NULL) {
|
||||
ngx_stream_close_connection(c);
|
||||
return;
|
||||
}
|
||||
|
||||
s->signature = NGX_STREAM_MODULE;
|
||||
s->main_conf = addr_conf->ctx->main_conf;
|
||||
s->srv_conf = addr_conf->ctx->srv_conf;
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
s->ssl = addr_conf->ssl;
|
||||
#endif
|
||||
|
||||
if (c->buffer) {
|
||||
s->received += c->buffer->last - c->buffer->pos;
|
||||
}
|
||||
|
||||
s->connection = c;
|
||||
c->data = s;
|
||||
|
||||
cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);
|
||||
|
||||
ngx_set_connection_log(c, cscf->error_log);
|
||||
|
||||
len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA %sclient %*s connected to %V",
|
||||
c->number, c->type == SOCK_DGRAM ? "udp " : "",
|
||||
len, text, &addr_conf->addr_text);
|
||||
|
||||
c->log->connection = c->number;
|
||||
c->log->handler = ngx_stream_log_error;
|
||||
c->log->data = s;
|
||||
c->log->action = "initializing session";
|
||||
c->log_error = NGX_ERROR_INFO;
|
||||
|
||||
s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module);
|
||||
if (s->ctx == NULL) {
|
||||
ngx_stream_close_connection(c);
|
||||
return;
|
||||
}
|
||||
|
||||
cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
||||
|
||||
s->variables = ngx_pcalloc(s->connection->pool,
|
||||
cmcf->variables.nelts
|
||||
* sizeof(ngx_stream_variable_value_t));
|
||||
|
||||
if (s->variables == NULL) {
|
||||
ngx_stream_close_connection(c);
|
||||
return;
|
||||
}
|
||||
|
||||
tp = ngx_timeofday();
|
||||
s->start_sec = tp->sec;
|
||||
s->start_msec = tp->msec;
|
||||
|
||||
rev = c->read;
|
||||
rev->handler = ngx_stream_session_handler;
|
||||
|
||||
if (addr_conf->proxy_protocol) {
|
||||
c->log->action = "reading PROXY protocol";
|
||||
|
||||
rev->handler = ngx_stream_proxy_protocol_handler;
|
||||
|
||||
if (!rev->ready) {
|
||||
ngx_add_timer(rev, cscf->proxy_protocol_timeout);
|
||||
|
||||
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s,
|
||||
NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_use_accept_mutex) {
|
||||
ngx_post_event(rev, &ngx_posted_events);
|
||||
return;
|
||||
}
|
||||
|
||||
rev->handler(rev);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_proxy_protocol_handler(ngx_event_t *rev)
|
||||
{
|
||||
u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
|
||||
size_t size;
|
||||
ssize_t n;
|
||||
ngx_err_t err;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
|
||||
c = rev->data;
|
||||
s = c->data;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"stream PROXY protocol handler");
|
||||
|
||||
if (rev->timedout) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK);
|
||||
|
||||
err = ngx_socket_errno;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "recv(): %z", n);
|
||||
|
||||
if (n == -1) {
|
||||
if (err == NGX_EAGAIN) {
|
||||
rev->ready = 0;
|
||||
|
||||
if (!rev->timer_set) {
|
||||
cscf = ngx_stream_get_module_srv_conf(s,
|
||||
ngx_stream_core_module);
|
||||
|
||||
ngx_add_timer(rev, cscf->proxy_protocol_timeout);
|
||||
}
|
||||
|
||||
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s,
|
||||
NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_connection_error(c, err, "recv() failed");
|
||||
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rev->timer_set) {
|
||||
ngx_del_timer(rev);
|
||||
}
|
||||
|
||||
p = ngx_proxy_protocol_read(c, buf, buf + n);
|
||||
|
||||
if (p == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
size = p - buf;
|
||||
|
||||
if (c->recv(c, buf, size) != (ssize_t) size) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
c->log->action = "initializing session";
|
||||
|
||||
ngx_stream_session_handler(rev);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_session_handler(ngx_event_t *rev)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_session_t *s;
|
||||
|
||||
c = rev->data;
|
||||
s = c->data;
|
||||
|
||||
ngx_stream_core_run_phases(s);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc)
|
||||
{
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"finalize stream session: %i", rc);
|
||||
|
||||
s->status = rc;
|
||||
|
||||
ngx_stream_log_session(s);
|
||||
|
||||
ngx_stream_close_connection(s->connection);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_log_session(ngx_stream_session_t *s)
|
||||
{
|
||||
ngx_uint_t i, n;
|
||||
ngx_stream_handler_pt *log_handler;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
||||
|
||||
log_handler = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.elts;
|
||||
n = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.nelts;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
log_handler[i](s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_close_connection(ngx_connection_t *c)
|
||||
{
|
||||
ngx_pool_t *pool;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"close stream connection: %d", c->fd);
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
|
||||
if (c->ssl) {
|
||||
if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
|
||||
c->ssl->handler = ngx_stream_close_connection;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (NGX_STAT_STUB)
|
||||
(void) ngx_atomic_fetch_add(ngx_stat_active, -1);
|
||||
#endif
|
||||
|
||||
pool = c->pool;
|
||||
|
||||
ngx_close_connection(c);
|
||||
|
||||
ngx_destroy_pool(pool);
|
||||
}
|
||||
|
||||
|
||||
static u_char *
|
||||
ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len)
|
||||
{
|
||||
u_char *p;
|
||||
ngx_stream_session_t *s;
|
||||
|
||||
if (log->action) {
|
||||
p = ngx_snprintf(buf, len, " while %s", log->action);
|
||||
len -= p - buf;
|
||||
buf = p;
|
||||
}
|
||||
|
||||
s = log->data;
|
||||
|
||||
p = ngx_snprintf(buf, len, ", %sclient: %V, server: %V",
|
||||
s->connection->type == SOCK_DGRAM ? "udp " : "",
|
||||
&s->connection->addr_text,
|
||||
&s->connection->listening->addr_text);
|
||||
len -= p - buf;
|
||||
buf = p;
|
||||
|
||||
if (s->log_handler) {
|
||||
p = s->log_handler(log, buf, len);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
646
src/stream/ngx_stream_limit_conn_module.c
Normal file
646
src/stream/ngx_stream_limit_conn_module.c
Normal file
@@ -0,0 +1,646 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char color;
|
||||
u_char len;
|
||||
u_short conn;
|
||||
u_char data[1];
|
||||
} ngx_stream_limit_conn_node_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_rbtree_node_t *node;
|
||||
} ngx_stream_limit_conn_cleanup_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_t *rbtree;
|
||||
ngx_stream_complex_value_t key;
|
||||
} ngx_stream_limit_conn_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_uint_t conn;
|
||||
} ngx_stream_limit_conn_limit_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t limits;
|
||||
ngx_uint_t log_level;
|
||||
} ngx_stream_limit_conn_conf_t;
|
||||
|
||||
|
||||
static ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree,
|
||||
ngx_str_t *key, uint32_t hash);
|
||||
static void ngx_stream_limit_conn_cleanup(void *data);
|
||||
static ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool);
|
||||
|
||||
static void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_stream_limit_conn_log_levels[] = {
|
||||
{ ngx_string("info"), NGX_LOG_INFO },
|
||||
{ ngx_string("notice"), NGX_LOG_NOTICE },
|
||||
{ ngx_string("warn"), NGX_LOG_WARN },
|
||||
{ ngx_string("error"), NGX_LOG_ERR },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_limit_conn_commands[] = {
|
||||
|
||||
{ ngx_string("limit_conn_zone"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,
|
||||
ngx_stream_limit_conn_zone,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_conn"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,
|
||||
ngx_stream_limit_conn,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("limit_conn_log_level"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_limit_conn_conf_t, log_level),
|
||||
&ngx_stream_limit_conn_log_levels },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_limit_conn_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_stream_limit_conn_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_limit_conn_create_conf, /* create server configuration */
|
||||
ngx_stream_limit_conn_merge_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_limit_conn_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_limit_conn_module_ctx, /* module context */
|
||||
ngx_stream_limit_conn_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_limit_conn_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
size_t n;
|
||||
uint32_t hash;
|
||||
ngx_str_t key;
|
||||
ngx_uint_t i;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_pool_cleanup_t *cln;
|
||||
ngx_stream_limit_conn_ctx_t *ctx;
|
||||
ngx_stream_limit_conn_node_t *lc;
|
||||
ngx_stream_limit_conn_conf_t *lccf;
|
||||
ngx_stream_limit_conn_limit_t *limits;
|
||||
ngx_stream_limit_conn_cleanup_t *lccln;
|
||||
|
||||
lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);
|
||||
limits = lccf->limits.elts;
|
||||
|
||||
for (i = 0; i < lccf->limits.nelts; i++) {
|
||||
ctx = limits[i].shm_zone->data;
|
||||
|
||||
if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (key.len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.len > 255) {
|
||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||
"the value of the \"%V\" key "
|
||||
"is more than 255 bytes: \"%V\"",
|
||||
&ctx->key.value, &key);
|
||||
continue;
|
||||
}
|
||||
|
||||
hash = ngx_crc32_short(key.data, key.len);
|
||||
|
||||
shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
node = ngx_stream_limit_conn_lookup(ctx->rbtree, &key, hash);
|
||||
|
||||
if (node == NULL) {
|
||||
|
||||
n = offsetof(ngx_rbtree_node_t, color)
|
||||
+ offsetof(ngx_stream_limit_conn_node_t, data)
|
||||
+ key.len;
|
||||
|
||||
node = ngx_slab_alloc_locked(shpool, n);
|
||||
|
||||
if (node == NULL) {
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
ngx_stream_limit_conn_cleanup_all(s->connection->pool);
|
||||
return NGX_STREAM_SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
lc = (ngx_stream_limit_conn_node_t *) &node->color;
|
||||
|
||||
node->key = hash;
|
||||
lc->len = (u_char) key.len;
|
||||
lc->conn = 1;
|
||||
ngx_memcpy(lc->data, key.data, key.len);
|
||||
|
||||
ngx_rbtree_insert(ctx->rbtree, node);
|
||||
|
||||
} else {
|
||||
|
||||
lc = (ngx_stream_limit_conn_node_t *) &node->color;
|
||||
|
||||
if ((ngx_uint_t) lc->conn >= limits[i].conn) {
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
ngx_log_error(lccf->log_level, s->connection->log, 0,
|
||||
"limiting connections by zone \"%V\"",
|
||||
&limits[i].shm_zone->shm.name);
|
||||
|
||||
ngx_stream_limit_conn_cleanup_all(s->connection->pool);
|
||||
return NGX_STREAM_SERVICE_UNAVAILABLE;
|
||||
}
|
||||
|
||||
lc->conn++;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"limit conn: %08Xi %d", node->key, lc->conn);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
cln = ngx_pool_cleanup_add(s->connection->pool,
|
||||
sizeof(ngx_stream_limit_conn_cleanup_t));
|
||||
if (cln == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cln->handler = ngx_stream_limit_conn_cleanup;
|
||||
lccln = cln->data;
|
||||
|
||||
lccln->shm_zone = limits[i].shm_zone;
|
||||
lccln->node = node;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
|
||||
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
||||
{
|
||||
ngx_rbtree_node_t **p;
|
||||
ngx_stream_limit_conn_node_t *lcn, *lcnt;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (node->key < temp->key) {
|
||||
|
||||
p = &temp->left;
|
||||
|
||||
} else if (node->key > temp->key) {
|
||||
|
||||
p = &temp->right;
|
||||
|
||||
} else { /* node->key == temp->key */
|
||||
|
||||
lcn = (ngx_stream_limit_conn_node_t *) &node->color;
|
||||
lcnt = (ngx_stream_limit_conn_node_t *) &temp->color;
|
||||
|
||||
p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
|
||||
? &temp->left : &temp->right;
|
||||
}
|
||||
|
||||
if (*p == sentinel) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp = *p;
|
||||
}
|
||||
|
||||
*p = node;
|
||||
node->parent = temp;
|
||||
node->left = sentinel;
|
||||
node->right = sentinel;
|
||||
ngx_rbt_red(node);
|
||||
}
|
||||
|
||||
|
||||
static ngx_rbtree_node_t *
|
||||
ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key,
|
||||
uint32_t hash)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_stream_limit_conn_node_t *lcn;
|
||||
|
||||
node = rbtree->root;
|
||||
sentinel = rbtree->sentinel;
|
||||
|
||||
while (node != sentinel) {
|
||||
|
||||
if (hash < node->key) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash > node->key) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* hash == node->key */
|
||||
|
||||
lcn = (ngx_stream_limit_conn_node_t *) &node->color;
|
||||
|
||||
rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);
|
||||
|
||||
if (rc == 0) {
|
||||
return node;
|
||||
}
|
||||
|
||||
node = (rc < 0) ? node->left : node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_limit_conn_cleanup(void *data)
|
||||
{
|
||||
ngx_stream_limit_conn_cleanup_t *lccln = data;
|
||||
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_stream_limit_conn_ctx_t *ctx;
|
||||
ngx_stream_limit_conn_node_t *lc;
|
||||
|
||||
ctx = lccln->shm_zone->data;
|
||||
shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
|
||||
node = lccln->node;
|
||||
lc = (ngx_stream_limit_conn_node_t *) &node->color;
|
||||
|
||||
ngx_shmtx_lock(&shpool->mutex);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0,
|
||||
"limit conn cleanup: %08Xi %d", node->key, lc->conn);
|
||||
|
||||
lc->conn--;
|
||||
|
||||
if (lc->conn == 0) {
|
||||
ngx_rbtree_delete(ctx->rbtree, node);
|
||||
ngx_slab_free_locked(shpool, node);
|
||||
}
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
}
|
||||
|
||||
|
||||
static ngx_inline void
|
||||
ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool)
|
||||
{
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
cln = pool->cleanup;
|
||||
|
||||
while (cln && cln->handler == ngx_stream_limit_conn_cleanup) {
|
||||
ngx_stream_limit_conn_cleanup(cln->data);
|
||||
cln = cln->next;
|
||||
}
|
||||
|
||||
pool->cleanup = cln;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
ngx_stream_limit_conn_ctx_t *octx = data;
|
||||
|
||||
size_t len;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_rbtree_node_t *sentinel;
|
||||
ngx_stream_limit_conn_ctx_t *ctx;
|
||||
|
||||
ctx = shm_zone->data;
|
||||
|
||||
if (octx) {
|
||||
if (ctx->key.value.len != octx->key.value.len
|
||||
|| ngx_strncmp(ctx->key.value.data, octx->key.value.data,
|
||||
ctx->key.value.len)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
|
||||
"limit_conn_zone \"%V\" uses the \"%V\" key "
|
||||
"while previously it used the \"%V\" key",
|
||||
&shm_zone->shm.name, &ctx->key.value,
|
||||
&octx->key.value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->rbtree = octx->rbtree;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
|
||||
if (shm_zone->shm.exists) {
|
||||
ctx->rbtree = shpool->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
|
||||
if (ctx->rbtree == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
shpool->data = ctx->rbtree;
|
||||
|
||||
sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
|
||||
if (sentinel == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_rbtree_init(ctx->rbtree, sentinel,
|
||||
ngx_stream_limit_conn_rbtree_insert_value);
|
||||
|
||||
len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
|
||||
|
||||
shpool->log_ctx = ngx_slab_alloc(shpool, len);
|
||||
if (shpool->log_ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
|
||||
&shm_zone->shm.name);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_limit_conn_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_limit_conn_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->limits.elts = NULL;
|
||||
*/
|
||||
|
||||
conf->log_level = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_limit_conn_conf_t *prev = parent;
|
||||
ngx_stream_limit_conn_conf_t *conf = child;
|
||||
|
||||
if (conf->limits.elts == NULL) {
|
||||
conf->limits = prev->limits;
|
||||
}
|
||||
|
||||
ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
u_char *p;
|
||||
ssize_t size;
|
||||
ngx_str_t *value, name, s;
|
||||
ngx_uint_t i;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_stream_limit_conn_ctx_t *ctx;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &ctx->key;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
name.len = 0;
|
||||
|
||||
for (i = 2; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
|
||||
|
||||
name.data = value[i].data + 5;
|
||||
|
||||
p = (u_char *) ngx_strchr(name.data, ':');
|
||||
|
||||
if (p == NULL) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len = p - name.data;
|
||||
|
||||
s.data = p + 1;
|
||||
s.len = value[i].data + value[i].len - s.data;
|
||||
|
||||
size = ngx_parse_size(&s);
|
||||
|
||||
if (size == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (size < (ssize_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"zone \"%V\" is too small", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (name.len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"%V\" must have \"zone\" parameter",
|
||||
&cmd->name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &name, size,
|
||||
&ngx_stream_limit_conn_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (shm_zone->data) {
|
||||
ctx = shm_zone->data;
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%V \"%V\" is already bound to key \"%V\"",
|
||||
&cmd->name, &name, &ctx->key.value);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
shm_zone->init = ngx_stream_limit_conn_init_zone;
|
||||
shm_zone->data = ctx;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_stream_limit_conn_conf_t *lccf = conf;
|
||||
ngx_stream_limit_conn_limit_t *limit, *limits;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t i;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
|
||||
&ngx_stream_limit_conn_module);
|
||||
if (shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limits = lccf->limits.elts;
|
||||
|
||||
if (limits == NULL) {
|
||||
if (ngx_array_init(&lccf->limits, cf->pool, 1,
|
||||
sizeof(ngx_stream_limit_conn_limit_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lccf->limits.nelts; i++) {
|
||||
if (shm_zone == limits[i].shm_zone) {
|
||||
return "is duplicate";
|
||||
}
|
||||
}
|
||||
|
||||
n = ngx_atoi(value[2].data, value[2].len);
|
||||
if (n <= 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid number of connections \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (n > 65535) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"connection limit must be less 65536");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limit = ngx_array_push(&lccf->limits);
|
||||
if (limit == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
limit->conn = n;
|
||||
limit->shm_zone = shm_zone;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_limit_conn_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_stream_limit_conn_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
1551
src/stream/ngx_stream_log_module.c
Normal file
1551
src/stream/ngx_stream_log_module.c
Normal file
File diff suppressed because it is too large
Load Diff
588
src/stream/ngx_stream_map_module.c
Normal file
588
src/stream/ngx_stream_map_module.c
Normal file
@@ -0,0 +1,588 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t hash_max_size;
|
||||
ngx_uint_t hash_bucket_size;
|
||||
} ngx_stream_map_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_keys_arrays_t keys;
|
||||
|
||||
ngx_array_t *values_hash;
|
||||
#if (NGX_PCRE)
|
||||
ngx_array_t regexes;
|
||||
#endif
|
||||
|
||||
ngx_stream_variable_value_t *default_value;
|
||||
ngx_conf_t *cf;
|
||||
unsigned hostnames:1;
|
||||
unsigned no_cacheable:1;
|
||||
} ngx_stream_map_conf_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_map_t map;
|
||||
ngx_stream_complex_value_t value;
|
||||
ngx_stream_variable_value_t *default_value;
|
||||
ngx_uint_t hostnames; /* unsigned hostnames:1 */
|
||||
} ngx_stream_map_ctx_t;
|
||||
|
||||
|
||||
static int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one,
|
||||
const void *two);
|
||||
static void *ngx_stream_map_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_map_commands[] = {
|
||||
|
||||
{ ngx_string("map"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
|
||||
ngx_stream_map_block,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("map_hash_max_size"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_stream_map_conf_t, hash_max_size),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("map_hash_bucket_size"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
offsetof(ngx_stream_map_conf_t, hash_bucket_size),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_map_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_stream_map_create_conf, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_map_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_map_module_ctx, /* module context */
|
||||
ngx_stream_map_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,
|
||||
uintptr_t data)
|
||||
{
|
||||
ngx_stream_map_ctx_t *map = (ngx_stream_map_ctx_t *) data;
|
||||
|
||||
ngx_str_t val, str;
|
||||
ngx_stream_complex_value_t *cv;
|
||||
ngx_stream_variable_value_t *value;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"stream map started");
|
||||
|
||||
if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
|
||||
val.len--;
|
||||
}
|
||||
|
||||
value = ngx_stream_map_find(s, &map->map, &val);
|
||||
|
||||
if (value == NULL) {
|
||||
value = map->default_value;
|
||||
}
|
||||
|
||||
if (!value->valid) {
|
||||
cv = (ngx_stream_complex_value_t *) value->data;
|
||||
|
||||
if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->len = str.len;
|
||||
v->data = str.data;
|
||||
|
||||
} else {
|
||||
*v = *value;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"stream map: \"%V\" \"%v\"", &val, v);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_map_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_map_conf_t *mcf;
|
||||
|
||||
mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t));
|
||||
if (mcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mcf->hash_max_size = NGX_CONF_UNSET_UINT;
|
||||
mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return mcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_map_conf_t *mcf = conf;
|
||||
|
||||
char *rv;
|
||||
ngx_str_t *value, name;
|
||||
ngx_conf_t save;
|
||||
ngx_pool_t *pool;
|
||||
ngx_hash_init_t hash;
|
||||
ngx_stream_map_ctx_t *map;
|
||||
ngx_stream_variable_t *var;
|
||||
ngx_stream_map_conf_ctx_t ctx;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
|
||||
mcf->hash_max_size = 2048;
|
||||
}
|
||||
|
||||
if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
|
||||
mcf->hash_bucket_size = ngx_cacheline_size;
|
||||
|
||||
} else {
|
||||
mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
|
||||
ngx_cacheline_size);
|
||||
}
|
||||
|
||||
map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t));
|
||||
if (map == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &map->value;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name = value[2];
|
||||
|
||||
if (name.data[0] != '$') {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid variable name \"%V\"", &name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len--;
|
||||
name.data++;
|
||||
|
||||
var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_stream_map_variable;
|
||||
var->data = (uintptr_t) map;
|
||||
|
||||
pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
|
||||
if (pool == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx.keys.pool = cf->pool;
|
||||
ctx.keys.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
|
||||
if (ctx.values_hash == NULL) {
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
if (ngx_array_init(&ctx.regexes, cf->pool, 2,
|
||||
sizeof(ngx_stream_map_regex_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
ctx.default_value = NULL;
|
||||
ctx.cf = &save;
|
||||
ctx.hostnames = 0;
|
||||
ctx.no_cacheable = 0;
|
||||
|
||||
save = *cf;
|
||||
cf->pool = pool;
|
||||
cf->ctx = &ctx;
|
||||
cf->handler = ngx_stream_map;
|
||||
cf->handler_conf = conf;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = save;
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
ngx_destroy_pool(pool);
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (ctx.no_cacheable) {
|
||||
var->flags |= NGX_STREAM_VAR_NOCACHEABLE;
|
||||
}
|
||||
|
||||
map->default_value = ctx.default_value ? ctx.default_value:
|
||||
&ngx_stream_variable_null_value;
|
||||
|
||||
map->hostnames = ctx.hostnames;
|
||||
|
||||
hash.key = ngx_hash_key_lc;
|
||||
hash.max_size = mcf->hash_max_size;
|
||||
hash.bucket_size = mcf->hash_bucket_size;
|
||||
hash.name = "map_hash";
|
||||
hash.pool = cf->pool;
|
||||
|
||||
if (ctx.keys.keys.nelts) {
|
||||
hash.hash = &map->map.hash.hash;
|
||||
hash.temp_pool = NULL;
|
||||
|
||||
if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.keys.dns_wc_head.nelts) {
|
||||
|
||||
ngx_qsort(ctx.keys.dns_wc_head.elts,
|
||||
(size_t) ctx.keys.dns_wc_head.nelts,
|
||||
sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
|
||||
ctx.keys.dns_wc_head.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
if (ctx.keys.dns_wc_tail.nelts) {
|
||||
|
||||
ngx_qsort(ctx.keys.dns_wc_tail.elts,
|
||||
(size_t) ctx.keys.dns_wc_tail.nelts,
|
||||
sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);
|
||||
|
||||
hash.hash = NULL;
|
||||
hash.temp_pool = pool;
|
||||
|
||||
if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
|
||||
ctx.keys.dns_wc_tail.nelts)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_destroy_pool(pool);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
if (ctx.regexes.nelts) {
|
||||
map->map.regex = ctx.regexes.elts;
|
||||
map->map.nregex = ctx.regexes.nelts;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ngx_destroy_pool(pool);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_stream_map_cmp_dns_wildcards(const void *one, const void *two)
|
||||
{
|
||||
ngx_hash_key_t *first, *second;
|
||||
|
||||
first = (ngx_hash_key_t *) one;
|
||||
second = (ngx_hash_key_t *) two;
|
||||
|
||||
return ngx_dns_strcmp(first->key.data, second->key.data);
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
{
|
||||
u_char *data;
|
||||
size_t len;
|
||||
ngx_int_t rv;
|
||||
ngx_str_t *value, v;
|
||||
ngx_uint_t i, key;
|
||||
ngx_stream_map_conf_ctx_t *ctx;
|
||||
ngx_stream_complex_value_t cv, *cvp;
|
||||
ngx_stream_variable_value_t *var, **vp;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
ctx = cf->ctx;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (cf->args->nelts == 1
|
||||
&& ngx_strcmp(value[0].data, "hostnames") == 0)
|
||||
{
|
||||
ctx->hostnames = 1;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 1
|
||||
&& ngx_strcmp(value[0].data, "volatile") == 0)
|
||||
{
|
||||
ctx->no_cacheable = 1;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (cf->args->nelts != 2) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid number of the map parameters");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[0].data, "include") == 0) {
|
||||
return ngx_conf_include(cf, dummy, conf);
|
||||
}
|
||||
|
||||
key = 0;
|
||||
|
||||
for (i = 0; i < value[1].len; i++) {
|
||||
key = ngx_hash(key, value[1].data[i]);
|
||||
}
|
||||
|
||||
key %= ctx->keys.hsize;
|
||||
|
||||
vp = ctx->values_hash[key].elts;
|
||||
|
||||
if (vp) {
|
||||
for (i = 0; i < ctx->values_hash[key].nelts; i++) {
|
||||
|
||||
if (vp[i]->valid) {
|
||||
data = vp[i]->data;
|
||||
len = vp[i]->len;
|
||||
|
||||
} else {
|
||||
cvp = (ngx_stream_complex_value_t *) vp[i]->data;
|
||||
data = cvp->value.data;
|
||||
len = cvp->value.len;
|
||||
}
|
||||
|
||||
if (value[1].len != len) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[1].data, data, len) == 0) {
|
||||
var = vp[i];
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
|
||||
sizeof(ngx_stream_variable_value_t *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t));
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
v.len = value[1].len;
|
||||
v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);
|
||||
if (v.data == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = ctx->cf;
|
||||
ccv.value = &v;
|
||||
ccv.complex_value = &cv;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cv.lengths != NULL) {
|
||||
cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t));
|
||||
if (cvp == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*cvp = cv;
|
||||
|
||||
var->len = 0;
|
||||
var->data = (u_char *) cvp;
|
||||
var->valid = 0;
|
||||
|
||||
} else {
|
||||
var->len = v.len;
|
||||
var->data = v.data;
|
||||
var->valid = 1;
|
||||
}
|
||||
|
||||
var->no_cacheable = 0;
|
||||
var->not_found = 0;
|
||||
|
||||
vp = ngx_array_push(&ctx->values_hash[key]);
|
||||
if (vp == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
*vp = var;
|
||||
|
||||
found:
|
||||
|
||||
if (ngx_strcmp(value[0].data, "default") == 0) {
|
||||
|
||||
if (ctx->default_value) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"duplicate default map parameter");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx->default_value = var;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
if (value[0].len && value[0].data[0] == '~') {
|
||||
ngx_regex_compile_t rc;
|
||||
ngx_stream_map_regex_t *regex;
|
||||
u_char errstr[NGX_MAX_CONF_ERRSTR];
|
||||
|
||||
regex = ngx_array_push(&ctx->regexes);
|
||||
if (regex == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value[0].len--;
|
||||
value[0].data++;
|
||||
|
||||
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
|
||||
|
||||
if (value[0].data[0] == '*') {
|
||||
value[0].len--;
|
||||
value[0].data++;
|
||||
rc.options = NGX_REGEX_CASELESS;
|
||||
}
|
||||
|
||||
rc.pattern = value[0];
|
||||
rc.err.len = NGX_MAX_CONF_ERRSTR;
|
||||
rc.err.data = errstr;
|
||||
|
||||
regex->regex = ngx_stream_regex_compile(ctx->cf, &rc);
|
||||
if (regex->regex == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
regex->value = var;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (value[0].len && value[0].data[0] == '\\') {
|
||||
value[0].len--;
|
||||
value[0].data++;
|
||||
}
|
||||
|
||||
rv = ngx_hash_add_key(&ctx->keys, &value[0], var,
|
||||
(ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
|
||||
|
||||
if (rv == NGX_OK) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (rv == NGX_DECLINED) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid hostname or wildcard \"%V\"", &value[0]);
|
||||
}
|
||||
|
||||
if (rv == NGX_BUSY) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"conflicting parameter \"%V\"", &value[0]);
|
||||
}
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
2170
src/stream/ngx_stream_proxy_module.c
Normal file
2170
src/stream/ngx_stream_proxy_module.c
Normal file
File diff suppressed because it is too large
Load Diff
348
src/stream/ngx_stream_realip_module.c
Normal file
348
src/stream/ngx_stream_realip_module.c
Normal file
@@ -0,0 +1,348 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *from; /* array of ngx_cidr_t */
|
||||
} ngx_stream_realip_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
ngx_str_t addr_text;
|
||||
} ngx_stream_realip_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_realip_handler(ngx_stream_session_t *s);
|
||||
static ngx_int_t ngx_stream_realip_set_addr(ngx_stream_session_t *s,
|
||||
ngx_addr_t *addr);
|
||||
static char *ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void *ngx_stream_realip_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static ngx_int_t ngx_stream_realip_add_variables(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_stream_realip_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_realip_commands[] = {
|
||||
|
||||
{ ngx_string("set_real_ip_from"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_stream_realip_from,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_realip_module_ctx = {
|
||||
ngx_stream_realip_add_variables, /* preconfiguration */
|
||||
ngx_stream_realip_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_realip_create_srv_conf, /* create server configuration */
|
||||
ngx_stream_realip_merge_srv_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_realip_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_realip_module_ctx, /* module context */
|
||||
ngx_stream_realip_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_variable_t ngx_stream_realip_vars[] = {
|
||||
|
||||
{ ngx_string("realip_remote_addr"), NULL,
|
||||
ngx_stream_realip_remote_addr_variable, 0, 0, 0 },
|
||||
|
||||
{ ngx_string("realip_remote_port"), NULL,
|
||||
ngx_stream_realip_remote_port_variable, 0, 0, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
ngx_addr_t addr;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_realip_srv_conf_t *rscf;
|
||||
|
||||
rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_realip_module);
|
||||
|
||||
if (rscf->from == NULL) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
c = s->connection;
|
||||
|
||||
if (c->proxy_protocol_addr.len == 0) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol_addr.data,
|
||||
c->proxy_protocol_addr.len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
ngx_inet_set_port(addr.sockaddr, c->proxy_protocol_port);
|
||||
|
||||
return ngx_stream_realip_set_addr(s, &addr);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_set_addr(ngx_stream_session_t *s, ngx_addr_t *addr)
|
||||
{
|
||||
size_t len;
|
||||
u_char *p;
|
||||
u_char text[NGX_SOCKADDR_STRLEN];
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_realip_ctx_t *ctx;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
ctx = ngx_palloc(c->pool, sizeof(ngx_stream_realip_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
|
||||
NGX_SOCKADDR_STRLEN, 0);
|
||||
if (len == 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
p = ngx_pnalloc(c->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(p, text, len);
|
||||
|
||||
ngx_stream_set_ctx(s, ctx, ngx_stream_realip_module);
|
||||
|
||||
ctx->sockaddr = c->sockaddr;
|
||||
ctx->socklen = c->socklen;
|
||||
ctx->addr_text = c->addr_text;
|
||||
|
||||
c->sockaddr = addr->sockaddr;
|
||||
c->socklen = addr->socklen;
|
||||
c->addr_text.len = len;
|
||||
c->addr_text.data = p;
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_realip_srv_conf_t *rscf = conf;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_str_t *value;
|
||||
ngx_cidr_t *cidr;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (rscf->from == NULL) {
|
||||
rscf->from = ngx_array_create(cf->pool, 2,
|
||||
sizeof(ngx_cidr_t));
|
||||
if (rscf->from == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cidr = ngx_array_push(rscf->from);
|
||||
if (cidr == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_UNIX_DOMAIN)
|
||||
|
||||
if (ngx_strcmp(value[1].data, "unix:") == 0) {
|
||||
cidr->family = AF_UNIX;
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
rc = ngx_ptocidr(&value[1], cidr);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
|
||||
&value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"low address bits of %V are meaningless", &value[1]);
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_realip_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_realip_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_realip_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->from = NULL;
|
||||
*/
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_realip_srv_conf_t *prev = parent;
|
||||
ngx_stream_realip_srv_conf_t *conf = child;
|
||||
|
||||
if (conf->from == NULL) {
|
||||
conf->from = prev->from;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_stream_realip_vars; v->name.len; v++) {
|
||||
var = ngx_stream_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_stream_realip_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_str_t *addr_text;
|
||||
ngx_stream_realip_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);
|
||||
|
||||
addr_text = ctx ? &ctx->addr_text : &s->connection->addr_text;
|
||||
|
||||
v->len = addr_text->len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->data = addr_text->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_uint_t port;
|
||||
struct sockaddr *sa;
|
||||
ngx_stream_realip_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);
|
||||
|
||||
sa = ctx ? ctx->sockaddr : s->connection->sockaddr;
|
||||
|
||||
v->len = 0;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
v->data = ngx_pnalloc(s->connection->pool, sizeof("65535") - 1);
|
||||
if (v->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
port = ngx_inet_get_port(sa);
|
||||
|
||||
if (port > 0 && port < 65536) {
|
||||
v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
218
src/stream/ngx_stream_return_module.c
Normal file
218
src/stream/ngx_stream_return_module.c
Normal file
@@ -0,0 +1,218 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_complex_value_t text;
|
||||
} ngx_stream_return_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_chain_t *out;
|
||||
} ngx_stream_return_ctx_t;
|
||||
|
||||
|
||||
static void ngx_stream_return_handler(ngx_stream_session_t *s);
|
||||
static void ngx_stream_return_write_handler(ngx_event_t *ev);
|
||||
|
||||
static void *ngx_stream_return_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_return_commands[] = {
|
||||
|
||||
{ ngx_string("return"),
|
||||
NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_stream_return,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_return_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_return_create_srv_conf, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_return_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_return_module_ctx, /* module context */
|
||||
ngx_stream_return_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_return_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
ngx_str_t text;
|
||||
ngx_buf_t *b;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_return_ctx_t *ctx;
|
||||
ngx_stream_return_srv_conf_t *rscf;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
c->log->action = "returning text";
|
||||
|
||||
rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_return_module);
|
||||
|
||||
if (ngx_stream_complex_value(s, &rscf->text, &text) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"stream return text: \"%V\"", &text);
|
||||
|
||||
if (text.len == 0) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_return_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_stream_set_ctx(s, ctx, ngx_stream_return_module);
|
||||
|
||||
b = ngx_calloc_buf(c->pool);
|
||||
if (b == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
b->memory = 1;
|
||||
b->pos = text.data;
|
||||
b->last = text.data + text.len;
|
||||
b->last_buf = 1;
|
||||
|
||||
ctx->out = ngx_alloc_chain_link(c->pool);
|
||||
if (ctx->out == NULL) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->out->buf = b;
|
||||
ctx->out->next = NULL;
|
||||
|
||||
c->write->handler = ngx_stream_return_write_handler;
|
||||
|
||||
ngx_stream_return_write_handler(c->write);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_return_write_handler(ngx_event_t *ev)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_return_ctx_t *ctx;
|
||||
|
||||
c = ev->data;
|
||||
s = c->data;
|
||||
|
||||
if (ev->timedout) {
|
||||
ngx_connection_error(c, NGX_ETIMEDOUT, "connection timed out");
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module);
|
||||
|
||||
if (ngx_stream_top_filter(s, ctx->out, 1) == NGX_ERROR) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->out = NULL;
|
||||
|
||||
if (!c->buffered) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"stream return done sending");
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ngx_handle_write_event(ev, 0) != NGX_OK) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_add_timer(ev, 5000);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_return_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_return_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_return_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_return_srv_conf_t *rscf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_stream_core_srv_conf_t *cscf;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
if (rscf->text.value.data) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &rscf->text;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);
|
||||
|
||||
cscf->handler = ngx_stream_return_handler;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
921
src/stream/ngx_stream_script.c
Normal file
921
src/stream/ngx_stream_script.c
Normal file
@@ -0,0 +1,921 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_script_init_arrays(
|
||||
ngx_stream_script_compile_t *sc);
|
||||
static ngx_int_t ngx_stream_script_done(ngx_stream_script_compile_t *sc);
|
||||
static ngx_int_t ngx_stream_script_add_copy_code(
|
||||
ngx_stream_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);
|
||||
static ngx_int_t ngx_stream_script_add_var_code(
|
||||
ngx_stream_script_compile_t *sc, ngx_str_t *name);
|
||||
#if (NGX_PCRE)
|
||||
static ngx_int_t ngx_stream_script_add_capture_code(
|
||||
ngx_stream_script_compile_t *sc, ngx_uint_t n);
|
||||
#endif
|
||||
static ngx_int_t ngx_stream_script_add_full_name_code(
|
||||
ngx_stream_script_compile_t *sc);
|
||||
static size_t ngx_stream_script_full_name_len_code(
|
||||
ngx_stream_script_engine_t *e);
|
||||
static void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e);
|
||||
|
||||
|
||||
#define ngx_stream_script_exit (u_char *) &ngx_stream_script_exit_code
|
||||
|
||||
static uintptr_t ngx_stream_script_exit_code = (uintptr_t) NULL;
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_script_flush_complex_value(ngx_stream_session_t *s,
|
||||
ngx_stream_complex_value_t *val)
|
||||
{
|
||||
ngx_uint_t *index;
|
||||
|
||||
index = val->flushes;
|
||||
|
||||
if (index) {
|
||||
while (*index != (ngx_uint_t) -1) {
|
||||
|
||||
if (s->variables[*index].no_cacheable) {
|
||||
s->variables[*index].valid = 0;
|
||||
s->variables[*index].not_found = 0;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_complex_value(ngx_stream_session_t *s,
|
||||
ngx_stream_complex_value_t *val, ngx_str_t *value)
|
||||
{
|
||||
size_t len;
|
||||
ngx_stream_script_code_pt code;
|
||||
ngx_stream_script_engine_t e;
|
||||
ngx_stream_script_len_code_pt lcode;
|
||||
|
||||
if (val->lengths == NULL) {
|
||||
*value = val->value;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_stream_script_flush_complex_value(s, val);
|
||||
|
||||
ngx_memzero(&e, sizeof(ngx_stream_script_engine_t));
|
||||
|
||||
e.ip = val->lengths;
|
||||
e.session = s;
|
||||
e.flushed = 1;
|
||||
|
||||
len = 0;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
lcode = *(ngx_stream_script_len_code_pt *) e.ip;
|
||||
len += lcode(&e);
|
||||
}
|
||||
|
||||
value->len = len;
|
||||
value->data = ngx_pnalloc(s->connection->pool, len);
|
||||
if (value->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
e.ip = val->values;
|
||||
e.pos = value->data;
|
||||
e.buf = *value;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_stream_script_code_pt *) e.ip;
|
||||
code((ngx_stream_script_engine_t *) &e);
|
||||
}
|
||||
|
||||
*value = e.buf;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_compile_complex_value(ngx_stream_compile_complex_value_t *ccv)
|
||||
{
|
||||
ngx_str_t *v;
|
||||
ngx_uint_t i, n, nv, nc;
|
||||
ngx_array_t flushes, lengths, values, *pf, *pl, *pv;
|
||||
ngx_stream_script_compile_t sc;
|
||||
|
||||
v = ccv->value;
|
||||
|
||||
nv = 0;
|
||||
nc = 0;
|
||||
|
||||
for (i = 0; i < v->len; i++) {
|
||||
if (v->data[i] == '$') {
|
||||
if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {
|
||||
nc++;
|
||||
|
||||
} else {
|
||||
nv++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((v->len == 0 || v->data[0] != '$')
|
||||
&& (ccv->conf_prefix || ccv->root_prefix))
|
||||
{
|
||||
if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ccv->conf_prefix = 0;
|
||||
ccv->root_prefix = 0;
|
||||
}
|
||||
|
||||
ccv->complex_value->value = *v;
|
||||
ccv->complex_value->flushes = NULL;
|
||||
ccv->complex_value->lengths = NULL;
|
||||
ccv->complex_value->values = NULL;
|
||||
|
||||
if (nv == 0 && nc == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
n = nv + 1;
|
||||
|
||||
if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = nv * (2 * sizeof(ngx_stream_script_copy_code_t)
|
||||
+ sizeof(ngx_stream_script_var_code_t))
|
||||
+ sizeof(uintptr_t);
|
||||
|
||||
if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = (nv * (2 * sizeof(ngx_stream_script_copy_code_t)
|
||||
+ sizeof(ngx_stream_script_var_code_t))
|
||||
+ sizeof(uintptr_t)
|
||||
+ v->len
|
||||
+ sizeof(uintptr_t) - 1)
|
||||
& ~(sizeof(uintptr_t) - 1);
|
||||
|
||||
if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
pf = &flushes;
|
||||
pl = &lengths;
|
||||
pv = &values;
|
||||
|
||||
ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));
|
||||
|
||||
sc.cf = ccv->cf;
|
||||
sc.source = v;
|
||||
sc.flushes = &pf;
|
||||
sc.lengths = &pl;
|
||||
sc.values = &pv;
|
||||
sc.complete_lengths = 1;
|
||||
sc.complete_values = 1;
|
||||
sc.zero = ccv->zero;
|
||||
sc.conf_prefix = ccv->conf_prefix;
|
||||
sc.root_prefix = ccv->root_prefix;
|
||||
|
||||
if (ngx_stream_script_compile(&sc) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (flushes.nelts) {
|
||||
ccv->complex_value->flushes = flushes.elts;
|
||||
ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;
|
||||
}
|
||||
|
||||
ccv->complex_value->lengths = lengths.elts;
|
||||
ccv->complex_value->values = values.elts;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf)
|
||||
{
|
||||
char *p = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_stream_complex_value_t **cv;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
cv = (ngx_stream_complex_value_t **) (p + cmd->offset);
|
||||
|
||||
if (*cv != NULL) {
|
||||
return "duplicate";
|
||||
}
|
||||
|
||||
*cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t));
|
||||
if (*cv == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = *cv;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_uint_t
|
||||
ngx_stream_script_variables_count(ngx_str_t *value)
|
||||
{
|
||||
ngx_uint_t i, n;
|
||||
|
||||
for (n = 0, i = 0; i < value->len; i++) {
|
||||
if (value->data[i] == '$') {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_script_compile(ngx_stream_script_compile_t *sc)
|
||||
{
|
||||
u_char ch;
|
||||
ngx_str_t name;
|
||||
ngx_uint_t i, bracket;
|
||||
|
||||
if (ngx_stream_script_init_arrays(sc) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < sc->source->len; /* void */ ) {
|
||||
|
||||
name.len = 0;
|
||||
|
||||
if (sc->source->data[i] == '$') {
|
||||
|
||||
if (++i == sc->source->len) {
|
||||
goto invalid_variable;
|
||||
}
|
||||
|
||||
if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
|
||||
#if (NGX_PCRE)
|
||||
ngx_uint_t n;
|
||||
|
||||
n = sc->source->data[i] - '0';
|
||||
|
||||
if (ngx_stream_script_add_capture_code(sc, n) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
continue;
|
||||
#else
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
|
||||
"using variable \"$%c\" requires "
|
||||
"PCRE library", sc->source->data[i]);
|
||||
return NGX_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sc->source->data[i] == '{') {
|
||||
bracket = 1;
|
||||
|
||||
if (++i == sc->source->len) {
|
||||
goto invalid_variable;
|
||||
}
|
||||
|
||||
name.data = &sc->source->data[i];
|
||||
|
||||
} else {
|
||||
bracket = 0;
|
||||
name.data = &sc->source->data[i];
|
||||
}
|
||||
|
||||
for ( /* void */ ; i < sc->source->len; i++, name.len++) {
|
||||
ch = sc->source->data[i];
|
||||
|
||||
if (ch == '}' && bracket) {
|
||||
i++;
|
||||
bracket = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| ch == '_')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (bracket) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
|
||||
"the closing bracket in \"%V\" "
|
||||
"variable is missing", &name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (name.len == 0) {
|
||||
goto invalid_variable;
|
||||
}
|
||||
|
||||
sc->variables++;
|
||||
|
||||
if (ngx_stream_script_add_var_code(sc, &name) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
name.data = &sc->source->data[i];
|
||||
|
||||
while (i < sc->source->len) {
|
||||
|
||||
if (sc->source->data[i] == '$') {
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
name.len++;
|
||||
}
|
||||
|
||||
sc->size += name.len;
|
||||
|
||||
if (ngx_stream_script_add_copy_code(sc, &name, (i == sc->source->len))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return ngx_stream_script_done(sc);
|
||||
|
||||
invalid_variable:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
u_char *
|
||||
ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value,
|
||||
void *code_lengths, size_t len, void *code_values)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_stream_script_code_pt code;
|
||||
ngx_stream_script_engine_t e;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
ngx_stream_script_len_code_pt lcode;
|
||||
|
||||
cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);
|
||||
|
||||
for (i = 0; i < cmcf->variables.nelts; i++) {
|
||||
if (s->variables[i].no_cacheable) {
|
||||
s->variables[i].valid = 0;
|
||||
s->variables[i].not_found = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_memzero(&e, sizeof(ngx_stream_script_engine_t));
|
||||
|
||||
e.ip = code_lengths;
|
||||
e.session = s;
|
||||
e.flushed = 1;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
lcode = *(ngx_stream_script_len_code_pt *) e.ip;
|
||||
len += lcode(&e);
|
||||
}
|
||||
|
||||
|
||||
value->len = len;
|
||||
value->data = ngx_pnalloc(s->connection->pool, len);
|
||||
if (value->data == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
e.ip = code_values;
|
||||
e.pos = value->data;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_stream_script_code_pt *) e.ip;
|
||||
code((ngx_stream_script_engine_t *) &e);
|
||||
}
|
||||
|
||||
return e.pos;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s,
|
||||
ngx_array_t *indices)
|
||||
{
|
||||
ngx_uint_t n, *index;
|
||||
|
||||
if (indices) {
|
||||
index = indices->elts;
|
||||
for (n = 0; n < indices->nelts; n++) {
|
||||
if (s->variables[index[n]].no_cacheable) {
|
||||
s->variables[index[n]].valid = 0;
|
||||
s->variables[index[n]].not_found = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_init_arrays(ngx_stream_script_compile_t *sc)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
|
||||
if (sc->flushes && *sc->flushes == NULL) {
|
||||
n = sc->variables ? sc->variables : 1;
|
||||
*sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
|
||||
if (*sc->flushes == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (*sc->lengths == NULL) {
|
||||
n = sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t)
|
||||
+ sizeof(ngx_stream_script_var_code_t))
|
||||
+ sizeof(uintptr_t);
|
||||
|
||||
*sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
|
||||
if (*sc->lengths == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (*sc->values == NULL) {
|
||||
n = (sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t)
|
||||
+ sizeof(ngx_stream_script_var_code_t))
|
||||
+ sizeof(uintptr_t)
|
||||
+ sc->source->len
|
||||
+ sizeof(uintptr_t) - 1)
|
||||
& ~(sizeof(uintptr_t) - 1);
|
||||
|
||||
*sc->values = ngx_array_create(sc->cf->pool, n, 1);
|
||||
if (*sc->values == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sc->variables = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_done(ngx_stream_script_compile_t *sc)
|
||||
{
|
||||
ngx_str_t zero;
|
||||
uintptr_t *code;
|
||||
|
||||
if (sc->zero) {
|
||||
|
||||
zero.len = 1;
|
||||
zero.data = (u_char *) "\0";
|
||||
|
||||
if (ngx_stream_script_add_copy_code(sc, &zero, 0) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->conf_prefix || sc->root_prefix) {
|
||||
if (ngx_stream_script_add_full_name_code(sc) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->complete_lengths) {
|
||||
code = ngx_stream_script_add_code(*sc->lengths, sizeof(uintptr_t),
|
||||
NULL);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*code = (uintptr_t) NULL;
|
||||
}
|
||||
|
||||
if (sc->complete_values) {
|
||||
code = ngx_stream_script_add_code(*sc->values, sizeof(uintptr_t),
|
||||
&sc->main);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*code = (uintptr_t) NULL;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code)
|
||||
{
|
||||
u_char *elts, **p;
|
||||
void *new;
|
||||
|
||||
elts = codes->elts;
|
||||
|
||||
new = ngx_array_push_n(codes, size);
|
||||
if (new == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (code) {
|
||||
if (elts != codes->elts) {
|
||||
p = code;
|
||||
*p += (u_char *) codes->elts - elts;
|
||||
}
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_add_copy_code(ngx_stream_script_compile_t *sc,
|
||||
ngx_str_t *value, ngx_uint_t last)
|
||||
{
|
||||
u_char *p;
|
||||
size_t size, len, zero;
|
||||
ngx_stream_script_copy_code_t *code;
|
||||
|
||||
zero = (sc->zero && last);
|
||||
len = value->len + zero;
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->lengths,
|
||||
sizeof(ngx_stream_script_copy_code_t),
|
||||
NULL);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = (ngx_stream_script_code_pt) ngx_stream_script_copy_len_code;
|
||||
code->len = len;
|
||||
|
||||
size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
|
||||
& ~(sizeof(uintptr_t) - 1);
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->values, size, &sc->main);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = ngx_stream_script_copy_code;
|
||||
code->len = len;
|
||||
|
||||
p = ngx_cpymem((u_char *) code + sizeof(ngx_stream_script_copy_code_t),
|
||||
value->data, value->len);
|
||||
|
||||
if (zero) {
|
||||
*p = '\0';
|
||||
sc->zero = 0;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
ngx_stream_script_copy_code_t *code;
|
||||
|
||||
code = (ngx_stream_script_copy_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_copy_code_t);
|
||||
|
||||
return code->len;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_script_copy_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
u_char *p;
|
||||
ngx_stream_script_copy_code_t *code;
|
||||
|
||||
code = (ngx_stream_script_copy_code_t *) e->ip;
|
||||
|
||||
p = e->pos;
|
||||
|
||||
if (!e->skip) {
|
||||
e->pos = ngx_copy(p, e->ip + sizeof(ngx_stream_script_copy_code_t),
|
||||
code->len);
|
||||
}
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_copy_code_t)
|
||||
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,
|
||||
"stream script copy: \"%*s\"", e->pos - p, p);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_add_var_code(ngx_stream_script_compile_t *sc, ngx_str_t *name)
|
||||
{
|
||||
ngx_int_t index, *p;
|
||||
ngx_stream_script_var_code_t *code;
|
||||
|
||||
index = ngx_stream_get_variable_index(sc->cf, name);
|
||||
|
||||
if (index == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (sc->flushes) {
|
||||
p = ngx_array_push(*sc->flushes);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*p = index;
|
||||
}
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->lengths,
|
||||
sizeof(ngx_stream_script_var_code_t),
|
||||
NULL);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = (ngx_stream_script_code_pt)
|
||||
ngx_stream_script_copy_var_len_code;
|
||||
code->index = (uintptr_t) index;
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->values,
|
||||
sizeof(ngx_stream_script_var_code_t),
|
||||
&sc->main);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = ngx_stream_script_copy_var_code;
|
||||
code->index = (uintptr_t) index;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
ngx_stream_variable_value_t *value;
|
||||
ngx_stream_script_var_code_t *code;
|
||||
|
||||
code = (ngx_stream_script_var_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_var_code_t);
|
||||
|
||||
if (e->flushed) {
|
||||
value = ngx_stream_get_indexed_variable(e->session, code->index);
|
||||
|
||||
} else {
|
||||
value = ngx_stream_get_flushed_variable(e->session, code->index);
|
||||
}
|
||||
|
||||
if (value && !value->not_found) {
|
||||
return value->len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
u_char *p;
|
||||
ngx_stream_variable_value_t *value;
|
||||
ngx_stream_script_var_code_t *code;
|
||||
|
||||
code = (ngx_stream_script_var_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_var_code_t);
|
||||
|
||||
if (!e->skip) {
|
||||
|
||||
if (e->flushed) {
|
||||
value = ngx_stream_get_indexed_variable(e->session, code->index);
|
||||
|
||||
} else {
|
||||
value = ngx_stream_get_flushed_variable(e->session, code->index);
|
||||
}
|
||||
|
||||
if (value && !value->not_found) {
|
||||
p = e->pos;
|
||||
e->pos = ngx_copy(p, value->data, value->len);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM,
|
||||
e->session->connection->log, 0,
|
||||
"stream script var: \"%*s\"", e->pos - p, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_add_capture_code(ngx_stream_script_compile_t *sc,
|
||||
ngx_uint_t n)
|
||||
{
|
||||
ngx_stream_script_copy_capture_code_t *code;
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->lengths,
|
||||
sizeof(ngx_stream_script_copy_capture_code_t),
|
||||
NULL);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = (ngx_stream_script_code_pt)
|
||||
ngx_stream_script_copy_capture_len_code;
|
||||
code->n = 2 * n;
|
||||
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->values,
|
||||
sizeof(ngx_stream_script_copy_capture_code_t),
|
||||
&sc->main);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = ngx_stream_script_copy_capture_code;
|
||||
code->n = 2 * n;
|
||||
|
||||
if (sc->ncaptures < n) {
|
||||
sc->ncaptures = n;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
int *cap;
|
||||
ngx_uint_t n;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_script_copy_capture_code_t *code;
|
||||
|
||||
s = e->session;
|
||||
|
||||
code = (ngx_stream_script_copy_capture_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_copy_capture_code_t);
|
||||
|
||||
n = code->n;
|
||||
|
||||
if (n < s->ncaptures) {
|
||||
cap = s->captures;
|
||||
return cap[n + 1] - cap[n];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
int *cap;
|
||||
u_char *p, *pos;
|
||||
ngx_uint_t n;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_script_copy_capture_code_t *code;
|
||||
|
||||
s = e->session;
|
||||
|
||||
code = (ngx_stream_script_copy_capture_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_copy_capture_code_t);
|
||||
|
||||
n = code->n;
|
||||
|
||||
pos = e->pos;
|
||||
|
||||
if (n < s->ncaptures) {
|
||||
cap = s->captures;
|
||||
p = s->captures_data;
|
||||
e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,
|
||||
"stream script capture: \"%*s\"", e->pos - pos, pos);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_script_add_full_name_code(ngx_stream_script_compile_t *sc)
|
||||
{
|
||||
ngx_stream_script_full_name_code_t *code;
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->lengths,
|
||||
sizeof(ngx_stream_script_full_name_code_t),
|
||||
NULL);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = (ngx_stream_script_code_pt)
|
||||
ngx_stream_script_full_name_len_code;
|
||||
code->conf_prefix = sc->conf_prefix;
|
||||
|
||||
code = ngx_stream_script_add_code(*sc->values,
|
||||
sizeof(ngx_stream_script_full_name_code_t), &sc->main);
|
||||
if (code == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
code->code = ngx_stream_script_full_name_code;
|
||||
code->conf_prefix = sc->conf_prefix;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ngx_stream_script_full_name_len_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
ngx_stream_script_full_name_code_t *code;
|
||||
|
||||
code = (ngx_stream_script_full_name_code_t *) e->ip;
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_full_name_code_t);
|
||||
|
||||
return code->conf_prefix ? ngx_cycle->conf_prefix.len:
|
||||
ngx_cycle->prefix.len;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e)
|
||||
{
|
||||
ngx_stream_script_full_name_code_t *code;
|
||||
|
||||
ngx_str_t value, *prefix;
|
||||
|
||||
code = (ngx_stream_script_full_name_code_t *) e->ip;
|
||||
|
||||
value.data = e->buf.data;
|
||||
value.len = e->pos - e->buf.data;
|
||||
|
||||
prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:
|
||||
(ngx_str_t *) &ngx_cycle->prefix;
|
||||
|
||||
if (ngx_get_full_name(e->session->connection->pool, prefix, &value)
|
||||
!= NGX_OK)
|
||||
{
|
||||
e->ip = ngx_stream_script_exit;
|
||||
return;
|
||||
}
|
||||
|
||||
e->buf = value;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,
|
||||
"stream script fullname: \"%V\"", &value);
|
||||
|
||||
e->ip += sizeof(ngx_stream_script_full_name_code_t);
|
||||
}
|
||||
127
src/stream/ngx_stream_script.h
Normal file
127
src/stream/ngx_stream_script.h
Normal file
@@ -0,0 +1,127 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_SCRIPT_H_INCLUDED_
|
||||
#define _NGX_STREAM_SCRIPT_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char *ip;
|
||||
u_char *pos;
|
||||
ngx_stream_variable_value_t *sp;
|
||||
|
||||
ngx_str_t buf;
|
||||
ngx_str_t line;
|
||||
|
||||
unsigned flushed:1;
|
||||
unsigned skip:1;
|
||||
|
||||
ngx_stream_session_t *session;
|
||||
} ngx_stream_script_engine_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_conf_t *cf;
|
||||
ngx_str_t *source;
|
||||
|
||||
ngx_array_t **flushes;
|
||||
ngx_array_t **lengths;
|
||||
ngx_array_t **values;
|
||||
|
||||
ngx_uint_t variables;
|
||||
ngx_uint_t ncaptures;
|
||||
ngx_uint_t size;
|
||||
|
||||
void *main;
|
||||
|
||||
unsigned complete_lengths:1;
|
||||
unsigned complete_values:1;
|
||||
unsigned zero:1;
|
||||
unsigned conf_prefix:1;
|
||||
unsigned root_prefix:1;
|
||||
} ngx_stream_script_compile_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t value;
|
||||
ngx_uint_t *flushes;
|
||||
void *lengths;
|
||||
void *values;
|
||||
} ngx_stream_complex_value_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_conf_t *cf;
|
||||
ngx_str_t *value;
|
||||
ngx_stream_complex_value_t *complex_value;
|
||||
|
||||
unsigned zero:1;
|
||||
unsigned conf_prefix:1;
|
||||
unsigned root_prefix:1;
|
||||
} ngx_stream_compile_complex_value_t;
|
||||
|
||||
|
||||
typedef void (*ngx_stream_script_code_pt) (ngx_stream_script_engine_t *e);
|
||||
typedef size_t (*ngx_stream_script_len_code_pt) (ngx_stream_script_engine_t *e);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_script_code_pt code;
|
||||
uintptr_t len;
|
||||
} ngx_stream_script_copy_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_script_code_pt code;
|
||||
uintptr_t index;
|
||||
} ngx_stream_script_var_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_script_code_pt code;
|
||||
uintptr_t n;
|
||||
} ngx_stream_script_copy_capture_code_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_script_code_pt code;
|
||||
uintptr_t conf_prefix;
|
||||
} ngx_stream_script_full_name_code_t;
|
||||
|
||||
|
||||
void ngx_stream_script_flush_complex_value(ngx_stream_session_t *s,
|
||||
ngx_stream_complex_value_t *val);
|
||||
ngx_int_t ngx_stream_complex_value(ngx_stream_session_t *s,
|
||||
ngx_stream_complex_value_t *val, ngx_str_t *value);
|
||||
ngx_int_t ngx_stream_compile_complex_value(
|
||||
ngx_stream_compile_complex_value_t *ccv);
|
||||
char *ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
ngx_uint_t ngx_stream_script_variables_count(ngx_str_t *value);
|
||||
ngx_int_t ngx_stream_script_compile(ngx_stream_script_compile_t *sc);
|
||||
u_char *ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value,
|
||||
void *code_lengths, size_t reserved, void *code_values);
|
||||
void ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s,
|
||||
ngx_array_t *indices);
|
||||
|
||||
void *ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code);
|
||||
|
||||
size_t ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e);
|
||||
void ngx_stream_script_copy_code(ngx_stream_script_engine_t *e);
|
||||
size_t ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e);
|
||||
void ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e);
|
||||
size_t ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e);
|
||||
void ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e);
|
||||
|
||||
#endif /* _NGX_STREAM_SCRIPT_H_INCLUDED_ */
|
||||
244
src/stream/ngx_stream_split_clients_module.c
Normal file
244
src/stream/ngx_stream_split_clients_module.c
Normal file
@@ -0,0 +1,244 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t percent;
|
||||
ngx_stream_variable_value_t value;
|
||||
} ngx_stream_split_clients_part_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_complex_value_t value;
|
||||
ngx_array_t parts;
|
||||
} ngx_stream_split_clients_ctx_t;
|
||||
|
||||
|
||||
static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
|
||||
void *conf);
|
||||
|
||||
static ngx_command_t ngx_stream_split_clients_commands[] = {
|
||||
|
||||
{ ngx_string("split_clients"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
|
||||
ngx_conf_split_clients_block,
|
||||
NGX_STREAM_MAIN_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_split_clients_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_split_clients_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_split_clients_module_ctx, /* module context */
|
||||
ngx_stream_split_clients_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_split_clients_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_stream_split_clients_ctx_t *ctx =
|
||||
(ngx_stream_split_clients_ctx_t *) data;
|
||||
|
||||
uint32_t hash;
|
||||
ngx_str_t val;
|
||||
ngx_uint_t i;
|
||||
ngx_stream_split_clients_part_t *part;
|
||||
|
||||
*v = ngx_stream_variable_null_value;
|
||||
|
||||
if (ngx_stream_complex_value(s, &ctx->value, &val) != NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
hash = ngx_murmur_hash2(val.data, val.len);
|
||||
|
||||
part = ctx->parts.elts;
|
||||
|
||||
for (i = 0; i < ctx->parts.nelts; i++) {
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"stream split: %uD %uD", hash, part[i].percent);
|
||||
|
||||
if (hash < part[i].percent || part[i].percent == 0) {
|
||||
*v = part[i].value;
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
char *rv;
|
||||
uint32_t sum, last;
|
||||
ngx_str_t *value, name;
|
||||
ngx_uint_t i;
|
||||
ngx_conf_t save;
|
||||
ngx_stream_variable_t *var;
|
||||
ngx_stream_split_clients_ctx_t *ctx;
|
||||
ngx_stream_split_clients_part_t *part;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_split_clients_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &ctx->value;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name = value[2];
|
||||
|
||||
if (name.data[0] != '$') {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid variable name \"%V\"", &name);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
name.len--;
|
||||
name.data++;
|
||||
|
||||
var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);
|
||||
if (var == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = ngx_stream_split_clients_variable;
|
||||
var->data = (uintptr_t) ctx;
|
||||
|
||||
if (ngx_array_init(&ctx->parts, cf->pool, 2,
|
||||
sizeof(ngx_stream_split_clients_part_t))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
save = *cf;
|
||||
cf->ctx = ctx;
|
||||
cf->handler = ngx_stream_split_clients;
|
||||
cf->handler_conf = conf;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = save;
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
last = 0;
|
||||
part = ctx->parts.elts;
|
||||
|
||||
for (i = 0; i < ctx->parts.nelts; i++) {
|
||||
sum = part[i].percent ? sum + part[i].percent : 10000;
|
||||
if (sum > 10000) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"percent total is greater than 100%%");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (part[i].percent) {
|
||||
last += part[i].percent * (uint64_t) 0xffffffff / 10000;
|
||||
part[i].percent = last;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
{
|
||||
ngx_int_t n;
|
||||
ngx_str_t *value;
|
||||
ngx_stream_split_clients_ctx_t *ctx;
|
||||
ngx_stream_split_clients_part_t *part;
|
||||
|
||||
ctx = cf->ctx;
|
||||
value = cf->args->elts;
|
||||
|
||||
part = ngx_array_push(&ctx->parts);
|
||||
if (part == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (value[0].len == 1 && value[0].data[0] == '*') {
|
||||
part->percent = 0;
|
||||
|
||||
} else {
|
||||
if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
n = ngx_atofp(value[0].data, value[0].len - 1, 2);
|
||||
if (n == NGX_ERROR || n == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
part->percent = (uint32_t) n;
|
||||
}
|
||||
|
||||
part->value.len = value[1].len;
|
||||
part->value.valid = 1;
|
||||
part->value.no_cacheable = 0;
|
||||
part->value.not_found = 0;
|
||||
part->value.data = value[1].data;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid percent value \"%V\"", &value[0]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
839
src/stream/ngx_stream_ssl_module.c
Normal file
839
src/stream/ngx_stream_ssl_module.c
Normal file
@@ -0,0 +1,839 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
|
||||
ngx_pool_t *pool, ngx_str_t *s);
|
||||
|
||||
|
||||
#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
|
||||
#define NGX_DEFAULT_ECDH_CURVE "auto"
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_ssl_handler(ngx_stream_session_t *s);
|
||||
static ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl,
|
||||
ngx_connection_t *c);
|
||||
static void ngx_stream_ssl_handshake_handler(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
|
||||
static ngx_int_t ngx_stream_ssl_add_variables(ngx_conf_t *cf);
|
||||
static void *ngx_stream_ssl_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
|
||||
static char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_conf_bitmask_t ngx_stream_ssl_protocols[] = {
|
||||
{ ngx_string("SSLv2"), NGX_SSL_SSLv2 },
|
||||
{ ngx_string("SSLv3"), NGX_SSL_SSLv3 },
|
||||
{ ngx_string("TLSv1"), NGX_SSL_TLSv1 },
|
||||
{ ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
|
||||
{ ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_stream_ssl_verify[] = {
|
||||
{ ngx_string("off"), 0 },
|
||||
{ ngx_string("on"), 1 },
|
||||
{ ngx_string("optional"), 2 },
|
||||
{ ngx_string("optional_no_ca"), 3 },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_ssl_commands[] = {
|
||||
|
||||
{ ngx_string("ssl_handshake_timeout"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_msec_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, handshake_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, certificates),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate_key"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, certificate_keys),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_password_file"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_stream_ssl_password_file,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_dhparam"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, dhparam),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_ecdh_curve"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, ecdh_curve),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_protocols"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
|
||||
ngx_conf_set_bitmask_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, protocols),
|
||||
&ngx_stream_ssl_protocols },
|
||||
|
||||
{ ngx_string("ssl_ciphers"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, ciphers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_verify_client"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, verify),
|
||||
&ngx_stream_ssl_verify },
|
||||
|
||||
{ ngx_string("ssl_verify_depth"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_num_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, verify_depth),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_client_certificate"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, client_certificate),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_trusted_certificate"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, trusted_certificate),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_prefer_server_ciphers"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, prefer_server_ciphers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_cache"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_ssl_session_cache,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_tickets"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, session_tickets),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_ticket_key"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, session_ticket_keys),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_session_timeout"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_sec_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, session_timeout),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_crl"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_conf_t, crl),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_ssl_module_ctx = {
|
||||
ngx_stream_ssl_add_variables, /* preconfiguration */
|
||||
ngx_stream_ssl_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_ssl_create_conf, /* create server configuration */
|
||||
ngx_stream_ssl_merge_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_ssl_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_ssl_module_ctx, /* module context */
|
||||
ngx_stream_ssl_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_variable_t ngx_stream_ssl_vars[] = {
|
||||
|
||||
{ ngx_string("ssl_protocol"), NULL, ngx_stream_ssl_static_variable,
|
||||
(uintptr_t) ngx_ssl_get_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_cipher"), NULL, ngx_stream_ssl_static_variable,
|
||||
(uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ciphers"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_curves"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_id"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_reused"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_reused, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_server_name"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_server_name, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_cert"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_raw_cert"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_raw_certificate,
|
||||
NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_s_dn"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_subject_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_i_dn"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_issuer_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_serial"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_serial_number, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_fingerprint"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_fingerprint, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_verify"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_verify, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_v_start"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_v_start, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_v_end"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_v_end, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_v_remain"), NULL, ngx_stream_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string("STREAM");
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
long rc;
|
||||
X509 *cert;
|
||||
ngx_int_t rv;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_ssl_conf_t *sslcf;
|
||||
|
||||
if (!s->ssl) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
c = s->connection;
|
||||
|
||||
sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
|
||||
|
||||
if (c->ssl == NULL) {
|
||||
c->log->action = "SSL handshaking";
|
||||
|
||||
if (sslcf->ssl.ctx == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||
"no \"ssl_certificate\" is defined "
|
||||
"in server listening on SSL port");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rv = ngx_stream_ssl_init_connection(&sslcf->ssl, c);
|
||||
|
||||
if (rv != NGX_OK) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (sslcf->verify) {
|
||||
rc = SSL_get_verify_result(c->ssl->connection);
|
||||
|
||||
if (rc != X509_V_OK
|
||||
&& (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client SSL certificate verify error: (%l:%s)",
|
||||
rc, X509_verify_cert_error_string(rc));
|
||||
|
||||
ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
|
||||
(SSL_get0_session(c->ssl->connection)));
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (sslcf->verify == 1) {
|
||||
cert = SSL_get_peer_certificate(c->ssl->connection);
|
||||
|
||||
if (cert == NULL) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"client sent no required SSL certificate");
|
||||
|
||||
ngx_ssl_remove_cached_session(sslcf->ssl.ctx,
|
||||
(SSL_get0_session(c->ssl->connection)));
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_stream_session_t *s;
|
||||
ngx_stream_ssl_conf_t *sslcf;
|
||||
|
||||
s = c->data;
|
||||
|
||||
if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rc = ngx_ssl_handshake(c);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);
|
||||
|
||||
ngx_add_timer(c->read, sslcf->handshake_timeout);
|
||||
|
||||
c->ssl->handler = ngx_stream_ssl_handshake_handler;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
/* rc == NGX_OK */
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_ssl_handshake_handler(ngx_connection_t *c)
|
||||
{
|
||||
ngx_stream_session_t *s;
|
||||
|
||||
s = c->data;
|
||||
|
||||
if (!c->ssl->handshaked) {
|
||||
ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c->read->timer_set) {
|
||||
ngx_del_timer(c->read);
|
||||
}
|
||||
|
||||
ngx_stream_core_run_phases(s);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_static_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
|
||||
|
||||
size_t len;
|
||||
ngx_str_t str;
|
||||
|
||||
if (s->connection->ssl) {
|
||||
|
||||
(void) handler(s->connection, NULL, &str);
|
||||
|
||||
v->data = str.data;
|
||||
|
||||
for (len = 0; v->data[len]; len++) { /* void */ }
|
||||
|
||||
v->len = len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
|
||||
|
||||
ngx_str_t str;
|
||||
|
||||
if (s->connection->ssl) {
|
||||
|
||||
if (handler(s->connection, s->connection->pool, &str) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->len = str.len;
|
||||
v->data = str.data;
|
||||
|
||||
if (v->len) {
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
v->not_found = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_stream_ssl_vars; v->name.len; v++) {
|
||||
var = ngx_stream_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_ssl_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_ssl_conf_t *scf;
|
||||
|
||||
scf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_conf_t));
|
||||
if (scf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* scf->protocols = 0;
|
||||
* scf->dhparam = { 0, NULL };
|
||||
* scf->ecdh_curve = { 0, NULL };
|
||||
* scf->client_certificate = { 0, NULL };
|
||||
* scf->trusted_certificate = { 0, NULL };
|
||||
* scf->crl = { 0, NULL };
|
||||
* scf->ciphers = { 0, NULL };
|
||||
* scf->shm_zone = NULL;
|
||||
*/
|
||||
|
||||
scf->handshake_timeout = NGX_CONF_UNSET_MSEC;
|
||||
scf->certificates = NGX_CONF_UNSET_PTR;
|
||||
scf->certificate_keys = NGX_CONF_UNSET_PTR;
|
||||
scf->passwords = NGX_CONF_UNSET_PTR;
|
||||
scf->prefer_server_ciphers = NGX_CONF_UNSET;
|
||||
scf->verify = NGX_CONF_UNSET_UINT;
|
||||
scf->verify_depth = NGX_CONF_UNSET_UINT;
|
||||
scf->builtin_session_cache = NGX_CONF_UNSET;
|
||||
scf->session_timeout = NGX_CONF_UNSET;
|
||||
scf->session_tickets = NGX_CONF_UNSET;
|
||||
scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
|
||||
|
||||
return scf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_ssl_conf_t *prev = parent;
|
||||
ngx_stream_ssl_conf_t *conf = child;
|
||||
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
ngx_conf_merge_msec_value(conf->handshake_timeout,
|
||||
prev->handshake_timeout, 60000);
|
||||
|
||||
ngx_conf_merge_value(conf->session_timeout,
|
||||
prev->session_timeout, 300);
|
||||
|
||||
ngx_conf_merge_value(conf->prefer_server_ciphers,
|
||||
prev->prefer_server_ciphers, 0);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1
|
||||
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
|
||||
|
||||
ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
|
||||
ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
|
||||
NULL);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
|
||||
|
||||
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
|
||||
"");
|
||||
ngx_conf_merge_str_value(conf->trusted_certificate,
|
||||
prev->trusted_certificate, "");
|
||||
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
|
||||
|
||||
ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
|
||||
NGX_DEFAULT_ECDH_CURVE);
|
||||
|
||||
ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
|
||||
|
||||
|
||||
conf->ssl.log = cf->log;
|
||||
|
||||
if (conf->certificates == NULL) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (conf->certificate_keys == NULL
|
||||
|| conf->certificate_keys->nelts < conf->certificates->nelts)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no \"ssl_certificate_key\" is defined "
|
||||
"for certificate \"%V\"",
|
||||
((ngx_str_t *) conf->certificates->elts)
|
||||
+ conf->certificates->nelts - 1);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
cln = ngx_pool_cleanup_add(cf->pool, 0);
|
||||
if (cln == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
cln->handler = ngx_ssl_cleanup_ctx;
|
||||
cln->data = &conf->ssl;
|
||||
|
||||
if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
|
||||
conf->certificate_keys, conf->passwords)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,
|
||||
conf->prefer_server_ciphers)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (conf->verify) {
|
||||
|
||||
if (conf->client_certificate.len == 0 && conf->verify != 3) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no ssl_client_certificate for ssl_client_verify");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_client_certificate(cf, &conf->ssl,
|
||||
&conf->client_certificate,
|
||||
conf->verify_depth)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
|
||||
&conf->trusted_certificate,
|
||||
conf->verify_depth)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_value(conf->builtin_session_cache,
|
||||
prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
|
||||
|
||||
if (conf->shm_zone == NULL) {
|
||||
conf->shm_zone = prev->shm_zone;
|
||||
}
|
||||
|
||||
if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx,
|
||||
conf->builtin_session_cache,
|
||||
conf->shm_zone, conf->session_timeout)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_value(conf->session_tickets,
|
||||
prev->session_tickets, 1);
|
||||
|
||||
#ifdef SSL_OP_NO_TICKET
|
||||
if (!conf->session_tickets) {
|
||||
SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
|
||||
}
|
||||
#endif
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->session_ticket_keys,
|
||||
prev->session_ticket_keys, NULL);
|
||||
|
||||
if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_ssl_conf_t *scf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
|
||||
if (scf->passwords != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
|
||||
|
||||
if (scf->passwords == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_ssl_conf_t *scf = conf;
|
||||
|
||||
size_t len;
|
||||
ngx_str_t *value, name, size;
|
||||
ngx_int_t n;
|
||||
ngx_uint_t i, j;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strcmp(value[i].data, "off") == 0) {
|
||||
scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "none") == 0) {
|
||||
scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "builtin") == 0) {
|
||||
scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i].len > sizeof("builtin:") - 1
|
||||
&& ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
|
||||
== 0)
|
||||
{
|
||||
n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
|
||||
value[i].len - (sizeof("builtin:") - 1));
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
scf->builtin_session_cache = n;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[i].len > sizeof("shared:") - 1
|
||||
&& ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
|
||||
== 0)
|
||||
{
|
||||
len = 0;
|
||||
|
||||
for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
|
||||
if (value[i].data[j] == ':') {
|
||||
break;
|
||||
}
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
name.len = len;
|
||||
name.data = value[i].data + sizeof("shared:") - 1;
|
||||
|
||||
size.len = value[i].len - j - 1;
|
||||
size.data = name.data + len + 1;
|
||||
|
||||
n = ngx_parse_size(&size);
|
||||
|
||||
if (n == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (n < (ngx_int_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"session cache \"%V\" is too small",
|
||||
&value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
|
||||
&ngx_stream_ssl_module);
|
||||
if (scf->shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
scf->shm_zone->init = ngx_ssl_session_cache_init;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
|
||||
scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid session cache \"%V\"", &value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_stream_ssl_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
56
src/stream/ngx_stream_ssl_module.h
Normal file
56
src/stream/ngx_stream_ssl_module.h
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_SSL_H_INCLUDED_
|
||||
#define _NGX_STREAM_SSL_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_msec_t handshake_timeout;
|
||||
|
||||
ngx_flag_t prefer_server_ciphers;
|
||||
|
||||
ngx_ssl_t ssl;
|
||||
|
||||
ngx_uint_t protocols;
|
||||
|
||||
ngx_uint_t verify;
|
||||
ngx_uint_t verify_depth;
|
||||
|
||||
ssize_t builtin_session_cache;
|
||||
|
||||
time_t session_timeout;
|
||||
|
||||
ngx_array_t *certificates;
|
||||
ngx_array_t *certificate_keys;
|
||||
|
||||
ngx_str_t dhparam;
|
||||
ngx_str_t ecdh_curve;
|
||||
ngx_str_t client_certificate;
|
||||
ngx_str_t trusted_certificate;
|
||||
ngx_str_t crl;
|
||||
|
||||
ngx_str_t ciphers;
|
||||
|
||||
ngx_array_t *passwords;
|
||||
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
|
||||
ngx_flag_t session_tickets;
|
||||
ngx_array_t *session_ticket_keys;
|
||||
} ngx_stream_ssl_conf_t;
|
||||
|
||||
|
||||
extern ngx_module_t ngx_stream_ssl_module;
|
||||
|
||||
|
||||
#endif /* _NGX_STREAM_SSL_H_INCLUDED_ */
|
||||
449
src/stream/ngx_stream_ssl_preread_module.c
Normal file
449
src/stream/ngx_stream_ssl_preread_module.c
Normal file
@@ -0,0 +1,449 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_flag_t enabled;
|
||||
} ngx_stream_ssl_preread_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t left;
|
||||
size_t size;
|
||||
u_char *pos;
|
||||
u_char *dst;
|
||||
u_char buf[4];
|
||||
ngx_str_t host;
|
||||
ngx_log_t *log;
|
||||
ngx_pool_t *pool;
|
||||
ngx_uint_t state;
|
||||
} ngx_stream_ssl_preread_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s);
|
||||
static ngx_int_t ngx_stream_ssl_preread_parse_record(
|
||||
ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);
|
||||
static ngx_int_t ngx_stream_ssl_preread_server_name_variable(
|
||||
ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf);
|
||||
static void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent,
|
||||
void *child);
|
||||
static ngx_int_t ngx_stream_ssl_preread_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_ssl_preread_commands[] = {
|
||||
|
||||
{ ngx_string("ssl_preread"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_stream_ssl_preread_srv_conf_t, enabled),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_ssl_preread_module_ctx = {
|
||||
ngx_stream_ssl_preread_add_variables, /* preconfiguration */
|
||||
ngx_stream_ssl_preread_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_ssl_preread_create_srv_conf, /* create server configuration */
|
||||
ngx_stream_ssl_preread_merge_srv_conf /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_ssl_preread_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_ssl_preread_module_ctx, /* module context */
|
||||
ngx_stream_ssl_preread_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_variable_t ngx_stream_ssl_preread_vars[] = {
|
||||
|
||||
{ ngx_string("ssl_preread_server_name"), NULL,
|
||||
ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_preread_handler(ngx_stream_session_t *s)
|
||||
{
|
||||
u_char *last, *p;
|
||||
size_t len;
|
||||
ngx_int_t rc;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_ssl_preread_ctx_t *ctx;
|
||||
ngx_stream_ssl_preread_srv_conf_t *sscf;
|
||||
|
||||
c = s->connection;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "ssl preread handler");
|
||||
|
||||
sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_preread_module);
|
||||
|
||||
if (!sscf->enabled) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (c->type != SOCK_STREAM) {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (c->buffer == NULL) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ssl_preread_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_stream_set_ctx(s, ctx, ngx_stream_ssl_preread_module);
|
||||
|
||||
ctx->pool = c->pool;
|
||||
ctx->log = c->log;
|
||||
ctx->pos = c->buffer->pos;
|
||||
}
|
||||
|
||||
p = ctx->pos;
|
||||
last = c->buffer->last;
|
||||
|
||||
while (last - p >= 5) {
|
||||
|
||||
if (p[0] != 0x16) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: not a handshake");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (p[1] != 3) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: unsupported SSL version");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
len = (p[3] << 8) + p[4];
|
||||
|
||||
/* read the whole record before parsing */
|
||||
if ((size_t) (last - p) < len + 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
p += 5;
|
||||
|
||||
rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len);
|
||||
if (rc != NGX_AGAIN) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
p += len;
|
||||
}
|
||||
|
||||
ctx->pos = p;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,
|
||||
u_char *pos, u_char *last)
|
||||
{
|
||||
size_t left, n, size;
|
||||
u_char *dst, *p;
|
||||
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_header, /* handshake msg_type, length */
|
||||
sw_head_tail, /* version, random */
|
||||
sw_sid_len, /* session_id length */
|
||||
sw_sid, /* session_id */
|
||||
sw_cs_len, /* cipher_suites length */
|
||||
sw_cs, /* cipher_suites */
|
||||
sw_cm_len, /* compression_methods length */
|
||||
sw_cm, /* compression_methods */
|
||||
sw_ext, /* extension */
|
||||
sw_ext_header, /* extension_type, extension_data length */
|
||||
sw_sni_len, /* SNI length */
|
||||
sw_sni_host_head, /* SNI name_type, host_name length */
|
||||
sw_sni_host /* SNI host_name */
|
||||
} state;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: state %ui left %z", ctx->state, ctx->left);
|
||||
|
||||
state = ctx->state;
|
||||
size = ctx->size;
|
||||
left = ctx->left;
|
||||
dst = ctx->dst;
|
||||
p = ctx->buf;
|
||||
|
||||
for ( ;; ) {
|
||||
n = ngx_min((size_t) (last - pos), size);
|
||||
|
||||
if (dst) {
|
||||
dst = ngx_cpymem(dst, pos, n);
|
||||
}
|
||||
|
||||
pos += n;
|
||||
size -= n;
|
||||
left -= n;
|
||||
|
||||
if (size != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
state = sw_header;
|
||||
dst = p;
|
||||
size = 4;
|
||||
left = size;
|
||||
break;
|
||||
|
||||
case sw_header:
|
||||
if (p[0] != 1) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: not a client hello");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
state = sw_head_tail;
|
||||
dst = NULL;
|
||||
size = 34;
|
||||
left = (p[1] << 16) + (p[2] << 8) + p[3];
|
||||
break;
|
||||
|
||||
case sw_head_tail:
|
||||
state = sw_sid_len;
|
||||
dst = p;
|
||||
size = 1;
|
||||
break;
|
||||
|
||||
case sw_sid_len:
|
||||
state = sw_sid;
|
||||
dst = NULL;
|
||||
size = p[0];
|
||||
break;
|
||||
|
||||
case sw_sid:
|
||||
state = sw_cs_len;
|
||||
dst = p;
|
||||
size = 2;
|
||||
break;
|
||||
|
||||
case sw_cs_len:
|
||||
state = sw_cs;
|
||||
dst = NULL;
|
||||
size = (p[0] << 8) + p[1];
|
||||
break;
|
||||
|
||||
case sw_cs:
|
||||
state = sw_cm_len;
|
||||
dst = p;
|
||||
size = 1;
|
||||
break;
|
||||
|
||||
case sw_cm_len:
|
||||
state = sw_cm;
|
||||
dst = NULL;
|
||||
size = p[0];
|
||||
break;
|
||||
|
||||
case sw_cm:
|
||||
if (left == 0) {
|
||||
/* no extensions */
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
state = sw_ext;
|
||||
dst = p;
|
||||
size = 2;
|
||||
break;
|
||||
|
||||
case sw_ext:
|
||||
if (left == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
state = sw_ext_header;
|
||||
dst = p;
|
||||
size = 4;
|
||||
break;
|
||||
|
||||
case sw_ext_header:
|
||||
if (p[0] == 0 && p[1] == 0) {
|
||||
/* SNI extension */
|
||||
state = sw_sni_len;
|
||||
dst = NULL;
|
||||
size = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_ext;
|
||||
dst = NULL;
|
||||
size = (p[2] << 8) + p[3];
|
||||
break;
|
||||
|
||||
case sw_sni_len:
|
||||
state = sw_sni_host_head;
|
||||
dst = p;
|
||||
size = 3;
|
||||
break;
|
||||
|
||||
case sw_sni_host_head:
|
||||
if (p[0] != 0) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: SNI hostname type is not DNS");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
state = sw_sni_host;
|
||||
size = (p[1] << 8) + p[2];
|
||||
|
||||
ctx->host.data = ngx_pnalloc(ctx->pool, size);
|
||||
if (ctx->host.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
dst = ctx->host.data;
|
||||
break;
|
||||
|
||||
case sw_sni_host:
|
||||
ctx->host.len = (p[1] << 8) + p[2];
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: SNI hostname \"%V\"", &ctx->host);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (left < size) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,
|
||||
"ssl preread: failed to parse handshake");
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->state = state;
|
||||
ctx->size = size;
|
||||
ctx->left = left;
|
||||
ctx->dst = dst;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s,
|
||||
ngx_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_stream_ssl_preread_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
v->len = ctx->host.len;
|
||||
v->data = ctx->host.data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) {
|
||||
var = ngx_stream_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_ssl_preread_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_preread_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->enabled = NGX_CONF_UNSET;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
ngx_stream_ssl_preread_srv_conf_t *prev = parent;
|
||||
ngx_stream_ssl_preread_srv_conf_t *conf = child;
|
||||
|
||||
ngx_conf_merge_value(conf->enabled, prev->enabled, 0);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_ssl_preread_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_handler_pt *h;
|
||||
ngx_stream_core_main_conf_t *cmcf;
|
||||
|
||||
cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
|
||||
|
||||
h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*h = ngx_stream_ssl_preread_handler;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
717
src/stream/ngx_stream_upstream.c
Normal file
717
src/stream/ngx_stream_upstream.c
Normal file
@@ -0,0 +1,717 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_upstream_add_variables(ngx_conf_t *cf);
|
||||
static ngx_int_t ngx_stream_upstream_addr_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_upstream_response_time_variable(
|
||||
ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
|
||||
static char *ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *dummy);
|
||||
static char *ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static void *ngx_stream_upstream_create_main_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_upstream_commands[] = {
|
||||
|
||||
{ ngx_string("upstream"),
|
||||
NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
|
||||
ngx_stream_upstream,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("server"),
|
||||
NGX_STREAM_UPS_CONF|NGX_CONF_1MORE,
|
||||
ngx_stream_upstream_server,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_upstream_module_ctx = {
|
||||
ngx_stream_upstream_add_variables, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
ngx_stream_upstream_create_main_conf, /* create main configuration */
|
||||
ngx_stream_upstream_init_main_conf, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_upstream_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_upstream_module_ctx, /* module context */
|
||||
ngx_stream_upstream_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_variable_t ngx_stream_upstream_vars[] = {
|
||||
|
||||
{ ngx_string("upstream_addr"), NULL,
|
||||
ngx_stream_upstream_addr_variable, 0,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("upstream_bytes_sent"), NULL,
|
||||
ngx_stream_upstream_bytes_variable, 0,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("upstream_connect_time"), NULL,
|
||||
ngx_stream_upstream_response_time_variable, 2,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("upstream_first_byte_time"), NULL,
|
||||
ngx_stream_upstream_response_time_variable, 1,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("upstream_session_time"), NULL,
|
||||
ngx_stream_upstream_response_time_variable, 0,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_string("upstream_bytes_received"), NULL,
|
||||
ngx_stream_upstream_bytes_variable, 1,
|
||||
NGX_STREAM_VAR_NOCACHEABLE, 0 },
|
||||
|
||||
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_add_variables(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_variable_t *var, *v;
|
||||
|
||||
for (v = ngx_stream_upstream_vars; v->name.len; v++) {
|
||||
var = ngx_stream_add_variable(cf, &v->name, v->flags);
|
||||
if (var == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
var->get_handler = v->get_handler;
|
||||
var->data = v->data;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_addr_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_stream_upstream_state_t *state;
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = 0;
|
||||
state = s->upstream_states->elts;
|
||||
|
||||
for (i = 0; i < s->upstream_states->nelts; i++) {
|
||||
if (state[i].peer) {
|
||||
len += state[i].peer->len;
|
||||
}
|
||||
|
||||
len += 2;
|
||||
}
|
||||
|
||||
p = ngx_pnalloc(s->connection->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->data = p;
|
||||
|
||||
i = 0;
|
||||
|
||||
for ( ;; ) {
|
||||
if (state[i].peer) {
|
||||
p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);
|
||||
}
|
||||
|
||||
if (++i == s->upstream_states->nelts) {
|
||||
break;
|
||||
}
|
||||
|
||||
*p++ = ',';
|
||||
*p++ = ' ';
|
||||
}
|
||||
|
||||
v->len = p - v->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_stream_upstream_state_t *state;
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = s->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
|
||||
|
||||
p = ngx_pnalloc(s->connection->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->data = p;
|
||||
|
||||
i = 0;
|
||||
state = s->upstream_states->elts;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (data == 1) {
|
||||
p = ngx_sprintf(p, "%O", state[i].bytes_received);
|
||||
|
||||
} else {
|
||||
p = ngx_sprintf(p, "%O", state[i].bytes_sent);
|
||||
}
|
||||
|
||||
if (++i == s->upstream_states->nelts) {
|
||||
break;
|
||||
}
|
||||
|
||||
*p++ = ',';
|
||||
*p++ = ' ';
|
||||
}
|
||||
|
||||
v->len = p - v->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_response_time_variable(ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_msec_int_t ms;
|
||||
ngx_stream_upstream_state_t *state;
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = s->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);
|
||||
|
||||
p = ngx_pnalloc(s->connection->pool, len);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
v->data = p;
|
||||
|
||||
i = 0;
|
||||
state = s->upstream_states->elts;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (data == 1) {
|
||||
if (state[i].first_byte_time == (ngx_msec_t) -1) {
|
||||
*p++ = '-';
|
||||
goto next;
|
||||
}
|
||||
|
||||
ms = state[i].first_byte_time;
|
||||
|
||||
} else if (data == 2 && state[i].connect_time != (ngx_msec_t) -1) {
|
||||
ms = state[i].connect_time;
|
||||
|
||||
} else {
|
||||
ms = state[i].response_time;
|
||||
}
|
||||
|
||||
ms = ngx_max(ms, 0);
|
||||
p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000);
|
||||
|
||||
next:
|
||||
|
||||
if (++i == s->upstream_states->nelts) {
|
||||
break;
|
||||
}
|
||||
|
||||
*p++ = ',';
|
||||
*p++ = ' ';
|
||||
}
|
||||
|
||||
v->len = p - v->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
|
||||
{
|
||||
char *rv;
|
||||
void *mconf;
|
||||
ngx_str_t *value;
|
||||
ngx_url_t u;
|
||||
ngx_uint_t m;
|
||||
ngx_conf_t pcf;
|
||||
ngx_stream_module_t *module;
|
||||
ngx_stream_conf_ctx_t *ctx, *stream_ctx;
|
||||
ngx_stream_upstream_srv_conf_t *uscf;
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
value = cf->args->elts;
|
||||
u.host = value[1];
|
||||
u.no_resolve = 1;
|
||||
u.no_port = 1;
|
||||
|
||||
uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE
|
||||
|NGX_STREAM_UPSTREAM_WEIGHT
|
||||
|NGX_STREAM_UPSTREAM_MAX_CONNS
|
||||
|NGX_STREAM_UPSTREAM_MAX_FAILS
|
||||
|NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_STREAM_UPSTREAM_DOWN
|
||||
|NGX_STREAM_UPSTREAM_BACKUP);
|
||||
if (uscf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
stream_ctx = cf->ctx;
|
||||
ctx->main_conf = stream_ctx->main_conf;
|
||||
|
||||
/* the upstream{}'s srv_conf */
|
||||
|
||||
ctx->srv_conf = ngx_pcalloc(cf->pool,
|
||||
sizeof(void *) * ngx_stream_max_module);
|
||||
if (ctx->srv_conf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx->srv_conf[ngx_stream_upstream_module.ctx_index] = uscf;
|
||||
|
||||
uscf->srv_conf = ctx->srv_conf;
|
||||
|
||||
for (m = 0; cf->cycle->modules[m]; m++) {
|
||||
if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
module = cf->cycle->modules[m]->ctx;
|
||||
|
||||
if (module->create_srv_conf) {
|
||||
mconf = module->create_srv_conf(cf);
|
||||
if (mconf == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;
|
||||
}
|
||||
}
|
||||
|
||||
uscf->servers = ngx_array_create(cf->pool, 4,
|
||||
sizeof(ngx_stream_upstream_server_t));
|
||||
if (uscf->servers == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* parse inside upstream{} */
|
||||
|
||||
pcf = *cf;
|
||||
cf->ctx = ctx;
|
||||
cf->cmd_type = NGX_STREAM_UPS_CONF;
|
||||
|
||||
rv = ngx_conf_parse(cf, NULL);
|
||||
|
||||
*cf = pcf;
|
||||
|
||||
if (rv != NGX_CONF_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (uscf->servers->nelts == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"no servers are inside upstream");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_upstream_srv_conf_t *uscf = conf;
|
||||
|
||||
time_t fail_timeout;
|
||||
ngx_str_t *value, s;
|
||||
ngx_url_t u;
|
||||
ngx_int_t weight, max_conns, max_fails;
|
||||
ngx_uint_t i;
|
||||
ngx_stream_upstream_server_t *us;
|
||||
|
||||
us = ngx_array_push(uscf->servers);
|
||||
if (us == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
weight = 1;
|
||||
max_conns = 0;
|
||||
max_fails = 1;
|
||||
fail_timeout = 10;
|
||||
|
||||
for (i = 2; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_WEIGHT)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
|
||||
|
||||
if (weight == NGX_ERROR || weight == 0) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "max_conns=", 10) == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_CONNS)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);
|
||||
|
||||
if (max_conns == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_FAILS)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
|
||||
|
||||
if (max_fails == NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_FAIL_TIMEOUT)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
s.len = value[i].len - 13;
|
||||
s.data = &value[i].data[13];
|
||||
|
||||
fail_timeout = ngx_parse_time(&s, 1);
|
||||
|
||||
if (fail_timeout == (time_t) NGX_ERROR) {
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "backup") == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_BACKUP)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
us->backup = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "down") == 0) {
|
||||
|
||||
if (!(uscf->flags & NGX_STREAM_UPSTREAM_DOWN)) {
|
||||
goto not_supported;
|
||||
}
|
||||
|
||||
us->down = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
u.url = value[1];
|
||||
|
||||
if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
|
||||
if (u.err) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%s in upstream \"%V\"", u.err, &u.url);
|
||||
}
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (u.no_port) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"no port in upstream \"%V\"", &u.url);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
us->name = u.url;
|
||||
us->addrs = u.addrs;
|
||||
us->naddrs = u.naddrs;
|
||||
us->weight = weight;
|
||||
us->max_conns = max_conns;
|
||||
us->max_fails = max_fails;
|
||||
us->fail_timeout = fail_timeout;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
invalid:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
|
||||
not_supported:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"balancing method does not support parameter \"%V\"",
|
||||
&value[i]);
|
||||
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
ngx_stream_upstream_srv_conf_t *
|
||||
ngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
|
||||
{
|
||||
ngx_uint_t i;
|
||||
ngx_stream_upstream_server_t *us;
|
||||
ngx_stream_upstream_srv_conf_t *uscf, **uscfp;
|
||||
ngx_stream_upstream_main_conf_t *umcf;
|
||||
|
||||
if (!(flags & NGX_STREAM_UPSTREAM_CREATE)) {
|
||||
|
||||
if (ngx_parse_url(cf->pool, u) != NGX_OK) {
|
||||
if (u->err) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"%s in upstream \"%V\"", u->err, &u->url);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);
|
||||
|
||||
uscfp = umcf->upstreams.elts;
|
||||
|
||||
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
||||
|
||||
if (uscfp[i]->host.len != u->host.len
|
||||
|| ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
|
||||
!= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flags & NGX_STREAM_UPSTREAM_CREATE)
|
||||
&& (uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE))
|
||||
{
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"duplicate upstream \"%V\"", &u->host);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE) && !u->no_port) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"upstream \"%V\" may not have port %d",
|
||||
&u->host, u->port);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((flags & NGX_STREAM_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"upstream \"%V\" may not have port %d in %s:%ui",
|
||||
&u->host, uscfp[i]->port,
|
||||
uscfp[i]->file_name, uscfp[i]->line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (uscfp[i]->port != u->port) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags & NGX_STREAM_UPSTREAM_CREATE) {
|
||||
uscfp[i]->flags = flags;
|
||||
}
|
||||
|
||||
return uscfp[i];
|
||||
}
|
||||
|
||||
uscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_srv_conf_t));
|
||||
if (uscf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uscf->flags = flags;
|
||||
uscf->host = u->host;
|
||||
uscf->file_name = cf->conf_file->file.name.data;
|
||||
uscf->line = cf->conf_file->line;
|
||||
uscf->port = u->port;
|
||||
uscf->no_port = u->no_port;
|
||||
|
||||
if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) {
|
||||
uscf->servers = ngx_array_create(cf->pool, 1,
|
||||
sizeof(ngx_stream_upstream_server_t));
|
||||
if (uscf->servers == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
us = ngx_array_push(uscf->servers);
|
||||
if (us == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));
|
||||
|
||||
us->addrs = u->addrs;
|
||||
us->naddrs = 1;
|
||||
}
|
||||
|
||||
uscfp = ngx_array_push(&umcf->upstreams);
|
||||
if (uscfp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*uscfp = uscf;
|
||||
|
||||
return uscf;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_upstream_create_main_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_upstream_main_conf_t *umcf;
|
||||
|
||||
umcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_main_conf_t));
|
||||
if (umcf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ngx_array_init(&umcf->upstreams, cf->pool, 4,
|
||||
sizeof(ngx_stream_upstream_srv_conf_t *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return umcf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
|
||||
{
|
||||
ngx_stream_upstream_main_conf_t *umcf = conf;
|
||||
|
||||
ngx_uint_t i;
|
||||
ngx_stream_upstream_init_pt init;
|
||||
ngx_stream_upstream_srv_conf_t **uscfp;
|
||||
|
||||
uscfp = umcf->upstreams.elts;
|
||||
|
||||
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
||||
|
||||
init = uscfp[i]->peer.init_upstream
|
||||
? uscfp[i]->peer.init_upstream
|
||||
: ngx_stream_upstream_init_round_robin;
|
||||
|
||||
if (init(cf, uscfp[i]) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
154
src/stream/ngx_stream_upstream.h
Normal file
154
src/stream/ngx_stream_upstream.h
Normal file
@@ -0,0 +1,154 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_UPSTREAM_H_INCLUDED_
|
||||
#define _NGX_STREAM_UPSTREAM_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
#include <ngx_event_connect.h>
|
||||
|
||||
|
||||
#define NGX_STREAM_UPSTREAM_CREATE 0x0001
|
||||
#define NGX_STREAM_UPSTREAM_WEIGHT 0x0002
|
||||
#define NGX_STREAM_UPSTREAM_MAX_FAILS 0x0004
|
||||
#define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT 0x0008
|
||||
#define NGX_STREAM_UPSTREAM_DOWN 0x0010
|
||||
#define NGX_STREAM_UPSTREAM_BACKUP 0x0020
|
||||
#define NGX_STREAM_UPSTREAM_MAX_CONNS 0x0100
|
||||
|
||||
|
||||
#define NGX_STREAM_UPSTREAM_NOTIFY_CONNECT 0x1
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t upstreams;
|
||||
/* ngx_stream_upstream_srv_conf_t */
|
||||
} ngx_stream_upstream_main_conf_t;
|
||||
|
||||
|
||||
typedef struct ngx_stream_upstream_srv_conf_s ngx_stream_upstream_srv_conf_t;
|
||||
|
||||
|
||||
typedef ngx_int_t (*ngx_stream_upstream_init_pt)(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
typedef ngx_int_t (*ngx_stream_upstream_init_peer_pt)(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_upstream_init_pt init_upstream;
|
||||
ngx_stream_upstream_init_peer_pt init;
|
||||
void *data;
|
||||
} ngx_stream_upstream_peer_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t name;
|
||||
ngx_addr_t *addrs;
|
||||
ngx_uint_t naddrs;
|
||||
ngx_uint_t weight;
|
||||
ngx_uint_t max_conns;
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
ngx_msec_t slow_start;
|
||||
|
||||
unsigned down:1;
|
||||
unsigned backup:1;
|
||||
|
||||
NGX_COMPAT_BEGIN(4)
|
||||
NGX_COMPAT_END
|
||||
} ngx_stream_upstream_server_t;
|
||||
|
||||
|
||||
struct ngx_stream_upstream_srv_conf_s {
|
||||
ngx_stream_upstream_peer_t peer;
|
||||
void **srv_conf;
|
||||
|
||||
ngx_array_t *servers;
|
||||
/* ngx_stream_upstream_server_t */
|
||||
|
||||
ngx_uint_t flags;
|
||||
ngx_str_t host;
|
||||
u_char *file_name;
|
||||
ngx_uint_t line;
|
||||
in_port_t port;
|
||||
ngx_uint_t no_port; /* unsigned no_port:1 */
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_msec_t response_time;
|
||||
ngx_msec_t connect_time;
|
||||
ngx_msec_t first_byte_time;
|
||||
off_t bytes_sent;
|
||||
off_t bytes_received;
|
||||
|
||||
ngx_str_t *peer;
|
||||
} ngx_stream_upstream_state_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t host;
|
||||
in_port_t port;
|
||||
ngx_uint_t no_port; /* unsigned no_port:1 */
|
||||
|
||||
ngx_uint_t naddrs;
|
||||
ngx_resolver_addr_t *addrs;
|
||||
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
ngx_str_t name;
|
||||
|
||||
ngx_resolver_ctx_t *ctx;
|
||||
} ngx_stream_upstream_resolved_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_peer_connection_t peer;
|
||||
|
||||
ngx_buf_t downstream_buf;
|
||||
ngx_buf_t upstream_buf;
|
||||
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *upstream_out;
|
||||
ngx_chain_t *upstream_busy;
|
||||
ngx_chain_t *downstream_out;
|
||||
ngx_chain_t *downstream_busy;
|
||||
|
||||
off_t received;
|
||||
time_t start_sec;
|
||||
ngx_uint_t responses;
|
||||
|
||||
ngx_str_t ssl_name;
|
||||
|
||||
ngx_stream_upstream_srv_conf_t *upstream;
|
||||
ngx_stream_upstream_resolved_t *resolved;
|
||||
ngx_stream_upstream_state_t *state;
|
||||
unsigned connected:1;
|
||||
unsigned proxy_protocol:1;
|
||||
} ngx_stream_upstream_t;
|
||||
|
||||
|
||||
ngx_stream_upstream_srv_conf_t *ngx_stream_upstream_add(ngx_conf_t *cf,
|
||||
ngx_url_t *u, ngx_uint_t flags);
|
||||
|
||||
|
||||
#define ngx_stream_conf_upstream_srv_conf(uscf, module) \
|
||||
uscf->srv_conf[module.ctx_index]
|
||||
|
||||
|
||||
extern ngx_module_t ngx_stream_upstream_module;
|
||||
|
||||
|
||||
#endif /* _NGX_STREAM_UPSTREAM_H_INCLUDED_ */
|
||||
675
src/stream/ngx_stream_upstream_hash_module.c
Normal file
675
src/stream/ngx_stream_upstream_hash_module.c
Normal file
@@ -0,0 +1,675 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Roman Arutyunyan
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t hash;
|
||||
ngx_str_t *server;
|
||||
} ngx_stream_upstream_chash_point_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t number;
|
||||
ngx_stream_upstream_chash_point_t point[1];
|
||||
} ngx_stream_upstream_chash_points_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_complex_value_t key;
|
||||
ngx_stream_upstream_chash_points_t *points;
|
||||
} ngx_stream_upstream_hash_srv_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* the round robin data must be first */
|
||||
ngx_stream_upstream_rr_peer_data_t rrp;
|
||||
ngx_stream_upstream_hash_srv_conf_t *conf;
|
||||
ngx_str_t key;
|
||||
ngx_uint_t tries;
|
||||
ngx_uint_t rehash;
|
||||
uint32_t hash;
|
||||
ngx_event_get_peer_pt get_rr_peer;
|
||||
} ngx_stream_upstream_hash_peer_data_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
static ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
static int ngx_libc_cdecl
|
||||
ngx_stream_upstream_chash_cmp_points(const void *one, const void *two);
|
||||
static ngx_uint_t ngx_stream_upstream_find_chash_point(
|
||||
ngx_stream_upstream_chash_points_t *points, uint32_t hash);
|
||||
static ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
static void *ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf);
|
||||
static char *ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_upstream_hash_commands[] = {
|
||||
|
||||
{ ngx_string("hash"),
|
||||
NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_upstream_hash,
|
||||
NGX_STREAM_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_upstream_hash_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
ngx_stream_upstream_hash_create_conf, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_upstream_hash_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_upstream_hash_module_ctx, /* module context */
|
||||
ngx_stream_upstream_hash_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_hash(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_stream_upstream_init_hash_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_stream_upstream_hash_srv_conf_t *hcf;
|
||||
ngx_stream_upstream_hash_peer_data_t *hp;
|
||||
|
||||
hp = ngx_palloc(s->connection->pool,
|
||||
sizeof(ngx_stream_upstream_hash_peer_data_t));
|
||||
if (hp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.data = &hp->rrp;
|
||||
|
||||
if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.get = ngx_stream_upstream_get_hash_peer;
|
||||
|
||||
hcf = ngx_stream_conf_upstream_srv_conf(us,
|
||||
ngx_stream_upstream_hash_module);
|
||||
|
||||
if (ngx_stream_complex_value(s, &hcf->key, &hp->key) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"upstream hash key:\"%V\"", &hp->key);
|
||||
|
||||
hp->conf = hcf;
|
||||
hp->tries = 0;
|
||||
hp->rehash = 0;
|
||||
hp->hash = 0;
|
||||
hp->get_rr_peer = ngx_stream_upstream_get_round_robin_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_stream_upstream_hash_peer_data_t *hp = data;
|
||||
|
||||
time_t now;
|
||||
u_char buf[NGX_INT_T_LEN];
|
||||
size_t size;
|
||||
uint32_t hash;
|
||||
ngx_int_t w;
|
||||
uintptr_t m;
|
||||
ngx_uint_t n, p;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get hash peer, try: %ui", pc->tries);
|
||||
|
||||
ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);
|
||||
|
||||
if (hp->tries > 20 || hp->rrp.peers->single) {
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
pc->connection = NULL;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
/*
|
||||
* Hash expression is compatible with Cache::Memcached:
|
||||
* ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
|
||||
* with REHASH omitted at the first iteration.
|
||||
*/
|
||||
|
||||
ngx_crc32_init(hash);
|
||||
|
||||
if (hp->rehash > 0) {
|
||||
size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
|
||||
ngx_crc32_update(&hash, buf, size);
|
||||
}
|
||||
|
||||
ngx_crc32_update(&hash, hp->key.data, hp->key.len);
|
||||
ngx_crc32_final(hash);
|
||||
|
||||
hash = (hash >> 16) & 0x7fff;
|
||||
|
||||
hp->hash += hash;
|
||||
hp->rehash++;
|
||||
|
||||
w = hp->hash % hp->rrp.peers->total_weight;
|
||||
peer = hp->rrp.peers->peer;
|
||||
p = 0;
|
||||
|
||||
while (w >= peer->weight) {
|
||||
w -= peer->weight;
|
||||
peer = peer->next;
|
||||
p++;
|
||||
}
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (hp->rrp.tried[n] & m) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get hash peer, value:%uD, peer:%ui", hp->hash, p);
|
||||
|
||||
if (peer->down) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
next:
|
||||
|
||||
if (++hp->tries > 20) {
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
}
|
||||
|
||||
hp->rrp.current = peer;
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
pc->name = &peer->name;
|
||||
|
||||
peer->conns++;
|
||||
|
||||
if (now - peer->checked > peer->fail_timeout) {
|
||||
peer->checked = now;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
|
||||
hp->rrp.tried[n] |= m;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_chash(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
u_char *host, *port, c;
|
||||
size_t host_len, port_len, size;
|
||||
uint32_t hash, base_hash;
|
||||
ngx_str_t *server;
|
||||
ngx_uint_t npoints, i, j;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
ngx_stream_upstream_chash_points_t *points;
|
||||
ngx_stream_upstream_hash_srv_conf_t *hcf;
|
||||
union {
|
||||
uint32_t value;
|
||||
u_char byte[4];
|
||||
} prev_hash;
|
||||
|
||||
if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_stream_upstream_init_chash_peer;
|
||||
|
||||
peers = us->peer.data;
|
||||
npoints = peers->total_weight * 160;
|
||||
|
||||
size = sizeof(ngx_stream_upstream_chash_points_t)
|
||||
+ sizeof(ngx_stream_upstream_chash_point_t) * (npoints - 1);
|
||||
|
||||
points = ngx_palloc(cf->pool, size);
|
||||
if (points == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
points->number = 0;
|
||||
|
||||
for (peer = peers->peer; peer; peer = peer->next) {
|
||||
server = &peer->server;
|
||||
|
||||
/*
|
||||
* Hash expression is compatible with Cache::Memcached::Fast:
|
||||
* crc32(HOST \0 PORT PREV_HASH).
|
||||
*/
|
||||
|
||||
if (server->len >= 5
|
||||
&& ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
|
||||
{
|
||||
host = server->data + 5;
|
||||
host_len = server->len - 5;
|
||||
port = NULL;
|
||||
port_len = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (j = 0; j < server->len; j++) {
|
||||
c = server->data[server->len - j - 1];
|
||||
|
||||
if (c == ':') {
|
||||
host = server->data;
|
||||
host_len = server->len - j - 1;
|
||||
port = server->data + server->len - j;
|
||||
port_len = j;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (c < '0' || c > '9') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
host = server->data;
|
||||
host_len = server->len;
|
||||
port = NULL;
|
||||
port_len = 0;
|
||||
|
||||
done:
|
||||
|
||||
ngx_crc32_init(base_hash);
|
||||
ngx_crc32_update(&base_hash, host, host_len);
|
||||
ngx_crc32_update(&base_hash, (u_char *) "", 1);
|
||||
ngx_crc32_update(&base_hash, port, port_len);
|
||||
|
||||
prev_hash.value = 0;
|
||||
npoints = peer->weight * 160;
|
||||
|
||||
for (j = 0; j < npoints; j++) {
|
||||
hash = base_hash;
|
||||
|
||||
ngx_crc32_update(&hash, prev_hash.byte, 4);
|
||||
ngx_crc32_final(hash);
|
||||
|
||||
points->point[points->number].hash = hash;
|
||||
points->point[points->number].server = server;
|
||||
points->number++;
|
||||
|
||||
#if (NGX_HAVE_LITTLE_ENDIAN)
|
||||
prev_hash.value = hash;
|
||||
#else
|
||||
prev_hash.byte[0] = (u_char) (hash & 0xff);
|
||||
prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff);
|
||||
prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff);
|
||||
prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
ngx_qsort(points->point,
|
||||
points->number,
|
||||
sizeof(ngx_stream_upstream_chash_point_t),
|
||||
ngx_stream_upstream_chash_cmp_points);
|
||||
|
||||
for (i = 0, j = 1; j < points->number; j++) {
|
||||
if (points->point[i].hash != points->point[j].hash) {
|
||||
points->point[++i] = points->point[j];
|
||||
}
|
||||
}
|
||||
|
||||
points->number = i + 1;
|
||||
|
||||
hcf = ngx_stream_conf_upstream_srv_conf(us,
|
||||
ngx_stream_upstream_hash_module);
|
||||
hcf->points = points;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static int ngx_libc_cdecl
|
||||
ngx_stream_upstream_chash_cmp_points(const void *one, const void *two)
|
||||
{
|
||||
ngx_stream_upstream_chash_point_t *first =
|
||||
(ngx_stream_upstream_chash_point_t *) one;
|
||||
ngx_stream_upstream_chash_point_t *second =
|
||||
(ngx_stream_upstream_chash_point_t *) two;
|
||||
|
||||
if (first->hash < second->hash) {
|
||||
return -1;
|
||||
|
||||
} else if (first->hash > second->hash) {
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_uint_t
|
||||
ngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points,
|
||||
uint32_t hash)
|
||||
{
|
||||
ngx_uint_t i, j, k;
|
||||
ngx_stream_upstream_chash_point_t *point;
|
||||
|
||||
/* find first point >= hash */
|
||||
|
||||
point = &points->point[0];
|
||||
|
||||
i = 0;
|
||||
j = points->number;
|
||||
|
||||
while (i < j) {
|
||||
k = (i + j) / 2;
|
||||
|
||||
if (hash > point[k].hash) {
|
||||
i = k + 1;
|
||||
|
||||
} else if (hash < point[k].hash) {
|
||||
j = k;
|
||||
|
||||
} else {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
uint32_t hash;
|
||||
ngx_stream_upstream_hash_srv_conf_t *hcf;
|
||||
ngx_stream_upstream_hash_peer_data_t *hp;
|
||||
|
||||
if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.get = ngx_stream_upstream_get_chash_peer;
|
||||
|
||||
hp = s->upstream->peer.data;
|
||||
hcf = ngx_stream_conf_upstream_srv_conf(us,
|
||||
ngx_stream_upstream_hash_module);
|
||||
|
||||
hash = ngx_crc32_long(hp->key.data, hp->key.len);
|
||||
|
||||
ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);
|
||||
|
||||
hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash);
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_stream_upstream_hash_peer_data_t *hp = data;
|
||||
|
||||
time_t now;
|
||||
intptr_t m;
|
||||
ngx_str_t *server;
|
||||
ngx_int_t total;
|
||||
ngx_uint_t i, n, best_i;
|
||||
ngx_stream_upstream_rr_peer_t *peer, *best;
|
||||
ngx_stream_upstream_chash_point_t *point;
|
||||
ngx_stream_upstream_chash_points_t *points;
|
||||
ngx_stream_upstream_hash_srv_conf_t *hcf;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get consistent hash peer, try: %ui", pc->tries);
|
||||
|
||||
ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);
|
||||
|
||||
pc->connection = NULL;
|
||||
|
||||
now = ngx_time();
|
||||
hcf = hp->conf;
|
||||
|
||||
points = hcf->points;
|
||||
point = &points->point[0];
|
||||
|
||||
for ( ;; ) {
|
||||
server = point[hp->hash % points->number].server;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"consistent hash peer:%uD, server:\"%V\"",
|
||||
hp->hash, server);
|
||||
|
||||
best = NULL;
|
||||
best_i = 0;
|
||||
total = 0;
|
||||
|
||||
for (peer = hp->rrp.peers->peer, i = 0;
|
||||
peer;
|
||||
peer = peer->next, i++)
|
||||
{
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (hp->rrp.tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->server.len != server->len
|
||||
|| ngx_strncmp(peer->server.data, server->data, server->len)
|
||||
!= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (best == NULL || peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
best_i = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (best) {
|
||||
best->current_weight -= total;
|
||||
break;
|
||||
}
|
||||
|
||||
hp->hash++;
|
||||
hp->tries++;
|
||||
|
||||
if (hp->tries >= points->number) {
|
||||
pc->name = hp->rrp.peers->name;
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return NGX_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
hp->rrp.current = best;
|
||||
|
||||
pc->sockaddr = best->sockaddr;
|
||||
pc->socklen = best->socklen;
|
||||
pc->name = &best->name;
|
||||
|
||||
best->conns++;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
|
||||
n = best_i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));
|
||||
|
||||
hp->rrp.tried[n] |= m;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_upstream_hash_srv_conf_t *conf;
|
||||
|
||||
conf = ngx_palloc(cf->pool, sizeof(ngx_stream_upstream_hash_srv_conf_t));
|
||||
if (conf == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->points = NULL;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_upstream_hash_srv_conf_t *hcf = conf;
|
||||
|
||||
ngx_str_t *value;
|
||||
ngx_stream_upstream_srv_conf_t *uscf;
|
||||
ngx_stream_compile_complex_value_t ccv;
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
|
||||
|
||||
ccv.cf = cf;
|
||||
ccv.value = &value[1];
|
||||
ccv.complex_value = &hcf->key;
|
||||
|
||||
if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
|
||||
|
||||
if (uscf->peer.init_upstream) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"load balancing method redefined");
|
||||
}
|
||||
|
||||
uscf->flags = NGX_STREAM_UPSTREAM_CREATE
|
||||
|NGX_STREAM_UPSTREAM_WEIGHT
|
||||
|NGX_STREAM_UPSTREAM_MAX_CONNS
|
||||
|NGX_STREAM_UPSTREAM_MAX_FAILS
|
||||
|NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_STREAM_UPSTREAM_DOWN;
|
||||
|
||||
if (cf->args->nelts == 2) {
|
||||
uscf->peer.init_upstream = ngx_stream_upstream_init_hash;
|
||||
|
||||
} else if (ngx_strcmp(value[2].data, "consistent") == 0) {
|
||||
uscf->peer.init_upstream = ngx_stream_upstream_init_chash;
|
||||
|
||||
} else {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
310
src/stream/ngx_stream_upstream_least_conn_module.c
Normal file
310
src/stream/ngx_stream_upstream_least_conn_module.c
Normal file
@@ -0,0 +1,310 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_upstream_init_least_conn_peer(
|
||||
ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_stream_upstream_get_least_conn_peer(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_upstream_least_conn_commands[] = {
|
||||
|
||||
{ ngx_string("least_conn"),
|
||||
NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS,
|
||||
ngx_stream_upstream_least_conn,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_upstream_least_conn_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_upstream_least_conn_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_upstream_least_conn_module_ctx, /* module context */
|
||||
ngx_stream_upstream_least_conn_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_least_conn(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0,
|
||||
"init least conn");
|
||||
|
||||
if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_stream_upstream_init_least_conn_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
|
||||
"init least conn peer");
|
||||
|
||||
if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
time_t now;
|
||||
uintptr_t m;
|
||||
ngx_int_t rc, total;
|
||||
ngx_uint_t i, n, p, many;
|
||||
ngx_stream_upstream_rr_peer_t *peer, *best;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get least conn peer, try: %ui", pc->tries);
|
||||
|
||||
if (rrp->peers->single) {
|
||||
return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
|
||||
}
|
||||
|
||||
pc->connection = NULL;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
peers = rrp->peers;
|
||||
|
||||
ngx_stream_upstream_rr_peers_wlock(peers);
|
||||
|
||||
best = NULL;
|
||||
total = 0;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
many = 0;
|
||||
p = 0;
|
||||
#endif
|
||||
|
||||
for (peer = peers->peer, i = 0;
|
||||
peer;
|
||||
peer = peer->next, i++)
|
||||
{
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (rrp->tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* select peer with least number of connections; if there are
|
||||
* multiple peers with the same number of connections, select
|
||||
* based on round-robin
|
||||
*/
|
||||
|
||||
if (best == NULL
|
||||
|| peer->conns * best->weight < best->conns * peer->weight)
|
||||
{
|
||||
best = peer;
|
||||
many = 0;
|
||||
p = i;
|
||||
|
||||
} else if (peer->conns * best->weight == best->conns * peer->weight) {
|
||||
many = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get least conn peer, no peer found");
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (many) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get least conn peer, many");
|
||||
|
||||
for (peer = best, i = p;
|
||||
peer;
|
||||
peer = peer->next, i++)
|
||||
{
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (rrp->tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->conns * best->weight != best->conns * peer->weight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best->current_weight -= total;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
pc->sockaddr = best->sockaddr;
|
||||
pc->socklen = best->socklen;
|
||||
pc->name = &best->name;
|
||||
|
||||
best->conns++;
|
||||
|
||||
rrp->current = best;
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried[n] |= m;
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
if (peers->next) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get least conn peer, backup servers");
|
||||
|
||||
rrp->peers = peers->next;
|
||||
|
||||
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rrp->tried[i] = 0;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp);
|
||||
|
||||
if (rc != NGX_BUSY) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_wlock(peers);
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
pc->name = peers->name;
|
||||
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_stream_upstream_srv_conf_t *uscf;
|
||||
|
||||
uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
|
||||
|
||||
if (uscf->peer.init_upstream) {
|
||||
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
|
||||
"load balancing method redefined");
|
||||
}
|
||||
|
||||
uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn;
|
||||
|
||||
uscf->flags = NGX_STREAM_UPSTREAM_CREATE
|
||||
|NGX_STREAM_UPSTREAM_WEIGHT
|
||||
|NGX_STREAM_UPSTREAM_MAX_CONNS
|
||||
|NGX_STREAM_UPSTREAM_MAX_FAILS
|
||||
|NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
|
||||
|NGX_STREAM_UPSTREAM_DOWN
|
||||
|NGX_STREAM_UPSTREAM_BACKUP;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
874
src/stream/ngx_stream_upstream_round_robin.c
Normal file
874
src/stream/ngx_stream_upstream_round_robin.c
Normal file
@@ -0,0 +1,874 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
#define ngx_stream_upstream_tries(p) ((p)->number \
|
||||
+ ((p)->next ? (p)->next->number : 0))
|
||||
|
||||
|
||||
static ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer(
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp);
|
||||
static void ngx_stream_upstream_notify_round_robin_peer(
|
||||
ngx_peer_connection_t *pc, void *data, ngx_uint_t state);
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
|
||||
static ngx_int_t ngx_stream_upstream_set_round_robin_peer_session(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static void ngx_stream_upstream_save_round_robin_peer_session(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static ngx_int_t ngx_stream_upstream_empty_set_session(
|
||||
ngx_peer_connection_t *pc, void *data);
|
||||
static void ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_url_t u;
|
||||
ngx_uint_t i, j, n, w;
|
||||
ngx_stream_upstream_server_t *server;
|
||||
ngx_stream_upstream_rr_peer_t *peer, **peerp;
|
||||
ngx_stream_upstream_rr_peers_t *peers, *backup;
|
||||
|
||||
us->peer.init = ngx_stream_upstream_init_round_robin_peer;
|
||||
|
||||
if (us->servers) {
|
||||
server = us->servers->elts;
|
||||
|
||||
n = 0;
|
||||
w = 0;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n += server[i].naddrs;
|
||||
w += server[i].naddrs * server[i].weight;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no servers in upstream \"%V\" in %s:%ui",
|
||||
&us->host, us->file_name, us->line);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
|
||||
if (peer == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (n == 1);
|
||||
peers->number = n;
|
||||
peers->weighted = (w != n);
|
||||
peers->total_weight = w;
|
||||
peers->name = &us->host;
|
||||
|
||||
n = 0;
|
||||
peerp = &peers->peer;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < server[i].naddrs; j++) {
|
||||
peer[n].sockaddr = server[i].addrs[j].sockaddr;
|
||||
peer[n].socklen = server[i].addrs[j].socklen;
|
||||
peer[n].name = server[i].addrs[j].name;
|
||||
peer[n].weight = server[i].weight;
|
||||
peer[n].effective_weight = server[i].weight;
|
||||
peer[n].current_weight = 0;
|
||||
peer[n].max_conns = server[i].max_conns;
|
||||
peer[n].max_fails = server[i].max_fails;
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
|
||||
/* backup servers */
|
||||
|
||||
n = 0;
|
||||
w = 0;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (!server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n += server[i].naddrs;
|
||||
w += server[i].naddrs * server[i].weight;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
backup = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (backup == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
|
||||
if (peer == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = 0;
|
||||
backup->single = 0;
|
||||
backup->number = n;
|
||||
backup->weighted = (w != n);
|
||||
backup->total_weight = w;
|
||||
backup->name = &us->host;
|
||||
|
||||
n = 0;
|
||||
peerp = &backup->peer;
|
||||
|
||||
for (i = 0; i < us->servers->nelts; i++) {
|
||||
if (!server[i].backup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < server[i].naddrs; j++) {
|
||||
peer[n].sockaddr = server[i].addrs[j].sockaddr;
|
||||
peer[n].socklen = server[i].addrs[j].socklen;
|
||||
peer[n].name = server[i].addrs[j].name;
|
||||
peer[n].weight = server[i].weight;
|
||||
peer[n].effective_weight = server[i].weight;
|
||||
peer[n].current_weight = 0;
|
||||
peer[n].max_conns = server[i].max_conns;
|
||||
peer[n].max_fails = server[i].max_fails;
|
||||
peer[n].fail_timeout = server[i].fail_timeout;
|
||||
peer[n].down = server[i].down;
|
||||
peer[n].server = server[i].name;
|
||||
|
||||
*peerp = &peer[n];
|
||||
peerp = &peer[n].next;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
peers->next = backup;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
/* an upstream implicitly defined by proxy_pass, etc. */
|
||||
|
||||
if (us->port == 0) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"no port in upstream \"%V\" in %s:%ui",
|
||||
&us->host, us->file_name, us->line);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memzero(&u, sizeof(ngx_url_t));
|
||||
|
||||
u.host = us->host;
|
||||
u.port = us->port;
|
||||
|
||||
if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
|
||||
if (u.err) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"%s in upstream \"%V\" in %s:%ui",
|
||||
u.err, &us->host, us->file_name, us->line);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = u.naddrs;
|
||||
|
||||
peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);
|
||||
if (peer == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (n == 1);
|
||||
peers->number = n;
|
||||
peers->weighted = 0;
|
||||
peers->total_weight = n;
|
||||
peers->name = &us->host;
|
||||
|
||||
peerp = &peers->peer;
|
||||
|
||||
for (i = 0; i < u.naddrs; i++) {
|
||||
peer[i].sockaddr = u.addrs[i].sockaddr;
|
||||
peer[i].socklen = u.addrs[i].socklen;
|
||||
peer[i].name = u.addrs[i].name;
|
||||
peer[i].weight = 1;
|
||||
peer[i].effective_weight = 1;
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
|
||||
us->peer.data = peers;
|
||||
|
||||
/* implicitly defined upstream has no backup servers */
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp;
|
||||
|
||||
rrp = s->upstream->peer.data;
|
||||
|
||||
if (rrp == NULL) {
|
||||
rrp = ngx_palloc(s->connection->pool,
|
||||
sizeof(ngx_stream_upstream_rr_peer_data_t));
|
||||
if (rrp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.data = rrp;
|
||||
}
|
||||
|
||||
rrp->peers = us->peer.data;
|
||||
rrp->current = NULL;
|
||||
rrp->config = 0;
|
||||
|
||||
n = rrp->peers->number;
|
||||
|
||||
if (rrp->peers->next && rrp->peers->next->number > n) {
|
||||
n = rrp->peers->next->number;
|
||||
}
|
||||
|
||||
if (n <= 8 * sizeof(uintptr_t)) {
|
||||
rrp->tried = &rrp->data;
|
||||
rrp->data = 0;
|
||||
|
||||
} else {
|
||||
n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));
|
||||
if (rrp->tried == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;
|
||||
s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;
|
||||
s->upstream->peer.notify = ngx_stream_upstream_notify_round_robin_peer;
|
||||
s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);
|
||||
#if (NGX_STREAM_SSL)
|
||||
s->upstream->peer.set_session =
|
||||
ngx_stream_upstream_set_round_robin_peer_session;
|
||||
s->upstream->peer.save_session =
|
||||
ngx_stream_upstream_save_round_robin_peer_session;
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_resolved_t *ur)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
socklen_t socklen;
|
||||
ngx_uint_t i, n;
|
||||
struct sockaddr *sockaddr;
|
||||
ngx_stream_upstream_rr_peer_t *peer, **peerp;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp;
|
||||
|
||||
rrp = s->upstream->peer.data;
|
||||
|
||||
if (rrp == NULL) {
|
||||
rrp = ngx_palloc(s->connection->pool,
|
||||
sizeof(ngx_stream_upstream_rr_peer_data_t));
|
||||
if (rrp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
s->upstream->peer.data = rrp;
|
||||
}
|
||||
|
||||
peers = ngx_pcalloc(s->connection->pool,
|
||||
sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peer = ngx_pcalloc(s->connection->pool,
|
||||
sizeof(ngx_stream_upstream_rr_peer_t) * ur->naddrs);
|
||||
if (peer == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
peers->single = (ur->naddrs == 1);
|
||||
peers->number = ur->naddrs;
|
||||
peers->name = &ur->host;
|
||||
|
||||
if (ur->sockaddr) {
|
||||
peer[0].sockaddr = ur->sockaddr;
|
||||
peer[0].socklen = ur->socklen;
|
||||
peer[0].name = ur->name;
|
||||
peer[0].weight = 1;
|
||||
peer[0].effective_weight = 1;
|
||||
peer[0].current_weight = 0;
|
||||
peer[0].max_conns = 0;
|
||||
peer[0].max_fails = 1;
|
||||
peer[0].fail_timeout = 10;
|
||||
peers->peer = peer;
|
||||
|
||||
} else {
|
||||
peerp = &peers->peer;
|
||||
|
||||
for (i = 0; i < ur->naddrs; i++) {
|
||||
|
||||
socklen = ur->addrs[i].socklen;
|
||||
|
||||
sockaddr = ngx_palloc(s->connection->pool, socklen);
|
||||
if (sockaddr == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
|
||||
ngx_inet_set_port(sockaddr, ur->port);
|
||||
|
||||
p = ngx_pnalloc(s->connection->pool, NGX_SOCKADDR_STRLEN);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
|
||||
|
||||
peer[i].sockaddr = sockaddr;
|
||||
peer[i].socklen = socklen;
|
||||
peer[i].name.len = len;
|
||||
peer[i].name.data = p;
|
||||
peer[i].weight = 1;
|
||||
peer[i].effective_weight = 1;
|
||||
peer[i].current_weight = 0;
|
||||
peer[i].max_conns = 0;
|
||||
peer[i].max_fails = 1;
|
||||
peer[i].fail_timeout = 10;
|
||||
*peerp = &peer[i];
|
||||
peerp = &peer[i].next;
|
||||
}
|
||||
}
|
||||
|
||||
rrp->peers = peers;
|
||||
rrp->current = NULL;
|
||||
rrp->config = 0;
|
||||
|
||||
if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
|
||||
rrp->tried = &rrp->data;
|
||||
rrp->data = 0;
|
||||
|
||||
} else {
|
||||
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));
|
||||
if (rrp->tried == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;
|
||||
s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;
|
||||
s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);
|
||||
#if (NGX_STREAM_SSL)
|
||||
s->upstream->peer.set_session = ngx_stream_upstream_empty_set_session;
|
||||
s->upstream->peer.save_session = ngx_stream_upstream_empty_save_session;
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t i, n;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get rr peer, try: %ui", pc->tries);
|
||||
|
||||
pc->connection = NULL;
|
||||
|
||||
peers = rrp->peers;
|
||||
ngx_stream_upstream_rr_peers_wlock(peers);
|
||||
|
||||
if (peers->single) {
|
||||
peer = peers->peer;
|
||||
|
||||
if (peer->down) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rrp->current = peer;
|
||||
|
||||
} else {
|
||||
|
||||
/* there are several peers */
|
||||
|
||||
peer = ngx_stream_upstream_get_peer(rrp);
|
||||
|
||||
if (peer == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"get rr peer, current: %p %i",
|
||||
peer, peer->current_weight);
|
||||
}
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
pc->name = &peer->name;
|
||||
|
||||
peer->conns++;
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
failed:
|
||||
|
||||
if (peers->next) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, "backup servers");
|
||||
|
||||
rrp->peers = peers->next;
|
||||
|
||||
n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
|
||||
/ (8 * sizeof(uintptr_t));
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rrp->tried[i] = 0;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
rc = ngx_stream_upstream_get_round_robin_peer(pc, rrp);
|
||||
|
||||
if (rc != NGX_BUSY) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_wlock(peers);
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
pc->name = peers->name;
|
||||
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
|
||||
static ngx_stream_upstream_rr_peer_t *
|
||||
ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp)
|
||||
{
|
||||
time_t now;
|
||||
uintptr_t m;
|
||||
ngx_int_t total;
|
||||
ngx_uint_t i, n, p;
|
||||
ngx_stream_upstream_rr_peer_t *peer, *best;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
best = NULL;
|
||||
total = 0;
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
p = 0;
|
||||
#endif
|
||||
|
||||
for (peer = rrp->peers->peer, i = 0;
|
||||
peer;
|
||||
peer = peer->next, i++)
|
||||
{
|
||||
n = i / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
|
||||
|
||||
if (rrp->tried[n] & m) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->down) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_fails
|
||||
&& peer->fails >= peer->max_fails
|
||||
&& now - peer->checked <= peer->fail_timeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (peer->max_conns && peer->conns >= peer->max_conns) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->current_weight += peer->effective_weight;
|
||||
total += peer->effective_weight;
|
||||
|
||||
if (peer->effective_weight < peer->weight) {
|
||||
peer->effective_weight++;
|
||||
}
|
||||
|
||||
if (best == NULL || peer->current_weight > best->current_weight) {
|
||||
best = peer;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rrp->current = best;
|
||||
|
||||
n = p / (8 * sizeof(uintptr_t));
|
||||
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
|
||||
|
||||
rrp->tried[n] |= m;
|
||||
|
||||
best->current_weight -= total;
|
||||
|
||||
if (now - best->checked > best->fail_timeout) {
|
||||
best->checked = now;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
|
||||
ngx_uint_t state)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
time_t now;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"free rr peer %ui %ui", pc->tries, state);
|
||||
|
||||
peer = rrp->current;
|
||||
|
||||
ngx_stream_upstream_rr_peers_rlock(rrp->peers);
|
||||
ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);
|
||||
|
||||
if (rrp->peers->single) {
|
||||
peer->conns--;
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(rrp->peers);
|
||||
|
||||
pc->tries = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state & NGX_PEER_FAILED) {
|
||||
now = ngx_time();
|
||||
|
||||
peer->fails++;
|
||||
peer->accessed = now;
|
||||
peer->checked = now;
|
||||
|
||||
if (peer->max_fails) {
|
||||
peer->effective_weight -= peer->weight / peer->max_fails;
|
||||
|
||||
if (peer->fails >= peer->max_fails) {
|
||||
ngx_log_error(NGX_LOG_WARN, pc->log, 0,
|
||||
"upstream server temporarily disabled");
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"free rr peer failed: %p %i",
|
||||
peer, peer->effective_weight);
|
||||
|
||||
if (peer->effective_weight < 0) {
|
||||
peer->effective_weight = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* mark peer live if check passed */
|
||||
|
||||
if (peer->accessed < peer->checked) {
|
||||
peer->fails = 0;
|
||||
}
|
||||
}
|
||||
|
||||
peer->conns--;
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(rrp->peers);
|
||||
|
||||
if (pc->tries) {
|
||||
pc->tries--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_upstream_notify_round_robin_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t type)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
|
||||
peer = rrp->current;
|
||||
|
||||
if (type == NGX_STREAM_UPSTREAM_NOTIFY_CONNECT
|
||||
&& pc->connection->type == SOCK_STREAM)
|
||||
{
|
||||
ngx_stream_upstream_rr_peers_rlock(rrp->peers);
|
||||
ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);
|
||||
|
||||
if (peer->accessed < peer->checked) {
|
||||
peer->fails = 0;
|
||||
}
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(rrp->peers);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_STREAM_SSL)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_ssl_session_t *ssl_session;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
int len;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
|
||||
const
|
||||
#endif
|
||||
u_char *p;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
#endif
|
||||
|
||||
peer = rrp->current;
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
peers = rrp->peers;
|
||||
|
||||
if (peers->shpool) {
|
||||
ngx_stream_upstream_rr_peers_rlock(peers);
|
||||
ngx_stream_upstream_rr_peer_lock(peers, peer);
|
||||
|
||||
if (peer->ssl_session == NULL) {
|
||||
ngx_stream_upstream_rr_peer_unlock(peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = peer->ssl_session_len;
|
||||
|
||||
ngx_memcpy(buf, peer->ssl_session, len);
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
p = buf;
|
||||
ssl_session = d2i_SSL_SESSION(NULL, &p, len);
|
||||
|
||||
rc = ngx_ssl_set_session(pc->connection, ssl_session);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"set session: %p", ssl_session);
|
||||
|
||||
ngx_ssl_free_session(ssl_session);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
ssl_session = peer->ssl_session;
|
||||
|
||||
rc = ngx_ssl_set_session(pc->connection, ssl_session);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"set session: %p", ssl_session);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
|
||||
void *data)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_data_t *rrp = data;
|
||||
|
||||
ngx_ssl_session_t *old_ssl_session, *ssl_session;
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
int len;
|
||||
u_char *p;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
#endif
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
peers = rrp->peers;
|
||||
|
||||
if (peers->shpool) {
|
||||
|
||||
ssl_session = SSL_get0_session(pc->connection->ssl->connection);
|
||||
|
||||
if (ssl_session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"save session: %p", ssl_session);
|
||||
|
||||
len = i2d_SSL_SESSION(ssl_session, NULL);
|
||||
|
||||
/* do not cache too big session */
|
||||
|
||||
if (len > NGX_SSL_MAX_SESSION_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
(void) i2d_SSL_SESSION(ssl_session, &p);
|
||||
|
||||
peer = rrp->current;
|
||||
|
||||
ngx_stream_upstream_rr_peers_rlock(peers);
|
||||
ngx_stream_upstream_rr_peer_lock(peers, peer);
|
||||
|
||||
if (len > peer->ssl_session_len) {
|
||||
ngx_shmtx_lock(&peers->shpool->mutex);
|
||||
|
||||
if (peer->ssl_session) {
|
||||
ngx_slab_free_locked(peers->shpool, peer->ssl_session);
|
||||
}
|
||||
|
||||
peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);
|
||||
|
||||
ngx_shmtx_unlock(&peers->shpool->mutex);
|
||||
|
||||
if (peer->ssl_session == NULL) {
|
||||
peer->ssl_session_len = 0;
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
return;
|
||||
}
|
||||
|
||||
peer->ssl_session_len = len;
|
||||
}
|
||||
|
||||
ngx_memcpy(peer->ssl_session, buf, len);
|
||||
|
||||
ngx_stream_upstream_rr_peer_unlock(peers, peer);
|
||||
ngx_stream_upstream_rr_peers_unlock(peers);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ssl_session = ngx_ssl_get_session(pc->connection);
|
||||
|
||||
if (ssl_session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"save session: %p", ssl_session);
|
||||
|
||||
peer = rrp->current;
|
||||
|
||||
old_ssl_session = peer->ssl_session;
|
||||
peer->ssl_session = ssl_session;
|
||||
|
||||
if (old_ssl_session) {
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
|
||||
"old session: %p", old_ssl_session);
|
||||
|
||||
/* TODO: may block */
|
||||
|
||||
ngx_ssl_free_session(old_ssl_session);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
146
src/stream/ngx_stream_upstream_round_robin.h
Normal file
146
src/stream/ngx_stream_upstream_round_robin.h
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
|
||||
#define _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct ngx_stream_upstream_rr_peer_s ngx_stream_upstream_rr_peer_t;
|
||||
|
||||
struct ngx_stream_upstream_rr_peer_s {
|
||||
struct sockaddr *sockaddr;
|
||||
socklen_t socklen;
|
||||
ngx_str_t name;
|
||||
ngx_str_t server;
|
||||
|
||||
ngx_int_t current_weight;
|
||||
ngx_int_t effective_weight;
|
||||
ngx_int_t weight;
|
||||
|
||||
ngx_uint_t conns;
|
||||
ngx_uint_t max_conns;
|
||||
|
||||
ngx_uint_t fails;
|
||||
time_t accessed;
|
||||
time_t checked;
|
||||
|
||||
ngx_uint_t max_fails;
|
||||
time_t fail_timeout;
|
||||
ngx_msec_t slow_start;
|
||||
ngx_msec_t start_time;
|
||||
|
||||
ngx_uint_t down;
|
||||
|
||||
void *ssl_session;
|
||||
int ssl_session_len;
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
ngx_atomic_t lock;
|
||||
#endif
|
||||
|
||||
ngx_stream_upstream_rr_peer_t *next;
|
||||
|
||||
NGX_COMPAT_BEGIN(25)
|
||||
NGX_COMPAT_END
|
||||
};
|
||||
|
||||
|
||||
typedef struct ngx_stream_upstream_rr_peers_s ngx_stream_upstream_rr_peers_t;
|
||||
|
||||
struct ngx_stream_upstream_rr_peers_s {
|
||||
ngx_uint_t number;
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_atomic_t rwlock;
|
||||
ngx_stream_upstream_rr_peers_t *zone_next;
|
||||
#endif
|
||||
|
||||
ngx_uint_t total_weight;
|
||||
|
||||
unsigned single:1;
|
||||
unsigned weighted:1;
|
||||
|
||||
ngx_str_t *name;
|
||||
|
||||
ngx_stream_upstream_rr_peers_t *next;
|
||||
|
||||
ngx_stream_upstream_rr_peer_t *peer;
|
||||
};
|
||||
|
||||
|
||||
#if (NGX_STREAM_UPSTREAM_ZONE)
|
||||
|
||||
#define ngx_stream_upstream_rr_peers_rlock(peers) \
|
||||
\
|
||||
if (peers->shpool) { \
|
||||
ngx_rwlock_rlock(&peers->rwlock); \
|
||||
}
|
||||
|
||||
#define ngx_stream_upstream_rr_peers_wlock(peers) \
|
||||
\
|
||||
if (peers->shpool) { \
|
||||
ngx_rwlock_wlock(&peers->rwlock); \
|
||||
}
|
||||
|
||||
#define ngx_stream_upstream_rr_peers_unlock(peers) \
|
||||
\
|
||||
if (peers->shpool) { \
|
||||
ngx_rwlock_unlock(&peers->rwlock); \
|
||||
}
|
||||
|
||||
|
||||
#define ngx_stream_upstream_rr_peer_lock(peers, peer) \
|
||||
\
|
||||
if (peers->shpool) { \
|
||||
ngx_rwlock_wlock(&peer->lock); \
|
||||
}
|
||||
|
||||
#define ngx_stream_upstream_rr_peer_unlock(peers, peer) \
|
||||
\
|
||||
if (peers->shpool) { \
|
||||
ngx_rwlock_unlock(&peer->lock); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ngx_stream_upstream_rr_peers_rlock(peers)
|
||||
#define ngx_stream_upstream_rr_peers_wlock(peers)
|
||||
#define ngx_stream_upstream_rr_peers_unlock(peers)
|
||||
#define ngx_stream_upstream_rr_peer_lock(peers, peer)
|
||||
#define ngx_stream_upstream_rr_peer_unlock(peers, peer)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t config;
|
||||
ngx_stream_upstream_rr_peers_t *peers;
|
||||
ngx_stream_upstream_rr_peer_t *current;
|
||||
uintptr_t *tried;
|
||||
uintptr_t data;
|
||||
} ngx_stream_upstream_rr_peer_data_t;
|
||||
|
||||
|
||||
ngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
ngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_srv_conf_t *us);
|
||||
ngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,
|
||||
ngx_stream_upstream_resolved_t *ur);
|
||||
ngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
|
||||
void *data);
|
||||
void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
|
||||
void *data, ngx_uint_t state);
|
||||
|
||||
|
||||
#endif /* _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
|
||||
243
src/stream/ngx_stream_upstream_zone_module.c
Normal file
243
src/stream/ngx_stream_upstream_zone_module.c
Normal file
@@ -0,0 +1,243 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Ruslan Ermilov
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
static char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone,
|
||||
void *data);
|
||||
static ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers(
|
||||
ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf);
|
||||
|
||||
|
||||
static ngx_command_t ngx_stream_upstream_zone_commands[] = {
|
||||
|
||||
{ ngx_string("zone"),
|
||||
NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,
|
||||
ngx_stream_upstream_zone,
|
||||
0,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_upstream_zone_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_upstream_zone_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_upstream_zone_module_ctx, /* module context */
|
||||
ngx_stream_upstream_zone_commands, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static char *
|
||||
ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ssize_t size;
|
||||
ngx_str_t *value;
|
||||
ngx_stream_upstream_srv_conf_t *uscf;
|
||||
ngx_stream_upstream_main_conf_t *umcf;
|
||||
|
||||
uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
|
||||
umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
if (!value[1].len) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone name \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (cf->args->nelts == 3) {
|
||||
size = ngx_parse_size(&value[2]);
|
||||
|
||||
if (size == NGX_ERROR) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid zone size \"%V\"", &value[2]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (size < (ssize_t) (8 * ngx_pagesize)) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"zone \"%V\" is too small", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
size = 0;
|
||||
}
|
||||
|
||||
uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,
|
||||
&ngx_stream_upstream_module);
|
||||
if (uscf->shm_zone == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
uscf->shm_zone->init = ngx_stream_upstream_init_zone;
|
||||
uscf->shm_zone->data = umcf;
|
||||
|
||||
uscf->shm_zone->noreuse = 1;
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)
|
||||
{
|
||||
size_t len;
|
||||
ngx_uint_t i;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_stream_upstream_rr_peers_t *peers, **peersp;
|
||||
ngx_stream_upstream_srv_conf_t *uscf, **uscfp;
|
||||
ngx_stream_upstream_main_conf_t *umcf;
|
||||
|
||||
shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
|
||||
umcf = shm_zone->data;
|
||||
uscfp = umcf->upstreams.elts;
|
||||
|
||||
if (shm_zone->shm.exists) {
|
||||
peers = shpool->data;
|
||||
|
||||
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
||||
uscf = uscfp[i];
|
||||
|
||||
if (uscf->shm_zone != shm_zone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uscf->peer.data = peers;
|
||||
peers = peers->zone_next;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
len = sizeof(" in upstream zone \"\"") + shm_zone->shm.name.len;
|
||||
|
||||
shpool->log_ctx = ngx_slab_alloc(shpool, len);
|
||||
if (shpool->log_ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(shpool->log_ctx, " in upstream zone \"%V\"%Z",
|
||||
&shm_zone->shm.name);
|
||||
|
||||
|
||||
/* copy peers to shared memory */
|
||||
|
||||
peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data;
|
||||
|
||||
for (i = 0; i < umcf->upstreams.nelts; i++) {
|
||||
uscf = uscfp[i];
|
||||
|
||||
if (uscf->shm_zone != shm_zone) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf);
|
||||
if (peers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*peersp = peers;
|
||||
peersp = &peers->zone_next;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_stream_upstream_rr_peers_t *
|
||||
ngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,
|
||||
ngx_stream_upstream_srv_conf_t *uscf)
|
||||
{
|
||||
ngx_stream_upstream_rr_peer_t *peer, **peerp;
|
||||
ngx_stream_upstream_rr_peers_t *peers, *backup;
|
||||
|
||||
peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (peers == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
|
||||
peers->shpool = shpool;
|
||||
|
||||
for (peerp = &peers->peer; *peerp; peerp = &peer->next) {
|
||||
/* pool is unlocked */
|
||||
peer = ngx_slab_calloc_locked(shpool,
|
||||
sizeof(ngx_stream_upstream_rr_peer_t));
|
||||
if (peer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
|
||||
|
||||
*peerp = peer;
|
||||
}
|
||||
|
||||
if (peers->next == NULL) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
if (backup == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t));
|
||||
|
||||
backup->shpool = shpool;
|
||||
|
||||
for (peerp = &backup->peer; *peerp; peerp = &peer->next) {
|
||||
/* pool is unlocked */
|
||||
peer = ngx_slab_calloc_locked(shpool,
|
||||
sizeof(ngx_stream_upstream_rr_peer_t));
|
||||
if (peer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_memcpy(peer, *peerp, sizeof(ngx_stream_upstream_rr_peer_t));
|
||||
|
||||
*peerp = peer;
|
||||
}
|
||||
|
||||
peers->next = backup;
|
||||
|
||||
done:
|
||||
|
||||
uscf->peer.data = peers;
|
||||
|
||||
return peers;
|
||||
}
|
||||
1130
src/stream/ngx_stream_variables.c
Normal file
1130
src/stream/ngx_stream_variables.c
Normal file
File diff suppressed because it is too large
Load Diff
109
src/stream/ngx_stream_variables.h
Normal file
109
src/stream/ngx_stream_variables.h
Normal file
@@ -0,0 +1,109 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_STREAM_VARIABLES_H_INCLUDED_
|
||||
#define _NGX_STREAM_VARIABLES_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef ngx_variable_value_t ngx_stream_variable_value_t;
|
||||
|
||||
#define ngx_stream_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
|
||||
|
||||
typedef struct ngx_stream_variable_s ngx_stream_variable_t;
|
||||
|
||||
typedef void (*ngx_stream_set_variable_pt) (ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
typedef ngx_int_t (*ngx_stream_get_variable_pt) (ngx_stream_session_t *s,
|
||||
ngx_stream_variable_value_t *v, uintptr_t data);
|
||||
|
||||
|
||||
#define NGX_STREAM_VAR_CHANGEABLE 1
|
||||
#define NGX_STREAM_VAR_NOCACHEABLE 2
|
||||
#define NGX_STREAM_VAR_INDEXED 4
|
||||
#define NGX_STREAM_VAR_NOHASH 8
|
||||
|
||||
|
||||
struct ngx_stream_variable_s {
|
||||
ngx_str_t name; /* must be first to build the hash */
|
||||
ngx_stream_set_variable_pt set_handler;
|
||||
ngx_stream_get_variable_pt get_handler;
|
||||
uintptr_t data;
|
||||
ngx_uint_t flags;
|
||||
ngx_uint_t index;
|
||||
};
|
||||
|
||||
|
||||
ngx_stream_variable_t *ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name,
|
||||
ngx_uint_t flags);
|
||||
ngx_int_t ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
|
||||
ngx_stream_variable_value_t *ngx_stream_get_indexed_variable(
|
||||
ngx_stream_session_t *s, ngx_uint_t index);
|
||||
ngx_stream_variable_value_t *ngx_stream_get_flushed_variable(
|
||||
ngx_stream_session_t *s, ngx_uint_t index);
|
||||
|
||||
ngx_stream_variable_value_t *ngx_stream_get_variable(ngx_stream_session_t *s,
|
||||
ngx_str_t *name, ngx_uint_t key);
|
||||
|
||||
|
||||
#if (NGX_PCRE)
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t capture;
|
||||
ngx_int_t index;
|
||||
} ngx_stream_regex_variable_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_regex_t *regex;
|
||||
ngx_uint_t ncaptures;
|
||||
ngx_stream_regex_variable_t *variables;
|
||||
ngx_uint_t nvariables;
|
||||
ngx_str_t name;
|
||||
} ngx_stream_regex_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_stream_regex_t *regex;
|
||||
void *value;
|
||||
} ngx_stream_map_regex_t;
|
||||
|
||||
|
||||
ngx_stream_regex_t *ngx_stream_regex_compile(ngx_conf_t *cf,
|
||||
ngx_regex_compile_t *rc);
|
||||
ngx_int_t ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,
|
||||
ngx_str_t *str);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_hash_combined_t hash;
|
||||
#if (NGX_PCRE)
|
||||
ngx_stream_map_regex_t *regex;
|
||||
ngx_uint_t nregex;
|
||||
#endif
|
||||
} ngx_stream_map_t;
|
||||
|
||||
|
||||
void *ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,
|
||||
ngx_str_t *match);
|
||||
|
||||
|
||||
ngx_int_t ngx_stream_variables_add_core_vars(ngx_conf_t *cf);
|
||||
ngx_int_t ngx_stream_variables_init_vars(ngx_conf_t *cf);
|
||||
|
||||
|
||||
extern ngx_stream_variable_value_t ngx_stream_variable_null_value;
|
||||
extern ngx_stream_variable_value_t ngx_stream_variable_true_value;
|
||||
|
||||
|
||||
#endif /* _NGX_STREAM_VARIABLES_H_INCLUDED_ */
|
||||
273
src/stream/ngx_stream_write_filter_module.c
Normal file
273
src/stream/ngx_stream_write_filter_module.c
Normal file
@@ -0,0 +1,273 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_stream.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_chain_t *from_upstream;
|
||||
ngx_chain_t *from_downstream;
|
||||
} ngx_stream_write_filter_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_stream_write_filter(ngx_stream_session_t *s,
|
||||
ngx_chain_t *in, ngx_uint_t from_upstream);
|
||||
static ngx_int_t ngx_stream_write_filter_init(ngx_conf_t *cf);
|
||||
|
||||
|
||||
static ngx_stream_module_t ngx_stream_write_filter_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
ngx_stream_write_filter_init, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL /* merge server configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_stream_write_filter_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_stream_write_filter_module_ctx, /* module context */
|
||||
NULL, /* module directives */
|
||||
NGX_STREAM_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
|
||||
};
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_write_filter(ngx_stream_session_t *s, ngx_chain_t *in,
|
||||
ngx_uint_t from_upstream)
|
||||
{
|
||||
off_t size;
|
||||
ngx_uint_t last, flush, sync;
|
||||
ngx_chain_t *cl, *ln, **ll, **out, *chain;
|
||||
ngx_connection_t *c;
|
||||
ngx_stream_write_filter_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_stream_get_module_ctx(s, ngx_stream_write_filter_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
ctx = ngx_pcalloc(s->connection->pool,
|
||||
sizeof(ngx_stream_write_filter_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_stream_set_ctx(s, ctx, ngx_stream_write_filter_module);
|
||||
}
|
||||
|
||||
if (from_upstream) {
|
||||
c = s->connection;
|
||||
out = &ctx->from_upstream;
|
||||
|
||||
} else {
|
||||
c = s->upstream->peer.connection;
|
||||
out = &ctx->from_downstream;
|
||||
}
|
||||
|
||||
if (c->error) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
size = 0;
|
||||
flush = 0;
|
||||
sync = 0;
|
||||
last = 0;
|
||||
ll = out;
|
||||
|
||||
/* find the size, the flush point and the last link of the saved chain */
|
||||
|
||||
for (cl = *out; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"write old buf t:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
cl->buf->temporary, cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
#if 1
|
||||
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"zero size buf in writer "
|
||||
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
||||
cl->buf->temporary,
|
||||
cl->buf->recycled,
|
||||
cl->buf->in_file,
|
||||
cl->buf->start,
|
||||
cl->buf->pos,
|
||||
cl->buf->last,
|
||||
cl->buf->file,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last);
|
||||
|
||||
ngx_debug_point();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
size += ngx_buf_size(cl->buf);
|
||||
|
||||
if (cl->buf->flush || cl->buf->recycled) {
|
||||
flush = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->sync) {
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* add the new chain to the existent one */
|
||||
|
||||
for (ln = in; ln; ln = ln->next) {
|
||||
cl = ngx_alloc_chain_link(c->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = ln->buf;
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"write new buf t:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
cl->buf->temporary, cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
|
||||
#if 1
|
||||
if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"zero size buf in writer "
|
||||
"t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
||||
cl->buf->temporary,
|
||||
cl->buf->recycled,
|
||||
cl->buf->in_file,
|
||||
cl->buf->start,
|
||||
cl->buf->pos,
|
||||
cl->buf->last,
|
||||
cl->buf->file,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last);
|
||||
|
||||
ngx_debug_point();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
size += ngx_buf_size(cl->buf);
|
||||
|
||||
if (cl->buf->flush || cl->buf->recycled) {
|
||||
flush = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->sync) {
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
if (cl->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
*ll = NULL;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"stream write filter: l:%ui f:%ui s:%O", last, flush, size);
|
||||
|
||||
if (size == 0
|
||||
&& !(c->buffered & NGX_LOWLEVEL_BUFFERED)
|
||||
&& !(last && c->need_last_buf))
|
||||
{
|
||||
if (last || flush || sync) {
|
||||
for (cl = *out; cl; /* void */) {
|
||||
ln = cl;
|
||||
cl = cl->next;
|
||||
ngx_free_chain(c->pool, ln);
|
||||
}
|
||||
|
||||
*out = NULL;
|
||||
c->buffered &= ~NGX_STREAM_WRITE_BUFFERED;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"the stream output chain is empty");
|
||||
|
||||
ngx_debug_point();
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
chain = c->send_chain(c, *out, 0);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,
|
||||
"stream write filter %p", chain);
|
||||
|
||||
if (chain == NGX_CHAIN_ERROR) {
|
||||
c->error = 1;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
for (cl = *out; cl && cl != chain; /* void */) {
|
||||
ln = cl;
|
||||
cl = cl->next;
|
||||
ngx_free_chain(c->pool, ln);
|
||||
}
|
||||
|
||||
*out = chain;
|
||||
|
||||
if (chain) {
|
||||
if (c->shared) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"shared connection is busy");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
c->buffered |= NGX_STREAM_WRITE_BUFFERED;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
c->buffered &= ~NGX_STREAM_WRITE_BUFFERED;
|
||||
|
||||
if (c->buffered & NGX_LOWLEVEL_BUFFERED) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_stream_write_filter_init(ngx_conf_t *cf)
|
||||
{
|
||||
ngx_stream_top_filter = ngx_stream_write_filter;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user