From: Henry Yuan Date: Thu, 16 Jul 2026 22:00:32 +0000 (+0000) Subject: Core: added data model and JSON serialization libraries. X-Git-Tag: release-1.31.4~8 X-Git-Url: https://git.kaiwu.me/%7B@url%7D?a=commitdiff_plain;h=836704c6e818d55912c714dca918ad253f913282;p=nginx.git Core: added data model and JSON serialization libraries. Co-authored-by: Valentin Bartenev Co-authored-by: Ruslan Ermilov Co-authored-by: Vladimir Homutov Co-authored-by: Evgeny Shirykalov Co-authored-by: Dmitry Plotnikov --- diff --git a/auto/sources b/auto/sources index 2bce9cf8d..52a59d089 100644 --- a/auto/sources +++ b/auto/sources @@ -41,6 +41,8 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_resolver.h \ src/core/ngx_open_file_cache.h \ src/core/ngx_crypt.h \ + src/core/ngx_data.h \ + src/core/ngx_json.h \ src/core/ngx_proxy_protocol.h \ src/core/ngx_syslog.h" @@ -79,6 +81,8 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_resolver.c \ src/core/ngx_open_file_cache.c \ src/core/ngx_crypt.c \ + src/core/ngx_data.c \ + src/core/ngx_json.c \ src/core/ngx_proxy_protocol.c \ src/core/ngx_syslog.c" diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h index b919d5b31..d0226ac89 100644 --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -79,6 +79,8 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); #include #include #include +#include +#include #include #include #include diff --git a/src/core/ngx_data.c b/src/core/ngx_data.c new file mode 100644 index 000000000..d3937c1b1 --- /dev/null +++ b/src/core/ngx_data.c @@ -0,0 +1,312 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static size_t ngx_data_sizes[] = { + sizeof(ngx_data_obj_t), /* NGX_DATA_OBJECT_TYPE */ + sizeof(ngx_data_obj_t), /* NGX_DATA_LIST_TYPE */ + sizeof(ngx_str_t), /* NGX_DATA_STRING_TYPE */ + sizeof(int64_t), /* NGX_DATA_INTEGER_TYPE */ + sizeof(ngx_uint_t), /* NGX_DATA_BOOLEAN_TYPE */ + 0, /* NGX_DATA_NULL_TYPE */ +}; + + +ngx_data_item_t * +ngx_data_new_item(ngx_pool_t *pool, ngx_uint_t type) +{ + ngx_data_item_t *item; + + item = ngx_pcalloc(pool, offsetof(ngx_data_item_t, data) + + ngx_data_sizes[type]); + if (item == NULL) { + return NULL; + } + + item->type = type; + + return item; +} + + +void +ngx_data_add_item(ngx_data_item_t *obj, ngx_str_t *name, ngx_data_item_t *item) +{ + ngx_data_obj_t *object; + + if (obj->type != NGX_DATA_OBJECT_TYPE && obj->type != NGX_DATA_LIST_TYPE) { + return; + } + + object = &obj->data.object; + + if (name) { + item->name = *name; + } + + if (object->next == NULL) { + object->next = &object->item; + } + + *object->next = item; + object->next = &item->next; +} + + +ngx_data_item_t * +ngx_data_obj_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_data_item_t *obj, *item; + ngx_data_decl_t *decl; + + obj = ngx_data_new_object(pool); + if (obj == NULL) { + return NULL; + } + + decl = (ngx_data_decl_t *) data; + + while (decl->name.len) { + + item = decl->handler(decl->data, pool, ctx); + if (item == NULL) { + return NULL; + } + + if (item != NGX_DATA_DECLINE) { + ngx_data_add_item(obj, &decl->name, item); + } + + decl++; + } + + return obj; +} + + +ngx_data_item_t * +ngx_data_obj_fields_handler(uintptr_t data, ngx_pool_t *pool, void *ctx, + ngx_array_t *fields) +{ + ngx_str_t *value; + ngx_uint_t i; + ngx_data_item_t *obj, *item; + ngx_data_decl_t *decl; + + obj = ngx_data_new_object(pool); + if (obj == NULL) { + return NULL; + } + + decl = (ngx_data_decl_t *) data; + + while (decl->name.len) { + + if (fields) { + value = fields->elts; + + for (i = 0; i < fields->nelts; i++) { + + if (decl->name.len == value[i].len + && ngx_strncmp(decl->name.data, value[i].data, + value[i].len) + == 0) + { + goto found; + } + } + + decl++; + continue; + } + +found: + + item = decl->handler(decl->data, pool, ctx); + if (item == NULL) { + return NULL; + } + + if (item != NGX_DATA_DECLINE) { + ngx_data_add_item(obj, &decl->name, item); + } + + decl++; + } + + return obj; +} + + +ngx_data_item_t * +ngx_data_number_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_data_item_t *item; + + item = ngx_data_new_integer(pool); + if (item == NULL) { + return NULL; + } + + item->data.integer = data; + + return item; +} + + +ngx_data_item_t * +ngx_data_string_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_str_t *value, *src; + ngx_data_item_t *item; + + item = ngx_data_new_string(pool); + if (item == NULL) { + return NULL; + } + + value = &item->data.string; + + src = (ngx_str_t *) data; + + value->len = src->len; + + value->data = ngx_pstrdup(pool, src); + if (value->data == NULL) { + return NULL; + } + + return item; +} + + +ngx_data_item_t * +ngx_data_boolean_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_data_item_t *item; + + item = ngx_data_new_boolean(pool); + if (item == NULL) { + return NULL; + } + + item->data.boolean = data; + + return item; +} + + +ngx_data_item_t * +ngx_data_time_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + u_char *p; + ngx_tm_t tm; + ngx_str_t src; + ngx_time_t *tp; + u_char iso8601[sizeof("1974-01-19T13:00:00.000Z") - 1]; + + tp = (ngx_time_t *) data; + + ngx_gmtime(tp->sec, &tm); + + p = ngx_sprintf(iso8601, "%4d-%02d-%02dT%02d:%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, + tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); + + if (tp->msec) { + p = ngx_sprintf(p, ".%03ui", tp->msec); + } + + *p++ = 'Z'; + + src.data = iso8601; + src.len = p - iso8601; + + return ngx_data_string_handler((uintptr_t) &src, pool, NULL); +} + + +ngx_data_item_t * +ngx_data_struct_int64_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_data_item_t *item; + + item = ngx_data_new_integer(pool); + if (item == NULL) { + return NULL; + } + + item->data.integer = *(int64_t *) ((u_char *) ctx + data); + + return item; +} + + +ngx_data_item_t * +ngx_data_struct_int_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_int_t value; + + value = *(ngx_int_t *) ((u_char *) ctx + data); + + return ngx_data_number_handler(value, pool, NULL); +} + + +ngx_data_item_t * +ngx_data_struct_atomic_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_data_item_t *item; + + item = ngx_data_new_integer(pool); + if (item == NULL) { + return NULL; + } + + item->data.integer = *(ngx_atomic_uint_t *) ((u_char *) ctx + data); + + return item; +} + + +ngx_data_item_t * +ngx_data_struct_str_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_str_t *value, *src; + ngx_data_item_t *item; + + item = ngx_data_new_string(pool); + if (item == NULL) { + return NULL; + } + + value = &item->data.string; + + src = (ngx_str_t *) ((u_char *) ctx + data); + + value->len = src->len; + + value->data = ngx_pstrdup(pool, src); + if (value->data == NULL) { + return NULL; + } + + return item; +} + + +ngx_data_item_t * +ngx_data_struct_boolean_handler(uintptr_t data, ngx_pool_t *pool, void *ctx) +{ + ngx_uint_t value; + + value = *(ngx_uint_t *) ((u_char *) ctx + data); + + return ngx_data_boolean_handler(value, pool, NULL); +} diff --git a/src/core/ngx_data.h b/src/core/ngx_data.h new file mode 100644 index 000000000..5a74c1da5 --- /dev/null +++ b/src/core/ngx_data.h @@ -0,0 +1,101 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_DATA_H_INCLUDED_ +#define _NGX_DATA_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_data_item_s ngx_data_item_t; + + +typedef struct { + ngx_data_item_t *item; + ngx_data_item_t **next; +} ngx_data_obj_t; + + +struct ngx_data_item_s { + ngx_data_item_t *next; + ngx_str_t name; + ngx_uint_t type; + + union { + ngx_data_obj_t object; + ngx_str_t string; + int64_t integer; + ngx_uint_t boolean; + } data; +}; + + +ngx_data_item_t *ngx_data_new_item(ngx_pool_t *pool, ngx_uint_t type); + +void ngx_data_add_item(ngx_data_item_t *obj, ngx_str_t *name, + ngx_data_item_t *item); + +#define NGX_DATA_OBJECT_TYPE 0 +#define NGX_DATA_LIST_TYPE 1 +#define NGX_DATA_STRING_TYPE 2 +#define NGX_DATA_INTEGER_TYPE 3 +#define NGX_DATA_BOOLEAN_TYPE 4 +#define NGX_DATA_NULL_TYPE 5 + + +#define ngx_data_new_object(pool) \ + ngx_data_new_item(pool, NGX_DATA_OBJECT_TYPE) +#define ngx_data_new_list(pool) \ + ngx_data_new_item(pool, NGX_DATA_LIST_TYPE) +#define ngx_data_new_string(pool) \ + ngx_data_new_item(pool, NGX_DATA_STRING_TYPE) +#define ngx_data_new_integer(pool) \ + ngx_data_new_item(pool, NGX_DATA_INTEGER_TYPE) +#define ngx_data_new_boolean(pool) \ + ngx_data_new_item(pool, NGX_DATA_BOOLEAN_TYPE) +#define ngx_data_new_null(pool) \ + ngx_data_new_item(pool, NGX_DATA_NULL_TYPE) + + +typedef struct { + ngx_str_t name; + ngx_data_item_t *(*handler)(uintptr_t data, ngx_pool_t *pool, void *ctx); + uintptr_t data; +} ngx_data_decl_t; + +#define ngx_data_null_decl { ngx_null_string, NULL, 0 } + +#define NGX_DATA_DECLINE (ngx_data_item_t *) -1 + + +ngx_data_item_t *ngx_data_obj_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_obj_fields_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx, ngx_array_t *fields); + +ngx_data_item_t *ngx_data_number_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_string_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_boolean_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_time_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); + +ngx_data_item_t *ngx_data_struct_int64_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_struct_int_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_struct_atomic_handler(uintptr_t data, + ngx_pool_t *pool, void *ctx); +ngx_data_item_t *ngx_data_struct_str_handler(uintptr_t data, ngx_pool_t *pool, + void *ctx); +ngx_data_item_t *ngx_data_struct_boolean_handler(uintptr_t data, + ngx_pool_t *pool, void *ctx); + +#endif /* _NGX_DATA_H_INCLUDED_ */ diff --git a/src/core/ngx_json.c b/src/core/ngx_json.c new file mode 100644 index 000000000..f826d4312 --- /dev/null +++ b/src/core/ngx_json.c @@ -0,0 +1,317 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +typedef ssize_t (*ngx_json_length_pt)(ngx_data_item_t *item); +typedef u_char *(*ngx_json_encode_pt)(u_char *p, ngx_data_item_t *item); + + +typedef struct { + ngx_json_length_pt length; + ngx_json_encode_pt encode; +} ngx_json_encode_t; + + +static ssize_t ngx_json_obj_length(ngx_data_item_t *item); +static ssize_t ngx_json_list_length(ngx_data_item_t *item); +static ssize_t ngx_json_string_length(ngx_data_item_t *item); +static ssize_t ngx_json_integer_length(ngx_data_item_t *item); +static ssize_t ngx_json_boolean_length(ngx_data_item_t *item); +static ssize_t ngx_json_null_length(ngx_data_item_t *item); + +static u_char *ngx_json_obj_encode(u_char *p, ngx_data_item_t *item); +static u_char *ngx_json_list_encode(u_char *p, ngx_data_item_t *item); +static u_char *ngx_json_string_encode(u_char *p, ngx_data_item_t *item); +static u_char *ngx_json_integer_encode(u_char *p, ngx_data_item_t *item); +static u_char *ngx_json_boolean_encode(u_char *p, ngx_data_item_t *item); +static u_char *ngx_json_null_encode(u_char *p, ngx_data_item_t *item); + + +static ngx_json_encode_t ngx_json_encode[] = { + + /* NGX_DATA_OBJECT_TYPE */ + { ngx_json_obj_length, + ngx_json_obj_encode }, + + /* NGX_DATA_LIST_TYPE */ + { ngx_json_list_length, + ngx_json_list_encode }, + + /* NGX_DATA_STRING_TYPE */ + { ngx_json_string_length, + ngx_json_string_encode }, + + /* NGX_DATA_INTEGER_TYPE */ + { ngx_json_integer_length, + ngx_json_integer_encode }, + + /* NGX_DATA_BOOLEAN_TYPE */ + { ngx_json_boolean_length, + ngx_json_boolean_encode }, + + /* NGX_DATA_NULL_TYPE */ + { ngx_json_null_length, + ngx_json_null_encode }, +}; + + +ngx_buf_t * +ngx_json_render(ngx_pool_t *pool, ngx_data_item_t *item) +{ + size_t length; + ngx_buf_t *buf; + + length = ngx_json_encode[item->type].length(item); + if (length == (size_t) NGX_ERROR) { + return NULL; + } + + buf = ngx_create_temp_buf(pool, length); + if (buf == NULL) { + return NULL; + } + + buf->last = ngx_json_encode[item->type].encode(buf->last, item); + + return buf; +} + + +static ssize_t +ngx_json_obj_length(ngx_data_item_t *item) +{ + size_t len, total; + ngx_data_obj_t *obj; + ngx_data_item_t *i; + + obj = &item->data.object; + total = sizeof("{}") - 1; + i = obj->item; + + if (i) { + for ( ;; ) { + len = sizeof("\"\":") - 1 + i->name.len + + ngx_escape_json(NULL, i->name.data, i->name.len); + + if (total > NGX_MAX_SIZE_T_VALUE - len) { + return NGX_ERROR; + } + + total += len; + + len = ngx_json_encode[i->type].length(i); + if (len == (size_t) NGX_ERROR) { + return NGX_ERROR; + } + + if (total > NGX_MAX_SIZE_T_VALUE - len) { + return NGX_ERROR; + } + + total += len; + + i = i->next; + + if (i == NULL) { + break; + } + + if (total > NGX_MAX_SIZE_T_VALUE - (sizeof(",") - 1)) { + return NGX_ERROR; + } + + total += sizeof(",") - 1; + } + } + + return total; +} + + +static u_char * +ngx_json_obj_encode(u_char *p, ngx_data_item_t *item) +{ + ngx_data_obj_t *obj; + ngx_data_item_t *i; + + obj = &item->data.object; + i = obj->item; + + *p++ = '{'; + + if (i) { + for ( ;; ) { + *p++ = '"'; + + p = (u_char *) ngx_escape_json(p, i->name.data, i->name.len); + + *p++ = '"'; + *p++ = ':'; + + p = ngx_json_encode[i->type].encode(p, i); + + i = i->next; + + if (i == NULL) { + break; + } + + *p++ = ','; + } + } + + *p++ = '}'; + + return p; +} + + +static ssize_t +ngx_json_list_length(ngx_data_item_t *item) +{ + size_t len, total; + ngx_data_obj_t *obj; + ngx_data_item_t *i; + + obj = &item->data.object; + total = sizeof("[]") - 1; + i = obj->item; + + if (i) { + for ( ;; ) { + len = ngx_json_encode[i->type].length(i); + if (len == (size_t) NGX_ERROR) { + return NGX_ERROR; + } + + if (total > NGX_MAX_SIZE_T_VALUE - len) { + return NGX_ERROR; + } + + total += len; + + i = i->next; + + if (i == NULL) { + break; + } + + if (total > NGX_MAX_SIZE_T_VALUE - (sizeof(",") - 1)) { + return NGX_ERROR; + } + + total += sizeof(",") - 1; + } + } + + return total; +} + + +static u_char * +ngx_json_list_encode(u_char *p, ngx_data_item_t *item) +{ + ngx_data_obj_t *obj; + ngx_data_item_t *i; + + obj = &item->data.object; + i = obj->item; + + *p++ = '['; + + if (i) { + for ( ;; ) { + p = ngx_json_encode[i->type].encode(p, i); + + i = i->next; + + if (i == NULL) { + break; + } + + *p++ = ','; + } + } + + *p++ = ']'; + + return p; +} + + +static ssize_t +ngx_json_string_length(ngx_data_item_t *item) +{ + ngx_str_t *str; + + str = &item->data.string; + + return sizeof("\"\"") - 1 + str->len + + ngx_escape_json(NULL, str->data, str->len); +} + + +static u_char * +ngx_json_string_encode(u_char *p, ngx_data_item_t *item) +{ + ngx_str_t *str; + + str = &item->data.string; + + *p++ = '"'; + + p = (u_char *) ngx_escape_json(p, str->data, str->len); + + *p++ = '"'; + + return p; +} + + +static ssize_t +ngx_json_integer_length(ngx_data_item_t *item) +{ + return NGX_INT64_LEN; +} + + +static u_char * +ngx_json_integer_encode(u_char *p, ngx_data_item_t *item) +{ + return ngx_sprintf(p, "%L", item->data.integer); +} + + +static ssize_t +ngx_json_boolean_length(ngx_data_item_t *item) +{ + return sizeof("false") - 1; +} + + +static u_char * +ngx_json_boolean_encode(u_char *p, ngx_data_item_t *item) +{ + return item->data.boolean ? ngx_cpymem(p, "true", 4) + : ngx_cpymem(p, "false", 5); +} + + +static ssize_t +ngx_json_null_length(ngx_data_item_t *item) +{ + return sizeof("null") - 1; +} + + +static u_char * +ngx_json_null_encode(u_char *p, ngx_data_item_t *item) +{ + return ngx_cpymem(p, "null", 4); +} diff --git a/src/core/ngx_json.h b/src/core/ngx_json.h new file mode 100644 index 000000000..b94a9dfc7 --- /dev/null +++ b/src/core/ngx_json.h @@ -0,0 +1,18 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_JSON_H_INCLUDED_ +#define _NGX_JSON_H_INCLUDED_ + + +#include +#include + + +ngx_buf_t *ngx_json_render(ngx_pool_t *pool, ngx_data_item_t *item); + + +#endif /* _NGX_JSON_H_INCLUDED_ */