small-package/luci-app-nginx-pingos/modules/nginx-toolkit-module/ngx_http_dynamic.c

1678 lines
43 KiB
C

/*
* Copyright (C) AlexWoo(Wu Jie) wj19840501@gmail.com
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include "ngx_dynamic_conf.h"
#include "ngx_http_dynamic.h"
static void *ngx_http_dynamic_create_conf(ngx_conf_t *cf);
static void *ngx_http_dynamic_core_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_dynamic_core_init_main_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_dynamic_core_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_dynamic_core_init_srv_conf(ngx_conf_t *cf, void *conf);
static void *ngx_http_dynamic_core_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_dynamic_core_init_loc_conf(ngx_conf_t *cf, void *conf);
static char *ngx_http_dynamic_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_dynamic_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_dynamic_core_server_name(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_dynamic_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
typedef struct ngx_http_dynamic_location_tree_node_s
ngx_http_dynamic_location_tree_node_t;
typedef struct ngx_http_dynamic_core_loc_conf_s
ngx_http_dynamic_core_loc_conf_t;
struct ngx_http_dynamic_core_loc_conf_s {
ngx_str_t name; /* location name */
#if (NGX_PCRE)
ngx_dynamic_regex_t *regex;
#endif
unsigned exact_match:1;
unsigned noregex:1;
ngx_queue_t *locations;
ngx_http_dynamic_location_tree_node_t *static_locations;
#if (NGX_PCRE)
ngx_http_dynamic_core_loc_conf_t **regex_locations;
#endif
void **loc_conf;
};
typedef struct {
ngx_queue_t queue;
ngx_http_dynamic_core_loc_conf_t *exact;
ngx_http_dynamic_core_loc_conf_t *inclusive;
ngx_str_t *name;
u_char *file_name;
ngx_uint_t line;
ngx_queue_t list;
} ngx_http_dynamic_location_queue_t;
struct ngx_http_dynamic_location_tree_node_s {
ngx_http_dynamic_location_tree_node_t *left;
ngx_http_dynamic_location_tree_node_t *right;
ngx_http_dynamic_location_tree_node_t *tree;
ngx_http_dynamic_core_loc_conf_t *exact;
ngx_http_dynamic_core_loc_conf_t *inclusive;
u_char len;
u_char name[1];
};
typedef struct {
/* array of the ngx_http_dynamic_server_name_t, "server_name" directive */
ngx_array_t server_names;
ngx_http_dynamic_core_loc_conf_t *hdclcf;
void **srv_conf;
} ngx_http_dynamic_core_srv_conf_t;
typedef struct {
#if (NGX_PCRE)
ngx_dynamic_regex_t *regex;
#endif
ngx_http_dynamic_core_srv_conf_t *server; /* virtual name server conf */
ngx_str_t name;
} ngx_http_dynamic_server_name_t;
typedef struct {
/* ngx_http_dynamic_srv_conf_t */
ngx_array_t servers;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
/* the default server configuration for this address:port */
ngx_http_dynamic_core_srv_conf_t *default_server;
ngx_hash_combined_t names;
#if (NGX_PCRE)
ngx_uint_t nregex;
ngx_http_dynamic_server_name_t *regex;
#endif
} ngx_http_dynamic_core_main_conf_t;
typedef struct {
void **main_conf;
} ngx_http_dynamic_conf_t;
static ngx_core_module_t ngx_http_dynamic_module_ctx = {
ngx_string("http_dynamic"),
NULL,
NULL
};
static ngx_command_t ngx_http_dynamic_dcommands[] = {
{ ngx_string("http"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_http_dynamic_block,
0,
0,
NULL },
ngx_null_command
};
static ngx_dynamic_core_module_t ngx_http_dynamic_module_dctx = {
ngx_string("http_dynamic"),
ngx_http_dynamic_create_conf,
NULL
};
ngx_module_t ngx_http_dynamic_module = {
NGX_MODULE_V1,
&ngx_http_dynamic_module_ctx, /* module context */
NULL, /* 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 */
(uintptr_t) &ngx_http_dynamic_module_dctx, /* module dynamic context */
(uintptr_t) ngx_http_dynamic_dcommands, /* module dynamic directives */
NGX_MODULE_V1_DYNAMIC_PADDING
};
static ngx_http_module_t ngx_http_dynamic_core_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
static ngx_command_t ngx_http_dynamic_core_dcommands[] = {
{ ngx_string("server_names_hash_max_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_http_dynamic_core_main_conf_t, server_names_hash_max_size),
NULL },
{ ngx_string("server_names_hash_bucket_size"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
0,
offsetof(ngx_http_dynamic_core_main_conf_t,
server_names_hash_bucket_size),
NULL },
{ ngx_string("server"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
ngx_http_dynamic_core_server,
0,
0,
NULL },
{ ngx_string("server_name"),
NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
ngx_http_dynamic_core_server_name,
0,
0,
NULL },
{ ngx_string("location"),
NGX_HTTP_SRV_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
ngx_http_dynamic_core_location,
0,
0,
NULL },
ngx_null_command
};
static ngx_http_dynamic_module_t ngx_http_dynamic_core_module_dctx = {
ngx_http_dynamic_core_create_main_conf, /* create main configuration */
ngx_http_dynamic_core_init_main_conf, /* init main configuration */
ngx_http_dynamic_core_create_srv_conf, /* create server configuration */
ngx_http_dynamic_core_init_srv_conf, /* init server configuration */
ngx_http_dynamic_core_create_loc_conf, /* create location configuration */
ngx_http_dynamic_core_init_loc_conf /* init location configuration */
};
ngx_module_t ngx_http_dynamic_core_module = {
NGX_MODULE_V1,
&ngx_http_dynamic_core_module_ctx, /* module context */
NULL, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
(uintptr_t) &ngx_http_dynamic_core_module_dctx, /* module dynamic context */
(uintptr_t) ngx_http_dynamic_core_dcommands, /* module dynamic directives */
NGX_MODULE_V1_DYNAMIC_PADDING
};
static void *
ngx_http_dynamic_create_conf(ngx_conf_t *cf)
{
ngx_http_dynamic_conf_t *hdccf;
hdccf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dynamic_conf_t));
if (hdccf == NULL) {
return NULL;
}
return hdccf;
}
static void *
ngx_http_dynamic_core_create_main_conf(ngx_conf_t *cf)
{
ngx_http_dynamic_core_main_conf_t *hdcmcf;
hdcmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dynamic_core_main_conf_t));
if (hdcmcf == NULL) {
return NULL;
}
if (ngx_array_init(&hdcmcf->servers, cf->pool, 4,
sizeof(ngx_http_dynamic_core_srv_conf_t *)) != NGX_OK)
{
return NULL;
}
hdcmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
hdcmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;
return hdcmcf;
}
static char *
ngx_http_dynamic_core_init_main_conf(ngx_conf_t *cf, void *conf)
{
ngx_http_dynamic_core_main_conf_t *hdcmcf;
hdcmcf = conf;
ngx_conf_init_uint_value(hdcmcf->server_names_hash_max_size, 512);
ngx_conf_init_uint_value(hdcmcf->server_names_hash_bucket_size,
ngx_cacheline_size);
return NGX_CONF_OK;
}
static void *
ngx_http_dynamic_core_create_srv_conf(ngx_conf_t *cf)
{
ngx_http_dynamic_core_srv_conf_t *hdcscf;
hdcscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dynamic_core_srv_conf_t));
if (hdcscf == NULL) {
return NULL;
}
if (ngx_array_init(&hdcscf->server_names, cf->pool, 4,
sizeof(ngx_http_dynamic_server_name_t)) != NGX_OK)
{
return NULL;
}
return hdcscf;
}
static char *
ngx_http_dynamic_core_init_srv_conf(ngx_conf_t *cf, void *conf)
{
return NGX_CONF_OK;
}
static void *
ngx_http_dynamic_core_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_dynamic_core_loc_conf_t *hdclcf;
hdclcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dynamic_core_loc_conf_t));
if (hdclcf == NULL) {
return NULL;
}
return hdclcf;
}
static char *
ngx_http_dynamic_core_init_loc_conf(ngx_conf_t *cf, void *conf)
{
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_dynamic_core_regex_location(ngx_conf_t *cf,
ngx_http_dynamic_core_loc_conf_t *hdclcf,
ngx_str_t *regex, ngx_uint_t caseless)
{
#if (NGX_PCRE)
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = *regex;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
#if (NGX_HAVE_CASELESS_FILESYSTEM)
rc.options = NGX_REGEX_CASELESS;
#else
rc.options = caseless ? NGX_REGEX_CASELESS : 0;
#endif
hdclcf->regex = ngx_dynamic_regex_compile(cf, &rc);
if (hdclcf->regex == NULL) {
return NGX_ERROR;
}
hdclcf->name = *regex;
return NGX_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"using regex \"%V\" requires PCRE library",
regex);
return NGX_ERROR;
#endif
}
static ngx_int_t
ngx_http_dynamic_core_predeal_location(ngx_conf_t *cf,
ngx_http_dynamic_core_loc_conf_t *hdclcf)
{
ngx_str_t *value, *name;
size_t len;
u_char *mod;
value = cf->args->elts;
if (cf->args->nelts == 3) {
len = value[1].len;
mod = value[1].data;
name = &value[2];
if (len == 1 && mod[0] == '=') {
hdclcf->name = *name;
hdclcf->exact_match = 1;
} else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
hdclcf->name = *name;
hdclcf->noregex = 1;
} else if (len == 1 && mod[0] == '~') {
if (ngx_http_dynamic_core_regex_location(cf, hdclcf, name, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
} else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
if (ngx_http_dynamic_core_regex_location(cf, hdclcf, name, 1)
!= NGX_OK)
{
return NGX_ERROR;
}
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid location modifier \"%V\"", &value[1]);
return NGX_ERROR;
}
} else { /* cf->args->nelts == 2 */
name = &value[1];
if (name->data[0] == '=') {
hdclcf->name.len = name->len - 1;
hdclcf->name.data = name->data + 1;
hdclcf->exact_match = 1;
} else if (name->data[0] == '^' && name->data[1] == '~') {
hdclcf->name.len = name->len - 2;
hdclcf->name.data = name->data + 2;
hdclcf->noregex = 1;
} else if (name->data[0] == '~') {
name->len--;
name->data++;
if (name->data[0] == '*') {
name->len--;
name->data++;
if (ngx_http_dynamic_core_regex_location(cf, hdclcf, name, 1)
!= NGX_OK)
{
return NGX_ERROR;
}
} else {
if (ngx_http_dynamic_core_regex_location(cf, hdclcf, name, 0)
!= NGX_OK)
{
return NGX_ERROR;
}
}
} else {
hdclcf->name = *name;
}
}
return NGX_OK;
}
static ngx_int_t
ngx_http_dynamic_core_cmp_locations(const ngx_queue_t *one,
const ngx_queue_t *two)
{
ngx_int_t rc;
ngx_http_dynamic_core_loc_conf_t *first, *second;
ngx_http_dynamic_location_queue_t *lq1, *lq2;
lq1 = (ngx_http_dynamic_location_queue_t *) one;
lq2 = (ngx_http_dynamic_location_queue_t *) two;
first = lq1->exact ? lq1->exact : lq1->inclusive;
second = lq2->exact ? lq2->exact : lq2->inclusive;
#if (NGX_PCRE)
if (first->regex && !second->regex) {
/* shift the regex matches to the end */
return 1;
}
if (!first->regex && second->regex) {
/* shift the regex matches to the end */
return -1;
}
if (first->regex || second->regex) {
/* do not sort the regex matches */
return 0;
}
#endif
rc = ngx_filename_cmp(first->name.data, second->name.data,
ngx_min(first->name.len, second->name.len) + 1);
if (rc == 0 && !first->exact_match && second->exact_match) {
/* an exact match must be before the same inclusive one */
return 1;
}
return rc;
}
static ngx_int_t
ngx_http_dynamic_core_join_exact_locations(ngx_conf_t *cf,
ngx_queue_t *locations)
{
ngx_queue_t *q, *x;
ngx_http_dynamic_location_queue_t *lq, *lx;
q = ngx_queue_head(locations);
while (q != ngx_queue_last(locations)) {
x = ngx_queue_next(q);
lq = (ngx_http_dynamic_location_queue_t *) q;
lx = (ngx_http_dynamic_location_queue_t *) x;
if (lq->name->len == lx->name->len
&& ngx_filename_cmp(lq->name->data, lx->name->data, lx->name->len)
== 0)
{
if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"duplicate location \"%V\" in %s:%ui",
lx->name, lx->file_name, lx->line);
return NGX_ERROR;
}
lq->inclusive = lx->inclusive;
ngx_queue_remove(x);
continue;
}
q = ngx_queue_next(q);
}
return NGX_OK;
}
static void
ngx_http_dynamic_core_create_locations_list(ngx_queue_t *locations,
ngx_queue_t *q)
{
u_char *name;
size_t len;
ngx_queue_t *x, tail;
ngx_http_dynamic_location_queue_t *lq, *lx;
if (q == ngx_queue_last(locations)) {
return;
}
lq = (ngx_http_dynamic_location_queue_t *) q;
if (lq->inclusive == NULL) {
ngx_http_dynamic_core_create_locations_list(locations,
ngx_queue_next(q));
return;
}
len = lq->name->len;
name = lq->name->data;
for (x = ngx_queue_next(q);
x != ngx_queue_sentinel(locations);
x = ngx_queue_next(x))
{
lx = (ngx_http_dynamic_location_queue_t *) x;
if (len > lx->name->len
|| ngx_filename_cmp(name, lx->name->data, len) != 0)
{
break;
}
}
q = ngx_queue_next(q);
if (q == x) {
ngx_http_dynamic_core_create_locations_list(locations, x);
return;
}
ngx_queue_split(locations, q, &tail);
ngx_queue_add(&lq->list, &tail);
if (x == ngx_queue_sentinel(locations)) {
ngx_http_dynamic_core_create_locations_list(&lq->list,
ngx_queue_head(&lq->list));
return;
}
ngx_queue_split(&lq->list, x, &tail);
ngx_queue_add(locations, &tail);
ngx_http_dynamic_core_create_locations_list(&lq->list,
ngx_queue_head(&lq->list));
ngx_http_dynamic_core_create_locations_list(locations, x);
}
/*
* to keep cache locality for left leaf nodes, allocate nodes in following
* order: node, left subtree, right subtree, inclusive subtree
*/
static ngx_http_dynamic_location_tree_node_t *
ngx_http_dynamic_core_create_locations_tree(ngx_conf_t *cf,
ngx_queue_t *locations, size_t prefix)
{
size_t len;
ngx_queue_t *q, tail;
ngx_http_dynamic_location_queue_t *lq;
ngx_http_dynamic_location_tree_node_t *node;
q = ngx_queue_middle(locations);
lq = (ngx_http_dynamic_location_queue_t *) q;
len = lq->name->len - prefix;
node = ngx_palloc(cf->pool,
offsetof(ngx_http_dynamic_location_tree_node_t, name) + len);
if (node == NULL) {
return NULL;
}
node->left = NULL;
node->right = NULL;
node->tree = NULL;
node->exact = lq->exact;
node->inclusive = lq->inclusive;
node->len = (u_char) len;
ngx_memcpy(node->name, &lq->name->data[prefix], len);
ngx_queue_split(locations, q, &tail);
if (ngx_queue_empty(locations)) {
/*
* ngx_queue_split() insures that if left part is empty,
* then right one is empty too
*/
goto inclusive;
}
node->left = ngx_http_dynamic_core_create_locations_tree(cf, locations,
prefix);
if (node->left == NULL) {
return NULL;
}
ngx_queue_remove(q);
if (ngx_queue_empty(&tail)) {
goto inclusive;
}
node->right = ngx_http_dynamic_core_create_locations_tree(cf, &tail,
prefix);
if (node->right == NULL) {
return NULL;
}
inclusive:
if (ngx_queue_empty(&lq->list)) {
return node;
}
node->tree = ngx_http_dynamic_core_create_locations_tree(cf, &lq->list,
prefix + len);
if (node->tree == NULL) {
return NULL;
}
return node;
}
static ngx_int_t
ngx_http_dynamic_core_init_locations(ngx_conf_t *cf,
ngx_http_dynamic_core_loc_conf_t *phdclcf)
{
ngx_queue_t *q, *locations, tail;
ngx_http_dynamic_core_loc_conf_t *hdclcf;
ngx_http_dynamic_location_queue_t *lq;
ngx_http_dynamic_core_loc_conf_t **hdclcfp;
#if (NGX_PCRE)
ngx_uint_t r;
ngx_queue_t *regex;
#endif
locations = phdclcf->locations;
if (locations == NULL) {
return NGX_OK;
}
ngx_queue_sort(locations, ngx_http_dynamic_core_cmp_locations);
#if (NGX_PCRE)
regex = NULL;
r = 0;
#endif
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
lq = (ngx_http_dynamic_location_queue_t *) q;
hdclcf = lq->exact ? lq->exact : lq->inclusive;
#if (NGX_PCRE)
if (hdclcf->regex) {
r++;
if (regex == NULL) {
regex = q;
}
continue;
}
#endif
}
if (q != ngx_queue_sentinel(locations)) {
ngx_queue_split(locations, q, &tail);
}
#if (NGX_PCRE)
if (regex) {
hdclcfp = ngx_palloc(cf->pool,
(r + 1) * sizeof(ngx_http_dynamic_core_loc_conf_t *));
if (hdclcfp == NULL) {
return NGX_ERROR;
}
phdclcf->regex_locations = hdclcfp;
for (q = regex;
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
lq = (ngx_http_dynamic_location_queue_t *) q;
*(hdclcfp++) = lq->exact;
}
*hdclcfp = NULL;
ngx_queue_split(locations, regex, &tail);
}
#endif
return NGX_OK;
}
static ngx_int_t
ngx_http_dynamic_core_init_static_location_trees(ngx_conf_t *cf,
ngx_http_dynamic_core_loc_conf_t *phdclcf)
{
ngx_queue_t *locations;
locations = phdclcf->locations;
if (locations == NULL) {
return NGX_OK;
}
if (ngx_queue_empty(locations)) {
return NGX_OK;
}
if (ngx_http_dynamic_core_join_exact_locations(cf, locations) != NGX_OK) {
return NGX_ERROR;
}
ngx_http_dynamic_core_create_locations_list(locations,
ngx_queue_head(locations));
phdclcf->static_locations =
ngx_http_dynamic_core_create_locations_tree(cf, locations, 0);
if (phdclcf->static_locations == NULL) {
return NGX_ERROR;
}
return NGX_OK;
}
static ngx_int_t
ngx_http_dynamic_core_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
ngx_http_dynamic_core_loc_conf_t *hdclcf)
{
ngx_http_dynamic_location_queue_t *lq;
if (ngx_http_dynamic_core_predeal_location(cf, hdclcf) != NGX_OK) {
return NGX_ERROR;
}
if (*locations == NULL) {
*locations = ngx_pcalloc(cf->temp_pool,
sizeof(ngx_http_location_queue_t));
if (*locations == NULL) {
return NGX_ERROR;
}
ngx_queue_init(*locations);
}
lq = ngx_pcalloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
if (lq == NULL) {
return NGX_ERROR;
}
if (hdclcf->exact_match
#if (NGX_PCRE)
|| hdclcf->regex
#endif
)
{
lq->exact = hdclcf;
lq->inclusive = NULL;
} else {
lq->exact = NULL;
lq->inclusive = hdclcf;
}
lq->name = &hdclcf->name;
lq->file_name = cf->conf_file->file.name.data;
lq->line = cf->conf_file->line;
ngx_queue_init(&lq->list);
ngx_queue_insert_tail(*locations, &lq->queue);
return NGX_OK;
}
/*
* NGX_OK - exact or regex match
* NGX_AGAIN - inclusive match
* NGX_DECLINED - no match
*/
static ngx_int_t
ngx_http_dynamic_core_find_static_location(ngx_http_request_t *r,
ngx_http_dynamic_core_loc_conf_t *phdclcf,
ngx_http_dynamic_core_loc_conf_t **hdclcf)
{
ngx_http_dynamic_location_tree_node_t *node;
u_char *uri;
size_t len, n;
ngx_int_t rc, rv;
len = r->uri.len;
uri = r->uri.data;
rv = NGX_DECLINED;
node = phdclcf->static_locations;
for ( ;; ) {
if (node == NULL) {
return rv;
}
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"test location: \"%*s\"",
(size_t) node->len, node->name);
n = (len <= (size_t) node->len) ? len : node->len;
rc = ngx_filename_cmp(uri, node->name, n);
if (rc != 0) {
node = (rc < 0) ? node->left : node->right;
continue;
}
if (len > (size_t) node->len) {
if (node->inclusive) {
*hdclcf = node->inclusive->
loc_conf[ngx_http_dynamic_core_module.ctx_index];
rv = NGX_AGAIN;
node = node->tree;
uri += n;
len -= n;
continue;
}
/* exact only */
node = node->right;
continue;
}
if (len == (size_t) node->len) {
if (node->exact) {
*hdclcf = node->exact->
loc_conf[ngx_http_dynamic_core_module.ctx_index];
return NGX_OK;
} else {
*hdclcf = node->inclusive->
loc_conf[ngx_http_dynamic_core_module.ctx_index];
return NGX_AGAIN;
}
}
/* len < node->len */
node = node->left;
}
}
/*
* NGX_OK - exact or regex match
* NGX_AGAIN - inclusive match
* NGX_ERROR - regex error
* NGX_DECLINED - no match
*/
static ngx_int_t
ngx_http_dynamic_core_find_location(ngx_http_request_t *r,
ngx_http_dynamic_core_srv_conf_t *hdcscf,
ngx_http_dynamic_core_loc_conf_t **hdclcf)
{
ngx_int_t rc;
ngx_http_dynamic_core_loc_conf_t *phdclcf;
#if (NGX_PCRE)
ngx_int_t n;
ngx_uint_t noregex;
ngx_http_dynamic_core_loc_conf_t **hdclcfp;
noregex = 0;
#endif
phdclcf = hdcscf->hdclcf;
rc = ngx_http_dynamic_core_find_static_location(r, phdclcf, hdclcf);
if (rc == NGX_AGAIN) {
#if (NGX_PCRE)
noregex = (*hdclcf)->noregex;
#endif
}
if (rc == NGX_OK || rc == NGX_DONE) {
return rc;
}
/* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */
#if (NGX_PCRE)
if (noregex == 0 && phdclcf->regex_locations) {
for (hdclcfp = phdclcf->regex_locations; *hdclcfp; hdclcfp++) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"test location: ~ \"%V\"", &(*hdclcfp)->name);
n = ngx_regex_exec((*hdclcfp)->regex->regex, &r->uri, NULL, 0);
if (n == NGX_OK) {
*hdclcf = (*hdclcfp)->
loc_conf[ngx_http_dynamic_core_module.ctx_index];
return NGX_OK;
}
if (n == NGX_REGEX_NO_MATCHED) {
continue;
}
return NGX_ERROR;
}
}
#endif
return rc;
}
static ngx_int_t
ngx_http_dynamic_core_init_virtual_servers(ngx_conf_t *cf,
ngx_http_dynamic_conf_t *hdcf)
{
ngx_http_dynamic_core_main_conf_t *hdcmcf;
ngx_http_dynamic_core_srv_conf_t **hdcscfp;
ngx_hash_init_t hash;
ngx_hash_keys_arrays_t ha;
ngx_http_dynamic_server_name_t *name;
ngx_uint_t s, n;
ngx_int_t rc;
#if (NGX_PCRE)
ngx_uint_t regex, i;
regex = 0;
#endif
hdcmcf = hdcf->main_conf[ngx_http_dynamic_core_module.ctx_index];
ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
if (ha.temp_pool == NULL) {
return NGX_ERROR;
}
ha.pool = cf->pool;
if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
goto failed;
}
hdcscfp = hdcmcf->servers.elts;
hdcmcf->default_server = hdcscfp[0];
for (s = 0; s < hdcmcf->servers.nelts; ++s) {
name = hdcscfp[s]->server_names.elts;
for (n = 0; n < hdcscfp[s]->server_names.nelts; ++n) {
#if (NGX_PCRE)
if (name[n].regex) {
++regex;
continue;
}
#endif
rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,
NGX_HASH_WILDCARD_KEY);
if (rc == NGX_ERROR) {
return NGX_ERROR;
}
if (rc == NGX_DECLINED) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"invalid server name or wildcard \"%V\"",
&name[n].name);
return NGX_ERROR;
}
if (rc == NGX_BUSY) {
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
"conflicting server name \"%V\", ignored",
&name[n].name);
}
}
}
hash.key = ngx_hash_key_lc;
hash.max_size = hdcmcf->server_names_hash_max_size;
hash.bucket_size = hdcmcf->server_names_hash_bucket_size;
hash.name = "http_dynamic_server_names_hash";
hash.pool = cf->pool;
if (ha.keys.nelts) {
hash.hash = &hdcmcf->names.hash;
hash.temp_pool = NULL;
if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
goto failed;
}
}
if (ha.dns_wc_head.nelts) {
ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
sizeof(ngx_hash_key_t), ngx_dynamic_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = ha.temp_pool;
if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
ha.dns_wc_head.nelts) != NGX_OK)
{
goto failed;
}
hdcmcf->names.wc_head = (ngx_hash_wildcard_t *) hash.hash;
}
if (ha.dns_wc_tail.nelts) {
ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
sizeof(ngx_hash_key_t), ngx_dynamic_cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = ha.temp_pool;
if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
ha.dns_wc_tail.nelts) != NGX_OK)
{
goto failed;
}
hdcmcf->names.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
}
ngx_destroy_pool(ha.temp_pool);
#if (NGX_PCRE)
if (regex == 0) {
return NGX_OK;
}
hdcmcf->nregex = regex;
hdcmcf->regex = ngx_pcalloc(cf->pool,
regex * sizeof(ngx_http_dynamic_server_name_t));
if (hdcmcf->regex == NULL) {
return NGX_ERROR;
}
i = 0;
for (s = 0; s < hdcmcf->servers.nelts; ++s) {
name = hdcscfp[s]->server_names.elts;
for (n = 0; n < hdcscfp[s]->server_names.nelts; ++n) {
if (name[n].regex) {
hdcmcf->regex[i++] = name[n];
}
}
}
#endif
return NGX_OK;
failed:
ngx_destroy_pool(ha.temp_pool);
return NGX_ERROR;
}
static ngx_int_t
ngx_http_dynamic_core_find_virtual_server(ngx_http_request_t *r,
ngx_http_dynamic_core_main_conf_t *hdcmcf,
ngx_http_dynamic_core_srv_conf_t **hdcscfp)
{
ngx_http_dynamic_core_srv_conf_t *hdcscf;
ngx_str_t *server;
server = &r->headers_in.server;
hdcscf = ngx_hash_find_combined(&hdcmcf->names,
ngx_hash_key(server->data, server->len),
server->data, server->len);
if (hdcscf) {
*hdcscfp = hdcscf;
return NGX_OK;
}
#if (NGX_PCRE)
if (server->len && hdcmcf->nregex) {
ngx_int_t n;
ngx_uint_t i;
ngx_http_dynamic_server_name_t *sn;
sn = hdcmcf->regex;
for (i = 0; i < hdcmcf->nregex; ++i) {
n = ngx_regex_exec(sn[i].regex->regex, server, NULL, 0);
if (n == NGX_REGEX_NO_MATCHED) {
continue;
}
if (n >= 0) {
*hdcscfp = sn[i].server;
return NGX_OK;
}
return NGX_ERROR;
}
}
#endif
return NGX_DECLINED;
}
static char *
ngx_http_dynamic_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_dynamic_module_t *module;
ngx_http_dynamic_core_loc_conf_t *hdclcf, *phdclcf;
ngx_http_dynamic_core_srv_conf_t *hdcscf;
ngx_conf_t pcf;
void *mconf, **loc_conf;
ngx_uint_t i, ci;
char *rv = NULL;
hdcscf = conf;
phdclcf = hdcscf->hdclcf;
/* loc_conf is temp cause hdclcf does not created now */
loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (loc_conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
if (module == NULL) {
continue;
}
if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;
}
}
hdclcf = loc_conf[ngx_http_dynamic_core_module.ctx_index];
hdclcf->loc_conf = loc_conf;
if (ngx_http_dynamic_core_add_location(cf, &phdclcf->locations, hdclcf)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
pcf = *cf;
cf->ctx = hdclcf->loc_conf;
cf->cmd_type = NGX_HTTP_LOC_CONF;
if (ngx_dynamic_conf_parse(cf, 0) != NGX_OK) {
goto failed;
}
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
ci = cf->cycle->modules[i]->ctx_index;
if (module == NULL) {
continue;
}
if (module->init_loc_conf) {
rv = module->init_loc_conf(cf, hdclcf->loc_conf[ci]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
*cf = pcf;
return NGX_CONF_OK;
failed:
*cf = pcf;
if (rv) {
return rv;
}
return NGX_CONF_ERROR;
}
static char *
ngx_http_dynamic_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_dynamic_core_srv_conf_t *hdcscf;
u_char ch;
ngx_str_t *value;
ngx_uint_t i;
ngx_http_dynamic_server_name_t *sn;
hdcscf = conf;
value = cf->args->elts;
for (i = 1; i < cf->args->nelts; i++) {
ch = value[i].data[0];
if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
|| (ch == '.' && value[i].len < 2))
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"server name \"%V\" is invalid", &value[i]);
return NGX_CONF_ERROR;
}
if (ngx_strchr(value[i].data, '/')) {
ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
"server name \"%V\" has suspicious symbols",
&value[i]);
}
sn = ngx_array_push(&hdcscf->server_names);
if (sn == NULL) {
return NGX_CONF_ERROR;
}
#if (NGX_PCRE)
sn->regex = NULL;
#endif
sn->server = hdcscf;
if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
sn->name = cf->cycle->hostname;
} else {
sn->name = value[i];
}
if (value[i].data[0] != '~') {
ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
continue;
}
#if (NGX_PCRE)
{
u_char *p;
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
if (value[i].len == 1) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"empty regex in server name \"%V\"", &value[i]);
return NGX_CONF_ERROR;
}
value[i].len--;
value[i].data++;
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
rc.pattern = value[i];
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
for (p = value[i].data; p < value[i].data + value[i].len; p++) {
if (*p >= 'A' && *p <= 'Z') {
rc.options = NGX_REGEX_CASELESS;
break;
}
}
sn->regex = ngx_dynamic_regex_compile(cf, &rc);
if (sn->regex == NULL) {
return NGX_CONF_ERROR;
}
sn->name = value[i];
}
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"using regex \"%V\" "
"requires PCRE library", &value[i]);
return NGX_CONF_ERROR;
#endif
}
return NGX_CONF_OK;
}
static char *
ngx_http_dynamic_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_dynamic_module_t *module;
ngx_http_dynamic_core_main_conf_t *hdcmcf;
ngx_http_dynamic_core_srv_conf_t *hdcscf, **hdcscfp;
ngx_http_dynamic_core_loc_conf_t *phdclcf;
ngx_conf_t pcf;
void *mconf, **srv_conf;
ngx_uint_t i, ci;
char *rv = NULL;
hdcmcf = conf;
/* srv_conf is temp cause hdcscf does not created now */
srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (srv_conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
if (module == NULL) {
continue;
}
if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
srv_conf[cf->cycle->modules[i]->ctx_index] = mconf;
}
}
hdcscf = srv_conf[ngx_http_dynamic_core_module.ctx_index];
hdcscf->srv_conf = srv_conf;
hdcscf->hdclcf = ngx_pcalloc(cf->pool,
sizeof(ngx_http_dynamic_core_loc_conf_t));
/* save hdcscf into hdcmcf */
hdcscfp = ngx_array_push(&hdcmcf->servers);
if (hdcscfp == NULL) {
return NGX_CONF_ERROR;
}
*hdcscfp = hdcscf;
pcf = *cf;
cf->ctx = hdcscf->srv_conf;
cf->cmd_type = NGX_HTTP_SRV_CONF;
if (ngx_dynamic_conf_parse(cf, 0) != NGX_OK) {
goto failed;
}
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
ci = cf->cycle->modules[i]->ctx_index;
if (module == NULL) {
continue;
}
if (module->init_srv_conf) {
rv = module->init_srv_conf(cf, hdcscf->srv_conf[ci]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
*cf = pcf;
phdclcf = hdcscf->hdclcf;
if (ngx_http_dynamic_core_init_locations(cf, phdclcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_http_dynamic_core_init_static_location_trees(cf, phdclcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
failed:
*cf = pcf;
if (rv) {
return rv;
}
return NGX_CONF_ERROR;
}
static char *
ngx_http_dynamic_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_dynamic_conf_t *hdccf;
ngx_http_dynamic_module_t *module;
ngx_conf_t pcf;
ngx_uint_t i, ci;
char *rv = NULL;
hdccf = conf;
/* create main_conf ctx */
hdccf->main_conf = ngx_pcalloc(cf->pool, sizeof(void *)
* ngx_http_max_module);
if (hdccf->main_conf == NULL) {
return NGX_CONF_ERROR;
}
/* create http dynamic conf for all http module */
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
ci = cf->cycle->modules[i]->ctx_index;
if (module == NULL) {
continue;
}
if (module->create_main_conf) {
hdccf->main_conf[ci] = module->create_main_conf(cf);
if (hdccf->main_conf[ci] == NULL) {
return NGX_CONF_ERROR;
}
}
}
/* save conf for recovery */
pcf = *cf;
cf->ctx = hdccf->main_conf;
cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
if (ngx_dynamic_conf_parse(cf, 0) != NGX_OK) {
goto failed;
}
for (i = 0; cf->cycle->modules[i]; ++i) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = (ngx_http_dynamic_module_t *)
cf->cycle->modules[i]->spare_hook0;
ci = cf->cycle->modules[i]->ctx_index;
if (module == NULL) {
continue;
}
if (module->init_main_conf) {
rv = module->init_main_conf(cf, hdccf->main_conf[ci]);
if (rv != NGX_CONF_OK) {
goto failed;
}
}
}
if (ngx_http_dynamic_core_init_virtual_servers(cf, hdccf) != NGX_OK) {
goto failed;
}
*cf = pcf;
return NGX_CONF_OK;
failed:
*cf = pcf;
if (rv) {
return rv;
}
return NGX_CONF_ERROR;
}
/* interface from here */
void *
ngx_http_get_module_main_dconf(ngx_http_request_t *r, ngx_module_t *m)
{
ngx_http_dynamic_conf_t *hdcf;
hdcf = ngx_get_dconf(&ngx_http_dynamic_module);
if (hdcf == NULL) {
return NULL;
}
return hdcf->main_conf[m->ctx_index];
}
void *
ngx_http_get_module_srv_dconf(ngx_http_request_t *r, ngx_module_t *m)
{
ngx_http_dynamic_core_main_conf_t *hdcmcf;
ngx_http_dynamic_core_srv_conf_t *hdcscf;
ngx_int_t rc;
hdcmcf = ngx_http_get_module_main_dconf(r, &ngx_http_dynamic_core_module);
if (hdcmcf == NULL) {
return NULL;
}
rc = ngx_http_dynamic_core_find_virtual_server(r, hdcmcf, &hdcscf);
switch (rc) {
case NGX_ERROR:
return NULL;
case NGX_DECLINED: /* virtual server not found */
return hdcmcf->default_server->srv_conf[m->ctx_index];
default:
return hdcscf->srv_conf[m->ctx_index];
}
}
void *
ngx_http_get_module_loc_dconf(ngx_http_request_t *r, ngx_module_t *m)
{
ngx_http_dynamic_core_srv_conf_t *hdcscf;
ngx_http_dynamic_core_loc_conf_t *hdclcf;
ngx_int_t rc;
hdcscf = ngx_http_get_module_srv_dconf(r, &ngx_http_dynamic_core_module);
if (hdcscf == NULL) {
return NULL;
}
hdclcf = NULL;
rc = ngx_http_dynamic_core_find_location(r, hdcscf, &hdclcf);
if (rc == NGX_ERROR) {
return NULL;
}
if (hdclcf) {
return hdclcf->loc_conf[m->ctx_index];
}
return NULL;
}