Refactor JSON and RPC API

This commit is contained in:
Sergey Lyubka 2022-07-30 07:55:26 +01:00
parent a4b003b7ac
commit 54d6bad5e5
11 changed files with 703 additions and 694 deletions

View File

@ -2802,18 +2802,17 @@ which is described below.
```c
enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
int mg_json_get(const char *buf, int len, const char *path, int *toklen);
int mg_json_get(struct mg_str json, const char *path, int *toklen);
```
Parse JSON string `buf`, `len` and return the offset of the element
specified by the JSON path `path`. The length of the element is stored
Parse JSON string `json` and return the offset of the element
specified by the JSON `path`. The length of the element is stored
in the `toklen`.
Parameters:
- `buf` - a string containing valid JSON
- `len` - a string length
- `path` - a JSON path. Must start with `$`
- `toklen` - a placeholder for element length, can be NULL
- `json` - a string containing valid JSON
- `path` - a JSON path. Must start with `$`, e.g. `$.user`
- `toklen` - a pointer that receives element's length, can be NULL
Return value: offset of the element, or negative `MG_JSON_*` on error.
@ -2823,19 +2822,20 @@ Usage example:
```c
// Create a json string: { "a": 1, "b": [2, 3] }
char *buf = mg_mprintf("{ %Q: %d, %Q: [%d, %d] }", "a", 1, "b", 2, 3);
int offset, length, len = (int) strlen(buf);
struct mg_str json = mg_str(buf);
int offset, length;
// Lookup "$", which is the whole JSON. Can be used for validation
offset = mg_json_get(buf, len, "$", &length); // offset = 0, length = 23
offset = mg_json_get(json, "$", &length); // offset = 0, length = 23
// Lookup attribute "a". Point to value "1"
offset = mg_json_get(buf, len, "$.a", &length); // offset = 7, length = 1
offset = mg_json_get(json, "$.a", &length); // offset = 7, length = 1
// Lookup attribute "b". Point to array [2, 3]
offset = mg_json_get(buf, len, "$.b", &length); // offset = 15, length = 6
offset = mg_json_get(json, "$.b", &length); // offset = 15, length = 6
// Lookup attribute "b[1]". Point to value "3"
offset = mg_json_get(buf, len, "$.b[1]", &length); // offset = 19, length = 1
offset = mg_json_get(json, "$.b[1]", &length); // offset = 19, length = 1
free(buf);
```

View File

@ -46,8 +46,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// Got websocket frame. Received data is wm->data
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
char *resp = NULL;
mg_rpc_process(&s_rpc_head, wm->data, mg_pfn_realloc, &resp);
mg_ws_send(c, resp, strlen(resp), WEBSOCKET_OP_TEXT);
struct mg_rpc_req r = {&s_rpc_head, mg_pfn_realloc, &resp, 0, 0, wm->data};
mg_rpc_process(&r);
if (resp) mg_ws_send(c, resp, strlen(resp), WEBSOCKET_OP_TEXT);
free(resp);
}
(void) fn_data;

File diff suppressed because it is too large Load Diff

View File

