From c11eda965efdca053360913d5b05422a9673db72 Mon Sep 17 00:00:00 2001 From: jwerle <joseph.werle@gmail.com> Date: Sun, 24 Nov 2024 23:54:55 -0500 Subject: [PATCH] refactor(core/json,serviceworker,ipc,window): lock down conduit, remove realloc, speed up JSON impl --- api/application.js | 2 +- api/index.d.ts | 28 +- api/internal/conduit.js | 22 +- bin/build-runtime-library.sh | 1 + src/core/codec.cc | 75 +++-- src/core/codec.hh | 23 +- src/core/headers.cc | 28 ++ src/core/headers.hh | 7 + src/core/json.cc | 266 +---------------- src/core/json.hh | 531 +++++++++------------------------ src/core/json/any.cc | 268 +++++++++++++++++ src/core/json/array.cc | 122 ++++++++ src/core/json/boolean.cc | 39 +++ src/core/json/error.cc | 81 +++++ src/core/json/number.cc | 57 ++++ src/core/json/object.cc | 179 +++++++++++ src/core/json/string.cc | 49 +++ src/core/modules/conduit.cc | 145 +++++---- src/core/modules/conduit.hh | 6 +- src/core/url.cc | 156 +++++++++- src/core/url.hh | 46 ++- src/ipc/message.cc | 2 +- src/ipc/preload.hh | 11 +- src/ipc/routes.cc | 50 +++- src/platform/types.hh | 1 + src/serviceworker/container.cc | 9 +- src/serviceworker/container.hh | 2 +- src/window/android.cc | 30 +- src/window/apple.mm | 24 +- src/window/linux.cc | 38 ++- src/window/win.cc | 18 +- 31 files changed, 1518 insertions(+), 798 deletions(-) create mode 100644 src/core/json/any.cc create mode 100644 src/core/json/array.cc create mode 100644 src/core/json/boolean.cc create mode 100644 src/core/json/error.cc create mode 100644 src/core/json/number.cc create mode 100644 src/core/json/object.cc create mode 100644 src/core/json/string.cc diff --git a/api/application.js b/api/application.js index 66bbf07aed..a910c92690 100644 --- a/api/application.js +++ b/api/application.js @@ -603,7 +603,7 @@ export const debug = !!globalThis.__args?.debug /** * Application configuration. - * @type {object} + * @type {Record<string, string|number|boolean|(string|number|boolean)[]>} */ export const config = globalThis.__args?.config ?? {} diff --git a/api/index.d.ts b/api/index.d.ts index 8d0020a72a..b9ebf2c094 100644 --- a/api/index.d.ts +++ b/api/index.d.ts @@ -8082,9 +8082,9 @@ declare module "socket:application" { export const debug: boolean; /** * Application configuration. - * @type {object} + * @type {Record<string, string|number|boolean|(string|number|boolean)[]>} */ - export const config: object; + export const config: Record<string, string | number | boolean | (string | number | boolean)[]>; export namespace backend { /** * @param {object} opts - an options object @@ -9433,9 +9433,12 @@ declare module "socket:internal/conduit" { /** * @typedef {{ options: object, payload: Uint8Array }} ReceiveMessage * @typedef {function(Error?, ReceiveCallback | undefined)} ReceiveCallback - * @typedef {{ id?: string|BigInt|number, reconnect?: {} }} ConduitOptions * @typedef {{ isActive: boolean, handles: { ids: string[], count: number }}} ConduitDiagnostics * @typedef {{ isActive: boolean, port: number }} ConduitStatus + * @typedef {{ + * id?: string|BigInt|number, + * sharedKey?: string + *}} ConduitOptions */ export const DEFALUT_MAX_RECONNECT_RETRIES: 32; export const DEFAULT_MAX_RECONNECT_TIMEOUT: 256; @@ -9476,12 +9479,9 @@ declare module "socket:internal/conduit" { /** * Creates an instance of Conduit. * - * @param {object} params - The parameters for the Conduit. - * @param {string} params.id - The ID for the connection. + * @param {ConduitOptions} options */ - constructor({ id }: { - id: string; - }); + constructor(options: ConduitOptions); /** * @type {boolean} */ @@ -9506,6 +9506,10 @@ declare module "socket:internal/conduit" { * @type {number?} */ id: number | null; + /** + * @type {string} + */ + sharedKey: string; /** * The URL string for the WebSocket server. * @type {string} @@ -9597,10 +9601,6 @@ declare module "socket:internal/conduit" { payload: Uint8Array; }; export type ReceiveCallback = (arg0: Error | null, arg1: ReceiveCallback | undefined) => any; - export type ConduitOptions = { - id?: string | BigInt | number; - reconnect?: {}; - }; export type ConduitDiagnostics = { isActive: boolean; handles: { @@ -9612,6 +9612,10 @@ declare module "socket:internal/conduit" { isActive: boolean; port: number; }; + export type ConduitOptions = { + id?: string | BigInt | number; + sharedKey?: string; + }; } declare module "socket:ip" { diff --git a/api/internal/conduit.js b/api/internal/conduit.js index 5ddd354557..791cc7f6e6 100644 --- a/api/internal/conduit.js +++ b/api/internal/conduit.js @@ -16,14 +16,17 @@ let isApplicationPaused = false * The default Conduit port * @type {number} */ -let defaultConduitPort = globalThis.__args.conduit || 0 +let defaultConduitPort = globalThis.__args.conduit?.port || 0 /** * @typedef {{ options: object, payload: Uint8Array }} ReceiveMessage * @typedef {function(Error?, ReceiveCallback | undefined)} ReceiveCallback - * @typedef {{ id?: string|BigInt|number, reconnect?: {} }} ConduitOptions * @typedef {{ isActive: boolean, handles: { ids: string[], count: number }}} ConduitDiagnostics * @typedef {{ isActive: boolean, port: number }} ConduitStatus + * @typedef {{ + * id?: string|BigInt|number, + * sharedKey?: string + *}} ConduitOptions */ export const DEFALUT_MAX_RECONNECT_RETRIES = 32 export const DEFAULT_MAX_RECONNECT_TIMEOUT = 256 @@ -160,6 +163,11 @@ export class Conduit extends EventTarget { */ id = null + /** + * @type {string} + */ + sharedKey = globalThis.__args?.conduit?.sharedKey ?? '' + /** * @private * @type {function(MessageEvent)} @@ -186,13 +194,13 @@ export class Conduit extends EventTarget { /** * Creates an instance of Conduit. * - * @param {object} params - The parameters for the Conduit. - * @param {string} params.id - The ID for the connection. + * @param {ConduitOptions} options */ - constructor ({ id }) { + constructor (options) { super() - this.id = id + this.id = options.id + this.sharedKey = options.sharedKey || this.sharedKey // @ts-ignore this.port = this.constructor.port this.connect() @@ -206,7 +214,7 @@ export class Conduit extends EventTarget { * @type {string} */ get url () { - return `ws://localhost:${this.port}/${this.id}/${client.top.id}` + return `ws://localhost:${this.port}/${this.id}/${client.top.id}?key=${this.sharedKey || ''}` } /** diff --git a/bin/build-runtime-library.sh b/bin/build-runtime-library.sh index a06358efc1..30971e4900 100755 --- a/bin/build-runtime-library.sh +++ b/bin/build-runtime-library.sh @@ -97,6 +97,7 @@ declare sources=( $(find "$root"/src/app/*.cc) $(find "$root"/src/core/*.cc) $(find "$root"/src/core/modules/*.cc) + $(find "$root"/src/core/json/*.cc) $(find "$root"/src/extension/*.cc) $(find "$root"/src/ipc/*.cc) $(find "$root"/src/platform/*.cc) diff --git a/src/core/codec.cc b/src/core/codec.cc index 12c24f7f37..8ac058048d 100644 --- a/src/core/codec.cc +++ b/src/core/codec.cc @@ -61,6 +61,12 @@ static const char SAFE[256] = { /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; +// encoding table for base64 encoding +static const char BASE64_ENCODING_TABLE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// modulus table for base64 encoding +static const int BASE64_MOD_TABLE[] = {0, 2, 1}; + namespace SSC { const Array<uint8_t, 8> toBytes (const uint64_t input) { Array<uint8_t, 8> bytes; @@ -174,31 +180,66 @@ namespace SSC { memcpy(dest, hash, 20); } - unsigned char* base64Encode(const unsigned char *data, size_t input_length, size_t *output_length) { - *output_length = (size_t) (4.0 * ceil((double) input_length / 3.0)); - unsigned char *encoded_data = (unsigned char *) malloc(*output_length + 1); - if (!encoded_data) return 0; + const String shacalc (const String& input, size_t size) { + char output[size]; + shacalc(input.data(), output); + return String(output, size); + } + size_t encodeBase64Length (size_t inputLength) { + return (size_t) (4.0 * ceil((double) inputLength / 3.0)); + } + + unsigned char* encodeBase64 ( + const unsigned char* input, + unsigned char* output, + size_t inputLength, + size_t* outputLength + ) { int i = 0; int j = 0; - for (i = 0, j = 0; i < input_length;) { - uint32_t octet_a = i < input_length ? data[i++] : 0; - uint32_t octet_b = i < input_length ? data[i++] : 0; - uint32_t octet_c = i < input_length ? data[i++] : 0; - uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; - encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; - encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + if (!output) { + return nullptr; + } + + const auto length = encodeBase64Length(inputLength); + + for (i = 0, j = 0; i < inputLength;) { + uint32_t octetA = i < inputLength ? input[i++] : 0; + uint32_t octetB = i < inputLength ? input[i++] : 0; + uint32_t octetC = i < inputLength ? input[i++] : 0; + uint32_t triple = (octetA << 0x10) + (octetB << 0x08) + octetC; + output[j++] = BASE64_ENCODING_TABLE[(triple >> 3 * 6) & 0x3F]; + output[j++] = BASE64_ENCODING_TABLE[(triple >> 2 * 6) & 0x3F]; + output[j++] = BASE64_ENCODING_TABLE[(triple >> 1 * 6) & 0x3F]; + output[j++] = BASE64_ENCODING_TABLE[(triple >> 0 * 6) & 0x3F]; } - for (i = 0; i < mod_table[input_length % 3]; i++) { - encoded_data[*output_length - 1 - i] = '='; + for (i = 0; i < BASE64_MOD_TABLE[inputLength % 3]; i++) { + output[length - 1 - i] = '='; } - encoded_data[*output_length] = '\0'; // Null-terminate the string - return encoded_data; + output[length] = 0; + + if (outputLength != nullptr) { + *outputLength = length; + } + + return output; + } + + const Vector<unsigned char> encodeBase64 (const String& input) { + size_t size; + unsigned char output[encodeBase64Length(input.size())]; + encodeBase64( + reinterpret_cast<const unsigned char*>(input.data()), + output, + input.size(), + &size + ); + + return Vector<unsigned char>(output, output + size + 1); } String encodeURIComponent (const String& input) { diff --git a/src/core/codec.hh b/src/core/codec.hh index 5c154ee647..cfada68a59 100644 --- a/src/core/codec.hh +++ b/src/core/codec.hh @@ -3,6 +3,8 @@ #include "../platform/types.hh" +#define SHA_DIGEST_LENGTH 20 + namespace SSC { /** * Encodes input by replacing certain characters by @@ -85,21 +87,26 @@ namespace SSC { * @param dest Pointer to the destination buffer to store the hash */ void shacalc (const char* src, char* dest); + const String shacalc (const String&, size_t size = SHA_DIGEST_LENGTH); /** * Encodes a given input data to a Base64 encoded string. - * @param data Pointer to the input data - * @param input_length Length of the input data - * @param output_length Pointer to store the length of the encoded output + * @param input Pointer to the input data + * @param output Pointer to the output data + * @param inputLength Length of the input data + * @param outputLength Pointer to store the length of the encoded output * @return Pointer to the Base64 encoded string */ - unsigned char* base64Encode(const unsigned char *data, size_t input_length, size_t *output_length); + unsigned char* encodeBase64 ( + const unsigned char* input, + unsigned char* output, + size_t inputLength, + size_t* outputLength + ); - // Encoding table for base64 encoding - const char encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t encodeBase64Length (size_t inputLength); - // Modulus table for base64 encoding - const int mod_table[] = {0, 2, 1}; + const Vector<unsigned char> encodeBase64 (const String&); } #endif diff --git a/src/core/headers.cc b/src/core/headers.cc index 5291d57358..4e062dab43 100644 --- a/src/core/headers.cc +++ b/src/core/headers.cc @@ -27,6 +27,18 @@ namespace SSC { return this->value.string != string; } + const String Headers::Header::operator + (const String& string) const { + return this->value + string; + } + + bool Headers::Header::empty () const { + return this->value.empty(); + } + + size_t Headers::Header::size() const { + return this->value.size(); + } + Headers::Headers (const String& source) { for (const auto& entry : split(source, '\n')) { const auto tuple = split(entry, ':'); @@ -106,6 +118,10 @@ namespace SSC { return this->entries.size(); } + bool Headers::empty () const { + return this->entries.empty(); + } + String Headers::str () const { StringStream headers; auto remaining = this->size(); @@ -235,6 +251,10 @@ namespace SSC { return this->string != string; } + const String Headers::Value::operator + (const String& string) const { + return this->string + string; + } + const String& Headers::Value::str () const { return this->string; } @@ -243,6 +263,14 @@ namespace SSC { return this->str().c_str(); } + size_t Headers::Value::size() const { + return this->string.size(); + } + + bool Headers::Value::empty () const { + return this->string.empty(); + } + const String toHeaderCase (const String& source) { Vector<String> parts; for (const auto& entry : split(trim(source), '-')) { diff --git a/src/core/headers.hh b/src/core/headers.hh index e358e68f56..a93e532743 100644 --- a/src/core/headers.hh +++ b/src/core/headers.hh @@ -26,9 +26,12 @@ namespace SSC { bool operator != (const Value&) const; bool operator == (const String&) const; bool operator != (const String&) const; + const String operator + (const String&) const; const String& str () const; const char * c_str() const; + bool empty () const; + size_t size () const; template <typename T> void set (T value) { auto v = Value(value); @@ -47,6 +50,9 @@ namespace SSC { bool operator != (const Header&) const; bool operator == (const String&) const; bool operator != (const String&) const; + const String operator + (const String&) const; + size_t size () const; + bool empty () const; }; using Entries = Vector<Header>; @@ -60,6 +66,7 @@ namespace SSC { Headers (const Entries& entries); size_t size () const; String str () const; + bool empty () const; void set (const String& name, const String& value) noexcept; void set (const Header& header) noexcept; diff --git a/src/core/json.cc b/src/core/json.cc index b4d495ecad..613ca1acf4 100644 --- a/src/core/json.cc +++ b/src/core/json.cc @@ -2,270 +2,32 @@ namespace SSC::JSON { Null null; - Any anyNull = nullptr; - Number::Number (const String& string) { - this->data = std::stod(string.str()); - } - - SSC::String Number::str () const { - if (this->data == 0) { - return "0"; - } - - auto value = this->data; - auto output = std::to_string(value); - auto decimal = output.find("."); - - // trim trailing zeros - if (decimal >= 0) { - auto i = output.size() - 1; - while (output[i] == '0' && i >= decimal) { - i--; - } - - return output.substr(0, i); - } - - return output; - } - - SSC::String Object::str () const { - SSC::StringStream stream; - auto count = this->data.size(); - stream << SSC::String("{"); - for (const auto& tuple : this->data) { - auto key = replace(tuple.first, "\"","\\\""); - auto value = tuple.second.str(); - - stream << SSC::String("\""); - stream << key; - stream << SSC::String("\":"); - stream << value; - - if (--count > 0) { - stream << SSC::String(","); - } - } - - stream << SSC::String("}"); - return stream.str(); - } - - SSC::String Array::str () const { - SSC::StringStream stream; - auto count = this->data.size(); - stream << SSC::String("["); - - for (const auto& value : this->data) { - stream << value.str(); - - if (--count > 0) { - stream << SSC::String(","); - } - } - - stream << SSC::String("]"); - return stream.str(); - } - - String::String (const Number& number) { - this->data = number.str(); - } - - SSC::String String::str () const { - auto escaped = replace(this->data, "\"", "\\\""); - escaped = replace(escaped, "\\n", "\\\\n"); - return "\"" + replace(escaped, "\n", "\\n") + "\""; - } - - Any::Any (const Null null) { - this->pointer = SharedPointer<void>(new Null()); - this->type = Type::Null; - } - - Any::Any (std::nullptr_t) { - this->pointer = SharedPointer<void>(new Null()); - this->type = Type::Null; - } - - Any::Any (const char *string) { - this->pointer = SharedPointer<void>(new String(string)); - this->type = Type::String; - } - - Any::Any (const char string) { - this->pointer = SharedPointer<void>(new String(string)); - this->type = Type::String; - } - - Any::Any (const SSC::Path path) - : Any(path.string()) + Null::Null (std::nullptr_t) + : Null() {} - Any::Any (const SSC::String string) { - this->pointer = SharedPointer<void>(new String(string)); - this->type = Type::String; - } - - Any::Any (const String string) { - this->pointer = SharedPointer<void>(new String(string)); - this->type = Type::String; - } - - Any::Any (bool boolean) { - this->pointer = SharedPointer<void>(new Boolean(boolean)); - this->type = Type::Boolean; - } - - Any::Any (const Boolean boolean) { - this->pointer = SharedPointer<void>(new Boolean(boolean)); - this->type = Type::Boolean; - } - - Any::Any (int32_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (uint32_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (int64_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (uint64_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (double number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - -#if SOCKET_RUNTIME_PLATFORM_APPLE - Any::Any (size_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } - - Any::Any (ssize_t number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } -#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS - Any::Any (long long number) { - this->pointer = SharedPointer<void>(new Number((double) number)); - this->type = Type::Number; - } -#endif - - Any::Any (const Number number) { - this->pointer = SharedPointer<void>(new Number(number)); - this->type = Type::Number; + std::nullptr_t Null::value () const { + return nullptr; } - Any::Any (const Object object) { - this->pointer = SharedPointer<void>(new Object(object)); - this->type = Type::Object; + const SSC::String Null::str () const { + return "null"; } - Any::Any (const Object::Entries entries) { - this->pointer = SharedPointer<void>(new Object(entries)); - this->type = Type::Object; + Raw::Raw (const Raw& raw) { + this->data = raw.data; } - Any::Any (const Array array) { - this->pointer = SharedPointer<void>(new Array(array)); - this->type = Type::Array; + Raw::Raw (const Raw* raw) { + this->data = raw->data; } - Any::Any (const Array::Entries entries) { - this->pointer = SharedPointer<void>(new Array(entries)); - this->type = Type::Array; + Raw::Raw (const SSC::String& source) { + this->data = source; } - Any::Any (const Raw source) { - this->pointer = SharedPointer<void>(new Raw(source)); - this->type = Type::Raw; - } - - Any::Any (const Error error) { - this->pointer = SharedPointer<void>(new Error(error)); - this->type = Type::Error; - } - -#if SOCKET_RUNTIME_PLATFORM_APPLE - Any::Any (const NSError* error) { - this->type = Type::Error; - this->pointer = SharedPointer<void>(new Error( - error.domain.UTF8String, - error.localizedDescription.UTF8String, - error.code - )); - } -#elif SOCKET_RUNTIME_PLATFORM_LINUX - Any::Any (const GError* error) { - this->type = Type::Error; - this->pointer = SharedPointer<void>(new Error( - g_quark_to_string(error->domain), - error->message, - error->code - )); - } -#endif - - Any Any::operator[](const SSC::String& key) const { - if (this->type == Type::Object) { - return this->as<Object>()[key]; - } - throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); - } - - Any& Any::operator[](const SSC::String& key) { - if (this->type == Type::Object) { - return this->as<Object>()[key]; - } - throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); - } - - Any Any::operator[](const unsigned int index) const { - if (this->type == Type::Array) { - return this->as<Array>()[index]; - } - throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); - } - - Any& Any::operator[](const unsigned int index) { - if (this->type == Type::Array) { - return this->as<Array>()[index]; - } - throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); - } - - SSC::String Any::str () const { - const auto ptr = this->pointer.get() == nullptr - ? reinterpret_cast<const void*>(this) - : this->pointer.get(); - - switch (this->type) { - case Type::Empty: return ""; - case Type::Any: return ""; - case Type::Raw: return reinterpret_cast<const Raw*>(ptr)->str(); - case Type::Null: return "null"; - case Type::Object: return reinterpret_cast<const Object*>(ptr)->str(); - case Type::Array: return reinterpret_cast<const Array*>(ptr)->str(); - case Type::Boolean: return reinterpret_cast<const Boolean*>(ptr)->str(); - case Type::Number: return reinterpret_cast<const Number*>(ptr)->str(); - case Type::String: return reinterpret_cast<const String*>(ptr)->str(); - case Type::Error: return reinterpret_cast<const Error*>(ptr)->str(); - } - - return ""; + const SSC::String Raw::str () const { + return this->data; } } diff --git a/src/core/json.hh b/src/core/json.hh index ea61725d9a..7445f62c6e 100644 --- a/src/core/json.hh +++ b/src/core/json.hh @@ -59,6 +59,10 @@ namespace SSC::JSON { bool isObject () const { return this->type == Type::Object; } bool isString () const { return this->type == Type::String; } bool isEmpty () const { return this->type == Type::Empty; } + + const SSC::String str () const { + return ""; + } }; class Error : public std::invalid_argument, public Value<SSC::String, Type::Error> { @@ -68,110 +72,45 @@ namespace SSC::JSON { SSC::String message; SSC::String location; - Error () : std::invalid_argument("") {}; - Error (const Error& error) : std::invalid_argument(error.str()) { - this->code = error.code; - this->name = error.name; - this->message = error.message; - this->location = error.location; - } - - Error (Error* error) : std::invalid_argument(error->str()) { - this->code = error->code; - this->name = error->name; - this->message = error->message; - this->location = error->location; - } - + Error (); + Error (const SSC::String& message); + Error (const Error& error); + Error (Error* error); Error ( const SSC::String& name, const SSC::String& message, int code = 0 - ) : std::invalid_argument(name + ": " + message) { - this->name = name; - this->code = code; - this->message = message; - } - - Error ( - const SSC::String& message - ) : std::invalid_argument(message) { - this->message = message; - } - + ); Error ( const SSC::String& name, const SSC::String& message, const SSC::String& location - ) : std::invalid_argument(name + ": " + message + " (from " + location + ")") { - this->name = name; - this->message = message; - this->location = location; - } - - SSC::String value () const { - return this->str(); - } - - const char* what () const noexcept override { - return this->message.c_str(); - } - - const SSC::String str () const { - if (this->name.size() > 0 && this->message.size() > 0 && this->location.size() > 0) { - return this->name + ": " + this->message + " (from " + this->location + ")"; - } else if (this->name.size() > 0 && this->message.size() > 0) { - return this->name + ": " + this->message; - } else if (this->name.size() > 0 && this->location.size() > 0) { - return this->name + " (from " + this->location + ")"; - } else if (this->message.size() > 0 && this->location.size() > 0) { - return this->message + " (from " + this->location + ")"; - } else if (this->name.size() > 0) { - return this->name; - } else if (this->message.size() > 0) { - return this->message; - } + ); - return ""; - } + const SSC::String value () const; + const char* what () const noexcept override; + const SSC::String str () const; }; class Null : public Value<std::nullptr_t, Type::Null> { public: - Null () {} - Null (std::nullptr_t) : Null() {} - std::nullptr_t value () const { - return nullptr; - } - - SSC::String str () const { - return "null"; - } + Null () = default; + Null (std::nullptr_t); + std::nullptr_t value () const; + const SSC::String str () const; }; - extern Null null; - class Any : public Value<void *, Type::Any> { public: SharedPointer<void> pointer = nullptr; - Any () { - this->pointer = nullptr; - this->type = Type::Null; - } - - Any (const Any& any) : pointer(any.pointer) { - this->type = any.type; - } - - Any (Type type, SharedPointer<void> pointer) : pointer(pointer) { - this->type = type; - } - + Any (); + Any (const Any& any); + Any (Type type, SharedPointer<void> pointer); Any (std::nullptr_t); Any (const Null); + Any (bool); - Any (const Boolean); Any (int64_t); Any (uint64_t); Any (uint32_t); @@ -183,30 +122,51 @@ namespace SSC::JSON { #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS Any (long long); #endif - Any (const Number); + + Any (Atomic<bool>&); + Any (Atomic<int64_t>&); + Any (Atomic<uint64_t>&); + Any (Atomic<uint32_t>&); + Any (Atomic<int32_t>&); + Any (Atomic<double>&); + #if SOCKET_RUNTIME_PLATFORM_APPLE + Any (Atomic<size_t>&); + Any (Atomic<ssize_t>&); + #elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any (Atomic<long long>&); + #endif + Any (const char); Any (const char *); - Any (const SSC::String); - Any (const SSC::Path); - Any (const String); - Any (const Object); - Any (const ObjectEntries); - Any (const Array); - Any (const ArrayEntries); - Any (const Raw); - Any (const Error); + + Any (const SSC::String&); + Any (const SSC::Path&); + + Any (const Boolean&); + Any (const Number&); + Any (const String&); + Any (const Object&); + Any (const Array&); + Any (const Raw&); + Any (const Error&); #if SOCKET_RUNTIME_PLATFORM_APPLE Any (const NSError*); #elif SOCKET_RUNTIME_PLATFORM_LINUX Any (const GError*); #endif - ~Any () { - this->pointer = nullptr; - this->type = Type::Any; - } + Any (const ArrayEntries&); + Any (const ObjectEntries&); + + ~Any (); - SSC::String str () const; + Any operator[](const SSC::String& key) const; + Any& operator[](const SSC::String& key); + Any operator[](const unsigned int index) const; + Any& operator[](const unsigned int index); + + bool operator == (const Any&) const; + bool operator != (const Any&) const; template <typename T> T& as () const { auto ptr = this->pointer.get(); @@ -218,336 +178,127 @@ namespace SSC::JSON { throw Error("BadCastError", "cannot cast to null value", __PRETTY_FUNCTION__); } - Any operator[](const SSC::String& key) const; - Any& operator[](const SSC::String& key); - Any operator[](const unsigned int index) const; - Any& operator[](const unsigned int index); + const SSC::String str () const; }; class Raw : public Value<SSC::String, Type::Raw> { public: - Raw (const Raw& raw) { this->data = raw.data; } - Raw (const Raw* raw) { this->data = raw->data; } - Raw (const SSC::String& source) { this->data = source; } - - const SSC::String str () const { - return this->data; - } + Raw (const Raw& raw); + Raw (const Raw* raw); + Raw (const SSC::String& source); + const SSC::String str () const; }; - extern Any anyNull; - - inline const auto typeof (const Any& any) { - return any.typeof(); - } - class Object : public Value<ObjectEntries, Type::Object> { public: using Entries = ObjectEntries; + using const_iterator = Entries::const_iterator; Object () = default; - Object (std::map<SSC::String, int> entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object (std::map<SSC::String, bool> entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object (std::map<SSC::String, double> entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object (std::map<SSC::String, int64_t> entries) { - for (auto const &tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object (const Object::Entries entries) { - for (const auto& tuple : entries) { - auto key = tuple.first; - auto value = tuple.second; - this->data.insert_or_assign(key, value); - } - } - - Object (const Object& object) { - this->data = object.value(); - } - - Object (const std::map<SSC::String, SSC::String> map) { - for (const auto& tuple : map) { - auto key = tuple.first; - auto value = Any(tuple.second); - this->data.insert_or_assign(key, value); - } - } - - Object (const Error& error) { - if (error.name.size() > 0) { - this->set("name", error.name); - } - - if (error.message.size() > 0) { - this->set("message", error.message); - } - - if (error.location.size() > 0) { - this->set("location", error.location); - } - - if (error.code != 0) { - this->set("code", error.code); - } - } - - SSC::String str () const; - - const Object::Entries value () const { - return this->data; - } - - Any& get (const SSC::String key) { - if (this->data.find(key) != this->data.end()) { - return this->data.at(key); - } - - return anyNull; - } - - void set (const SSC::String key, Any value) { - this->data[key] = value; - } - - bool has (const SSC::String& key) const { - return this->data.find(key) != this->data.end(); - } - - Any operator [] (const SSC::String& key) const { - if (this->data.find(key) != this->data.end()) { - return this->data.at(key); - } - - return nullptr; - } - - Any &operator [] (const SSC::String& key) { - return this->data[key]; - } - - auto size () const { - return this->data.size(); - } + #if SOCKET_RUNTIME_PLATFORM_LINUX + Object (JSCValue* value); + #endif + Object (const std::map<SSC::String, int>& entries); + Object (const std::map<SSC::String, bool>& entries); + Object (const std::map<SSC::String, double>& entries); + Object (const std::map<SSC::String, int64_t>& entries); + Object (const Object::Entries& entries); + Object (const Object& object); + Object (const std::map<SSC::String, SSC::String>& map); + Object (const Error& error); + + Any operator [] (const SSC::String& key) const; + Any &operator [] (const SSC::String& key); + + const SSC::String str () const; + const Object::Entries value () const; + const Any& get (const SSC::String& key) const; + Any& get (const SSC::String& key); + void set (const SSC::String& key, const Any& value); + bool has (const SSC::String& key) const; + bool contains (const SSC::String& key) const; + Entries::size_type size () const; + const const_iterator begin () const noexcept; + const const_iterator end () const noexcept; }; class Array : public Value<ArrayEntries, Type::Array> { public: using Entries = ArrayEntries; + using const_iterator = Entries::const_iterator; Array () = default; - Array (const Array& array) { - this->data = array.value(); - } - - Array (const Array::Entries entries) { - for (const auto& value : entries) { - this->data.push_back(value); - } - } - - SSC::String str () const; - - Array::Entries value () const { - return this->data; - } - - bool has (const unsigned int index) const { - return this->data.size() < index; - } - - auto size () const { - return this->data.size(); - } - - Any get (const unsigned int index) const { - if (index < this->data.size()) { - return this->data.at(index); - } - - return nullptr; - } - - void set (const unsigned int index, Any value) { - if (index >= this->data.size()) { - this->data.resize(index + 1); - } - - this->data[index] = value; - } - - void push (Any value) { - this->set(this->size(), value); - } - - Any& pop () { - if (this->size() == 0) { - return anyNull; - } - - auto& value = this->data.back(); - this->data.pop_back(); - return value; - } - - Any operator [] (const unsigned int index) const { - if (index >= this->data.size()) { - return nullptr; - } - - return this->data.at(index); - } - - Any &operator [] (const unsigned int index) { - if (index >= this->data.size()) { - this->data.resize(index + 1); - } + Array (const Array& array); + Array (const Array::Entries& entries); + #if SOCKET_RUNTIME_PLATFORM_LINUX + Array (JSCValue* value); + #endif - return this->data.at(index); - } + Any operator [] (const unsigned int index) const; + Any &operator [] (const unsigned int index); + + const SSC::String str () const; + Array::Entries value () const; + bool has (const unsigned int index) const; + Entries::size_type size () const; + Any get (const unsigned int index) const; + void set (const unsigned int index, const Any& value); + void push (Any value); + Any& pop (); + const const_iterator begin () const noexcept; + const const_iterator end () const noexcept; }; class Boolean : public Value<bool, Type::Boolean> { public: Boolean () = default; - Boolean (const Boolean& boolean) { - this->data = boolean.value(); - } - - Boolean (bool boolean) { - this->data = boolean; - } - - Boolean (int data) { - this->data = data != 0; - } - - Boolean (int64_t data) { - this->data = data != 0; - } - - Boolean (double data) { - this->data = data != 0; - } - - Boolean (void *data) { - this->data = data != nullptr; - } - - Boolean (SSC::String string) { - this->data = string.size() > 0; - } - - bool value () const { - return this->data; - } - - SSC::String str () const { - return this->data ? "true" : "false"; - } + Boolean (const Boolean& boolean); + Boolean (bool boolean); + Boolean (int data); + Boolean (int64_t data); + Boolean (double data); + Boolean (void *data); + Boolean (const SSC::String& string); + + bool value () const; + const SSC::String str () const; }; class Number : public Value<double, Type::Number> { public: Number () = default; - Number (const Number& number) { - this->data = number.value(); - } - - Number (double number) { - this->data = number; - } - - Number (char number) { - this->data = (double) number; - } - - Number (int number) { - this->data = (double) number; - } - - Number (int64_t number) { - this->data = (double) number; - } - - Number (bool number) { - this->data = (double) number; - } - + Number (const Number& number); + Number (double number); + Number (char number); + Number (int number); + Number (int64_t number); + Number (bool number); Number (const String& string); - float value () const { - return this->data; - } - - SSC::String str () const; + float value () const; + const SSC::String str () const; }; class String : public Value<SSC::String, Type::String> { public: String () = default; - String (const String& data) { - this->data = SSC::String(data.value()); - } - - String (const SSC::String data) { - this->data = data; - } - - String (const char data) { - this->data = SSC::String(1, data); - } - - String (const char *data) { - this->data = SSC::String(data); - } - - String (const Any& any) { - this->data = any.str(); - } - + String (const String& data); + String (const SSC::String& data); + String (const char data); + String (const char *data); + String (const Any& any); String (const Number& number); + String (const Boolean& boolean); + String (const Error& error); - String (const Boolean& boolean) { - this->data = boolean.str(); - } - - String (const Error& error) { - this->data = error.str(); - } - - SSC::String str () const; + const SSC::String str () const; + SSC::String value () const; + SSC::String::size_type size () const; + }; - SSC::String value () const { - return this->data; - } + extern Null null; + extern Any anyNull; - auto size () const { - return this->data.size(); - } - }; + inline const auto typeof (const Any& any) { + return any.typeof(); + } } - #endif diff --git a/src/core/json/any.cc b/src/core/json/any.cc new file mode 100644 index 0000000000..2737737e17 --- /dev/null +++ b/src/core/json/any.cc @@ -0,0 +1,268 @@ +#include "../json.hh" + +namespace SSC::JSON { + Any anyNull = nullptr; + + Any::Any () { + this->pointer = nullptr; + this->type = Type::Null; + } + + Any::Any (const Any& any) : pointer(any.pointer) { + this->type = any.type; + } + + Any::Any (Type type, SharedPointer<void> pointer) : pointer(pointer) { + this->type = type; + } + + Any::Any (const Null null) { + this->pointer = SharedPointer<void>(new Null()); + this->type = Type::Null; + } + + Any::Any (std::nullptr_t) { + this->pointer = SharedPointer<void>(new Null()); + this->type = Type::Null; + } + + Any::Any (const char *string) { + this->pointer = SharedPointer<void>(new String(string)); + this->type = Type::String; + } + + Any::Any (const char string) { + this->pointer = SharedPointer<void>(new String(string)); + this->type = Type::String; + } + + Any::Any (const SSC::Path& path) + : Any(path.string()) + {} + + Any::Any (const SSC::String& string) { + this->pointer = SharedPointer<void>(new String(string)); + this->type = Type::String; + } + + Any::Any (const String& string) { + this->pointer = SharedPointer<void>(new String(string)); + this->type = Type::String; + } + + Any::Any (bool boolean) { + this->pointer = SharedPointer<void>(new Boolean(boolean)); + this->type = Type::Boolean; + } + + Any::Any (const Boolean& boolean) { + this->pointer = SharedPointer<void>(new Boolean(boolean)); + this->type = Type::Boolean; + } + + Any::Any (int32_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + + Any::Any (uint32_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + + Any::Any (int64_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + + Any::Any (uint64_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + + Any::Any (double number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Any::Any (size_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } + + Any::Any (ssize_t number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } +#elif !SOCKET_RUNTIME_PLATFORM_WINDOWS + Any::Any (long long number) { + this->pointer = SharedPointer<void>(new Number((double) number)); + this->type = Type::Number; + } +#endif + + Any::Any (Atomic<bool>& boolean) : Any(boolean.load()) {} + Any::Any (Atomic<int64_t>& number) : Any(number.load()) {} + Any::Any (Atomic<uint64_t>& number) : Any(number.load()) {} + Any::Any (Atomic<uint32_t>& number) : Any(number.load()) {} + Any::Any (Atomic<int32_t>& number) : Any(number.load()) {} + Any::Any (Atomic<double>& number) : Any(number.load()) {} + + Any::Any (const Number& number) { + this->pointer = SharedPointer<void>(new Number(number)); + this->type = Type::Number; + } + + Any::Any (const Object& object) { + this->pointer = SharedPointer<void>(new Object(object)); + this->type = Type::Object; + } + + Any::Any (const Object::Entries& entries) { + this->pointer = SharedPointer<void>(new Object(entries)); + this->type = Type::Object; + } + + Any::Any (const Array& array) { + this->pointer = SharedPointer<void>(new Array(array)); + this->type = Type::Array; + } + + Any::Any (const Array::Entries& entries) { + this->pointer = SharedPointer<void>(new Array(entries)); + this->type = Type::Array; + } + + Any::Any (const Raw& source) { + this->pointer = SharedPointer<void>(new Raw(source)); + this->type = Type::Raw; + } + + Any::Any (const Error& error) { + this->pointer = SharedPointer<void>(new Error(error)); + this->type = Type::Error; + } + +#if SOCKET_RUNTIME_PLATFORM_APPLE + Any::Any (const NSError* error) { + this->type = Type::Error; + this->pointer = SharedPointer<void>(new Error( + error.domain.UTF8String, + error.localizedDescription.UTF8String, + error.code + )); + } +#elif SOCKET_RUNTIME_PLATFORM_LINUX + Any::Any (const GError* error) { + this->type = Type::Error; + this->pointer = SharedPointer<void>(new Error( + g_quark_to_string(error->domain), + error->message, + error->code + )); + } +#endif + + Any::~Any () { + this->pointer = nullptr; + this->type = Type::Any; + } + + Any Any::operator[](const SSC::String& key) const { + if (this->type == Type::Object) { + return this->as<Object>()[key]; + } + throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); + } + + Any& Any::operator[](const SSC::String& key) { + if (this->type == Type::Object) { + return this->as<Object>()[key]; + } + throw Error("TypeError", "cannot use operator[] on non-object type", __PRETTY_FUNCTION__); + } + + Any Any::operator[](const unsigned int index) const { + if (this->type == Type::Array) { + return this->as<Array>()[index]; + } + throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); + } + + Any& Any::operator[](const unsigned int index) { + if (this->type == Type::Array) { + return this->as<Array>()[index]; + } + throw Error("TypeError", "cannot use operator[] on non-array type", __PRETTY_FUNCTION__); + } + + bool Any::operator == (const Any& input) const { + if (this->isEmpty() && input.isEmpty()) { + return true; + } + + if (this->isNull() && input.isNull()) { + return true; + } + + if (this->isString() && input.isString()) { + return this->str() == input.str(); + } + + if (this->isNumber() && input.isNumber()) { + return this->as<Number>().value() == input.as<Number>().value(); + } + + if (this->isBoolean() && input.isBoolean()) { + return this->as<Boolean>().value() == input.as<Boolean>().value(); + } + + return false; + } + + bool Any::operator != (const Any& input) const { + if (this->isEmpty() && !input.isEmpty()) { + return true; + } + + if (this->isNull() && !input.isNull()) { + return true; + } + + if (this->isString() && input.isString()) { + return this->str() != input.str(); + } + + if (this->isNumber() && input.isNumber()) { + return this->as<Number>().value() != input.as<Number>().value(); + } + + if (this->isBoolean() && input.isBoolean()) { + return this->as<Boolean>().value() != input.as<Boolean>().value(); + } + + return true; + } + + const SSC::String Any::str () const { + const auto ptr = this->pointer.get() == nullptr + ? reinterpret_cast<const void*>(this) + : this->pointer.get(); + + switch (this->type) { + case Type::Empty: return ""; + case Type::Any: return ""; + case Type::Raw: return reinterpret_cast<const Raw*>(ptr)->str(); + case Type::Null: return "null"; + case Type::Object: return reinterpret_cast<const Object*>(ptr)->str(); + case Type::Array: return reinterpret_cast<const Array*>(ptr)->str(); + case Type::Boolean: return reinterpret_cast<const Boolean*>(ptr)->str(); + case Type::Number: return reinterpret_cast<const Number*>(ptr)->str(); + case Type::String: return reinterpret_cast<const String*>(ptr)->str(); + case Type::Error: return reinterpret_cast<const Error*>(ptr)->str(); + } + + return ""; + } +} diff --git a/src/core/json/array.cc b/src/core/json/array.cc new file mode 100644 index 0000000000..5be15b62d5 --- /dev/null +++ b/src/core/json/array.cc @@ -0,0 +1,122 @@ +#include "../json.hh" + +namespace SSC::JSON { + Array::Array (const Array& array) { + this->data = array.value(); + } + + Array::Array (const Array::Entries& entries) { + for (const auto& value : entries) { + this->data.push_back(value); + } + } +#if SOCKET_RUNTIME_PLATFORM_LINUX + Array::Array (JSCValue* value) { + if (value != nullptr && jsc_value_is_array(value)) { + const auto length = jsc_value_object_get_property(value, "length"); + if (jsc_value_is_number(length)) { + const auto count = jsc_value_to_int32(length); + for (int i = 0; i < count; ++i) { + const auto item = jsc_value_object_get_property_at_index(value, i); + if (jsc_value_is_string(item)) { + this->push(JSON::String(jsc_value_to_string(item))); + } else if (jsc_value_is_boolean(item)) { + this->push(JSON::Boolean(jsc_value_to_boolean(item))); + } else if (jsc_value_is_null(item)) { + this->push(JSON::Null()); + } else if (jsc_value_is_number(item)) { + this->push(JSON::Number(jsc_value_to_double(item))); + } else if (jsc_value_is_array(item)) { + this->push(JSON::Array(item)); + } else if (jsc_value_is_object(item)) { + this->push(JSON::Object(item)); + } + } + } + } + } +#endif + + const SSC::String Array::str () const { + SSC::StringStream stream; + auto count = this->data.size(); + stream << SSC::String("["); + + for (const auto& value : this->data) { + stream << value.str(); + + if (--count > 0) { + stream << SSC::String(","); + } + } + + stream << SSC::String("]"); + return stream.str(); + } + + Array::Entries Array::value () const { + return this->data; + } + + bool Array::has (const unsigned int index) const { + return this->data.size() < index; + } + + Array::Entries::size_type Array::size () const { + return this->data.size(); + } + + Any Array::get (const unsigned int index) const { + if (index < this->data.size()) { + return this->data.at(index); + } + + return nullptr; + } + + void Array::set (const unsigned int index, const Any& value) { + if (index >= this->data.size()) { + this->data.resize(index + 1); + } + + this->data[index] = value; + } + + void Array::push (Any value) { + this->set(this->size(), value); + } + + Any& Array::pop () { + if (this->size() == 0) { + return anyNull; + } + + auto& value = this->data.back(); + this->data.pop_back(); + return value; + } + + Any Array::operator [] (const unsigned int index) const { + if (index >= this->data.size()) { + return nullptr; + } + + return this->data.at(index); + } + + Any& Array::operator [] (const unsigned int index) { + if (index >= this->data.size()) { + this->data.resize(index + 1); + } + + return this->data.at(index); + } + + const Array::const_iterator Array::begin () const noexcept { + return this->data.begin(); + } + + const Array::const_iterator Array::end () const noexcept { + return this->data.end(); + } +} diff --git a/src/core/json/boolean.cc b/src/core/json/boolean.cc new file mode 100644 index 0000000000..e8fba677f5 --- /dev/null +++ b/src/core/json/boolean.cc @@ -0,0 +1,39 @@ +#include "../json.hh" + +namespace SSC::JSON { + Boolean::Boolean (const Boolean& boolean) { + this->data = boolean.value(); + } + + Boolean::Boolean (bool boolean) { + this->data = boolean; + } + + Boolean::Boolean (int data) { + this->data = data != 0; + } + + Boolean::Boolean (int64_t data) { + this->data = data != 0; + } + + Boolean::Boolean (double data) { + this->data = data != 0; + } + + Boolean:: Boolean (void *data) { + this->data = data != nullptr; + } + + Boolean:: Boolean (const SSC::String& string) { + this->data = string.size() > 0; + } + + bool Boolean::value () const { + return this->data; + } + + const SSC::String Boolean::str () const { + return this->data ? "true" : "false"; + } +} diff --git a/src/core/json/error.cc b/src/core/json/error.cc new file mode 100644 index 0000000000..41832ac995 --- /dev/null +++ b/src/core/json/error.cc @@ -0,0 +1,81 @@ +#include "../json.hh" + +namespace SSC::JSON { + Error::Error () + : std::invalid_argument("") + {} + + Error::Error (const Error& error) + : std::invalid_argument(error.str()) + { + this->code = error.code; + this->name = error.name; + this->message = error.message; + this->location = error.location; + } + + Error::Error (Error* error) + : std::invalid_argument(error->str()) + { + this->code = error->code; + this->name = error->name; + this->message = error->message; + this->location = error->location; + } + + Error::Error ( + const SSC::String& name, + const SSC::String& message, + int code + ) : std::invalid_argument(name + ": " + message) + { + this->name = name; + this->code = code; + this->message = message; + } + + Error::Error (const SSC::String& message) + : std::invalid_argument(message) + { + this->message = message; + } + + Error::Error ( + const SSC::String& name, + const SSC::String& message, + const SSC::String& location + ) : std::invalid_argument( + name + ": " + message + " (from " + location + ")" + ) + { + this->name = name; + this->message = message; + this->location = location; + } + + const SSC::String Error::value () const { + return this->str(); + } + + const char* Error::what () const noexcept { + return this->message.c_str(); + } + + const SSC::String Error::str () const { + if (this->name.size() > 0 && this->message.size() > 0 && this->location.size() > 0) { + return this->name + ": " + this->message + " (from " + this->location + ")"; + } else if (this->name.size() > 0 && this->message.size() > 0) { + return this->name + ": " + this->message; + } else if (this->name.size() > 0 && this->location.size() > 0) { + return this->name + " (from " + this->location + ")"; + } else if (this->message.size() > 0 && this->location.size() > 0) { + return this->message + " (from " + this->location + ")"; + } else if (this->name.size() > 0) { + return this->name; + } else if (this->message.size() > 0) { + return this->message; + } + + return ""; + } +} diff --git a/src/core/json/number.cc b/src/core/json/number.cc new file mode 100644 index 0000000000..0f54df156e --- /dev/null +++ b/src/core/json/number.cc @@ -0,0 +1,57 @@ +#include "../json.hh" + +namespace SSC::JSON { + Number::Number (const String& string) { + this->data = std::stod(string.str()); + } + + Number::Number (const Number& number) { + this->data = number.value(); + } + + Number::Number (double number) { + this->data = number; + } + + Number::Number (char number) { + this->data = (double) number; + } + + Number::Number (int number) { + this->data = (double) number; + } + + Number::Number (int64_t number) { + this->data = (double) number; + } + + Number::Number (bool number) { + this->data = (double) number; + } + + float Number::value () const { + return this->data; + } + + const SSC::String Number::str () const { + if (this->data == 0) { + return "0"; + } + + auto value = this->data; + auto output = std::to_string(value); + auto decimal = output.find("."); + + // trim trailing zeros + if (decimal >= 0) { + auto i = output.size() - 1; + while (output[i] == '0' && i >= decimal) { + i--; + } + + return output.substr(0, i); + } + + return output; + } +} diff --git a/src/core/json/object.cc b/src/core/json/object.cc new file mode 100644 index 0000000000..a45542d3bd --- /dev/null +++ b/src/core/json/object.cc @@ -0,0 +1,179 @@ +#include "../json.hh" + +namespace SSC::JSON { +#if SOCKET_RUNTIME_PLATFORM_LINUX + Object::Object (JSCValue* value) { + if (value != nullptr && jsc_value_is_object(value) && !jsc_value_is_array(value)) { + const auto keys = jsc_value_object_enumerate_properties(value); + if (keys != nullptr) { + for (int i = 0; keys[i] != nullptr; ++i) { + const auto key = keys[i]; + const auto property = jsc_value_object_get_property(value, key); + if (jsc_value_is_string(property)) { + this->set(key, JSON::String(jsc_value_to_string(property))); + } else if (jsc_value_is_boolean(property)) { + this->set(key, JSON::Boolean(jsc_value_to_boolean(property))); + } else if (jsc_value_is_null(property)) { + this->set(key, JSON::Null()); + } else if (jsc_value_is_number(property)) { + this->set(key, JSON::Number(jsc_value_to_double(property))); + } else if (jsc_value_is_array(property)) { + this->set(key, JSON::Array(property)); + } else if (jsc_value_is_object(property)) { + this->set(key, JSON::Object(property)); + } + } + + g_strfreev(keys); + } + } + } +#endif + Object::Object (const std::map<SSC::String, int>& entries) { + for (auto const &tuple : entries) { + auto key = tuple.first; + auto value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const std::map<SSC::String, bool>& entries) { + for (auto const &tuple : entries) { + auto key = tuple.first; + auto value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const std::map<SSC::String, double>& entries) { + for (auto const &tuple : entries) { + auto key = tuple.first; + auto value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const std::map<SSC::String, int64_t>& entries) { + for (auto const &tuple : entries) { + auto key = tuple.first; + auto value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const Object::Entries& entries) { + for (const auto& tuple : entries) { + auto key = tuple.first; + auto value = tuple.second; + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const Object& object) { + this->data = object.value(); + } + + Object::Object (const std::map<SSC::String, SSC::String>& map) { + for (const auto& tuple : map) { + auto key = tuple.first; + auto value = Any(tuple.second); + this->data.insert_or_assign(key, value); + } + } + + Object::Object (const Error& error) { + if (error.name.size() > 0) { + this->set("name", error.name); + } + + if (error.message.size() > 0) { + this->set("message", error.message); + } + + if (error.location.size() > 0) { + this->set("location", error.location); + } + + if (error.code != 0) { + this->set("code", error.code); + } + } + + const SSC::String Object::str () const { + SSC::StringStream stream; + auto count = this->data.size(); + stream << SSC::String("{"); + for (const auto& tuple : this->data) { + auto key = replace(tuple.first, "\"","\\\""); + auto value = tuple.second.str(); + + stream << SSC::String("\""); + stream << key; + stream << SSC::String("\":"); + stream << value; + + if (--count > 0) { + stream << SSC::String(","); + } + } + + stream << SSC::String("}"); + return stream.str(); + } + + const Object::Entries Object::value () const { + return this->data; + } + + const Any& Object::get (const SSC::String& key) const { + if (this->data.find(key) != this->data.end()) { + return this->data.at(key); + } + + return anyNull; + } + + Any& Object::get (const SSC::String& key) { + if (this->data.find(key) != this->data.end()) { + return this->data.at(key); + } + + return anyNull; + } + + void Object::set (const SSC::String& key, const Any& value) { + this->data[key] = value; + } + + bool Object::has (const SSC::String& key) const { + return this->contains(key); + } + + bool Object::contains (const SSC::String& key) const { + return this->data.contains(key); + } + + Any Object::operator [] (const SSC::String& key) const { + if (this->data.find(key) != this->data.end()) { + return this->data.at(key); + } + + return nullptr; + } + + Any& Object::operator [] (const SSC::String& key) { + return this->data[key]; + } + + ObjectEntries::size_type Object::size () const { + return this->data.size(); + } + + const Object::const_iterator Object::begin () const noexcept { + return this->data.begin(); + } + + const Object::const_iterator Object::end () const noexcept { + return this->data.end(); + } +} diff --git a/src/core/json/string.cc b/src/core/json/string.cc new file mode 100644 index 0000000000..80c985c123 --- /dev/null +++ b/src/core/json/string.cc @@ -0,0 +1,49 @@ +#include "../json.hh" + +namespace SSC::JSON { + String::String (const Number& number) { + this->data = number.str(); + } + + String::String (const String& data) { + this->data = SSC::String(data.value()); + } + + String::String (const SSC::String& data) { + this->data = data; + } + + String::String (const char data) { + this->data = SSC::String(1, data); + } + + String::String (const char *data) { + this->data = SSC::String(data); + } + + String::String (const Any& any) { + this->data = any.str(); + } + + String::String (const Boolean& boolean) { + this->data = boolean.str(); + } + + String::String (const Error& error) { + this->data = error.str(); + } + + const SSC::String String::str () const { + auto escaped = replace(this->data, "\"", "\\\""); + escaped = replace(escaped, "\\n", "\\\\n"); + return "\"" + replace(escaped, "\n", "\\n") + "\""; + } + + SSC::String String::value () const { + return this->data; + } + + SSC::String::size_type String::size () const { + return this->data.size(); + } +} diff --git a/src/core/modules/conduit.cc b/src/core/modules/conduit.cc index c3531697de..e9c83e069e 100644 --- a/src/core/modules/conduit.cc +++ b/src/core/modules/conduit.cc @@ -17,6 +17,20 @@ namespace SSC { return std::move(pointer); } + CoreConduit::CoreConduit (Core* core) : CoreModule(core) { + auto userConfig = getUserConfig(); + const auto sharedKey = Env::get( + "SOCKET_RUNTIME_CONDUIT_SHARED_KEY", + userConfig["application_conduit_shared_key"] + ); + + if (sharedKey.size() >= 8) { + this->sharedKey = sharedKey; + } else { + this->sharedKey = std::to_string(rand64()); + } + } + CoreConduit::~CoreConduit () { this->stop(); } @@ -142,52 +156,57 @@ namespace SSC { return nullptr; } - void CoreConduit::handshake (CoreConduit::Client *client, const char *request) { - String requestString(request); + void CoreConduit::handshake ( + CoreConduit::Client* client, + const char* buffer + ) { + const auto request = String(buffer); + const auto crlf = request.find("\r\n"); - auto reqeol = requestString.find("\r\n"); - if (reqeol == String::npos) return; // nope + if (crlf == String::npos) { + // TODO(@jwerle); handle malformed handshake request + return; + } - std::istringstream iss(requestString.substr(0, reqeol)); String method; - String url; + String uri; String version; - iss >> method >> url >> version; + auto inputStream = std::istringstream(request.substr(0, crlf)); + inputStream + >> method + >> uri + >> version; - URL parsed(url); - Headers headers(request); - auto keyHeader = headers["Sec-WebSocket-Key"]; + const auto url = URL(uri); + const auto headers = Headers(request); + const auto webSocketKey = headers.get("Sec-WebSocket-Key"); + const auto sharedKey = url.searchParams.get("key"); - if (keyHeader.empty()) { + if (webSocketKey.empty()) { // debug("Sec-WebSocket-Key is required but missing."); return; } - auto parts = split(parsed.pathname, "/"); - uint64_t socketId = 0; - uint64_t clientId = 0; - - try { - socketId = std::stoull(trim(parts[1])); - } catch (...) { - // debug("Unable to parse socket id"); - } + if (url.pathComponents.size() >= 2) { + try { + client->id = url.pathComponents.get<int>(0); + } catch (...) { + // debug("Unable to parse socket id"); + } - try { - clientId = std::stoull(trim(parts[2])); - } catch (...) { - // debug("Unable to parse client id"); + try { + client->clientId = url.pathComponents.get<int>(1); + } catch (...) { + // debug("Unable to parse client id"); + } } - client->id = socketId; - client->clientId = clientId; - do { Lock lock(this->mutex); - if (this->clients.contains(socketId)) { - auto existingClient = this->clients.at(socketId); - this->clients.erase(socketId); + if (this->clients.contains(client->id)) { + auto existingClient = this->clients.at(client->id); + this->clients.erase(client->id); existingClient->close([existingClient]() { if (existingClient->isClosed) { delete existingClient; @@ -195,19 +214,22 @@ namespace SSC { }); } - this->clients.emplace(socketId, client); + this->clients.emplace(client->id, client); // std::cout << "added client " << this->clients.size() << std::endl; } while (0); - // debug("Received key: %s", keyHeader.c_str()); + if (sharedKey != this->sharedKey) { + debug("conduit client failed auth"); + client->close(); + return; + } - String acceptKey = keyHeader + WS_GUID; - char calculatedHash[SHA_DIGEST_LENGTH]; - shacalc(acceptKey.c_str(), calculatedHash); + // debug("Received key: %s", webSocketKey.c_str()); - size_t base64_len; - unsigned char *base64_accept_key = base64Encode((unsigned char*)calculatedHash, SHA_DIGEST_LENGTH, &base64_len); + const auto acceptKey = webSocketKey + WS_GUID; + const auto acceptKeyHash = shacalc(acceptKey); + const auto encodedAcceptKeyHash = encodeBase64(acceptKeyHash); // debug("Generated Accept Key: %s\n", base64_accept_key); // Debugging statement @@ -216,7 +238,7 @@ namespace SSC { << "HTTP/1.1 101 Switching Protocols\r\n" << "Upgrade: websocket\r\n" << "Connection: Upgrade\r\n" - << "Sec-WebSocket-Accept: " << base64_accept_key << "\r\n\r\n"; + << "Sec-WebSocket-Accept: " << encodedAcceptKeyHash.data() << "\r\n\r\n"; const auto response = oss.str(); const auto size = response.size(); @@ -224,32 +246,23 @@ namespace SSC { memcpy(data, response.c_str(), size); const auto buf = uv_buf_init(data, size); - auto req = new uv_write_t; - uv_handle_set_data( - reinterpret_cast<uv_handle_t*>(req), - data - ); - - uv_write( - req, - reinterpret_cast<uv_stream_t*>(&client->handle), - &buf, - 1, - [](uv_write_t *req, int status) { - const auto data = reinterpret_cast<char*>( - uv_handle_get_data(reinterpret_cast<uv_handle_t*>(req)) - ); + auto req = new uv_write_t; + auto handle = reinterpret_cast<uv_handle_t*>(req); + auto stream = reinterpret_cast<uv_stream_t*>(&client->handle); - if (data != nullptr) { - delete [] data; - } + uv_handle_set_data(handle, data); + uv_write(req, stream, &buf, 1, [](uv_write_t *req, int status) { + const auto data = reinterpret_cast<char*>( + uv_handle_get_data(reinterpret_cast<uv_handle_t*>(req)) + ); - delete req; + if (data != nullptr) { + delete [] data; } - ); - free(base64_accept_key); + delete req; + }); client->isHandshakeDone = 1; } @@ -322,7 +335,7 @@ namespace SSC { auto recipient = this->clients[to]; auto client = this->clients[from]; if (client != nullptr && recipient != nullptr) { - recipient->emit(options, payload, size); + recipient->send(options, payload, size); } }); } @@ -385,13 +398,13 @@ namespace SSC { [client](const auto result) { auto token = result.token; if (result.post.body != nullptr && result.post.length > 0) { - client->emit({{"token", token}}, result.post.body, result.post.length); + client->send({{"token", token}}, result.post.body, result.post.length); } else { const auto data = result.json().str(); const auto size = data.size(); const auto payload = std::make_shared<char[]>(size); memcpy(payload.get(), data.c_str(), size); - client->emit({{"token", token}}, payload, size); + client->send({{"token", token}}, payload, size); } } ); @@ -404,7 +417,7 @@ namespace SSC { const Function<void()> callback = nullptr; }; - bool CoreConduit::Client::emit ( + bool CoreConduit::Client::send ( const CoreConduit::Options& options, SharedPointer<char[]> bytes, size_t length, @@ -584,7 +597,7 @@ namespace SSC { }); }; - this->emit({}, vectorToSharedPointer({ 0x00 }), 1, 0x08, closeHandle); + this->send({}, vectorToSharedPointer({ 0x00 }), 1, 0x08, closeHandle); } void CoreConduit::start (const StartCallback& callback) { @@ -599,7 +612,7 @@ namespace SSC { this->isStarting = true; - auto ip = Env::get("SOCKET_RUNTIME_CONDUIT_HOSTNAME", "0.0.0.0"); + this->hostname = Env::get("SOCKET_RUNTIME_CONDUIT_HOSTNAME", this->hostname); auto port = this->port.load(); if (Env::has("SOCKET_RUNTIME_CONDUIT_PORT")) { @@ -608,7 +621,7 @@ namespace SSC { } catch (...) {} } - uv_ip4_addr(ip.c_str(), port, &this->addr); + uv_ip4_addr(this->hostname.c_str(), port, &this->addr); uv_tcp_init(loop, &this->socket); uv_tcp_bind( &this->socket, diff --git a/src/core/modules/conduit.hh b/src/core/modules/conduit.hh index 73fb7e07d2..4b0163a7af 100644 --- a/src/core/modules/conduit.hh +++ b/src/core/modules/conduit.hh @@ -130,7 +130,7 @@ namespace SSC { ~Client (); - bool emit ( + bool send ( const CoreConduit::Options& options, SharedPointer<char[]> payload, size_t length, @@ -143,11 +143,13 @@ namespace SSC { // state std::map<uint64_t, Client*> clients; + String sharedKey; Atomic<bool> isStarting = false; Atomic<int> port = 0; + String hostname = "0.0.0.0"; Mutex mutex; - CoreConduit (Core* core) : CoreModule(core) {}; + CoreConduit (Core* core); ~CoreConduit (); // codec diff --git a/src/core/url.cc b/src/core/url.cc index 141a0bb1eb..2445073c92 100644 --- a/src/core/url.cc +++ b/src/core/url.cc @@ -1,3 +1,5 @@ +#include <type_traits> + #include "codec.hh" #include "debug.hh" #include "url.hh" @@ -83,6 +85,154 @@ namespace SSC { return components; } + URL::PathComponents::PathComponents (const String& pathname) { + this->set(pathname); + } + + void URL::PathComponents::set (const String& pathname) { + const auto parts = split(pathname, "/"); + for (const auto& part : parts) { + const auto value = trim(part); + if (value.size() > 0) { + this->parts.push_back(value); + } + } + } + + const String URL::PathComponents::operator[] (const size_t index) const { + return this->parts[index]; + } + + const String& URL::PathComponents::operator[] (const size_t index) { + return this->parts[index]; + } + + const String& URL::PathComponents:: at (const size_t index) const { + return this->parts.at(index); + } + + const String URL::PathComponents::str () const noexcept { + return "/" + join(this->parts, "/"); + } + + const URL::PathComponents::Iterator URL::PathComponents::begin () const noexcept { + return this->parts.begin(); + } + + const URL::PathComponents::Iterator URL::PathComponents::end () const noexcept { + return this->parts.end(); + } + + const size_t URL::PathComponents::size () const noexcept { + return this->parts.size(); + } + + const bool URL::PathComponents::empty () const noexcept { + return this->parts.empty(); + } + + template <typename T> + const T URL::PathComponents::get (const size_t index) const { + if (std::is_same<T, uint64_t>::value) { + return std::stoull(this->at(index)); + } + + if (std::is_same<T, int64_t>::value) { + return std::stoll(this->at(index)); + } + + if (std::is_same<T, uint32_t>::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same<T, int32_t>::value) { + return std::stol(this->at(index)); + } + + if (std::is_same<T, uint16_t>::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same<T, int16_t>::value) { + return std::stol(this->at(index)); + } + + if (std::is_same<T, uint8_t>::value) { + return std::stoul(this->at(index)); + } + + if (std::is_same<T, int8_t>::value) { + return std::stod(this->at(index)); + } + + if (std::is_same<T, bool>::value) { + if (std::stod(this->at(index)) == 0) { + return false; + } + + return true; + } + + throw new Error("unhandled type in URL::PathComponents::get"); + } + + template const uint64_t URL::PathComponents::get (const size_t index) const; + template const int64_t URL::PathComponents::get (const size_t index) const; + template const uint32_t URL::PathComponents::get (const size_t index) const; + template const int32_t URL::PathComponents::get (const size_t index) const; + template const uint16_t URL::PathComponents::get (const size_t index) const; + template const int16_t URL::PathComponents::get (const size_t index) const; + template const uint8_t URL::PathComponents::get (const size_t index) const; + template const int8_t URL::PathComponents::get (const size_t index) const; + template const bool URL::PathComponents::get (const size_t index) const; + + URL::SearchParams::SearchParams (const String& input) { + const auto query = input.starts_with("?") + ? input.substr(1) + : input; + + for (const auto& entry : split(query, '&')) { + const auto parts = split(entry, '='); + if (parts.size() == 2) { + const auto key = decodeURIComponent(trim(parts[0])); + const auto value = decodeURIComponent(trim(parts[1])); + this->set(key, value); + } + } + } + + URL::SearchParams::SearchParams (const SearchParams& input) { + for (const auto& entry : input) { + this->set(entry.first, entry.second); + } + } + + URL::SearchParams::SearchParams (const Map& input) { + for (const auto& entry : input) { + this->set(entry.first, entry.second); + } + } + + URL::SearchParams::SearchParams (const JSON::Object& input) { + for (const auto& entry : input) { + this->set(entry.first, entry.second); + } + } + + const String URL::SearchParams::str () const { + Vector<String> components; + for (const auto& entry : *this) { + const auto parts = Vector<String> { + entry.first, + entry.second.str() + }; + + components.push_back(join(parts, "=")); + } + + return join(components, "&"); + } + URL::Builder& URL::Builder::setProtocol (const String& protocol) { this->protocol = protocol; return *this; @@ -281,10 +431,14 @@ namespace SSC { if (parts.size() == 2) { const auto key = decodeURIComponent(trim(parts[0])); const auto value = decodeURIComponent(trim(parts[1])); - this->searchParams.insert_or_assign(key, value); + this->searchParams.set(key, value); } } } + + if (this->pathname.size() > 0) { + this->pathComponents.set(this->pathname); + } } const String URL::str () const { diff --git a/src/core/url.hh b/src/core/url.hh index bca1d1196d..fc07d209fb 100644 --- a/src/core/url.hh +++ b/src/core/url.hh @@ -16,6 +16,47 @@ namespace SSC { static const Components parse (const String& url); }; + struct PathComponents { + using Iterator = Vector<String>::const_iterator; + using const_iterator = Vector<String>::const_iterator; + + Vector<String> parts; + + PathComponents () = default; + PathComponents (const String& pathname); + const String operator[] (const size_t index) const; + const String& operator[] (const size_t index); + + void set (const String& pathname); + const String& at (const size_t index) const; + const String str () const noexcept; + const Iterator begin () const noexcept; + const size_t size () const noexcept; + const Iterator end () const noexcept; + const bool empty () const noexcept; + + template <typename T> + const T get (const size_t index) const; + }; + + class SearchParams : public JSON::Object { + public: + class Value : public JSON::Any { + public: + Value (const Any& value) + : JSON::Any(value.str()) + {} + }; + + using Entries = JSON::Object::Entries; + SearchParams () = default; + SearchParams (const SearchParams&); + SearchParams (const String&); + SearchParams (const Map&); + SearchParams (const JSON::Object&); + const String str () const; + }; + struct Builder { String protocol = ""; String username = ""; @@ -60,7 +101,10 @@ namespace SSC { String scheme; String fragment; String query; - Map searchParams; + + SearchParams searchParams; + + PathComponents pathComponents; URL () = default; URL (const String& href); diff --git a/src/ipc/message.cc b/src/ipc/message.cc index 0b514ea4de..2331b32747 100644 --- a/src/ipc/message.cc +++ b/src/ipc/message.cc @@ -56,7 +56,7 @@ namespace SSC::IPC { } if (pair[0].compare("value") == 0) { - this->value = decodeURIComponent(pair[1]); + this->value = decodeValues ? decodeURIComponent(pair[1]) : pair[1]; } if (pair[0].compare("seq") == 0) { diff --git a/src/ipc/preload.hh b/src/ipc/preload.hh index de8dd5135b..1833768c08 100644 --- a/src/ipc/preload.hh +++ b/src/ipc/preload.hh @@ -76,15 +76,18 @@ namespace SSC::IPC { UniqueClient client; int index = 0; - int conduit = 0; + Vector<String> argv; Headers headers = {}; // depends on 'features.useHTMLMarkup' + String userScript = ""; Map metadata; // depends on 'features.useHTMLMarkup' Map env; Map userConfig; - Vector<String> argv; - - String userScript = ""; + JSON::Object::Entries conduit = { + {"host", "0.0.0.0"}, + {"port", JSON::Number(0)}, + {"sharedKey", ""} + }; // only use if you know what you are doing String RUNTIME_PRIMORDIAL_OVERRIDES = ""; diff --git a/src/ipc/routes.cc b/src/ipc/routes.cc index cd2a332576..08b0481505 100644 --- a/src/ipc/routes.cc +++ b/src/ipc/routes.cc @@ -2927,7 +2927,7 @@ static void mapIPCRoutes (Router *router) { }; auto client = router->bridge->core->conduit.get(id); - client->emit(options, post.body, post.length); + client->send(options, post.body, post.length); return; } @@ -3581,22 +3581,64 @@ static void mapIPCRoutes (Router *router) { }); }); + /** + * @param index + * @param value + */ + router->map("window.eval", [=](auto message, auto router, auto reply) { + const auto app = App::sharedApplication(); + + if (app == nullptr) { + return reply(Result::Err { message, "Application is in invalid state" }); + } + + int index = 0; + if (message.has("index")) { + REQUIRE_AND_GET_MESSAGE_VALUE(index, "index", std::stoi); + } + + const auto value = message.get("value"); + const auto window = app->windowManager.getWindow(index); + + if (!window) { + return reply(Result::Err { + message, + JSON::Object::Entries { + {"message", "Target window not found"}, + {"type", "NotFoundError"} + } + }); + } + + window->eval(value, [=](const auto result) { + reply(Result::Data { message, result }); + }); + }); + /** * Send an event to another window * @param event * @param value - * @param targetWindowIndex + * @param targetWindowIndex (DEPRECATED) use `index` instead + * @param index */ router->map("window.send", [=](auto message, auto router, auto reply) { const auto app = App::sharedApplication(); if (app == nullptr) { - return reply(Result::Err { message, "Application is invalid state" }); + return reply(Result::Err { message, "Application is in invalid state" }); } + int targetWindowIndex = -1; + const auto event = message.get("event"); const auto value = message.get("value"); - const auto targetWindowIndex = message.get("targetWindowIndex").size() >= 0 ? std::stoi(message.get("targetWindowIndex")) : -1; + + try { + targetWindowIndex = message.get("targetWindowIndex").size() >= 0 + ? std::stoi(message.get("targetWindowIndex")) + : -1; + } catch (...) {} if (targetWindowIndex < 0) { return reply(Result::Err { message, "Invalid target window index" }); diff --git a/src/platform/types.hh b/src/platform/types.hh index def72808bd..de7387624e 100644 --- a/src/platform/types.hh +++ b/src/platform/types.hh @@ -39,6 +39,7 @@ namespace SSC { using Path = fs::path; using Thread = std::thread; using Exception = std::exception; + using Error = std::runtime_error; template <typename T> using Atomic = std::atomic<T>; template <typename T, int k> using Array = std::array<T, k>; diff --git a/src/serviceworker/container.cc b/src/serviceworker/container.cc index 7efd6e9271..9d9c5eef3a 100644 --- a/src/serviceworker/container.cc +++ b/src/serviceworker/container.cc @@ -466,12 +466,15 @@ namespace SSC { Lock lock(this->mutex); auto scope = options.scope; auto scriptURL = options.scriptURL; + auto userConfig = this->bridge != nullptr + ? this->bridge->userConfig + : getUserConfig(); if (scope.size() == 0) { - auto tmp = options.scriptURL; + auto tmp = trim(options.scriptURL); tmp = replace(tmp, "https://", ""); tmp = replace(tmp, "socket://", ""); - tmp = replace(tmp, this->bridge->userConfig["meta_bundle_identifier"], ""); + tmp = replace(tmp, userConfig["meta_bundle_identifier"], ""); auto parts = split(tmp, "/"); parts = Vector<String>(parts.begin(), parts.end() - 1); @@ -482,7 +485,7 @@ namespace SSC { scriptURL = String("socket://"); #endif - scriptURL += bridge->userConfig["meta_bundle_identifier"]; + scriptURL += userConfig["meta_bundle_identifier"]; scriptURL += tmp; scope = join(parts, "/"); diff --git a/src/serviceworker/container.hh b/src/serviceworker/container.hh index 3b74079aa8..530d965c62 100644 --- a/src/serviceworker/container.hh +++ b/src/serviceworker/container.hh @@ -101,7 +101,7 @@ namespace SSC { }; using Registrations = std::map<String, Registration>; - using FetchCallback = std::function<void(const FetchResponse)>; + using FetchCallback = Function<void(const FetchResponse)>; using FetchCallbacks = std::map<ID, FetchCallback>; using FetchRequests = std::map<ID, FetchRequest>; diff --git a/src/window/android.cc b/src/window/android.cc index e41821cdba..d150cee5e1 100644 --- a/src/window/android.cc +++ b/src/window/android.cc @@ -16,6 +16,7 @@ namespace SSC { hotkey(this), dialog(this) { + auto userConfig = options.userConfig; const auto app = App::sharedApplication(); const auto attachment = Android::JNIEnvironmentAttachment(app->jvm); @@ -37,8 +38,13 @@ namespace SSC { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); // `activity.createWindow(index, shouldExitApplicationOnClose): Unit` @@ -500,22 +506,22 @@ extern "C" { if (callback != nullptr) { if (result == "null" || result == "undefined") { - callback(nullptr); + callback(JSON::Null()); } else if (result == "true") { - callback(true); + callback(JSON::Boolean(true)); } else if (result == "false") { - callback(false); + callback(JSON::Boolean(false)); } else { double number = 0.0f; try { number = std::stod(result); } catch (...) { - callback(result); + callback(JSON::String(result)); return; } - callback(number); + callback(JSON::Number(number)); } } } @@ -564,6 +570,7 @@ extern "C" { return nullptr; } + auto userConfig = window->options.userConfig; auto preloadUserScriptSource = IPC::Preload::compile({ .features = IPC::Preload::Options::Features { .useGlobalCommonJS = false, @@ -578,8 +585,13 @@ extern "C" { .index = window->bridge.client.index }, .index = window->options.index, - .conduit = window->core->conduit.port, - .userScript = window->options.userScript + .userScript = window->options.userScript, + .userConfig = window->options.userConfig, + .conduit = { + {"port", window->core->conduit.port}, + {"hostname", window->core->conduit.hostname}, + {"sharedKey", window->core->conduit.sharedKey} + } }); return env->NewStringUTF(preloadUserScriptSource.compile().c_str()); diff --git a/src/window/apple.mm b/src/window/apple.mm index e568e21573..149a21c15a 100644 --- a/src/window/apple.mm +++ b/src/window/apple.mm @@ -207,8 +207,13 @@ - (void) scrollViewDidScroll: (UIScrollView*) scrollView { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); configuration.defaultWebpagePreferences.allowsContentJavaScript = YES; @@ -250,8 +255,13 @@ - (void) scrollViewDidScroll: (UIScrollView*) scrollView { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); [preloadUserScript @@ -848,11 +858,11 @@ - (void) scrollViewDidScroll: (UIScrollView*) scrollView { if ([result isKindOfClass: NSString.class]) { const auto value = String([result UTF8String]); if (value == "null" || value == "undefined") { - callback(nullptr); + callback(JSON::Null()); } else if (value == "true") { - callback(true); + callback(JSON::Boolean(true)); } else if (value == "false") { - callback(value); + callback(JSON::Boolean(false)); } else { double number = 0.0f; diff --git a/src/window/linux.cc b/src/window/linux.cc index 4a8f92b4f3..217bdf5f6a 100644 --- a/src/window/linux.cc +++ b/src/window/linux.cc @@ -226,8 +226,13 @@ namespace SSC { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); auto preloadUserScript = webkit_user_script_new( @@ -282,8 +287,13 @@ namespace SSC { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); gtk_box_pack_end(GTK_BOX(this->vbox), GTK_WIDGET(this->webview), true, true, 0); @@ -1132,7 +1142,7 @@ namespace SSC { const auto message = jsc_exception_get_message(exception); (*callback)(JSON::Error(message)); } else { - (*callback)(stringValue); + (*callback)(JSON::String(stringValue)); } g_free(stringValue); @@ -1145,7 +1155,7 @@ namespace SSC { const auto message = jsc_exception_get_message(exception); (*callback)(JSON::Error(message)); } else { - (*callback)(booleanValue); + (*callback)(JSON::Boolean(booleanValue)); } } else if (jsc_value_is_null(value)) { const auto context = jsc_value_get_context(value); @@ -1155,7 +1165,7 @@ namespace SSC { const auto message = jsc_exception_get_message(exception); (*callback)(JSON::Error(message)); } else { - (*callback)(nullptr); + (*callback)(JSON::Null()); } } else if (jsc_value_is_number(value)) { const auto context = jsc_value_get_context(value); @@ -1166,7 +1176,7 @@ namespace SSC { const auto message = jsc_exception_get_message(exception); (*callback)(JSON::Error(message)); } else { - (*callback)(numberValue); + (*callback)(JSON::Number(numberValue)); } } else if (jsc_value_is_undefined(value)) { const auto context = jsc_value_get_context(value); @@ -1178,6 +1188,18 @@ namespace SSC { } else { (*callback)(nullptr); } + } else if (jsc_value_is_array(value) || jsc_value_is_object(value)) { + const auto context = jsc_value_get_context(value); + const auto exception = jsc_context_get_exception(context); + + if (exception) { + const auto message = jsc_exception_get_message(exception); + (*callback)(JSON::Error(message)); + } else if (jsc_value_is_array(value)) { + (*callback)(JSON::Array(value)); + } else { + (*callback)(JSON::Object(value)); + } } if (value) { diff --git a/src/window/win.cc b/src/window/win.cc index a438909c31..24dad992ca 100644 --- a/src/window/win.cc +++ b/src/window/win.cc @@ -707,8 +707,13 @@ namespace SSC { .index = this->bridge.client.index }, .index = options.index, - .conduit = this->core->conduit.port, - .userScript = options.userScript + .userScript = options.userScript, + .userConfig = options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); if (options.aspectRatio.size() > 0) { @@ -911,8 +916,13 @@ namespace SSC { .index = this->bridge.client.index }, .index = this->options.index, - .conduit = this->core->conduit.port, - .userScript = this->options.userScript + .userScript = this->options.userScript, + .userConfig = this->options.userConfig, + .conduit = { + {"port", this->core->conduit.port}, + {"hostname", this->core->conduit.hostname}, + {"sharedKey", this->core->conduit.sharedKey} + } }); this->webview->AddScriptToExecuteOnDocumentCreated(