One Level Up
Top Level
src/http/modules/ngx_http_json_module.c - nginx-1.31.7
nginx/ @ 939334eff
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
- #include <ngx_config.h>
- #include <ngx_core.h>
- #include <ngx_http.h>
- #include <ngx_json_parse.h>
- #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_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,
- NULL,
- ngx_http_json_create_main_conf,
- ngx_http_json_init_main_conf,
- NULL,
- NULL,
- NULL,
- NULL
- };
- ngx_module_t ngx_http_json_module = {
- NGX_MODULE_V1,
- &ngx_http_json_module_ctx,
- ngx_http_json_commands,
- NGX_HTTP_MODULE,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- 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;
- }
One Level Up
Top Level