@ -2470,7 +2470,9 @@ static int mg_pass_string(const char *s, int len) {
return MG_JSON_INVALID;
}
int mg_json_get(const char *s, int len, const char *path, int *toklen) {
int mg_json_get(struct mg_str json, const char *path, int *toklen) {
const char *s = json.ptr;
int len = (int) json.len;
enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
unsigned char nesting[MG_JSON_MAX_DEPTH];
int i, j = 0, depth = 0;
@ -2607,7 +2609,7 @@ int mg_json_get(const char *s, int len, const char *path, int *toklen) {
bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
int n, toklen, found = 0;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
(json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) {
if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL);
found = 1;
@ -2616,10 +2618,9 @@ bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
}
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
int n, toklen, found = 0;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
(json.ptr[n] == 't' || json.ptr[n] == 'f')) {
if (v != NULL) *v = json.ptr[n] == 't';
int found = 0, off = mg_json_get(json, path, NULL);
if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) {
if (v != NULL) *v = json.ptr[off] == 't';
found = 1;
}
return found;
@ -2651,13 +2652,12 @@ static bool json_unescape(const char *s, size_t len, char *to, size_t n) {
}
char *mg_json_get_str(struct mg_str json, const char *path) {
int n, toklen;
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"') {
if ((result = (char *) calloc(1, (size_t) toklen)) != NULL &&
!json_unescape(json.ptr + n + 1, (size_t) (toklen - 2), result,
(size_t) toklen)) {
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && len > 1 && json.ptr[off] == '"') {
if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
!json_unescape(json.ptr + off + 1, (size_t) (len - 2), result,
(size_t) len)) {
free(result);
result = NULL;
}
@ -2665,27 +2665,25 @@ char *mg_json_get_str(struct mg_str json, const char *path) {
return result;
}
char *mg_json_get_b64(struct mg_str json, const char *path, int *len) {
int n, toklen;
char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"' && toklen > 1 &&
(result = (char *) calloc(1, (size_t) toklen)) != NULL) {
int k = mg_base64_decode(json.ptr + n + 1, toklen - 2, result);
if (len != NULL) *len = k;
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
(result = (char *) calloc(1, (size_t) len)) != NULL) {
int k = mg_base64_decode(json.ptr + off + 1, len - 2, result);
if (slen != NULL) *slen = k;
}
return result;
}
char *mg_json_get_hex(struct mg_str json, const char *path, int *len) {
int n, toklen;
char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"' && toklen > 1 &&
(result = (char *) calloc(1, (size_t) toklen / 2)) != NULL) {
mg_unhex(json.ptr + n + 1, (size_t) (toklen - 2), (uint8_t *) result);
result[(toklen - 2) / 2] = '\0';
if (len != NULL) *len = (toklen - 2) / 2;
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
(result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result);
result[len / 2 - 1] = '\0';
if (slen != NULL) *slen = len / 2 - 1;
}
return result;
}
@ -3584,26 +3582,25 @@ void mg_rpc_free(void **head) {
}
}
void mg_rpc_process(void **head, struct mg_str s, mg_pfn_t pfn, void *pfnd) {
struct mg_rpc_req req = {{s.ptr, s.len}, pfn, pfnd, NULL};
int len, off = mg_json_get(s.ptr, (int) s.len, "$.method", &len);
if (off > 0 && s.ptr[off] == '"') {
struct mg_str m = mg_str_n(&s.ptr[off + 1], (size_t) len - 2);
struct mg_rpc *h = *(struct mg_rpc **) head;
void mg_rpc_process(struct mg_rpc_req *r) {
int len, off = mg_json_get(r->frame, "$.method", &len);
if (off > 0 && r->frame.ptr[off] == '"') {
struct mg_str m = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2);
struct mg_rpc *h = *(struct mg_rpc **) r->head;
while (h != NULL && !mg_match(m, h->method, NULL)) h = h->next;
if (h != NULL) {
req.fn_data = h->fn_data;
h->fn(&req);
r->handler_data = h->fn_data;
h->fn(r);
} else {
mg_rpc_err(&req, -32601, "\"%.*s not found\"", (int) m.len, m.ptr);
mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) m.len, m.ptr);
}
} else {
mg_rpc_err(&req, -32700, "%.*Q", (int) s.len, s.ptr);
mg_rpc_err(r, -32700, "%.*Q", (int) r->frame.len, r->frame.ptr);
}
}
void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
int len, off = mg_json_get(r->frame.ptr, (int) r->frame.len, "$.id", &len);
int len, off = mg_json_get(r->frame, "$.id", &len);
if (off > 0) {
mg_rprintf(r->pfn, r->pfn_data, "{%Q:%.*s,%Q:", "id", len,
&r->frame.ptr[off], "result");
@ -3620,7 +3617,7 @@ void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
}
void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
int len, off = mg_json_get(r->frame.ptr, (int) r->frame.len, "$.id", &len);
int len, off = mg_json_get(r->frame, "$.id", &len);
mg_rprintf(r->pfn, r->pfn_data, "{");
if (off > 0) {
mg_rprintf(r->pfn, r->pfn_data, "%Q:%.*s,", "id", len, &r->frame.ptr[off]);
@ -3649,7 +3646,7 @@ static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
}
void mg_rpc_list(struct mg_rpc_req *r) {
mg_rpc_ok(r, "[%M]", print_methods, r->fn_data);
mg_rpc_ok(r, "[%M]", print_methods, r->head);
}
#ifdef MG_ENABLE_LINES

