From: Roman Semenov Date: Wed, 12 Aug 2026 16:52:08 +0000 (-0700) Subject: Added the json parser module X-Git-Tag: release-1.31.5~13 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=a1f4a617699089204a5e76267941ceee844f19ea;p=nginx.git Added the json parser module Added the http_json_module with a json_set directive that extracts values from JSON stored in nginx variables and exposes them as new nginx variables. Parsed results are cached per request, so multiple json_set directives using the same source expression trigger only one parse. The directive is available in the http main context. Example usage: http { json_set $user_name $arg_data user.name; json_set $user_age $arg_data user.age; json_set $tag0 $arg_data tags[0]; server { listen 8080; location / { return 200 "name=$user_name age=$user_age " "tag0=$tag0\n"; } } # quoted keys for names containing '.', '[', ']', or that # are empty; the whole argument is single-quoted so that the # inner '"' reach the path parser intact json_set $host $arg_data '["a.b.com"]'; json_set $nested $arg_data 'obj["a.b"].c[2]'; json_set $quoted $arg_data 'obj["a\\"b"]'; } Co-authored-by: Dmitry Volyntsev --- diff --git a/auto/modules b/auto/modules index 572eebe6e..f02860247 100644 --- a/auto/modules +++ b/auto/modules @@ -638,6 +638,17 @@ if [ $HTTP = YES ]; then . auto/module fi + if [ $HTTP_JSON = YES ]; then + ngx_module_name=ngx_http_json_module + ngx_module_incs= + ngx_module_deps= + ngx_module_srcs=src/http/modules/ngx_http_json_module.c + ngx_module_libs= + ngx_module_link=$HTTP_JSON + + . auto/module + fi + if [ $HTTP_GEO = YES ]; then have=NGX_HTTP_X_FORWARDED_FOR . auto/have diff --git a/auto/options b/auto/options index 6699c6914..1aeb73074 100644 --- a/auto/options +++ b/auto/options @@ -75,6 +75,7 @@ HTTP_AUTH_REQUEST=NO HTTP_MIRROR=YES HTTP_USERID=YES HTTP_SLICE=NO +HTTP_JSON=NO HTTP_AUTOINDEX=YES HTTP_RANDOM_INDEX=NO HTTP_STATUS=NO @@ -263,6 +264,7 @@ $0: warning: the \"--with-ipv6\" option is deprecated" --with-http_secure_link_module) HTTP_SECURE_LINK=YES ;; --with-http_degradation_module) HTTP_DEGRADATION=YES ;; --with-http_slice_module) HTTP_SLICE=YES ;; + --with-http_json_module) HTTP_JSON=YES ;; --without-http_charset_module) HTTP_CHARSET=NO ;; --without-http_gzip_module) HTTP_GZIP=NO ;; @@ -495,6 +497,7 @@ cat << END --with-http_secure_link_module enable ngx_http_secure_link_module --with-http_degradation_module enable ngx_http_degradation_module --with-http_slice_module enable ngx_http_slice_module + --with-http_json_module enable ngx_http_json_module --with-http_stub_status_module enable ngx_http_stub_status_module --without-http_charset_module disable ngx_http_charset_module diff --git a/src/http/modules/ngx_http_json_module.c b/src/http/modules/ngx_http_json_module.c new file mode 100644 index 000000000..78099a43d --- /dev/null +++ b/src/http/modules/ngx_http_json_module.c @@ -0,0 +1,978 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +#define NGX_HTTP_JSON_DEFAULT_MAX_DEPTH 32 + +typedef struct { + ngx_uint_t is_index; + ngx_str_t key; + ngx_uint_t index; +} ngx_http_json_seg_t; + + +typedef struct ngx_http_json_node_s ngx_http_json_node_t; + + +struct ngx_http_json_node_s { + ngx_http_json_seg_t seg; + ngx_array_t *children; /* ngx_http_json_node_t */ + ngx_uint_t *dests; + ngx_uint_t ndests; +}; + + +typedef struct { + ngx_int_t index; + ngx_http_json_node_t *root; +} ngx_http_json_source_t; + + +typedef struct { + ngx_uint_t source_index; +} ngx_http_json_variable_t; + + +typedef struct { + ngx_uint_t index; + u_char *start; + ngx_http_json_node_t *node; + ngx_uint_t is_array; +} ngx_http_json_frame_t; + + +typedef struct { + ngx_int_t max_depth; + ngx_array_t *sources; + ngx_array_t *variables; +} ngx_http_json_main_conf_t; + + +typedef struct { + ngx_http_request_t *r; + ngx_http_variable_value_t *values; + ngx_array_t stack; + ngx_str_t current_key; + ngx_http_json_node_t *current_node; + ngx_http_json_node_t *root; +} ngx_http_json_state_t; + + +static char *ngx_http_json_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static ngx_int_t ngx_http_json_variable(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static void ngx_http_json_reset(ngx_http_variable_value_t *values, + ngx_http_json_main_conf_t *jmcf, ngx_uint_t source_index); +static char *ngx_http_json_insert_path(ngx_conf_t *cf, + ngx_http_json_node_t *root, ngx_uint_t dest, ngx_str_t *path); +static ngx_http_json_node_t *ngx_http_json_child(ngx_conf_t *cf, + ngx_http_json_node_t *parent, ngx_http_json_seg_t *seg); +static ngx_int_t ngx_http_json_handler(ngx_json_ctx_t *ctx, + ngx_json_event_e event, ngx_str_t *token); +static void ngx_http_json_inc_index(ngx_http_json_state_t *state); +static ngx_http_json_node_t *ngx_http_json_lookup_key( + ngx_http_json_node_t *parent, ngx_str_t *key); +static ngx_http_json_node_t *ngx_http_json_lookup_index( + ngx_http_json_node_t *parent, ngx_uint_t index); +static ngx_int_t ngx_http_json_store(ngx_http_json_state_t *state, + ngx_http_json_node_t *node, ngx_str_t *value, ngx_uint_t unescape); +static ngx_int_t ngx_http_json_push(ngx_http_json_state_t *state, + ngx_json_event_e event, u_char *start, ngx_http_json_node_t *node); +static void *ngx_http_json_create_main_conf(ngx_conf_t *cf); +static char *ngx_http_json_init_main_conf(ngx_conf_t *cf, void *conf); + + +static ngx_conf_num_bounds_t ngx_http_json_max_depth_bounds = { + ngx_conf_check_num_bounds, 1, 256 +}; + + +static ngx_command_t ngx_http_json_commands[] = { + + { ngx_string("json_set"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_json_set, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("json_max_depth"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_MAIN_CONF_OFFSET, + offsetof(ngx_http_json_main_conf_t, max_depth), + &ngx_http_json_max_depth_bounds }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_json_module_ctx = { + NULL, /* preconfiguration */ + NULL, /* postconfiguration */ + + ngx_http_json_create_main_conf, /* create main configuration */ + ngx_http_json_init_main_conf, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + NULL, /* create location configuration */ + NULL /* merge location configuration */ +}; + + +ngx_module_t ngx_http_json_module = { + NGX_MODULE_V1, + &ngx_http_json_module_ctx, /* module context */ + ngx_http_json_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static char * +ngx_http_json_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_json_main_conf_t *jmcf = conf; + + ngx_str_t *value, name, source; + ngx_int_t sindex; + ngx_uint_t i, oi, si; + ngx_http_variable_t *var; + ngx_http_json_source_t *src, *srcs; + ngx_http_json_variable_t *jv; + + value = cf->args->elts; + + name = value[1]; + + 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_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + if (var->get_handler == ngx_http_json_variable) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "json_set variable \"%V\" is already defined", + &name); + return NGX_CONF_ERROR; + } + + source = value[2]; + + if (source.data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &source); + return NGX_CONF_ERROR; + } + + source.len--; + source.data++; + + sindex = ngx_http_get_variable_index(cf, &source); + if (sindex == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + if (jmcf->sources == NULL) { + jmcf->sources = ngx_array_create(cf->pool, 4, + sizeof(ngx_http_json_source_t)); + if (jmcf->sources == NULL) { + return NGX_CONF_ERROR; + } + } + + srcs = jmcf->sources->elts; + si = jmcf->sources->nelts; + + for (i = 0; i < jmcf->sources->nelts; i++) { + if (srcs[i].index == sindex) { + si = i; + break; + } + } + + if (si == jmcf->sources->nelts) { + src = ngx_array_push(jmcf->sources); + if (src == NULL) { + return NGX_CONF_ERROR; + } + + src->index = sindex; + + src->root = ngx_pcalloc(cf->pool, sizeof(ngx_http_json_node_t)); + if (src->root == NULL) { + return NGX_CONF_ERROR; + } + + } else { + src = &srcs[si]; + } + + if (jmcf->variables == NULL) { + jmcf->variables = ngx_array_create(cf->pool, 4, + sizeof(ngx_http_json_variable_t)); + if (jmcf->variables == NULL) { + return NGX_CONF_ERROR; + } + } + + oi = jmcf->variables->nelts; + + jv = ngx_array_push(jmcf->variables); + if (jv == NULL) { + return NGX_CONF_ERROR; + } + + jv->source_index = si; + + if (ngx_http_json_insert_path(cf, src->root, oi, &value[3]) + != NGX_CONF_OK) + { + return NGX_CONF_ERROR; + } + + var->get_handler = ngx_http_json_variable; + var->data = (uintptr_t) oi; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_json_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_int_t rc; + ngx_uint_t oi, si; + ngx_json_ctx_t jctx; + ngx_http_json_state_t state; + ngx_http_json_source_t *src, *srcs; + ngx_http_json_variable_t *jv; + ngx_http_variable_value_t *cached, *values, *vv; + ngx_http_json_main_conf_t *jmcf; + + oi = (ngx_uint_t) data; + + jmcf = ngx_http_get_module_main_conf(r, ngx_http_json_module); + + if (jmcf->variables == NULL || oi >= jmcf->variables->nelts) { + v->not_found = 1; + return NGX_OK; + } + + values = ngx_http_get_module_ctx(r, ngx_http_json_module); + + if (values == NULL) { + values = ngx_pcalloc(r->pool, jmcf->variables->nelts + * sizeof(ngx_http_variable_value_t)); + if (values == NULL) { + return NGX_ERROR; + } + + ngx_http_set_ctx(r, values, ngx_http_json_module); + } + + cached = &values[oi]; + + if (cached->valid || cached->not_found) { + *v = *cached; + return NGX_OK; + } + + jv = jmcf->variables->elts; + si = jv[oi].source_index; + + srcs = jmcf->sources->elts; + src = &srcs[si]; + + vv = ngx_http_get_flushed_variable(r, (ngx_uint_t) src->index); + + if (vv == NULL) { + return NGX_ERROR; + } + + ngx_http_json_reset(values, jmcf, si); + + if (vv->not_found || vv->len == 0) { + *v = *cached; + return NGX_OK; + } + + ngx_memzero(&state, sizeof(ngx_http_json_state_t)); + + state.r = r; + state.values = values; + state.root = src->root; + + if (ngx_array_init(&state.stack, r->pool, 8, sizeof(ngx_http_json_frame_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + ngx_json_ctx_init(&jctx, r->pool); + + jctx.handler = ngx_http_json_handler; + jctx.data = &state; + jctx.max_depth = jmcf->max_depth; + + rc = ngx_json_parse_ctx(&jctx, vv->data, vv->len); + + if (rc == NGX_OK) { + *v = *cached; + return NGX_OK; + } + + ngx_http_json_reset(values, jmcf, si); + + if (rc == NGX_DECLINED) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "json_set: invalid JSON source"); + } + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + *v = *cached; + + return NGX_OK; +} + + +static void +ngx_http_json_reset(ngx_http_variable_value_t *values, + ngx_http_json_main_conf_t *jmcf, ngx_uint_t source_index) +{ + ngx_uint_t i; + ngx_http_json_variable_t *jv; + + jv = jmcf->variables->elts; + + for (i = 0; i < jmcf->variables->nelts; i++) { + if (jv[i].source_index != source_index) { + continue; + } + + values[i].valid = 0; + values[i].not_found = 1; + } +} + + +static char * +ngx_http_json_insert_path(ngx_conf_t *cf, ngx_http_json_node_t *root, + ngx_uint_t dest, ngx_str_t *path) +{ + u_char *p, *last, *start, *dst; + ngx_int_t index; + ngx_uint_t *term; + ngx_http_json_seg_t seg; + ngx_http_json_node_t *node; + enum { + sw_start = 0, + sw_key, + sw_dot, + sw_bracket, + sw_index, + sw_quoted, + sw_quoted_escape, + sw_quoted_close, + sw_after_bracket + } state; + + if (path->len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty json_set path"); + return NGX_CONF_ERROR; + } + + node = root; + start = NULL; + + last = path->data + path->len; + state = sw_start; + + for (p = path->data; p < last; p++) { + + switch (state) { + + case sw_start: + + if (*p == '[') { + state = sw_bracket; + break; + } + + if (*p == '.' || *p == '$') { + goto invalid; + } + + start = p; + state = sw_key; + break; + + case sw_key: + + if (*p == '.' || *p == '[') { + + seg.is_index = 0; + seg.key.data = start; + seg.key.len = p - start; + seg.index = 0; + + node = ngx_http_json_child(cf, node, &seg); + if (node == NULL) { + return NGX_CONF_ERROR; + } + + state = (*p == '.') ? sw_dot : sw_bracket; + } + + break; + + case sw_dot: + + if (*p == '.' || *p == '[' || *p == '$') { + goto invalid; + } + + start = p; + state = sw_key; + break; + + case sw_bracket: + + if (*p == '"') { + start = p + 1; + state = sw_quoted; + break; + } + + if (*p >= '0' && *p <= '9') { + start = p; + state = sw_index; + break; + } + + goto invalid; + + case sw_index: + + if (*p >= '0' && *p <= '9') { + break; + } + + if (*p != ']') { + goto invalid; + } + + index = ngx_atoi(start, p - start); + if (index == NGX_ERROR) { + goto invalid; + } + + seg.is_index = 1; + seg.index = index; + ngx_str_null(&seg.key); + + node = ngx_http_json_child(cf, node, &seg); + if (node == NULL) { + return NGX_CONF_ERROR; + } + + state = sw_after_bracket; + break; + + case sw_quoted: + + if (*p == '\\') { + state = sw_quoted_escape; + break; + } + + if (*p == '"') { + + dst = ngx_pnalloc(cf->pool, (size_t) (p - start)); + if (dst == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(dst, start, p - start); + + seg.is_index = 0; + seg.key.data = dst; + seg.key.len = p - start; + seg.index = 0; + + if (ngx_json_unescape_string(&seg.key) != NGX_OK) { + goto invalid; + } + + node = ngx_http_json_child(cf, node, &seg); + if (node == NULL) { + return NGX_CONF_ERROR; + } + + state = sw_quoted_close; + } + + break; + + case sw_quoted_escape: + + state = sw_quoted; + break; + + case sw_quoted_close: + + if (*p != ']') { + goto invalid; + } + + state = sw_after_bracket; + break; + + case sw_after_bracket: + + if (*p == '.') { + state = sw_dot; + break; + } + + if (*p == '[') { + state = sw_bracket; + break; + } + + goto invalid; + } + } + + switch (state) { + + case sw_key: + + seg.is_index = 0; + seg.key.data = start; + seg.key.len = last - start; + seg.index = 0; + + node = ngx_http_json_child(cf, node, &seg); + if (node == NULL) { + return NGX_CONF_ERROR; + } + + break; + + case sw_after_bracket: + break; + + default: + goto invalid; + } + + term = ngx_palloc(cf->pool, (node->ndests + 1) * sizeof(ngx_uint_t)); + if (term == NULL) { + return NGX_CONF_ERROR; + } + + if (node->ndests) { + ngx_memcpy(term, node->dests, node->ndests * sizeof(ngx_uint_t)); + } + + term[node->ndests] = dest; + node->dests = term; + node->ndests++; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid json_set path \"%V\"", path); + return NGX_CONF_ERROR; +} + + +static ngx_http_json_node_t * +ngx_http_json_child(ngx_conf_t *cf, ngx_http_json_node_t *parent, + ngx_http_json_seg_t *seg) +{ + ngx_uint_t i; + ngx_http_json_seg_t *cseg; + ngx_http_json_node_t *child, *elts; + + if (parent->children != NULL) { + elts = parent->children->elts; + + for (i = 0; i < parent->children->nelts; i++) { + child = &elts[i]; + cseg = &child->seg; + + if (seg->is_index) { + if (cseg->is_index && cseg->index == seg->index) { + return child; + } + + } else { + if (!cseg->is_index + && cseg->key.len == seg->key.len + && ngx_memcmp(cseg->key.data, seg->key.data, seg->key.len) + == 0) + { + return child; + } + } + } + + } else { + parent->children = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_json_node_t)); + if (parent->children == NULL) { + return NULL; + } + } + + child = ngx_array_push(parent->children); + if (child == NULL) { + return NULL; + } + + ngx_memzero(child, sizeof(ngx_http_json_node_t)); + + child->seg = *seg; + + return child; +} + + +static ngx_int_t +ngx_http_json_handler(ngx_json_ctx_t *ctx, ngx_json_event_e event, + ngx_str_t *token) +{ + ngx_str_t slice, value; + ngx_uint_t is_member; + ngx_http_json_node_t *node; + ngx_http_json_state_t *state; + ngx_http_json_frame_t *top; + + state = ctx->data; + + top = NULL; + if (state->stack.nelts) { + top = (ngx_http_json_frame_t *) state->stack.elts + + state->stack.nelts - 1; + } + + switch (event) { + + case NGX_JSON_OBJECT_OPEN: + case NGX_JSON_ARRAY_OPEN: + + if (state->stack.nelts == 0) { + + ngx_str_null(&state->current_key); + state->current_node = NULL; + + return ngx_http_json_push(state, event, token->data, state->root); + } + + is_member = (state->current_key.data != NULL); + ngx_str_null(&state->current_key); + + if (is_member) { + + node = state->current_node; + state->current_node = NULL; + + } else { + + node = ngx_http_json_lookup_index(top->node, top->index); + + ngx_http_json_inc_index(state); + + if (node == NULL) { + return NGX_JSON_SKIP; + } + } + + return ngx_http_json_push(state, event, token->data, node); + + case NGX_JSON_OBJECT_CLOSE: + case NGX_JSON_ARRAY_CLOSE: + + if (top != NULL) { + + if (top->node->ndests > 0) { + slice.data = top->start; + slice.len = token->data - top->start + 1; + + if (ngx_http_json_store(state, top->node, &slice, 0) + != NGX_OK) + { + return NGX_ERROR; + } + } + + state->stack.nelts--; + } + + break; + + case NGX_JSON_KEY: + + if (ngx_strlchr(token->data, token->data + token->len, '\\') == NULL) { + state->current_key = *token; + + } else { + state->current_key.data = ngx_pstrdup(ctx->pool, token); + if (state->current_key.data == NULL) { + return NGX_ERROR; + } + + state->current_key.len = token->len; + + if (ngx_json_unescape_string(&state->current_key) != NGX_OK) { + return NGX_ERROR; + } + } + + node = ngx_http_json_lookup_key(top->node, &state->current_key); + if (node == NULL) { + ngx_str_null(&state->current_key); + state->current_node = NULL; + return NGX_JSON_SKIP; + } + + state->current_node = node; + + break; + + case NGX_JSON_VALUE_STRING: + case NGX_JSON_VALUE_NUMBER: + case NGX_JSON_VALUE_BOOL: + case NGX_JSON_VALUE_NULL: + + if (top == NULL) { + ngx_str_null(&state->current_key); + state->current_node = NULL; + break; + } + + is_member = (state->current_key.data != NULL); + ngx_str_null(&state->current_key); + + if (is_member) { + + node = state->current_node; + state->current_node = NULL; + + } else { + + node = ngx_http_json_lookup_index(top->node, top->index); + + ngx_http_json_inc_index(state); + } + + if (node != NULL && node->ndests > 0) { + + value = *token; + + if (ngx_http_json_store(state, node, &value, + event == NGX_JSON_VALUE_STRING) + != NGX_OK) + { + return NGX_ERROR; + } + } + + break; + } + + return NGX_OK; +} + + +static void +ngx_http_json_inc_index(ngx_http_json_state_t *state) +{ + ngx_http_json_frame_t *top; + + if (state->stack.nelts == 0) { + return; + } + + top = state->stack.elts; + top += state->stack.nelts - 1; + + if (top->is_array) { + top->index++; + } +} + + +static ngx_http_json_node_t * +ngx_http_json_lookup_key(ngx_http_json_node_t *parent, ngx_str_t *key) +{ + ngx_uint_t i; + ngx_http_json_seg_t *seg; + ngx_http_json_node_t *child, *elts; + + if (parent->children == NULL) { + return NULL; + } + + elts = parent->children->elts; + + for (i = 0; i < parent->children->nelts; i++) { + child = &elts[i]; + seg = &child->seg; + + if (!seg->is_index + && seg->key.len == key->len + && ngx_memcmp(seg->key.data, key->data, key->len) == 0) + { + return child; + } + } + + return NULL; +} + + +static ngx_http_json_node_t * +ngx_http_json_lookup_index(ngx_http_json_node_t *parent, ngx_uint_t index) +{ + ngx_uint_t i; + ngx_http_json_seg_t *seg; + ngx_http_json_node_t *child, *elts; + + if (parent->children == NULL) { + return NULL; + } + + elts = parent->children->elts; + + for (i = 0; i < parent->children->nelts; i++) { + child = &elts[i]; + seg = &child->seg; + + if (seg->is_index && seg->index == index) { + return child; + } + } + + return NULL; +} + + +static ngx_int_t +ngx_http_json_store(ngx_http_json_state_t *state, ngx_http_json_node_t *node, + ngx_str_t *value, ngx_uint_t unescape) +{ + ngx_str_t val; + ngx_uint_t i; + ngx_http_variable_value_t *slot; + + if (value->len == 0) { + val.len = 0; + val.data = (u_char *) ""; + + } else { + val.data = ngx_pstrdup(state->r->pool, value); + if (val.data == NULL) { + return NGX_ERROR; + } + + val.len = value->len; + + if (unescape && ngx_json_unescape_string(&val) != NGX_OK) { + return NGX_ERROR; + } + } + + for (i = 0; i < node->ndests; i++) { + slot = &state->values[node->dests[i]]; + slot->valid = 1; + slot->not_found = 0; + slot->no_cacheable = 0; + slot->escape = 0; + slot->len = val.len; + slot->data = val.data; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_json_push(ngx_http_json_state_t *state, ngx_json_event_e event, + u_char *start, ngx_http_json_node_t *node) +{ + ngx_http_json_frame_t *frame; + + frame = ngx_array_push(&state->stack); + if (frame == NULL) { + return NGX_ERROR; + } + + frame->index = 0; + frame->start = start; + frame->is_array = (event == NGX_JSON_ARRAY_OPEN); + frame->node = node; + + return NGX_OK; +} + + +static void * +ngx_http_json_create_main_conf(ngx_conf_t *cf) +{ + ngx_http_json_main_conf_t *jmcf; + + jmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_json_main_conf_t)); + if (jmcf == NULL) { + return NULL; + } + + jmcf->max_depth = NGX_CONF_UNSET; + + return jmcf; +} + + +static char * +ngx_http_json_init_main_conf(ngx_conf_t *cf, void *conf) +{ + ngx_http_json_main_conf_t *jmcf = conf; + + ngx_conf_init_value(jmcf->max_depth, NGX_HTTP_JSON_DEFAULT_MAX_DEPTH); + + return NGX_CONF_OK; +}