kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit 9cfcd9a4be6e17c24f606440fbd2c224aac892e8
parent 0031f0271eb23f1fb29de7cee97ca63c9e6584e3
Author: Joris Vink <joris@coders.se>
Date:   Tue, 30 Mar 2021 14:19:48 +0200

JSON API improvements.

- Try harder to mark integers as KORE_JSON_TYPE_INTEGER, especially if
  they fit in the internal representation of one (int64_t).

- Move error codes into the JSON code itself, rather then requiring
  a kore_json data structure. This allows the JSON API to relay errors
  such as "item not found" or "type mismatch" properly when looking at items.

- When asking for a KORE_JSON_TYPE_INTEGER_U64 and a KORE_JSON_TYPE_INTEGER
  was found with the same name, check if it could be returned properly and do
  so if possible.

Diffstat:
examples/json/src/json.c | 14++++++++++++--
include/kore/kore.h | 6+++---
src/json.c | 96++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
3 files changed, 83 insertions(+), 33 deletions(-)

diff --git a/examples/json/src/json.c b/examples/json/src/json.c @@ -30,14 +30,24 @@ page(struct http_request *req) kore_json_init(&json, req->http_body->data, req->http_body->length); if (!kore_json_parse(&json)) { - kore_buf_appendf(&buf, "%s\n", kore_json_strerror(&json)); + kore_buf_appendf(&buf, "%s\n", kore_json_strerror()); } else { item = kore_json_find_string(json.root, "foo/bar"); if (item != NULL) { kore_buf_appendf(&buf, "foo.bar = '%s'\n", item->data.string); } else { - kore_buf_appendf(&buf, "string foo.bar not found\n"); + kore_buf_appendf(&buf, "foo.bar %s\n", + kore_json_strerror()); + } + + item = kore_json_find_integer_u64(json.root, "foo/integer"); + if (item != NULL) { + kore_buf_appendf(&buf, + "foo.integer = '%" PRIu64 "'\n", item->data.u64); + } else { + kore_buf_appendf(&buf, "foo.integer %s\n", + kore_json_strerror()); } } diff --git a/include/kore/kore.h b/include/kore/kore.h @@ -570,7 +570,6 @@ struct kore_buf { struct kore_json { const u_int8_t *data; int depth; - int error; size_t length; size_t offset; @@ -588,7 +587,7 @@ struct kore_json_item { char *string; double number; int literal; - int64_t s64; + int64_t integer; u_int64_t u64; } data; @@ -1038,13 +1037,14 @@ void kore_buf_appendv(struct kore_buf *, const char *, va_list); void kore_buf_replace_string(struct kore_buf *, const char *, const void *, size_t); +int kore_json_errno(void); int kore_json_parse(struct kore_json *); void kore_json_cleanup(struct kore_json *); void kore_json_item_free(struct kore_json_item *); void kore_json_init(struct kore_json *, const void *, size_t); void kore_json_item_tobuf(struct kore_json_item *, struct kore_buf *); -const char *kore_json_strerror(struct kore_json *); +const char *kore_json_strerror(void); struct kore_json_item *kore_json_find(struct kore_json_item *, const char *, u_int32_t); struct kore_json_item *kore_json_create_item(struct kore_json_item *, diff --git a/src/json.c b/src/json.c @@ -49,6 +49,8 @@ static u_int8_t json_null_literal[] = { 'n', 'u', 'l', 'l' }; static u_int8_t json_true_literal[] = { 't', 'r', 'u', 'e' }; static u_int8_t json_false_literal[] = { 'f', 'a', 'l', 's', 'e' }; +static int json_errno = 0; + static const char *json_errtab[] = { "no error", "invalid JSON object", @@ -84,8 +86,10 @@ kore_json_parse(struct kore_json *json) if (json->root) return (KORE_RESULT_OK); + json_errno = 0; + if (json_consume_whitespace(json) == -1) { - json->error = KORE_JSON_ERR_INVALID_JSON; + json_errno = KORE_JSON_ERR_INVALID_JSON; return (KORE_RESULT_ERROR); } @@ -93,22 +97,22 @@ kore_json_parse(struct kore_json *json) return (KORE_RESULT_ERROR); if (!json_guess_type(ch, &type)) { - json->error = KORE_JSON_ERR_INVALID_JSON; + json_errno = KORE_JSON_ERR_INVALID_JSON; return (KORE_RESULT_ERROR); } json->root = json_item_alloc(type, NULL, NULL); if (!json->root->parse(json, json->root)) { - if (json->error == 0) - json->error = KORE_JSON_ERR_INVALID_JSON; + if (json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_JSON; return (KORE_RESULT_ERROR); } /* Don't allow garbage at the end. */ (void)json_consume_whitespace(json); if (json->offset != json->length) { - json->error = KORE_JSON_ERR_INVALID_JSON; + json_errno = KORE_JSON_ERR_INVALID_JSON; return (KORE_RESULT_ERROR); } @@ -122,16 +126,21 @@ kore_json_find(struct kore_json_item *root, const char *path, u_int32_t type) char *copy; char *tokens[KORE_JSON_DEPTH_MAX + 1]; + json_errno = 0; copy = kore_strdup(path); if (!kore_split_string(copy, "/", tokens, KORE_JSON_DEPTH_MAX)) { kore_free(copy); + json_errno = KORE_JSON_ERR_INVALID_SEARCH; return (NULL); } item = json_find_item(root, tokens, type, 0); kore_free(copy); + if (item == NULL && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_SEARCH; + return (item); } @@ -145,11 +154,17 @@ kore_json_cleanup(struct kore_json *json) kore_json_item_free(json->root); } +int +kore_json_errno(void) +{ + return (json_errno); +} + const char * -kore_json_strerror(struct kore_json *json) +kore_json_strerror(void) { - if (json->error >= 0 && json->error <= KORE_JSON_ERR_LAST) - return (json_errtab[json->error]); + if (json_errno >= 0 && json_errno <= KORE_JSON_ERR_LAST) + return (json_errtab[json_errno]); return ("unknown JSON error"); } @@ -182,7 +197,7 @@ kore_json_create_item(struct kore_json_item *parent, const char *name, item->data.number = va_arg(args, double); break; case KORE_JSON_TYPE_INTEGER: - item->data.s64 = va_arg(args, int64_t); + item->data.integer = va_arg(args, int64_t); break; case KORE_JSON_TYPE_INTEGER_U64: item->data.u64 = va_arg(args, u_int64_t); @@ -248,7 +263,7 @@ kore_json_item_tobuf(struct kore_json_item *item, struct kore_buf *buf) kore_buf_appendf(buf, "%f", item->data.number); break; case KORE_JSON_TYPE_INTEGER: - kore_buf_appendf(buf, "%" PRId64, item->data.s64); + kore_buf_appendf(buf, "%" PRId64, item->data.integer); break; case KORE_JSON_TYPE_INTEGER_U64: kore_buf_appendf(buf, "%" PRIu64, item->data.u64); @@ -321,15 +336,33 @@ json_find_item(struct kore_json_item *object, char **tokens, break; } - if (nitem == NULL) + if (nitem == NULL) { + json_errno = KORE_JSON_ERR_NOT_FOUND; return (NULL); + } item = nitem; } if (tokens[pos + 1] == NULL) { + /* + * If an uint64 was required and we find an item + * with the same name but marked as an integer check + * if it can be represented as a uint64. + * + * If it can, reduce the type to integer so we match + * on it as well. + */ + if (type == KORE_JSON_TYPE_INTEGER_U64 && + item->type == KORE_JSON_TYPE_INTEGER) { + if (item->data.integer >= 0) + type = KORE_JSON_TYPE_INTEGER; + } + if (item->type == type) return (item); + + json_errno = KORE_JSON_ERR_TYPE_MISMATCH; return (NULL); } @@ -343,6 +376,9 @@ json_find_item(struct kore_json_item *object, char **tokens, break; } + if (item == NULL && json_errno == 0) + json_errno = KORE_JSON_ERR_NOT_FOUND; + return (item); } @@ -443,7 +479,7 @@ static int json_next_byte(struct kore_json *json, u_int8_t *ch, int peek) { if (json->offset >= json->length) { - json->error = KORE_JSON_ERR_EOF; + json_errno = KORE_JSON_ERR_EOF; return (KORE_RESULT_ERROR); } @@ -513,7 +549,7 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object) int ret, hasnext; if (json->depth++ >= KORE_JSON_DEPTH_MAX) { - json->error = KORE_JSON_ERR_DEPTH; + json_errno = KORE_JSON_ERR_DEPTH; return (KORE_RESULT_ERROR); } @@ -537,7 +573,7 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object) switch (ch) { case '}': if (hasnext) { - json->error = KORE_JSON_ERR_INVALID_JSON; + json_errno = KORE_JSON_ERR_INVALID_JSON; goto cleanup; } json->offset++; @@ -596,8 +632,8 @@ json_parse_object(struct kore_json *json, struct kore_json_item *object) } cleanup: - if (ret == KORE_RESULT_ERROR && json->error == 0) - json->error = KORE_JSON_ERR_INVALID_OBJECT; + if (ret == KORE_RESULT_ERROR && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_OBJECT; json->depth--; @@ -614,7 +650,7 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array) int ret, hasnext; if (json->depth++ >= KORE_JSON_DEPTH_MAX) { - json->error = KORE_JSON_ERR_DEPTH; + json_errno = KORE_JSON_ERR_DEPTH; return (KORE_RESULT_ERROR); } @@ -637,7 +673,7 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array) if (ch == ']') { if (hasnext) { - json->error = KORE_JSON_ERR_INVALID_JSON; + json_errno = KORE_JSON_ERR_INVALID_JSON; goto cleanup; } json->offset++; @@ -675,8 +711,8 @@ json_parse_array(struct kore_json *json, struct kore_json_item *array) } cleanup: - if (ret == KORE_RESULT_ERROR && json->error == 0) - json->error = KORE_JSON_ERR_INVALID_ARRAY; + if (ret == KORE_RESULT_ERROR && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_ARRAY; json->depth--; @@ -762,10 +798,14 @@ json_parse_number(struct kore_json *json, struct kore_json_item *number) kore_strtodouble(str, -DBL_MAX, DBL_MAX, &ret); break; case KORE_JSON_TYPE_INTEGER: - number->data.s64 = (int64_t)kore_strtonum64(str, 1, &ret); + number->data.integer = (int64_t)kore_strtonum64(str, 1, &ret); break; case KORE_JSON_TYPE_INTEGER_U64: - number->data.s64 = kore_strtonum64(str, 0, &ret); + number->data.u64 = kore_strtonum64(str, 0, &ret); + if (number->data.u64 <= INT64_MAX) { + type = KORE_JSON_TYPE_INTEGER; + number->data.integer = number->data.u64; + } break; default: goto cleanup; @@ -774,8 +814,8 @@ json_parse_number(struct kore_json *json, struct kore_json_item *number) number->type = type; cleanup: - if (ret == KORE_RESULT_ERROR && json->error == 0) - json->error = KORE_JSON_ERR_INVALID_NUMBER; + if (ret == KORE_RESULT_ERROR && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_NUMBER; return (ret); } @@ -826,8 +866,8 @@ json_parse_literal(struct kore_json *json, struct kore_json_item *literal) ret = KORE_RESULT_OK; cleanup: - if (ret == KORE_RESULT_ERROR && json->error == 0) - json->error = KORE_JSON_ERR_INVALID_LITERAL; + if (ret == KORE_RESULT_ERROR && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_LITERAL; return (ret); } @@ -895,8 +935,8 @@ json_get_string(struct kore_json *json) res = kore_buf_stringify(&json->tmpbuf, NULL); cleanup: - if (res == NULL && json->error == 0) - json->error = KORE_JSON_ERR_INVALID_STRING; + if (res == NULL && json_errno == 0) + json_errno = KORE_JSON_ERR_INVALID_STRING; return (res); }