View File

@ -1350,7 +1350,7 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
// Error return values - negative. Successful returns are >= 0
enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
int mg_json_get(const char *buf, int len, const char *path, int *toklen);
int mg_json_get(struct mg_str json, const char *path, int *toklen);
bool mg_json_get_num(struct mg_str json, const char *path, double *v);
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
@ -1364,16 +1364,18 @@ char *mg_json_get_b64(struct mg_str json, const char *path, int *len);
// JSON-RPC request descriptor
struct mg_rpc_req {
struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
void **head; // List of all RPC handlers
mg_pfn_t pfn; // Response printing function
void *pfn_data; // Response printing function data
void *fn_data; // Endpoint handler data
void *handler_data; // Endpoint handler data
void *process_data; // Arbitrary user data
struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
};
void mg_rpc_add(void **head, struct mg_str method_pattern,
void (*handler)(struct mg_rpc_req *), void *handler_data);
void mg_rpc_free(void **head);
void mg_rpc_process(void **head, struct mg_str json, mg_pfn_t pfn, void *pfnd);
void mg_rpc_process(struct mg_rpc_req *);
// Helper functions to print result or error frame
void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);

View File

@ -28,7 +28,9 @@ static int mg_pass_string(const char *s, int len) {
return MG_JSON_INVALID;
}
int mg_json_get(const char *s, int len, const char *path, int *toklen) {
int mg_json_get(struct mg_str json, const char *path, int *toklen) {
const char *s = json.ptr;
int len = (int) json.len;
enum { S_VALUE, S_KEY, S_COLON, S_COMMA_OR_EOO } expecting = S_VALUE;
unsigned char nesting[MG_JSON_MAX_DEPTH];
int i, j = 0, depth = 0;
@ -165,7 +167,7 @@ int mg_json_get(const char *s, int len, const char *path, int *toklen) {
bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
int n, toklen, found = 0;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
if ((n = mg_json_get(json, path, &toklen)) >= 0 &&
(json.ptr[n] == '-' || (json.ptr[n] >= '0' && json.ptr[n] <= '9'))) {
if (v != NULL) *v = mg_atod(json.ptr + n, toklen, NULL);
found = 1;
@ -174,10 +176,9 @@ bool mg_json_get_num(struct mg_str json, const char *path, double *v) {
}
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v) {
int n, toklen, found = 0;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
(json.ptr[n] == 't' || json.ptr[n] == 'f')) {
if (v != NULL) *v = json.ptr[n] == 't';
int found = 0, off = mg_json_get(json, path, NULL);
if (off >= 0 && (json.ptr[off] == 't' || json.ptr[off] == 'f')) {
if (v != NULL) *v = json.ptr[off] == 't';
found = 1;
}
return found;
@ -209,13 +210,12 @@ static bool json_unescape(const char *s, size_t len, char *to, size_t n) {
}
char *mg_json_get_str(struct mg_str json, const char *path) {
int n, toklen;
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"') {
if ((result = (char *) calloc(1, (size_t) toklen)) != NULL &&
!json_unescape(json.ptr + n + 1, (size_t) (toklen - 2), result,
(size_t) toklen)) {
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && len > 1 && json.ptr[off] == '"') {
if ((result = (char *) calloc(1, (size_t) len)) != NULL &&
!json_unescape(json.ptr + off + 1, (size_t) (len - 2), result,
(size_t) len)) {
free(result);
result = NULL;
}
@ -223,27 +223,25 @@ char *mg_json_get_str(struct mg_str json, const char *path) {
return result;
}
char *mg_json_get_b64(struct mg_str json, const char *path, int *len) {
int n, toklen;
char *mg_json_get_b64(struct mg_str json, const char *path, int *slen) {
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"' && toklen > 1 &&
(result = (char *) calloc(1, (size_t) toklen)) != NULL) {
int k = mg_base64_decode(json.ptr + n + 1, toklen - 2, result);
if (len != NULL) *len = k;
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
(result = (char *) calloc(1, (size_t) len)) != NULL) {
int k = mg_base64_decode(json.ptr + off + 1, len - 2, result);
if (slen != NULL) *slen = k;
}
return result;
}
char *mg_json_get_hex(struct mg_str json, const char *path, int *len) {
int n, toklen;
char *mg_json_get_hex(struct mg_str json, const char *path, int *slen) {
char *result = NULL;
if ((n = mg_json_get(json.ptr, (int) json.len, path, &toklen)) >= 0 &&
json.ptr[n] == '"' && toklen > 1 &&
(result = (char *) calloc(1, (size_t) toklen / 2)) != NULL) {
mg_unhex(json.ptr + n + 1, (size_t) (toklen - 2), (uint8_t *) result);
result[(toklen - 2) / 2] = '\0';
if (len != NULL) *len = (toklen - 2) / 2;
int len = 0, off = mg_json_get(json, path, &len);
if (off >= 0 && json.ptr[off] == '"' && len > 1 &&
(result = (char *) calloc(1, (size_t) len / 2)) != NULL) {
mg_unhex(json.ptr + off + 1, (size_t) (len - 2), (uint8_t *) result);
result[len / 2 - 1] = '\0';
if (slen != NULL) *slen = len / 2 - 1;
}
return result;
}

View File

@ -9,7 +9,7 @@
// Error return values - negative. Successful returns are >= 0
enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
int mg_json_get(const char *buf, int len, const char *path, int *toklen);
int mg_json_get(struct mg_str json, const char *path, int *toklen);
bool mg_json_get_num(struct mg_str json, const char *path, double *v);
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);

View File

@ -23,26 +23,25 @@ void mg_rpc_free(void **head) {
}
}
void mg_rpc_process(void **head, struct mg_str s, mg_pfn_t pfn, void *pfnd) {
struct mg_rpc_req req = {{s.ptr, s.len}, pfn, pfnd, NULL};
int len, off = mg_json_get(s.ptr, (int) s.len, "$.method", &len);
if (off > 0 && s.ptr[off] == '"') {
struct mg_str m = mg_str_n(&s.ptr[off + 1], (size_t) len - 2);
struct mg_rpc *h = *(struct mg_rpc **) head;
void mg_rpc_process(struct mg_rpc_req *r) {
int len, off = mg_json_get(r->frame, "$.method", &len);
if (off > 0 && r->frame.ptr[off] == '"') {
struct mg_str m = mg_str_n(&r->frame.ptr[off + 1], (size_t) len - 2);
struct mg_rpc *h = *(struct mg_rpc **) r->head;
while (h != NULL && !mg_match(m, h->method, NULL)) h = h->next;
if (h != NULL) {
req.fn_data = h->fn_data;
h->fn(&req);
r->handler_data = h->fn_data;
h->fn(r);
} else {
mg_rpc_err(&req, -32601, "\"%.*s not found\"", (int) m.len, m.ptr);
mg_rpc_err(r, -32601, "\"%.*s not found\"", (int) m.len, m.ptr);
}
} else {
mg_rpc_err(&req, -32700, "%.*Q", (int) s.len, s.ptr);
mg_rpc_err(r, -32700, "%.*Q", (int) r->frame.len, r->frame.ptr);
}
}
void mg_rpc_vok(struct mg_rpc_req *r, const char *fmt, va_list *ap) {
int len, off = mg_json_get(r->frame.ptr, (int) r->frame.len, "$.id", &len);
int len, off = mg_json_get(r->frame, "$.id", &len);
if (off > 0) {
mg_rprintf(r->pfn, r->pfn_data, "{%Q:%.*s,%Q:", "id", len,
&r->frame.ptr[off], "result");
@ -59,7 +58,7 @@ void mg_rpc_ok(struct mg_rpc_req *r, const char *fmt, ...) {
}
void mg_rpc_verr(struct mg_rpc_req *r, int code, const char *fmt, va_list *ap) {
int len, off = mg_json_get(r->frame.ptr, (int) r->frame.len, "$.id", &len);
int len, off = mg_json_get(r->frame, "$.id", &len);
mg_rprintf(r->pfn, r->pfn_data, "{");
if (off > 0) {
mg_rprintf(r->pfn, r->pfn_data, "%Q:%.*s,", "id", len, &r->frame.ptr[off]);
@ -88,5 +87,5 @@ static size_t print_methods(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
}
void mg_rpc_list(struct mg_rpc_req *r) {
mg_rpc_ok(r, "[%M]", print_methods, r->fn_data);
mg_rpc_ok(r, "[%M]", print_methods, r->head);
}

View File

@ -4,16 +4,18 @@
// JSON-RPC request descriptor
struct mg_rpc_req {
struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
void **head; // List of all RPC handlers
mg_pfn_t pfn; // Response printing function
void *pfn_data; // Response printing function data
void *fn_data; // Endpoint handler data
void *handler_data; // Endpoint handler data
void *process_data; // Arbitrary user data
struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
};
void mg_rpc_add(void **head, struct mg_str method_pattern,
void (*handler)(struct mg_rpc_req *), void *handler_data);
void mg_rpc_free(void **head);
void mg_rpc_process(void **head, struct mg_str json, mg_pfn_t pfn, void *pfnd);
void mg_rpc_process(struct mg_rpc_req *);
// Helper functions to print result or error frame
void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);

View File

@ -46,7 +46,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
while (mg_commalist(&s, &k, &v)) k.len = v.len = 0;
int n;
mg_json_get((char *) data, (int) size, "$", &n);
mg_json_get(mg_str_n((char *) data, size), "$", &n);
return 0;
}

View File

@ -2315,108 +2315,121 @@ static void test_get_header_var(void) {
}
static void test_json(void) {
const char *s;
const char *s1 = "{\"a\":{},\"b\":7,\"c\":[[],2]}";
const char *s2 = "{\"a\":{\"b1\":{}},\"c\":7,\"d\":{\"b2\":{}}}";
int n, n1 = (int) strlen(s1), n2 = (int) strlen(s2);
int n;
struct mg_str json;
ASSERT(mg_json_get(" true ", 6, "", &n) == MG_JSON_INVALID);
ASSERT(mg_json_get(" true ", 6, "$", &n) == 1 && n == 4);
ASSERT(mg_json_get("null ", 5, "$", &n) == 0 && n == 4);
s = " \"hi\\nthere\"";
ASSERT(mg_json_get(s, (int) strlen(s), "$", &n) == 2 && n == 11);
ASSERT(mg_json_get(" { } ", 5, "$", &n) == 1);
ASSERT(mg_json_get(" [[]]", 5, "$", &n) == 1);
ASSERT(mg_json_get(" [ ] ", 5, "$", &n) == 1);
ASSERT(mg_json_get(mg_str_n(" true ", 6), "", &n) == MG_JSON_INVALID);
ASSERT(mg_json_get(mg_str_n(" true ", 6), "$", &n) == 1 && n == 4);
ASSERT(mg_json_get(mg_str_n("null ", 5), "$", &n) == 0 && n == 4);
json = mg_str(" \"hi\\nthere\"");
ASSERT(mg_json_get(json, "$", &n) == 2 && n == 11);
ASSERT(mg_json_get(mg_str_n(" { } ", 5), "$", &n) == 1);
ASSERT(mg_json_get(mg_str_n(" [[]]", 5), "$", &n) == 1);
ASSERT(mg_json_get(mg_str_n(" [ ] ", 5), "$", &n) == 1);
ASSERT(mg_json_get("[1,2]", 5, "$", &n) == 0 && n == 5);
ASSERT(mg_json_get("[1,2]", 5, "$[0]", &n) == 1 && n == 1);
ASSERT(mg_json_get("[1,2]", 5, "$[1]", &n) == 3 && n == 1);
ASSERT(mg_json_get("[1,2]", 5, "$[3]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(mg_str_n("[1,2]", 5), "$", &n) == 0 && n == 5);
ASSERT(mg_json_get(mg_str_n("[1,2]", 5), "$[0]", &n) == 1 && n == 1);
ASSERT(mg_json_get(mg_str_n("[1,2]", 5), "$[1]", &n) == 3 && n == 1);
ASSERT(mg_json_get(mg_str_n("[1,2]", 5), "$[3]", &n) == MG_JSON_NOT_FOUND);
s = "{\"a\":[]}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":[1,2]}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":[1,[1]]}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":[[]]}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":[[1,2]]}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":{}}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":{\"a\":{}}}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
s = "{\"a\":{\"a\":[]}}";
ASSERT(mg_json_get(s, (int) strlen(s), "$.a", &n) == 5);
json = mg_str("{\"a\":[]}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":[1,2]}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":[1,[1]]}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":[[]]}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":[[1,2]]}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":{}}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":{\"a\":{}}}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
json = mg_str("{\"a\":{\"a\":[]}}");
ASSERT(mg_json_get(json, "$.a", &n) == 5);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$", &n) == 0 && n == 13);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0]", &n) == 1 && n == 9);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[1]", &n) == 11);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[1]", &n) == 11 && n == 1);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0][0]", &n) == 2 && n == 1);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0][1]", &n) == 4 && n == 5);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0][2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0][1][0]", &n) == 5 && n == 1);
ASSERT(mg_json_get("[[1,[2,3]],4]", 13, "$[0][1][1]", &n) == 7 && n == 1);
json = mg_str("[[1,[2,3]],4]");
ASSERT(mg_json_get(json, "$", &n) == 0 && n == 13);
ASSERT(mg_json_get(json, "$[0]", &n) == 1 && n == 9);
ASSERT(mg_json_get(json, "$[1]", &n) == 11);
ASSERT(mg_json_get(json, "$[1]", &n) == 11 && n == 1);
ASSERT(mg_json_get(json, "$[2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$[0][0]", &n) == 2 && n == 1);
ASSERT(mg_json_get(json, "$[0][1]", &n) == 4 && n == 5);
ASSERT(mg_json_get(json, "$[0][2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$[0][1][0]", &n) == 5 && n == 1);
ASSERT(mg_json_get(json, "$[0][1][1]", &n) == 7 && n == 1);
ASSERT(mg_json_get("[[1,2],3]", 9, "$", &n) == 0 && n == 9);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[0][0]", &n) == 2 && n == 1);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[0][1]", &n) == 4 && n == 1);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[0][2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[1][0]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[1]", &n) == 7 && n == 1);
ASSERT(mg_json_get("[[1,2],3]", 9, "$[1][0]", &n) == MG_JSON_NOT_FOUND);
json = mg_str("[[1,2],3]");
ASSERT(mg_json_get(json, "$", &n) == 0 && n == 9);
ASSERT(mg_json_get(json, "$[0][0]", &n) == 2 && n == 1);
ASSERT(mg_json_get(json, "$[0][1]", &n) == 4 && n == 1);
ASSERT(mg_json_get(json, "$[0][2]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$[1][0]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$[1]", &n) == 7 && n == 1);
ASSERT(mg_json_get(json, "$[1][0]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[1,[2,3]]", 9, "$", &n) == 0 && n == 9);
ASSERT(mg_json_get("[1,[2,3]]", 9, "$[0][1]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get("[1,[2,3]]", 9, "$[1][0]", &n) == 4 && n == 1);
ASSERT(mg_json_get(json, "$", &n) == 0 && n == 9);
ASSERT(mg_json_get(json, "$[1][0]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$[0][1]", &n) == 4 && n == 1);
ASSERT(mg_json_get(s1, n1, "$.a", &n) == 5 && n == 2);
ASSERT(mg_json_get(s1, n1, "$.b", &n) == 12 && n == 1);
ASSERT(mg_json_get(s1, n1, "$.c", &n) == 18 && n == 6);
ASSERT(mg_json_get(s1, n1, "$.c[0]", &n) == 19 && n == 2);
ASSERT(mg_json_get(s1, n1, "$.c[1]", &n) == 22 && n == 1);
ASSERT(mg_json_get(s1, n1, "$.c[3]", &n) == MG_JSON_NOT_FOUND);
json = mg_str(s1);
ASSERT(mg_json_get(json, "$.a", &n) == 5 && n == 2);
ASSERT(mg_json_get(json, "$.b", &n) == 12 && n == 1);
ASSERT(mg_json_get(json, "$.c", &n) == 18 && n == 6);
ASSERT(mg_json_get(json, "$.c[0]", &n) == 19 && n == 2);
ASSERT(mg_json_get(json, "$.c[1]", &n) == 22 && n == 1);
ASSERT(mg_json_get(json, "$.c[3]", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(s2, n2, "$.a", &n) == 5 && n == 9);
ASSERT(mg_json_get(s2, n2, "$.a.b1", &n) == 11 && n == 2);
ASSERT(mg_json_get(s2, n2, "$.a.b2", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(s2, n2, "$.a.b", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(s2, n2, "$.a1", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(s2, n2, "$.c", &n) == 19 && n == 1);
json = mg_str(s2);
ASSERT(mg_json_get(json, "$.a", &n) == 5 && n == 9);
ASSERT(mg_json_get(json, "$.a.b1", &n) == 11 && n == 2);
ASSERT(mg_json_get(json, "$.a.b2", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$.a.b", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$.a1", &n) == MG_JSON_NOT_FOUND);
ASSERT(mg_json_get(json, "$.c", &n) == 19 && n == 1);
{
double d = 0;
bool b = false;
int len;
const char *json = "{\"a\": \"hi\\nthere\",\"b\": [12345, true]}";
char *str = mg_json_get_str(mg_str(json), "$.a");
char *str = NULL;
json = mg_str("{\"a\":\"b\"}");
str = mg_json_get_str(json, "$.a");
ASSERT(str != NULL);
// printf("---> [%s]\n", str);
ASSERT(strcmp(str, "b") == 0);
free(str);
json = mg_str("{\"a\": \"hi\\nthere\",\"b\": [12345, true]}");
str = mg_json_get_str(json, "$.a");
ASSERT(str != NULL);
ASSERT(strcmp(str, "hi\nthere") == 0);
free(str);
ASSERT(mg_json_get_long(mg_str(json), "$.foo", -42) == -42);
ASSERT(mg_json_get_long(mg_str(json), "$.b[0]", -42) == 12345);
ASSERT(mg_json_get_long(json, "$.foo", -42) == -42);
ASSERT(mg_json_get_long(json, "$.b[0]", -42) == 12345);
ASSERT(mg_json_get_num(mg_str(json), "$.a", &d) == false);
ASSERT(mg_json_get_num(mg_str(json), "$.c", &d) == false);
ASSERT(mg_json_get_num(mg_str(json), "$.b[0]", &d) == true);
ASSERT(mg_json_get_num(json, "$.a", &d) == false);
ASSERT(mg_json_get_num(json, "$.c", &d) == false);
ASSERT(mg_json_get_num(json, "$.b[0]", &d) == true);
ASSERT(d == 12345);
ASSERT(mg_json_get_bool(mg_str(json), "$.b", &b) == false);
ASSERT(mg_json_get_bool(mg_str(json), "$.b[0]", &b) == false);
ASSERT(mg_json_get_bool(mg_str(json), "$.b[1]", &b) == true);
ASSERT(mg_json_get_bool(json, "$.b", &b) == false);
ASSERT(mg_json_get_bool(json, "$.b[0]", &b) == false);
ASSERT(mg_json_get_bool(json, "$.b[1]", &b) == true);
ASSERT(b == true);
json = "[\"YWJj\", \"0100026869\"]";
ASSERT((str = mg_json_get_b64(mg_str(json), "$[0]", &len)) != NULL);
json = mg_str("[\"YWJj\", \"0100026869\"]");
ASSERT((str = mg_json_get_b64(json, "$[0]", &len)) != NULL);
ASSERT(len == 3 && memcmp(str, "abc", (size_t) len) == 0);
free(str);
ASSERT((str = mg_json_get_hex(mg_str(json), "$[1]", &len)) != NULL);
ASSERT((str = mg_json_get_hex(json, "$[1]", &len)) != NULL);
ASSERT(len == 5 && memcmp(str, "\x01\x00\x02hi", (size_t) len) == 0);
free(str);
}
@ -2424,46 +2437,43 @@ static void test_json(void) {
static void test_rpc(void) {
void *head = NULL;
mg_rpc_add(&head, mg_str("rpc.list"), mg_rpc_list, &head);
char *s = NULL;
struct mg_rpc_req req = {&head, mg_pfn_realloc, &s, 0, 0, {0, 0}};
mg_rpc_add(&head, mg_str("rpc.list"), mg_rpc_list, NULL);
{
char *s = NULL;
const char *req = "{\"method\":\"rpc.list\"}";
mg_rpc_process(&head, mg_str(req), mg_pfn_realloc, &s);
req.frame = mg_str("{\"method\":\"rpc.list\"}");
mg_rpc_process(&req);
ASSERT(s == NULL);
}
{
char *s = NULL;
const char *req = "{\"id\": 1,\"method\":\"rpc.list\"}";
const char *resp = "{\"id\":1,\"result\":[\"rpc.list\"]}";
mg_rpc_process(&head, mg_str(req), mg_pfn_realloc, &s);
req.frame = mg_str("{\"id\": 1,\"method\":\"rpc.list\"}");
mg_rpc_process(&req);
MG_INFO(("-> %s", s));
ASSERT(strcmp(s, resp) == 0);
free(s);
free(s), s = NULL;
}
{
char *s = NULL;
const char *req = "{\"id\": true,\"method\":\"foo\"}";
const char *resp =
"{\"id\":true,\"error\":{\"code\":-32601,\"message\":\"foo not "
"found\"}}";
mg_rpc_process(&head, mg_str(req), mg_pfn_realloc, &s);
req.frame = mg_str("{\"id\": true,\"method\":\"foo\"}");
mg_rpc_process(&req);
MG_INFO(("-> %s", s));
ASSERT(strcmp(s, resp) == 0);
free(s);
free(s), s = NULL;
}
{
char *s = NULL;
const char *req = "haha";
const char *resp = "{\"error\":{\"code\":-32700,\"message\":\"haha\"}}";
mg_rpc_process(&head, mg_str(req), mg_pfn_realloc, &s);
ASSERT(s != NULL);
req.frame = mg_str("haha");
mg_rpc_process(&req);
MG_INFO(("-> %s", s));
ASSERT(strcmp(s, resp) == 0);
free(s);
free(s), s = NULL;
}
mg_rpc_free(&head);