diff --git a/src/inspector/core.c b/src/inspector/core.c index 002ecf8..95bbd28 100644 --- a/src/inspector/core.c +++ b/src/inspector/core.c @@ -1,5 +1,6 @@ #include "bind.h" #include "http/websocket.h" +#include "json.h" #include #include @@ -151,8 +152,18 @@ void inspector_send_ws(inspector_client_t *client, const char *json) { void inspector_send_response_obj(inspector_client_t *client, int id, const char *result_obj) { sbuf_t b = {0}; - if (sbuf_appendf(&b, "{\"id\":%d,\"result\":%s}", id, result_obj ? result_obj : "{}")) + inspector_json_t json; + inspector_json_init(&json, &b); + if ( + inspector_json_begin_object(&json) && + inspector_json_key(&json, "id") && + inspector_json_int(&json, id) && + inspector_json_key(&json, "result") && + inspector_json_raw(&json, result_obj ? result_obj : "{}") && + inspector_json_end_object(&json) + ) { inspector_send_ws(client, b.data); + } free(b.data); } @@ -162,11 +173,23 @@ void inspector_send_empty_result(inspector_client_t *client, int id) { void inspector_send_error(inspector_client_t *client, int id, int code, const char *message) { sbuf_t b = {0}; + inspector_json_t json; + inspector_json_init(&json, &b); if ( - sbuf_appendf(&b, "{\"id\":%d,\"error\":{\"code\":%d,\"message\":", id, code) && - sbuf_json_string(&b, message ? message : "Inspector error") && - sbuf_append(&b, "}}") - ) inspector_send_ws(client, b.data); + inspector_json_begin_object(&json) && + inspector_json_key(&json, "id") && + inspector_json_int(&json, id) && + inspector_json_key(&json, "error") && + inspector_json_begin_object(&json) && + inspector_json_key(&json, "code") && + inspector_json_int(&json, code) && + inspector_json_key(&json, "message") && + inspector_json_string(&json, message ? message : "Inspector error") && + inspector_json_end_object(&json) && + inspector_json_end_object(&json) + ) { + inspector_send_ws(client, b.data); + } free(b.data); } diff --git a/src/inspector/json.c b/src/inspector/json.c new file mode 100644 index 0000000..dac428a --- /dev/null +++ b/src/inspector/json.c @@ -0,0 +1,130 @@ +#include "json.h" + +#include +#include + +void inspector_json_init(inspector_json_t *json, sbuf_t *out) { + json->out = out; + json->depth = 0; + json->wrote_root = false; +} + +static bool inspector_json_before_value(inspector_json_t *json) { + if (!json || !json->out) return false; + + if (json->depth == 0) { + if (json->wrote_root) return false; + json->wrote_root = true; + return true; + } + + inspector_json_frame_t *frame = &json->stack[json->depth - 1]; + if (frame->type == INSPECTOR_JSON_ARRAY) { + if (frame->needs_comma && !sbuf_append(json->out, ",")) return false; + frame->needs_comma = true; + return true; + } + + if (!frame->expecting_value) return false; + frame->expecting_value = false; + return true; +} + +static bool inspector_json_push(inspector_json_t *json, inspector_json_container_t type) { + if (json->depth >= sizeof(json->stack) / sizeof(json->stack[0])) return false; + json->stack[json->depth++] = (inspector_json_frame_t){ + .type = type, + .needs_comma = false, + .expecting_value = false, + }; + return true; +} + +static bool inspector_json_pop(inspector_json_t *json, inspector_json_container_t type) { + if (!json || json->depth == 0) return false; + inspector_json_frame_t *frame = &json->stack[json->depth - 1]; + if (frame->type != type || frame->expecting_value) return false; + json->depth--; + return true; +} + +bool inspector_json_begin_object(inspector_json_t *json) { + return + inspector_json_before_value(json) && + sbuf_append(json->out, "{") && + inspector_json_push(json, INSPECTOR_JSON_OBJECT); +} + +bool inspector_json_end_object(inspector_json_t *json) { + return + inspector_json_pop(json, INSPECTOR_JSON_OBJECT) && + sbuf_append(json->out, "}"); +} + +bool inspector_json_begin_array(inspector_json_t *json) { + return + inspector_json_before_value(json) && + sbuf_append(json->out, "[") && + inspector_json_push(json, INSPECTOR_JSON_ARRAY); +} + +bool inspector_json_end_array(inspector_json_t *json) { + return + inspector_json_pop(json, INSPECTOR_JSON_ARRAY) && + sbuf_append(json->out, "]"); +} + +bool inspector_json_key(inspector_json_t *json, const char *key) { + if (!json || json->depth == 0) return false; + inspector_json_frame_t *frame = &json->stack[json->depth - 1]; + if (frame->type != INSPECTOR_JSON_OBJECT || frame->expecting_value) return false; + + if (frame->needs_comma && !sbuf_append(json->out, ",")) return false; + if (!sbuf_json_string(json->out, key ? key : "")) return false; + if (!sbuf_append(json->out, ":")) return false; + + frame->needs_comma = true; + frame->expecting_value = true; + return true; +} + +bool inspector_json_raw(inspector_json_t *json, const char *raw) { + return inspector_json_before_value(json) && sbuf_append(json->out, raw ? raw : "null"); +} + +bool inspector_json_string(inspector_json_t *json, const char *value) { + return inspector_json_string_len(json, value ? value : "", value ? strlen(value) : 0); +} + +bool inspector_json_string_len(inspector_json_t *json, const char *value, size_t len) { + return inspector_json_before_value(json) && + sbuf_json_string_len(json->out, value ? value : "", value ? len : 0); +} + +bool inspector_json_bool(inspector_json_t *json, bool value) { + return inspector_json_raw(json, value ? "true" : "false"); +} + +bool inspector_json_int(inspector_json_t *json, int value) { + if (!inspector_json_before_value(json)) return false; + return sbuf_appendf(json->out, "%d", value); +} + +bool inspector_json_uint64(inspector_json_t *json, uint64_t value) { + if (!inspector_json_before_value(json)) return false; + return sbuf_appendf(json->out, "%" PRIu64, value); +} + +bool inspector_json_size(inspector_json_t *json, size_t value) { + if (!inspector_json_before_value(json)) return false; + return sbuf_appendf(json->out, "%zu", value); +} + +bool inspector_json_double(inspector_json_t *json, double value) { + if (!inspector_json_before_value(json)) return false; + return sbuf_appendf(json->out, "%.6f", value); +} + +bool inspector_json_null(inspector_json_t *json) { + return inspector_json_raw(json, "null"); +} diff --git a/src/inspector/json.h b/src/inspector/json.h new file mode 100644 index 0000000..6095299 --- /dev/null +++ b/src/inspector/json.h @@ -0,0 +1,41 @@ +#ifndef ANT_INSPECTOR_JSON_H +#define ANT_INSPECTOR_JSON_H + +#include "bind.h" + +typedef enum { + INSPECTOR_JSON_OBJECT, + INSPECTOR_JSON_ARRAY +} inspector_json_container_t; + +typedef struct { + inspector_json_container_t type; + bool needs_comma; + bool expecting_value; +} inspector_json_frame_t; + +typedef struct { + sbuf_t *out; + inspector_json_frame_t stack[32]; + size_t depth; + bool wrote_root; +} inspector_json_t; + +void inspector_json_init(inspector_json_t *json, sbuf_t *out); +bool inspector_json_begin_object(inspector_json_t *json); +bool inspector_json_end_object(inspector_json_t *json); +bool inspector_json_begin_array(inspector_json_t *json); +bool inspector_json_end_array(inspector_json_t *json); + +bool inspector_json_key(inspector_json_t *json, const char *key); +bool inspector_json_raw(inspector_json_t *json, const char *raw); +bool inspector_json_string(inspector_json_t *json, const char *value); +bool inspector_json_string_len(inspector_json_t *json, const char *value, size_t len); +bool inspector_json_bool(inspector_json_t *json, bool value); +bool inspector_json_int(inspector_json_t *json, int value); +bool inspector_json_uint64(inspector_json_t *json, uint64_t value); +bool inspector_json_size(inspector_json_t *json, size_t value); +bool inspector_json_double(inspector_json_t *json, double value); +bool inspector_json_null(inspector_json_t *json); + +#endif diff --git a/src/inspector/network.c b/src/inspector/network.c index 0ff7bf8..a1792a5 100644 --- a/src/inspector/network.c +++ b/src/inspector/network.c @@ -1,6 +1,7 @@ #include "bind.h" #include "base64.h" #include "inspector.h" +#include "json.h" #include #include @@ -145,31 +146,29 @@ void inspector_get_response_body(inspector_client_t *client, int id, yyjson_val uint64_t request_id = inspector_request_id_param(params); inspector_network_entry_t *entry = inspector_network_entry_for_id(request_id, false); sbuf_t b = {0}; + inspector_json_t json; size_t out_len = 0; char *encoded = NULL; + bool ok = false; - if (!entry || !entry->response_body || entry->response_body_len == 0) { - inspector_send_response_obj(client, id, "{\"body\":\"\",\"base64Encoded\":false}"); - return; + if (!entry || !entry->response_body || entry->response_body_len == 0) out_len = 0; else { + encoded = ant_base64_encode(entry->response_body, entry->response_body_len, &out_len); + if (!encoded) { inspector_send_error(client, id, -32000, "Out of memory"); return; } } - encoded = ant_base64_encode(entry->response_body, entry->response_body_len, &out_len); - if (!encoded) { - inspector_send_error(client, id, -32000, "Out of memory"); - return; - } - - if (!sbuf_append(&b, "{\"body\":") || - !sbuf_json_string_len(&b, encoded, out_len) || - !sbuf_append(&b, ",\"base64Encoded\":true}")) { - free(encoded); - free(b.data); - inspector_send_error(client, id, -32000, "Out of memory"); - return; - } + inspector_json_init(&json, &b); + if (!inspector_json_begin_object(&json)) goto done; + if (!inspector_json_key(&json, "body")) goto done; + if (!inspector_json_string_len(&json, encoded ? encoded : "", out_len)) goto done; + if (!inspector_json_key(&json, "base64Encoded")) goto done; + if (!inspector_json_bool(&json, encoded != NULL)) goto done; + if (!inspector_json_end_object(&json)) goto done; + ok = true; +done: + if (ok) inspector_send_response_obj(client, id, b.data); + else inspector_send_error(client, id, -32000, "Out of memory"); free(encoded); - inspector_send_response_obj(client, id, b.data); free(b.data); } @@ -177,21 +176,18 @@ void inspector_get_request_post_data(inspector_client_t *client, int id, yyjson_ uint64_t request_id = inspector_request_id_param(params); inspector_network_entry_t *entry = inspector_network_entry_for_id(request_id, false); sbuf_t b = {0}; - - if (!entry || !entry->request_body || entry->request_body_len == 0) { - inspector_send_response_obj(client, id, "{\"postData\":\"\"}"); - return; - } - - if (!sbuf_append(&b, "{\"postData\":") || - !sbuf_json_string_len(&b, (const char *)entry->request_body, entry->request_body_len) || - !sbuf_append(&b, "}")) { - free(b.data); - inspector_send_error(client, id, -32000, "Out of memory"); - return; - } - - inspector_send_response_obj(client, id, b.data); + inspector_json_t json; + inspector_json_init(&json, &b); + const char *body = entry && entry->request_body ? (const char *)entry->request_body : ""; + size_t body_len = entry && entry->request_body ? entry->request_body_len : 0; + bool ok = + inspector_json_begin_object(&json) && + inspector_json_key(&json, "postData") && + inspector_json_string_len(&json, body, body_len) && + inspector_json_end_object(&json); + + if (ok) inspector_send_response_obj(client, id, b.data); + else inspector_send_error(client, id, -32000, "Out of memory"); free(b.data); } uint64_t ant_inspector_network_request( diff --git a/src/inspector/router.c b/src/inspector/router.c index 833a835..988b764 100644 --- a/src/inspector/router.c +++ b/src/inspector/router.c @@ -1,6 +1,8 @@ #include "bind.h" #include "gc.h" +#include "json.h" +#include #include typedef void (*inspector_route_fn)( @@ -66,10 +68,41 @@ static void route_get_isolate_id(inspector_client_t *client, int id, yyjson_val } static void route_schema_domains(inspector_client_t *client, int id, yyjson_val *params) { - inspector_send_response_obj( - client, id, - "{\"domains\":[{\"name\":\"Runtime\",\"version\":\"1.3\"},{\"name\":\"Console\",\"version\":\"1.3\"},{\"name\":\"Debugger\",\"version\":\"1.3\"},{\"name\":\"Network\",\"version\":\"1.3\"},{\"name\":\"HeapProfiler\",\"version\":\"1.3\"},{\"name\":\"Profiler\",\"version\":\"1.3\"}]}" - ); + static const char *domains[] = { + "Runtime", + "Console", + "Debugger", + "Network", + "HeapProfiler", + "Profiler", + }; + + sbuf_t b = {0}; + inspector_json_t json; + inspector_json_init(&json, &b); + bool ok = false; + + if (!inspector_json_begin_object(&json)) goto done; + if (!inspector_json_key(&json, "domains")) goto done; + if (!inspector_json_begin_array(&json)) goto done; + + for (size_t i = 0; i < sizeof(domains) / sizeof(domains[0]); i++) { + if (!inspector_json_begin_object(&json)) goto done; + if (!inspector_json_key(&json, "name")) goto done; + if (!inspector_json_string(&json, domains[i])) goto done; + if (!inspector_json_key(&json, "version")) goto done; + if (!inspector_json_string(&json, "1.3")) goto done; + if (!inspector_json_end_object(&json)) goto done; + } + + if (!inspector_json_end_array(&json)) goto done; + if (!inspector_json_end_object(&json)) goto done; + ok = true; + +done: + if (ok) inspector_send_response_obj(client, id, b.data); + else inspector_send_error(client, id, -32000, "Out of memory"); + free(b.data); } static void route_network_get_response_body(inspector_client_t *client, int id, yyjson_val *params) { diff --git a/src/inspector/server.c b/src/inspector/server.c index 4588653..28f409d 100644 --- a/src/inspector/server.c +++ b/src/inspector/server.c @@ -2,6 +2,7 @@ #include "internal.h" #include "inspector.h" #include "http/websocket.h" +#include "json.h" #include "modules/crypto.h" #include @@ -104,16 +105,12 @@ static bool request_path_matches_uuid(const char *req) { static bool inspector_append_target_url(sbuf_t *b, const char *file) { if (!file || !*file || strcmp(file, "[repl]") == 0 || strcmp(file, "ant") == 0) - return sbuf_json_string(b, "file:///"); - if (inspector_is_url_like(file)) return sbuf_json_string(b, file); + return sbuf_append(b, "file:///"); + if (inspector_is_url_like(file)) return sbuf_append(b, file); if (file[0] == '/') { - if (!sbuf_append(b, "\"file://")) return false; - if (!sbuf_append(b, file)) return false; - return sbuf_append(b, "\""); + return sbuf_append(b, "file://") && sbuf_append(b, file); } - if (!sbuf_append(b, "\"file:///")) return false; - if (!sbuf_append(b, file)) return false; - return sbuf_append(b, "\""); + return sbuf_append(b, "file:///") && sbuf_append(b, file); } static bool inspector_append_devtools_url(sbuf_t *b) { @@ -132,28 +129,54 @@ static bool inspector_append_browser_devtools_url(sbuf_t *b) { static char *json_list_response(void) { sbuf_t b = {0}; + sbuf_t devtools = {0}; + sbuf_t target = {0}; + sbuf_t websocket = {0}; + inspector_script_t *entry = inspector_entry_script(); const char *file = entry && entry->url ? entry->url : (g_inspector.js && g_inspector.js->filename ? g_inspector.js->filename : "ant"); uv_pid_t pid = uv_os_getpid(); - if (!sbuf_append(&b, "[")) goto fail; - if (!sbuf_append(&b, "{\"description\":\"ant[")) goto fail; - if (!sbuf_appendf(&b, "%d", (int)pid)) goto fail; - if (!sbuf_append(&b, "]\",\"devtoolsFrontendUrl\":\"")) goto fail; - if (!inspector_append_devtools_url(&b)) goto fail; - if (!sbuf_append(&b, "\",\"id\":\"")) goto fail; - if (!sbuf_append(&b, g_inspector.uuid)) goto fail; - if (!sbuf_append(&b, "\",\"title\":\"ant[")) goto fail; - if (!sbuf_appendf(&b, "%d", (int)pid)) goto fail; - if (!sbuf_append(&b, "]\"")) goto fail; - if (!sbuf_append(&b, ",\"type\":\"node\",\"url\":")) goto fail; - if (!inspector_append_target_url(&b, file)) goto fail; - if (!sbuf_append(&b, ",\"webSocketDebuggerUrl\":\"ws://")) goto fail; - if (!sbuf_append(&b, g_inspector.host)) goto fail; - if (!sbuf_appendf(&b, ":%d/%s\"}]", g_inspector.port, g_inspector.uuid)) goto fail; + + if (!inspector_append_devtools_url(&devtools)) goto fail; + if (!inspector_append_target_url(&target, file)) goto fail; + if (!sbuf_append(&websocket, "ws://")) goto fail; + if (!sbuf_append(&websocket, g_inspector.host)) goto fail; + if (!sbuf_appendf(&websocket, ":%d/%s", g_inspector.port, g_inspector.uuid)) goto fail; + + char title[64]; + snprintf(title, sizeof(title), "ant[%d]", (int)pid); + + inspector_json_t json; + inspector_json_init(&json, &b); + if (!inspector_json_begin_array(&json)) goto fail; + if (!inspector_json_begin_object(&json)) goto fail; + if (!inspector_json_key(&json, "description")) goto fail; + if (!inspector_json_string(&json, title)) goto fail; + if (!inspector_json_key(&json, "devtoolsFrontendUrl")) goto fail; + if (!inspector_json_string(&json, devtools.data)) goto fail; + if (!inspector_json_key(&json, "id")) goto fail; + if (!inspector_json_string(&json, g_inspector.uuid)) goto fail; + if (!inspector_json_key(&json, "title")) goto fail; + if (!inspector_json_string(&json, title)) goto fail; + if (!inspector_json_key(&json, "type")) goto fail; + if (!inspector_json_string(&json, "node")) goto fail; + if (!inspector_json_key(&json, "url")) goto fail; + if (!inspector_json_string(&json, target.data)) goto fail; + if (!inspector_json_key(&json, "webSocketDebuggerUrl")) goto fail; + if (!inspector_json_string(&json, websocket.data)) goto fail; + if (!inspector_json_end_object(&json)) goto fail; + if (!inspector_json_end_array(&json)) goto fail; + + free(devtools.data); + free(target.data); + free(websocket.data); return b.data; fail: + free(devtools.data); + free(target.data); + free(websocket.data); free(b.data); return NULL; }