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.
This commit is contained in:
Joris Vink 2021-03-30 14:19:48 +02:00
parent 0031f0271e
commit 9cfcd9a4be
3 changed files with 84 additions and 34 deletions

View File

@ -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());
}
}

View File

@ -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 *,

View File

@ -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);
}
const char *
kore_json_strerror(struct kore_json *json)
int
kore_json_errno(void)
{
if (json->error >= 0 && json->error <= KORE_JSON_ERR_LAST)
return (json_errtab[json->error]);
return (json_errno);
}
const char *
kore_json_strerror(void)
{
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);
}