diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nxt_conf.h | 19 | ||||
-rw-r--r-- | src/nxt_conf_json.c | 772 | ||||
-rw-r--r-- | src/nxt_controller.c | 44 |
3 files changed, 821 insertions, 14 deletions
diff --git a/src/nxt_conf.h b/src/nxt_conf.h new file mode 100644 index 00000000..5d29419a --- /dev/null +++ b/src/nxt_conf.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Valentin V. Bartenev + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_CONF_INCLUDED_ +#define _NXT_CONF_INCLUDED_ + + +typedef struct nxt_conf_json_value_s nxt_conf_json_value_t; + + +nxt_conf_json_value_t *nxt_conf_json_parse(nxt_buf_mem_t *b, + nxt_mem_pool_t *pool); + + +#endif /* _NXT_CONF_INCLUDED_ */ diff --git a/src/nxt_conf_json.c b/src/nxt_conf_json.c new file mode 100644 index 00000000..5f181611 --- /dev/null +++ b/src/nxt_conf_json.c @@ -0,0 +1,772 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Valentin V. Bartenev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <nxt_conf.h> +#if 0 +#include <math.h> +#include <float.h> +#endif + + +typedef enum { + NXT_CONF_JSON_NULL = 0, + NXT_CONF_JSON_BOOLEAN, + NXT_CONF_JSON_INTEGER, + NXT_CONF_JSON_NUMBER, + NXT_CONF_JSON_SHORT_STRING, + NXT_CONF_JSON_STRING, + NXT_CONF_JSON_ARRAY, + NXT_CONF_JSON_OBJECT, +} nxt_conf_json_type_t; + + +struct nxt_conf_json_value_s { + union { + uint32_t boolean; /* 1 bit. */ + int64_t integer; + /* double number; */ + u_char str[15]; + nxt_str_t *string; + nxt_lvlhsh_t *object; + nxt_array_t *array; + } u; + + nxt_conf_json_type_t type:8; /* 3 bits. */ +}; + + +typedef struct { + nxt_conf_json_value_t name; + nxt_conf_json_value_t value; +} nxt_conf_json_obj_member_t; + + +static nxt_int_t nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq, + void *data); +static nxt_int_t nxt_conf_json_object_member_add(nxt_lvlhsh_t *lvlhsh, + nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool); +#if 0 +static nxt_conf_json_value_t *nxt_conf_json_object_member_get( + nxt_lvlhsh_t *lvlhsh, u_char *name, size_t length); +#endif + + +static u_char *nxt_conf_json_skip_space(u_char *pos, u_char *end); +static u_char *nxt_conf_json_parse_value(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); +static u_char *nxt_conf_json_parse_object(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); +static u_char *nxt_conf_json_parse_array(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); +static u_char *nxt_conf_json_parse_string(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); +static u_char *nxt_conf_json_parse_number(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool); + + +static const nxt_lvlhsh_proto_t nxt_conf_json_object_hash_proto + nxt_aligned(64) = +{ + NXT_LVLHSH_DEFAULT, + 0, + nxt_conf_json_object_hash_test, + nxt_lvlhsh_pool_alloc, + nxt_lvlhsh_pool_free, +}; + + +static nxt_int_t +nxt_conf_json_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data) +{ + nxt_conf_json_value_t *name; + + name = &((nxt_conf_json_obj_member_t *) data)->name; + + if (name->type == NXT_CONF_JSON_SHORT_STRING) { + + if (nxt_str_eq(&lhq->key, &name->u.str[1], name->u.str[0])) { + return NXT_OK; + } + + } else { + + if (nxt_strstr_eq(&lhq->key, name->u.string)) { + return NXT_OK; + } + } + + return NXT_DECLINED; +} + + +static nxt_int_t +nxt_conf_json_object_member_add(nxt_lvlhsh_t *lvlhsh, + nxt_conf_json_obj_member_t *member, nxt_mem_pool_t *pool) +{ + nxt_lvlhsh_query_t lhq; + nxt_conf_json_value_t *name; + + name = &member->name; + + if (name->type == NXT_CONF_JSON_SHORT_STRING) { + lhq.key.length = name->u.str[0]; + lhq.key.start = &name->u.str[1]; + + } else { + lhq.key = *name->u.string; + } + + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); + lhq.replace = 0; + lhq.value = member; + lhq.proto = &nxt_conf_json_object_hash_proto; + lhq.pool = pool; + + return nxt_lvlhsh_insert(lvlhsh, &lhq); +} + + +#if 0 +static nxt_conf_json_value_t * +nxt_conf_json_object_member_get(nxt_lvlhsh_t *lvlhsh, u_char *name, + size_t length) +{ + nxt_lvlhsh_query_t lhq; + nxt_conf_json_obj_member_t *member; + + lhq.key_hash = nxt_djb_hash(name, length); + lhq.key.length = length; + lhq.key.start = name; + lhq.proto = &nxt_conf_json_object_hash_proto; + + if (nxt_fast_path(nxt_lvlhsh_find(lvlhsh, &lhq) == NXT_OK)) { + member = lhq.value; + return &member->value; + } + + return NULL; +} +#endif + + +nxt_conf_json_value_t * +nxt_conf_json_parse(nxt_buf_mem_t *b, nxt_mem_pool_t *pool) +{ + u_char *pos, *end; + nxt_conf_json_value_t *value; + + value = nxt_mem_alloc(pool, sizeof(nxt_conf_json_value_t)); + if (nxt_slow_path(value == NULL)) { + return NULL; + } + + pos = b->pos; + end = b->free; + + pos = nxt_conf_json_skip_space(pos, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + pos = nxt_conf_json_parse_value(pos, end, value, pool); + + if (nxt_slow_path(pos == NULL)) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos, end); + + if (nxt_slow_path(pos != end)) { + return NULL; + } + + return value; +} + + +static u_char * +nxt_conf_json_skip_space(u_char *pos, u_char *end) +{ + for ( /* void */ ; nxt_fast_path(pos != end); pos++) { + + switch (*pos) { + case ' ': + case '\t': + case '\r': + case '\n': + continue; + } + + break; + } + + return pos; +} + + +static u_char * +nxt_conf_json_parse_value(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) +{ + u_char ch; + + ch = *pos; + + switch (ch) { + case '{': + return nxt_conf_json_parse_object(pos, end, value, pool); + + case '[': + return nxt_conf_json_parse_array(pos, end, value, pool); + + case '"': + return nxt_conf_json_parse_string(pos, end, value, pool); + + case 't': + if (nxt_fast_path(end - pos >= 4 + || nxt_memcmp(pos, (u_char *) "true", 4) == 0)) + { + value->u.boolean = 1; + value->type = NXT_CONF_JSON_BOOLEAN; + + return pos + 4; + } + + return NULL; + + case 'f': + if (nxt_fast_path(end - pos >= 5 + || nxt_memcmp(pos, (u_char *) "false", 5) == 0)) + { + value->u.boolean = 0; + value->type = NXT_CONF_JSON_BOOLEAN; + + return pos + 5; + } + + return NULL; + + case 'n': + if (nxt_fast_path(end - pos >= 4 + || nxt_memcmp(pos, (u_char *) "null", 4) == 0)) + { + value->type = NXT_CONF_JSON_NULL; + return pos + 4; + } + + return NULL; + } + + if (nxt_fast_path(ch == '-' || (ch - '0') <= 9)) { + return nxt_conf_json_parse_number(pos, end, value, pool); + } + + return NULL; +} + + +static u_char * +nxt_conf_json_parse_object(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) +{ + nxt_int_t rc; + nxt_lvlhsh_t *object; + nxt_conf_json_obj_member_t *member; + + object = nxt_mem_zalloc(pool, sizeof(nxt_lvlhsh_t)); + if (nxt_slow_path(object == NULL)) { + return NULL; + } + + value->type = NXT_CONF_JSON_OBJECT; + value->u.object = object; + + pos = nxt_conf_json_skip_space(pos + 1, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + if (*pos != '}') { + + for ( ;; ) { + if (*pos != '"') { + return NULL; + } + + member = nxt_mem_alloc(pool, sizeof(nxt_conf_json_obj_member_t)); + if (nxt_slow_path(member == NULL)) { + return NULL; + } + + pos = nxt_conf_json_parse_string(pos, end, &member->name, pool); + + if (nxt_slow_path(pos == NULL)) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos, end); + + if (nxt_slow_path(pos == end || *pos != ':')) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos + 1, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + pos = nxt_conf_json_parse_value(pos, end, &member->value, pool); + + if (nxt_slow_path(pos == NULL)) { + return NULL; + } + + rc = nxt_conf_json_object_member_add(object, member, pool); + + if (nxt_slow_path(rc != NXT_OK)) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + if (*pos == '}') { + break; + } + + if (nxt_slow_path(*pos != ',')) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos + 1, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + } + } + + return pos + 1; +} + + +static u_char * +nxt_conf_json_parse_array(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) +{ + nxt_array_t *array; + + array = nxt_array_create(pool, 8, sizeof(nxt_conf_json_value_t)); + if (nxt_slow_path(array == NULL)) { + return NULL; + } + + value->type = NXT_CONF_JSON_ARRAY; + value->u.array = array; + + pos = nxt_conf_json_skip_space(pos + 1, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + if (*pos != ']') { + + for ( ;; ) { + value = nxt_array_add(array); + if (nxt_slow_path(value == NULL)) { + return NULL; + } + + pos = nxt_conf_json_parse_value(pos, end, value, pool); + + if (nxt_slow_path(pos == NULL)) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + + if (*pos == ']') { + break; + } + + if (nxt_slow_path(*pos != ',')) { + return NULL; + } + + pos = nxt_conf_json_skip_space(pos + 1, end); + + if (nxt_slow_path(pos == end)) { + return NULL; + } + } + } + + return pos + 1; +} + + +static u_char * +nxt_conf_json_parse_string(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) +{ + u_char ch, *last, *s; + size_t size, surplus; + uint32_t utf, utf_high; + nxt_uint_t i; + enum { + sw_usual = 0, + sw_escape, + sw_encoded1, + sw_encoded2, + sw_encoded3, + sw_encoded4, + } state; + + pos++; + + state = 0; + surplus = 0; + + for (last = pos; last != end; last++) { + ch = *last; + + switch (state) { + + case sw_usual: + + if (ch == '"') { + break; + } + + if (ch == '\\') { + state = sw_escape; + continue; + } + + if (nxt_fast_path(ch >= ' ')) { + continue; + } + + return NULL; + + case sw_escape: + + switch (ch) { + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + surplus++; + state = sw_usual; + continue; + + case 'u': + /* + * Basic unicode 6 bytes "\uXXXX" in JSON + * and up to 3 bytes in UTF-8. + * + * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON + * and 3 or 4 bytes in UTF-8. + */ + surplus += 3; + state = sw_encoded1; + continue; + } + + return NULL; + + case sw_encoded1: + case sw_encoded2: + case sw_encoded3: + case sw_encoded4: + + if (nxt_fast_path((ch >= '0' && ch <= '9') + || (ch >= 'A' && ch <= 'F'))) + { + state = (state == sw_encoded4) ? sw_usual : state + 1; + continue; + } + + return NULL; + } + + break; + } + + if (nxt_slow_path(last == end)) { + return NULL; + } + + size = last - pos - surplus; + + if (size > 14) { + value->type = NXT_CONF_JSON_STRING; + value->u.string = nxt_str_alloc(pool, size); + + if (nxt_slow_path(value->u.string == NULL)) { + return NULL; + } + + s = value->u.string->start; + + } else { + value->type = NXT_CONF_JSON_SHORT_STRING; + s = &value->u.str[1]; + } + + if (surplus == 0) { + nxt_memcpy(s, pos, size); + return last + 1; + } + + state = 0; + + do { + ch = *pos++; + + if (ch != '\\') { + *s++ = ch; + continue; + } + + ch = *pos++; + + switch (ch) { + case '"': + case '\\': + case '/': + *s++ = ch; + continue; + + case 'b': + *s++ = '\b'; + continue; + + case 'f': + *s++ = '\f'; + continue; + + case 'n': + *s++ = '\n'; + continue; + + case 'r': + *s++ = '\r'; + continue; + + case 't': + *s++ = '\t'; + continue; + } + + utf = 0; + utf_high = 0; + + for ( ;; ) { + for (i = 0; i < 4; i++) { + utf = (utf << 4) + (pos[i] - (pos[i] >= 'A' ? 'A' : '0')); + } + + pos += 4; + + if (utf < 0xd800 || utf > 0xdbff || utf_high) { + break; + } + + utf_high = utf; + utf = 0; + + if (pos[0] != '\\' || pos[1] != 'u') { + break; + } + + pos += 2; + } + + if (utf_high != 0) { + if (nxt_slow_path(utf_high < 0xd800 + || utf_high > 0xdbff + || utf < 0xdc00 + || utf > 0xdfff)) + { + /* invalid surrogate pair */ + return NULL; + } + + utf = ((utf_high - 0xd800) << 10) + (utf - 0xdc00); + } + + s = nxt_utf8_encode(s, utf); + + } while (pos != last); + + if (size > 14) { + value->u.string->length = s - value->u.string->start; + + } else { + value->u.str[0] = s - &value->u.str[1]; + } + + return pos + 1; +} + + +static u_char * +nxt_conf_json_parse_number(u_char *pos, u_char *end, + nxt_conf_json_value_t *value, nxt_mem_pool_t *pool) +{ + u_char ch, *p; + uint64_t integer; + nxt_int_t sign; +#if 0 + uint64_t frac, power + nxt_int_t e, negative; +#endif + + static const uint64_t cutoff = NXT_INT64_T_MAX / 10; + static const uint64_t cutlim = NXT_INT64_T_MAX % 10; + + ch = *pos; + + if (ch == '-') { + sign = -1; + pos++; + + } else { + sign = 1; + } + + integer = 0; + + for (p = pos; nxt_fast_path(p != end); p++) { + ch = *p; + + /* Values below '0' become >= 208. */ + ch = ch - '0'; + + if (ch > 9) { + break; + } + + if (nxt_slow_path(integer >= cutoff + && (integer > cutoff || ch > cutlim))) + { + return NULL; + } + + integer = integer * 10 + ch; + } + + if (nxt_slow_path(p == pos || (p > pos + 1 && *pos == '0'))) { + return NULL; + } + + if (ch != '.') { + value->type = NXT_CONF_JSON_INTEGER; + value->u.integer = sign * integer; + return p; + } + +#if 0 + pos = p + 1; + + frac = 0; + power = 1; + + for (p = pos; nxt_fast_path(p != end); p++) { + ch = *p; + + /* Values below '0' become >= 208. */ + ch = ch - '0'; + + if (ch > 9) { + break; + } + + if (nxt_slow_path((frac >= cutoff && (frac > cutoff || ch > cutlim)) + || power > cutoff)) + { + return NULL; + } + + frac = frac * 10 + ch; + power *= 10; + } + + if (nxt_slow_path(p == pos)) { + return NULL; + } + + value->type = NXT_CONF_JSON_NUMBER; + value->u.number = integer + (double) frac / power; + + value->u.number = copysign(value->u.number, sign); + + if (ch == 'e' || ch == 'E') { + pos = p + 1; + + ch = *pos; + + if (ch == '-' || ch == '+') { + pos++; + } + + negative = (ch == '-') ? 1 : 0; + e = 0; + + for (p = pos; nxt_fast_path(p != end); p++) { + ch = *p; + + /* Values below '0' become >= 208. */ + ch = ch - '0'; + + if (ch > 9) { + break; + } + + e = e * 10 + ch; + + if (nxt_slow_path(e > DBL_MAX_10_EXP)) { + return NULL; + } + } + + if (nxt_slow_path(p == pos)) { + return NULL; + } + + if (negative) { + value->u.number /= exp10(e); + + } else { + value->u.number *= exp10(e); + } + } + + if (nxt_fast_path(isfinite(value->u.number))) { + return p; + } +#endif + + return NULL; +} diff --git a/src/nxt_controller.c b/src/nxt_controller.c index 2c8efd09..e4ca0669 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -8,6 +8,7 @@ #include <nxt_main.h> #include <nxt_runtime.h> #include <nxt_master_process.h> +#include <nxt_conf.h> typedef struct { @@ -39,7 +40,8 @@ static nxt_int_t nxt_controller_request_content_length(void *ctx, static void nxt_controller_process_request(nxt_task_t *task, nxt_event_conn_t *c, nxt_controller_request_t *r); -static nxt_int_t nxt_controller_request_body_parse(nxt_buf_mem_t *b); +static nxt_int_t nxt_controller_request_body_parse(nxt_task_t *task, + nxt_event_conn_t *c); static nxt_http_fields_t nxt_controller_request_fields[] = { @@ -511,27 +513,31 @@ static void nxt_controller_process_request(nxt_task_t *task, nxt_event_conn_t *c, nxt_controller_request_t *r) { - size_t size; - nxt_buf_t *b, *wb; + nxt_str_t response; + nxt_buf_t *b; - static const u_char response[] = "HTTP/1.0 200 OK\r\n\r\n"; + static const u_char response_good[] + = "HTTP/1.0 200 OK\r\n\r\ndone\r\n"; - b = c->read; + static const u_char response_bad[] + = "HTTP/1.0 418 I'm a teapot\r\n\r\nerror\r\n"; - nxt_controller_request_body_parse(&b->mem); + if (nxt_controller_request_body_parse(task, c) == NXT_OK) { + nxt_str_set(&response, response_good); - size = nxt_buf_mem_used_size(&b->mem); + } else { + nxt_str_set(&response, response_bad); + } - wb = nxt_buf_mem_alloc(c->mem_pool, sizeof(response) - 1 + size, 0); - if (nxt_slow_path(wb == NULL)) { + b = nxt_buf_mem_alloc(c->mem_pool, response.length, 0); + if (nxt_slow_path(b == NULL)) { nxt_controller_conn_close(task, c, r); return; } - wb->mem.free = nxt_cpymem(wb->mem.free, response, sizeof(response) - 1); - wb->mem.free = nxt_cpymem(wb->mem.free, b->mem.pos, size); + b->mem.free = nxt_cpymem(b->mem.free, response.start, response.length); - c->write = wb; + c->write = b; c->write_state = &nxt_controller_conn_write_state; nxt_event_conn_write(task->thread->engine, c); @@ -539,8 +545,18 @@ nxt_controller_process_request(nxt_task_t *task, nxt_event_conn_t *c, static nxt_int_t -nxt_controller_request_body_parse(nxt_buf_mem_t *b) +nxt_controller_request_body_parse(nxt_task_t *task, nxt_event_conn_t *c) { - /* TODO */ + nxt_buf_t *b; + nxt_conf_json_value_t *value; + + b = c->read; + + value = nxt_conf_json_parse(&b->mem, c->mem_pool); + + if (value == NULL) { + return NXT_ERROR; + } + return NXT_OK; } |