From: Evgeny Shirykalov Date: Wed, 30 Sep 2026 16:52:47 +0000 (-0700) Subject: JSON: report escaped strings as separate parser events X-Git-Tag: release-1.31.8~8 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=1725db9059e8d4057ce8d5090673d16e20713b45;p=nginx.git JSON: report escaped strings as separate parser events The parser now emits NGX_JSON_KEY_ESCAPED and NGX_JSON_VALUE_STRING_ESCAPED for strings holding escape sequences, so consumers no longer rescan tokens. --- diff --git a/src/core/ngx_json_parse.c b/src/core/ngx_json_parse.c index 7eefae0b1..5def8e2a6 100644 --- a/src/core/ngx_json_parse.c +++ b/src/core/ngx_json_parse.c @@ -52,6 +52,7 @@ ngx_json_ctx_init(ngx_json_ctx_t *ctx, ngx_pool_t *pool) ctx->stack = NULL; ctx->in_key = 0; + ctx->escaped = 0; } @@ -61,6 +62,7 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) u_char *p, *last, *start; ngx_int_t rc, hd; ngx_uint_t skip_depth; + ngx_json_event_e event; ngx_json_state_e state; if (len == 0) { @@ -130,6 +132,7 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) if (*p == '"') { ctx->in_key = 1; + ctx->escaped = 0; start = p + 1; state = ngx_json_string; break; @@ -216,6 +219,7 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) if (*p == '"') { ctx->in_key = 0; + ctx->escaped = 0; start = p + 1; state = ngx_json_string; break; @@ -287,7 +291,10 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) if (*p == '"') { if (ctx->in_key) { - rc = ngx_json_emit(ctx, NGX_JSON_KEY, start, p - start); + event = ctx->escaped ? NGX_JSON_KEY_ESCAPED + : NGX_JSON_KEY; + + rc = ngx_json_emit(ctx, event, start, p - start); if (rc != NGX_OK) { return rc; } @@ -296,8 +303,10 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) state = ngx_json_object_name_separator; } else { - rc = ngx_json_emit(ctx, NGX_JSON_VALUE_STRING, start, - p - start); + event = ctx->escaped ? NGX_JSON_VALUE_STRING_ESCAPED + : NGX_JSON_VALUE_STRING; + + rc = ngx_json_emit(ctx, event, start, p - start); if (rc != NGX_OK) { return rc; } @@ -309,6 +318,7 @@ ngx_json_parse_ctx(ngx_json_ctx_t *ctx, u_char *data, size_t len) } if (*p == '\\') { + ctx->escaped = 1; state = ngx_json_escaped; break; } @@ -734,12 +744,14 @@ ngx_json_emit(ngx_json_ctx_t *ctx, ngx_json_event_e event, u_char *data, switch (event) { case NGX_JSON_KEY: + case NGX_JSON_KEY_ESCAPED: case NGX_JSON_OBJECT_OPEN: case NGX_JSON_ARRAY_OPEN: ctx->skip_until_depth = ctx->depth; break; case NGX_JSON_VALUE_STRING: + case NGX_JSON_VALUE_STRING_ESCAPED: case NGX_JSON_VALUE_NUMBER: case NGX_JSON_VALUE_BOOL: case NGX_JSON_VALUE_NULL: diff --git a/src/core/ngx_json_parse.h b/src/core/ngx_json_parse.h index 2d96aa774..38ede957e 100644 --- a/src/core/ngx_json_parse.h +++ b/src/core/ngx_json_parse.h @@ -24,7 +24,9 @@ typedef enum { NGX_JSON_ARRAY_OPEN, NGX_JSON_ARRAY_CLOSE, NGX_JSON_KEY, + NGX_JSON_KEY_ESCAPED, NGX_JSON_VALUE_STRING, + NGX_JSON_VALUE_STRING_ESCAPED, NGX_JSON_VALUE_NUMBER, NGX_JSON_VALUE_BOOL, NGX_JSON_VALUE_NULL @@ -98,7 +100,8 @@ struct ngx_json_ctx_s { ngx_json_handler_pt handler; void *data; - unsigned in_key; /* unsigned in_key:1 */ + ngx_uint_t in_key; /* unsigned in_key:1; */ + ngx_uint_t escaped; /* unsigned escaped:1; */ }; diff --git a/src/http/modules/ngx_http_json_module.c b/src/http/modules/ngx_http_json_module.c index 57f106c6c..eb0008fa6 100644 --- a/src/http/modules/ngx_http_json_module.c +++ b/src/http/modules/ngx_http_json_module.c @@ -60,7 +60,8 @@ typedef struct { ngx_http_request_t *r; ngx_http_variable_value_t *values; ngx_array_t stack; - ngx_str_t current_key; + ngx_str_t key_buffer; + ngx_uint_t in_member; ngx_http_json_node_t *current_node; ngx_http_json_node_t *root; } ngx_http_json_state_t; @@ -678,7 +679,9 @@ 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; + size_t size; + u_char *p; + ngx_str_t key, slice, value; ngx_uint_t is_member; ngx_http_json_node_t *node; ngx_http_json_state_t *state; @@ -699,14 +702,14 @@ ngx_http_json_handler(ngx_json_ctx_t *ctx, ngx_json_event_e event, if (state->stack.nelts == 0) { - ngx_str_null(&state->current_key); + state->in_member = 0; 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); + is_member = state->in_member; + state->in_member = 0; if (is_member) { @@ -748,48 +751,60 @@ ngx_http_json_handler(ngx_json_ctx_t *ctx, ngx_json_event_e event, break; case NGX_JSON_KEY: + case NGX_JSON_KEY_ESCAPED: - if (ngx_strlchr(token->data, token->data + token->len, '\\') == NULL) { - state->current_key = *token; + key = *token; - } else { - state->current_key.data = ngx_pnalloc(ctx->pool, token->len); - if (state->current_key.data == NULL) { - return NGX_ERROR; + if (event == NGX_JSON_KEY_ESCAPED) { + if (token->len > state->key_buffer.len) { + size = token->len; + + if (state->key_buffer.len <= NGX_MAX_SIZE_T_VALUE / 2) { + size = ngx_max(size, state->key_buffer.len * 2); + } + + p = ngx_pnalloc(ctx->pool, size); + if (p == NULL) { + return NGX_ERROR; + } + + state->key_buffer.data = p; + state->key_buffer.len = size; } - state->current_key.len = token->len; + key = state->key_buffer; - if (ngx_json_unescape_string(&state->current_key, token) != NGX_OK) - { + if (ngx_json_unescape_string(&key, token) != NGX_OK) { return NGX_ERROR; } } - node = ngx_http_json_lookup_key(top->node, &state->current_key); + node = ngx_http_json_lookup_key(top->node, &key); if (node == NULL) { - ngx_str_null(&state->current_key); + state->in_member = 0; state->current_node = NULL; return NGX_JSON_SKIP; } + state->in_member = 1; state->current_node = node; break; case NGX_JSON_VALUE_STRING: + case NGX_JSON_VALUE_STRING_ESCAPED: 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->in_member = 0; state->current_node = NULL; break; } - is_member = (state->current_key.data != NULL); - ngx_str_null(&state->current_key); + is_member = state->in_member; + state->in_member = 0; if (is_member) { @@ -808,7 +823,7 @@ ngx_http_json_handler(ngx_json_ctx_t *ctx, ngx_json_event_e event, value = *token; if (ngx_http_json_store(state, node, &value, - event == NGX_JSON_VALUE_STRING) + event == NGX_JSON_VALUE_STRING_ESCAPED) != NGX_OK) { return NGX_ERROR;