From 0ead5ed967b6d6d12062909dfc56fe41e6ba643e Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 28 Sep 2025 13:43:28 -0400 Subject: [PATCH] cleanup: Remove server-side JS socket and HTTP request support. Not used/useful enough to justify keeping all this code around. --- Doxyfile | 1 - apps/api/docs.js | 12 - core/core.js | 6 - core/http.js | 121 ----- docs/usage.md | 1 - src/socket.js.c | 1165 ------------------------------------------- src/socket.js.h | 30 -- src/task.c | 6 - src/tests.c | 88 ---- src/tlscontext.js.c | 105 ---- src/tlscontext.js.h | 37 -- src/util.js.c | 65 --- 12 files changed, 1637 deletions(-) delete mode 100644 core/http.js delete mode 100644 src/socket.js.c delete mode 100644 src/socket.js.h delete mode 100644 src/tlscontext.js.c delete mode 100644 src/tlscontext.js.h diff --git a/Doxyfile b/Doxyfile index 872f7190..f3ba7d3a 100644 --- a/Doxyfile +++ b/Doxyfile @@ -910,7 +910,6 @@ INPUT = README.md \ core/app.js \ core/client.js \ core/core.js \ - core/http.js \ core/tfrpc.js \ docs/ \ src/ diff --git a/apps/api/docs.js b/apps/api/docs.js index 167e784e..20494307 100644 --- a/apps/api/docs.js +++ b/apps/api/docs.js @@ -195,18 +195,6 @@ Call a function after some delay. * *Number* **timeout** Number of milliseconds to wait before calling the callback function. `; -docs['parseHttpResponse()'] = ` -Parses an HTTP response. -### Parameters - * *Uint8Array* **response** The response data. Maybe be partial or contain extra data. The return value will - indicate when and where it is complete. - * *Number* **last_length** The length of the data passed on a previous attempt for the same response, or 0 initially. -### Returns - * *Integer* **-2** if the response is incomplete. - * *Integer* **-1** if the response could not be parsed. - * *Object* An object with **bytes_parsed**, **minor_version**, **status**, **message**, and **headers** fields on successful parse. -`; - docs['exit()'] = ` Exits the app. But why would you want to do that? diff --git a/core/core.js b/core/core.js index 2745ecd0..3fbd39e4 100644 --- a/core/core.js +++ b/core/core.js @@ -7,7 +7,6 @@ /** \cond */ import * as app from './app.js'; -import * as http from './http.js'; export {invoke, getProcessBlob}; /** \endcond */ @@ -240,7 +239,6 @@ async function getProcessBlob(blobId, key, options) { let settings = await loadSettings(); return settings?.permissions?.[user] ?? []; }, - getSockets: getSockets, permissionTest: async function (permission) { let user = process?.credentials?.session?.name; let settings = await loadSettings(); @@ -556,10 +554,6 @@ async function getProcessBlob(blobId, key, options) { imports.ssb.addEventListener = undefined; imports.ssb.removeEventListener = undefined; imports.ssb.getIdentityInfo = undefined; - imports.fetch = async function (url, options) { - let settings = await loadSettings(); - return http.fetch(url, options, settings?.fetch_hosts); - }; if ( process.credentials && diff --git a/core/http.js b/core/http.js deleted file mode 100644 index c321d222..00000000 --- a/core/http.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * \file - * \defgroup tfhttp Tilde Friends HTTP Client JS - * Tilde Friends server-side HTTP client. - * @{ - */ - -/** - * Parse a URL into protocol, host, path, and port parts. - * @param url - * @return An object of the URL parts. - */ -function parseUrl(url) { - // XXX: Hack. - let match = url.match(new RegExp('(\\w+)://([^/:]+)(?::(\\d+))?(.*)')); - return { - protocol: match[1], - host: match[2], - path: match[4], - port: match[3] ? parseInt(match[3]) : match[1] == 'http' ? 80 : 443, - }; -} - -/** - * Parse an HTTP response into headers and body content. - * @param data The response data, headers and body included. - * @return headers and body data. - */ -function parseResponse(data) { - let firstLine; - let headers = {}; - while (true) { - let endLine = data.indexOf('\r\n'); - let line = data.substring(0, endLine); - data = data.substring(endLine + 2); - if (!line.length) { - break; - } else if (!firstLine) { - firstLine = line; - } else { - let colon = line.indexOf(':'); - headers[line.substring(colon)] = line.substring(colon + 1); - } - } - return {headers: headers, body: data}; -} - -/** - * Make an HTTP request. - * @param url The URL. - * @param options Request options. - * @param allowed_hosts List of allowed hosts. - * @return A promise resolved with the response headers and body. - */ -export function fetch(url, options, allowed_hosts) { - let parsed = parseUrl(url); - return new Promise(function (resolve, reject) { - if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) { - throw new Error(`fetch() request to host ${parsed.host} is not allowed.`); - } - let socket = new Socket(); - let buffer = new Uint8Array(0); - - return socket - .connect(parsed.host, parsed.port) - .then(function () { - socket.read(function (data) { - if (data && data.length) { - let newBuffer = new Uint8Array(buffer.length + data.length); - newBuffer.set(buffer, 0); - newBuffer.set(data, buffer.length); - buffer = newBuffer; - } else { - let result = parseHttpResponse(buffer); - if (!result) { - reject(new Exception('Parse failed.')); - } - if (typeof result == 'number') { - if (result == -2) { - reject('Incomplete request.'); - } else { - reject('Bad request.'); - } - } else if (typeof result == 'object') { - resolve({ - body: buffer.slice(result.bytes_parsed), - status: result.status, - message: result.message, - headers: result.headers, - }); - } else { - reject(new Exception('Unexpected parse result.')); - } - resolve(parseResponse(utf8Decode(buffer))); - } - }); - - if (parsed.port == 443) { - return socket.startTls(); - } - }) - .then(function () { - let body = - typeof options?.body == 'string' - ? utf8Encode(options.body) - : options.body || new Uint8Array(0); - let headers = utf8Encode( - `${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n` - ); - let fullRequest = new Uint8Array(headers.length + body.length); - fullRequest.set(headers, 0); - fullRequest.set(body, headers.length); - socket.write(fullRequest); - }) - .catch(function (error) { - reject(error); - }); - }); -} - -/** @} */ diff --git a/docs/usage.md b/docs/usage.md index 95192591..647f344d 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -45,7 +45,6 @@ options: out_http_port_file (default: ""): File to which to write bound HTTP port. blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched. blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted. - fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty. http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com") index (default: "/~core/intro/"): Default path. index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/" diff --git a/src/socket.js.c b/src/socket.js.c deleted file mode 100644 index 7d577ba6..00000000 --- a/src/socket.js.c +++ /dev/null @@ -1,1165 +0,0 @@ -#include "socket.js.h" - -#include "log.h" -#include "mem.h" -#include "task.h" -#include "tls.h" -#include "tlscontext.js.h" -#include "util.js.h" - -#include "uv.h" - -#include - -typedef int promiseid_t; -typedef struct _socket_t socket_t; - -static JSClassID _classId; -static int _count; -static int _open_count; -static tf_tls_context_t* _defaultTlsContext; - -static socket_t** _sockets; -static int _sockets_count; - -typedef enum _socket_direction_t -{ - kUndetermined, - kAccept, - kConnect, -} socket_direction_t; - -typedef struct _socket_t -{ - tf_task_t* _task; - uv_tcp_t _socket; - uv_timer_t _timer; - tf_tls_session_t* _tls; - promiseid_t _startTlsPromise; - promiseid_t _closePromise; - bool _connected; - bool _noDelay; - bool _reading; - bool _listening; - int _active; - char _peerName[256]; - socket_direction_t _direction; - JSValue _object; - JSValue _onConnect; - JSValue _onRead; - JSValue _onError; - uint64_t created_ms; - uint64_t timeout_ms; -} socket_t; - -static JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static void _socket_finalizer(JSRuntime* runtime, JSValue value); - -static JSValue _socket_startTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_stopTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_close(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_shutdown(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_isConnected(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static JSValue _socket_setActivityTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); - -static void _socket_onClose(uv_handle_t* handle); -static void _socket_onShutdown(uv_shutdown_t* request, int status); -static void _socket_onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result); -static void _socket_onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result); -static void _socket_onConnect(uv_connect_t* request, int status); -static void _socket_onNewConnection(uv_stream_t* server, int status); -static void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buffer); -static void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer); -static void _socket_onWrite(uv_write_t* request, int status); -static void _socket_onTlsShutdown(uv_write_t* request, int status); -static void _socket_resetTimeout(socket_t* socket); -static void _socket_pauseTimeout(socket_t* socket); -static void _socket_resumeTimeout(socket_t* socket); - -static void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length); -static int _socket_writeBytes(socket_t* socket, promiseid_t promise, int (*callback)(socket_t* socket, promiseid_t promise, const char*, size_t), JSValue value, int* outLength); -static int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* data, size_t length); -static void _socket_processTlsShutdown(socket_t* socket, promiseid_t promise); -static void _socket_shutdownInternal(socket_t* socket, promiseid_t promise); -static bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_write_cb callback); -static void _socket_processOutgoingTls(socket_t* socket); -static void _socket_reportTlsErrors(socket_t* socket); -static void _socket_reportError(socket_t* socket, const char* error); - -static void _socket_set_handler(socket_t* socket, JSValue* handler, JSValue new_value) -{ - JSContext* context = tf_task_get_context(socket->_task); - - JSValue old_handler = *handler; - - if (JS_IsUndefined(old_handler) && !JS_IsUndefined(new_value)) - { - JS_DupValue(context, socket->_object); - } - - *handler = JS_DupValue(context, new_value); - - JS_FreeValue(context, old_handler); - - if (!JS_IsUndefined(old_handler) && JS_IsUndefined(new_value)) - { - JS_FreeValue(context, socket->_object); - } -} - -static void _socket_gc_mark(JSRuntime* runtime, JSValueConst value, JS_MarkFunc mark_func) -{ - socket_t* socket = JS_GetOpaque(value, _classId); - if (socket) - { - JS_MarkValue(runtime, socket->_onConnect, mark_func); - JS_MarkValue(runtime, socket->_onRead, mark_func); - JS_MarkValue(runtime, socket->_onError, mark_func); - } -} - -JSValue tf_socket_register(JSContext* context) -{ - JS_NewClassID(&_classId); - JSClassDef def = { - .class_name = "Socket", - .finalizer = &_socket_finalizer, - .gc_mark = _socket_gc_mark, - }; - if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0) - { - fprintf(stderr, "Failed to register Socket.\n"); - } - - JSValue global = JS_GetGlobalObject(context); - JS_SetPropertyStr(context, global, "getSockets", JS_NewCFunction(context, _sockets_get, "getSockets", 0)); - JS_FreeValue(context, global); - return JS_NewCFunction2(context, _socket_create, "Socket", 0, JS_CFUNC_constructor, 0); -} - -int tf_socket_get_count() -{ - return _count; -} - -int tf_socket_get_open_count() -{ - return _open_count; -} - -typedef struct _socket_resolve_data_t -{ - uv_getaddrinfo_t resolver; - socket_t* socket; - promiseid_t promise; -} socket_resolve_data_t; - -static socket_t* _socket_create_internal(JSContext* context) -{ - socket_t* socket = tf_malloc(sizeof(socket_t)); - memset(socket, 0, sizeof(*socket)); - _sockets = tf_resize_vec(_sockets, sizeof(socket_t*) * (_sockets_count + 1)); - _sockets[_sockets_count++] = socket; - - socket->_closePromise = -1; - socket->_startTlsPromise = -1; - - ++_count; - JSValue object = JS_NewObjectClass(context, _classId); - socket->_task = tf_task_get(context); - socket->_object = object; - JS_SetOpaque(object, socket); - - socket->created_ms = uv_now(tf_task_get_loop(socket->_task)); - - socket->_onRead = JS_UNDEFINED; - socket->_onError = JS_UNDEFINED; - socket->_onConnect = JS_UNDEFINED; - - JS_SetPropertyStr(context, object, "bind", JS_NewCFunction(context, _socket_bind, "bind", 2)); - JS_SetPropertyStr(context, object, "connect", JS_NewCFunction(context, _socket_connect, "connect", 2)); - JS_SetPropertyStr(context, object, "listen", JS_NewCFunction(context, _socket_listen, "listen", 2)); - JS_SetPropertyStr(context, object, "accept", JS_NewCFunction(context, _socket_accept, "accept", 0)); - JS_SetPropertyStr(context, object, "startTls", JS_NewCFunction(context, _socket_startTls, "startTls", 0)); - JS_SetPropertyStr(context, object, "stopTls", JS_NewCFunction(context, _socket_stopTls, "stopTls", 0)); - JS_SetPropertyStr(context, object, "shutdown", JS_NewCFunction(context, _socket_shutdown, "shutdown", 0)); - JS_SetPropertyStr(context, object, "close", JS_NewCFunction(context, _socket_close, "close", 0)); - JS_SetPropertyStr(context, object, "read", JS_NewCFunction(context, _socket_read, "read", 0)); - JS_SetPropertyStr(context, object, "onError", JS_NewCFunction(context, _socket_onError, "onError", 1)); - JS_SetPropertyStr(context, object, "write", JS_NewCFunction(context, _socket_write, "write", 1)); - JS_SetPropertyStr(context, object, "setActivityTimeout", JS_NewCFunction(context, _socket_setActivityTimeout, "setActivityTimeout", 1)); - - JSAtom atom = JS_NewAtom(context, "isConnected"); - JS_DefinePropertyGetSet(context, object, atom, JS_NewCFunction(context, _socket_isConnected, "isConnected", 0), JS_NULL, 0); - JS_FreeAtom(context, atom); - - atom = JS_NewAtom(context, "peerName"); - JS_DefinePropertyGetSet(context, object, atom, JS_NewCFunction(context, _socket_getPeerName, "peerName", 0), JS_NULL, 0); - JS_FreeAtom(context, atom); - - atom = JS_NewAtom(context, "peerCertificate"); - JS_DefinePropertyGetSet(context, object, atom, JS_NewCFunction(context, _socket_getPeerCertificate, "peerCertificate", 0), JS_NULL, 0); - JS_FreeAtom(context, atom); - - atom = JS_NewAtom(context, "noDelay"); - JSValue get_no_delay = JS_NewCFunction(context, _socket_getNoDelay, "getNoDelay", 0); - JSValue set_no_delay = JS_NewCFunction(context, _socket_setNoDelay, "setNoDelay", 1); - JS_DefinePropertyGetSet(context, object, atom, get_no_delay, set_no_delay, 0); - JS_FreeAtom(context, atom); - - ++_open_count; - uv_tcp_init(tf_task_get_loop(socket->_task), &socket->_socket); - socket->_socket.data = socket; - uv_timer_init(tf_task_get_loop(socket->_task), &socket->_timer); - socket->_timer.data = socket; - - return socket; -} - -static JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - return _socket_create_internal(context)->_object; -} - -static void _socket_close_internal(socket_t* socket) -{ - _socket_set_handler(socket, &socket->_onRead, JS_UNDEFINED); - _socket_set_handler(socket, &socket->_onError, JS_UNDEFINED); - _socket_set_handler(socket, &socket->_onConnect, JS_UNDEFINED); - if (socket->_tls) - { - tf_tls_session_destroy(socket->_tls); - socket->_tls = NULL; - } - if (socket->_socket.data && !uv_is_closing((uv_handle_t*)&socket->_socket)) - { - uv_close((uv_handle_t*)&socket->_socket, _socket_onClose); - } - if (socket->_timer.data && !uv_is_closing((uv_handle_t*)&socket->_timer)) - { - uv_close((uv_handle_t*)&socket->_timer, _socket_onClose); - } - - if (!socket->_socket.data && !socket->_timer.data && JS_IsUndefined(socket->_object)) - { - --_count; - for (int i = 0; i < _sockets_count; i++) - { - if (_sockets[i] == socket) - { - _sockets[i] = _sockets[_sockets_count - 1]; - --_sockets_count; - _sockets = tf_resize_vec(_sockets, sizeof(socket_t*) * _sockets_count); - break; - } - } - tf_free(socket); - } -} - -static void _socket_finalizer(JSRuntime* runtime, JSValue value) -{ - socket_t* socket = JS_GetOpaque(value, _classId); - socket->_object = JS_UNDEFINED; - _socket_close_internal(socket); -} - -static void _socket_reportError(socket_t* socket, const char* error) -{ - JSContext* context = tf_task_get_context(socket->_task); - JSValue ref = JS_DupValue(context, socket->_object); - if (JS_IsFunction(context, socket->_onError)) - { - JSValue exception = JS_ThrowInternalError(context, "%s", error); - JSValue cb_ref = JS_DupValue(context, socket->_onError); - JSValue result = JS_Call(context, socket->_onError, socket->_object, 1, &exception); - JS_FreeValue(context, cb_ref); - tf_util_report_error(context, result); - JS_FreeValue(context, exception); - JS_FreeValue(context, result); - } - else - { - fprintf(stderr, "Socket::reportError: %s\n", error); - } - JS_FreeValue(context, ref); -} - -static void _socket_reportTlsErrors(socket_t* socket) -{ - char buffer[4096]; - while (socket->_tls && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) - { - _socket_reportError(socket, buffer); - } -} - -static JSValue _socket_startTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - if (!socket->_tls) - { - tf_tls_context_t* context = 0; - - if (argc > 0 && JS_IsObject(argv[0])) - { - context = tf_tls_context_get(argv[0]); - } - else - { - if (!_defaultTlsContext) - { - _defaultTlsContext = tf_tls_context_create(); - } - context = _defaultTlsContext; - } - - if (context) - { - socket->_tls = tf_tls_context_create_session(context); - } - - if (socket->_tls) - { - tf_tls_session_set_hostname(socket->_tls, socket->_peerName); - if (socket->_direction == kAccept) - { - tf_tls_session_start_accept(socket->_tls); - } - else if (socket->_direction == kConnect) - { - tf_tls_session_start_connect(socket->_tls); - } - JSValue result = tf_task_allocate_promise(socket->_task, &socket->_startTlsPromise); - _socket_processOutgoingTls(socket); - return result; - } - else - { - return JS_ThrowInternalError(tf_task_get_context(socket->_task), "Failed to get TLS context"); - } - } - else - { - return JS_ThrowInternalError(tf_task_get_context(socket->_task), "startTls with TLS already started"); - } -} - -static JSValue _socket_stopTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - if (socket->_tls) - { - _socket_processOutgoingTls(socket); - tf_tls_session_destroy(socket->_tls); - socket->_tls = NULL; - } - else - { - JS_ThrowInternalError(tf_task_get_context(socket->_task), "stopTls with TLS already stopped"); - } - return JS_NULL; -} - -static bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_write_cb callback) -{ - if (!socket->_socket.data) - { - return false; - } - - char buffer[65536]; - int result = tf_tls_session_read_encrypted(socket->_tls, buffer, sizeof(buffer)); - if (result > 0) - { - char* request_buffer = tf_malloc(sizeof(uv_write_t) + result); - uv_write_t* request = (uv_write_t*)request_buffer; - memset(request, 0, sizeof(*request)); - request->data = (void*)(intptr_t)(promise); - char* rawBuffer = request_buffer + sizeof(uv_write_t); - memcpy(rawBuffer, buffer, result); - - uv_buf_t writeBuffer = { - .base = rawBuffer, - .len = result, - }; - - _socket_pauseTimeout(socket); - int writeResult = uv_write(request, (uv_stream_t*)&socket->_socket, &writeBuffer, 1, callback); - if (writeResult != 0) - { - tf_free(request_buffer); - char error[256]; - snprintf(error, sizeof(error), "uv_write: %s", uv_strerror(writeResult)); - _socket_reportError(socket, error); - } - } - else - { - _socket_reportTlsErrors(socket); - } - return result > 0; -} - -static void _socket_processOutgoingTls(socket_t* socket) -{ - while (_socket_processSomeOutgoingTls(socket, -1, _socket_onWrite)) - { - } -} - -static JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - const char* node = JS_ToCString(tf_task_get_context(socket->_task), argv[0]); - const char* port = JS_ToCString(tf_task_get_context(socket->_task), argv[1]); - - socket_resolve_data_t* data = tf_malloc(sizeof(socket_resolve_data_t)); - memset(data, 0, sizeof(*data)); - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - .ai_flags = 0, - }; - data->resolver.data = data; - data->socket = socket; - JSValue promise = tf_task_allocate_promise(socket->_task, &data->promise); - int result = uv_getaddrinfo(tf_task_get_loop(socket->_task), &data->resolver, _socket_onResolvedForBind, node, port, &hints); - if (result != 0) - { - tf_task_reject_promise(socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_getaddrinfo: %s", uv_strerror(result))); - tf_free(data); - } - return promise; -} - -static void _socket_onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) -{ - socket_resolve_data_t* data = (socket_resolve_data_t*)resolver->data; - if (status != 0) - { - tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "uv_getaddrinfo: %s", uv_strerror(status))); - } - else - { - int bindResult = uv_tcp_bind(&data->socket->_socket, result->ai_addr, 0); - if (bindResult != 0) - { - tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "uv_tcp_bind: %s", uv_strerror(bindResult))); - } - else - { - struct sockaddr_storage addr = { 0 }; - int port = 0; - int size = (int)sizeof(addr); - if (uv_tcp_getsockname(&data->socket->_socket, (struct sockaddr*)&addr, &size) == 0) - { - if (addr.ss_family == AF_INET) - { - port = ntohs(((struct sockaddr_in*)&addr)->sin_port); - } - else if (addr.ss_family == AF_INET6) - { - port = ntohs(((struct sockaddr_in6*)&addr)->sin6_port); - } - } - tf_task_resolve_promise(data->socket->_task, data->promise, JS_NewInt32(tf_task_get_context(data->socket->_task), port)); - } - } - tf_free(data); -} - -static JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - socket->_direction = kConnect; - const char* node = JS_ToCString(context, argv[0]); - const char* port = JS_ToCString(context, argv[1]); - - tf_string_set(socket->_peerName, sizeof(socket->_peerName), node); - - socket_resolve_data_t* data = tf_malloc(sizeof(socket_resolve_data_t)); - memset(data, 0, sizeof(*data)); - struct addrinfo hints = { - .ai_family = PF_INET, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - }; - data->resolver.data = data; - data->socket = socket; - JSValue promise = tf_task_allocate_promise(socket->_task, &data->promise); - int result = uv_getaddrinfo(tf_task_get_loop(socket->_task), &data->resolver, _socket_onResolvedForConnect, node, port, &hints); - if (result != 0) - { - char error[256]; - snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(result)); - tf_task_reject_promise(socket->_task, data->promise, JS_ThrowInternalError(context, "%s", error)); - _socket_close_internal(socket); - tf_free(data); - } - - JS_FreeCString(context, node); - JS_FreeCString(context, port); - return promise; -} - -static void _socket_onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) -{ - socket_resolve_data_t* data = resolver->data; - if (status != 0) - { - char error[256]; - snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(status)); - tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "%s", error)); - _socket_close_internal(data->socket); - } - else - { - uv_connect_t* request = tf_malloc(sizeof(uv_connect_t)); - memset(request, 0, sizeof(*request)); - request->data = (void*)(intptr_t)data->promise; - int connectResult = uv_tcp_connect(request, &data->socket->_socket, result->ai_addr, _socket_onConnect); - if (connectResult != 0) - { - char error[256]; - snprintf(error, sizeof(error), "uv_tcp_connect: %s", uv_strerror(connectResult)); - tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "%s", error)); - _socket_close_internal(data->socket); - tf_free(request); - } - } - uv_freeaddrinfo(result); - tf_free(data); -} - -static void _socket_onConnect(uv_connect_t* request, int status) -{ - promiseid_t promise = (intptr_t)request->data; - if (promise != -1) - { - socket_t* socket = request->handle->data; - if (status == 0) - { - socket->_connected = true; - tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(tf_task_get_context(socket->_task), status)); - } - else - { - char error[256]; - snprintf(error, sizeof(error), "uv_tcp_connect: %s", uv_strerror(status)); - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error)); - _socket_close_internal(socket); - } - } - tf_free(request); -} - -static JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - socket->_listening = true; - int backlog = 16; - JS_ToInt32(context, &backlog, argv[0]); - if (JS_IsUndefined(socket->_onConnect)) - { - _socket_set_handler(socket, &socket->_onConnect, argv[1]); - int result = uv_listen((uv_stream_t*)&socket->_socket, backlog, _socket_onNewConnection); - if (result != 0) - { - return JS_ThrowInternalError(context, "uv_listen: %s", uv_strerror(result)); - } - return JS_NewInt32(context, result); - } - else - { - return JS_ThrowInternalError(context, "listen: Already listening."); - } -} - -static void _socket_onNewConnection(uv_stream_t* server, int status) -{ - socket_t* socket = server->data; - JSContext* context = tf_task_get_context(socket->_task); - JSValue ref = JS_DupValue(context, socket->_object); - if (!JS_IsUndefined(socket->_onConnect)) - { - JSValue cb_ref = JS_DupValue(context, socket->_onConnect); - JSValue result = JS_Call(context, socket->_onConnect, socket->_object, 0, NULL); - JS_FreeValue(context, cb_ref); - tf_util_report_error(context, result); - JS_FreeValue(context, result); - } - JS_FreeValue(context, ref); -} - -static JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - - socket_t* client = _socket_create_internal(context); - client->_direction = kAccept; - promiseid_t promise; - JSValue ref = JS_DupValue(context, client->_object); - JSValue result = tf_task_allocate_promise(socket->_task, &promise); - int status = uv_accept((uv_stream_t*)&socket->_socket, (uv_stream_t*)&client->_socket); - if (status == 0) - { - struct sockaddr_storage name = { 0 }; - int namelen = (int)sizeof(name); - if (uv_tcp_getpeername(&client->_socket, (struct sockaddr*)&name, &namelen) == 0) - { - uv_ip_name((const struct sockaddr*)&name, client->_peerName, sizeof(client->_peerName)); - } - client->_connected = true; - tf_task_resolve_promise(socket->_task, promise, client->_object); - JS_FreeValue(context, client->_object); - } - else - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "uv_accept: %s", uv_strerror(status))); - } - JS_FreeValue(context, ref); - return result; -} - -static JSValue _socket_close(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - if (socket->_closePromise == -1 && socket->_socket.data && !uv_is_closing((uv_handle_t*)&socket->_socket)) - { - JSValue result = tf_task_allocate_promise(socket->_task, &socket->_closePromise); - _socket_close_internal(socket); - return result; - } - return JS_UNDEFINED; -} - -static JSValue _socket_shutdown(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - promiseid_t promise = -1; - JSValue result = tf_task_allocate_promise(socket->_task, &promise); - if (socket->_tls) - { - _socket_processTlsShutdown(socket, promise); - } - else - { - _socket_shutdownInternal(socket, promise); - } - return result; -} - -static void _socket_shutdownInternal(socket_t* socket, promiseid_t promise) -{ - uv_shutdown_t* request = tf_malloc(sizeof(uv_shutdown_t)); - memset(request, 0, sizeof(*request)); - request->data = (void*)(intptr_t)promise; - int result = uv_shutdown(request, (uv_stream_t*)&socket->_socket, _socket_onShutdown); - if (result != 0) - { - char error[256]; - snprintf(error, sizeof(error), "uv_shutdown: %s", uv_strerror(result)); - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error)); - tf_free(request); - } -} - -static void _socket_processTlsShutdown(socket_t* socket, promiseid_t promise) -{ - if (!socket->_tls) - { - _socket_shutdownInternal(socket, promise); - } - else - { - tf_tls_session_shutdown(socket->_tls); - if (!_socket_processSomeOutgoingTls(socket, promise, _socket_onTlsShutdown)) - { - _socket_shutdownInternal(socket, promise); - } - } -} - -static void _socket_onTlsShutdown(uv_write_t* request, int status) -{ - socket_t* socket = request->handle->data; - promiseid_t promise = (intptr_t)request->data; - _socket_processTlsShutdown(socket, promise); - tf_free(request); -} - -static JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - _socket_set_handler(socket, &socket->_onError, argv[0]); - return JS_NULL; -} - -static JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - JSValue ref = JS_DupValue(context, socket->_object); - _socket_set_handler(socket, &socket->_onRead, argv[0]); - - promiseid_t promise = -1; - JSValue read_result = tf_task_allocate_promise(socket->_task, &promise); - if (!socket->_reading && socket->_socket.data) - { - _socket_resetTimeout(socket); - int result = uv_read_start((uv_stream_t*)&socket->_socket, _socket_allocateBuffer, _socket_onRead); - if (result != 0) - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "uv_read_start: %s", uv_strerror(result))); - } - else - { - socket->_reading = true; - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - } - else - { - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - JS_FreeValue(context, ref); - return read_result; -} - -static void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) -{ - *buf = uv_buf_init(tf_malloc(suggestedSize), suggestedSize); -} - -static void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer) -{ - socket_t* socket = stream->data; - _socket_resetTimeout(socket); - JSContext* context = tf_task_get_context(socket->_task); - JSValue ref = JS_DupValue(context, socket->_object); - if (readSize <= 0) - { - socket->_connected = false; - if (!JS_IsUndefined(socket->_onRead)) - { - JSValue args[] = { JS_UNDEFINED }; - JSValue cb_ref = JS_DupValue(context, socket->_onRead); - JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); - JS_FreeValue(context, cb_ref); - tf_util_report_error(context, result); - JS_FreeValue(context, result); - } - _socket_close_internal(socket); - } - else - { - if (socket->_tls) - { - _socket_reportTlsErrors(socket); - tf_tls_session_write_encrypted(socket->_tls, buffer->base, readSize); - if (socket->_startTlsPromise != -1) - { - tf_tls_handshake_t result = tf_tls_session_handshake(socket->_tls); - if (result == k_tls_handshake_done) - { - promiseid_t promise = socket->_startTlsPromise; - socket->_startTlsPromise = -1; - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - else if (result == k_tls_handshake_failed) - { - promiseid_t promise = socket->_startTlsPromise; - socket->_startTlsPromise = -1; - char buffer[8192]; - if (tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "%s", buffer)); - } - else - { - tf_task_reject_promise(socket->_task, promise, JS_UNDEFINED); - } - } - } - - while (socket->_tls) - { - char plain[8192]; - int result = tf_tls_session_read_plain(socket->_tls, plain, sizeof(plain)); - if (result > 0) - { - _socket_notifyDataRead(socket, plain, result); - } - else if (result == k_tls_read_failed) - { - _socket_reportTlsErrors(socket); - _socket_close_internal(socket); - break; - } - else if (result == k_tls_read_zero) - { - if (!JS_IsUndefined(socket->_onRead)) - { - JSValue args[] = { JS_UNDEFINED }; - JSValue cb_ref = JS_DupValue(context, socket->_onRead); - JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); - JS_FreeValue(context, cb_ref); - tf_util_report_error(context, result); - JS_FreeValue(context, result); - } - _socket_close_internal(socket); - break; - } - else - { - break; - } - } - - if (socket->_tls) - { - _socket_processOutgoingTls(socket); - } - } - else - { - _socket_notifyDataRead(socket, buffer->base, readSize); - } - } - tf_free(buffer->base); - JS_FreeValue(context, ref); -} - -static void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length) -{ - if (data && length > 0) - { - JSContext* context = tf_task_get_context(socket->_task); - JSValue ref = JS_DupValue(context, socket->_object); - JSValue typedArray = tf_util_new_uint8_array(context, (const uint8_t*)data, length); - JSValue args[] = { typedArray }; - if (!JS_IsUndefined(socket->_onRead)) - { - JSValue cb_ref = JS_DupValue(context, socket->_onRead); - JSValue result = JS_Call(context, socket->_onRead, socket->_object, 1, args); - JS_FreeValue(context, cb_ref); - tf_util_report_error(context, result); - JS_FreeValue(context, result); - } - JS_FreeValue(context, typedArray); - JS_FreeValue(context, ref); - } -} - -static int _socket_writeBytes(socket_t* socket, promiseid_t promise, int (*callback)(socket_t* socket, promiseid_t promise, const char*, size_t), JSValue value, int* outLength) -{ - int result = -1; - size_t length; - uint8_t* array = NULL; - JSContext* context = tf_task_get_context(socket->_task); - if (JS_IsString(value)) - { - const char* stringValue = JS_ToCStringLen(context, &length, value); - result = callback(socket, promise, stringValue, length); - JS_FreeCString(context, stringValue); - } - else if ((array = tf_util_try_get_array_buffer(context, &length, value)) != 0) - { - result = callback(socket, promise, (const char*)array, length); - } - else - { - size_t offset; - size_t element_size; - JSValue buffer = tf_util_try_get_typed_array_buffer(context, value, &offset, &length, &element_size); - if (!JS_IsException(buffer)) - { - size_t size; - if ((array = tf_util_try_get_array_buffer(context, &size, buffer)) != 0) - { - result = callback(socket, promise, (const char*)array, length); - } - } - JS_FreeValue(context, buffer); - } - - if (outLength) - { - *outLength = (int)length; - } - - return result; -} - -static int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* data, size_t length) -{ - if (!socket->_socket.data) - { - return UV_ENOTCONN; - } - - char* rawBuffer = tf_malloc(sizeof(uv_write_t) + length); - uv_write_t* request = (uv_write_t*)rawBuffer; - memcpy(rawBuffer + sizeof(uv_write_t), data, length); - - uv_buf_t buffer = { - .base = rawBuffer + sizeof(uv_write_t), - .len = length, - }; - - request->data = (void*)(intptr_t)promise; - _socket_pauseTimeout(socket); - int result = uv_write(request, (uv_stream_t*)&socket->_socket, &buffer, 1, _socket_onWrite); - if (result != 0) - { - tf_free(rawBuffer); - } - return result; -} - -static int _socket_write_tls(socket_t* socket, promiseid_t promise, const char* data, size_t size) -{ - return tf_tls_session_write_plain(socket->_tls, data, size); -} - -static JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - promiseid_t promise = -1; - JSValue write_result = tf_task_allocate_promise(socket->_task, &promise); - JSValue ref = JS_DupValue(context, socket->_object); - if (!JS_IsUndefined(argv[0])) - { - if (socket->_tls) - { - _socket_reportTlsErrors(socket); - int length = 0; - int result = _socket_writeBytes(socket, -1, _socket_write_tls, argv[0], &length); - char buffer[8192]; - if (result <= 0 && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "%s", buffer)); - } - else if (result < length) - { - tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, result)); - } - else - { - tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(context, result)); - } - _socket_processOutgoingTls(socket); - } - else - { - int length; - int result = _socket_writeBytes(socket, promise, _socket_writeInternal, argv[0], &length); - - if (result != 0) - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "uv_write: %s", uv_strerror(result))); - } - } - } - else - { - tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, -2)); - } - JS_FreeValue(context, ref); - return write_result; -} - -static void _socket_onWrite(uv_write_t* request, int status) -{ - socket_t* socket = request->handle->data; - _socket_resumeTimeout(socket); - promiseid_t promise = (intptr_t)request->data; - if (promise != -1) - { - if (status == 0) - { - tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(tf_task_get_context(socket->_task), status)); - } - else - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_write: %s", uv_strerror(status))); - } - } - tf_free(request); -} - -static void _socket_timeout(uv_timer_t* timer) -{ - socket_t* socket = timer->data; - _socket_close_internal(socket); -} - -static JSValue _socket_setActivityTimeout(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - int64_t timeout = 0; - if (JS_ToInt64(context, &timeout, argv[0]) == 0 && socket->timeout_ms > 0) - { - socket->timeout_ms = timeout; - uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0); - } - else - { - uv_timer_stop(&socket->_timer); - } - return JS_UNDEFINED; -} - -static JSValue _socket_isConnected(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - return socket->_connected ? JS_TRUE : JS_FALSE; -} - -static void _socket_onClose(uv_handle_t* handle) -{ - --_open_count; - socket_t* socket = handle->data; - handle->data = NULL; - if (socket->_closePromise != -1) - { - promiseid_t promise = socket->_closePromise; - socket->_closePromise = -1; - socket->_connected = false; - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - if (socket->_startTlsPromise != -1) - { - promiseid_t promise = socket->_startTlsPromise; - socket->_startTlsPromise = -1; - socket->_connected = false; - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - _socket_close_internal(socket); -} - -static void _socket_onShutdown(uv_shutdown_t* request, int status) -{ - socket_t* socket = request->handle->data; - _socket_resetTimeout(socket); - promiseid_t promise = (intptr_t)request->data; - if (status == 0) - { - tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED); - } - else - { - tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_shutdown: %s", uv_strerror(status))); - } - tf_free(request); -} - -static JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - struct sockaddr_in6 addr; - int nameLength = sizeof(addr); - if (uv_tcp_getpeername(&socket->_socket, (struct sockaddr*)&addr, &nameLength) == 0) - { - char name[1024]; - if ((size_t)nameLength > sizeof(struct sockaddr_in)) - { - if (uv_ip6_name(&addr, name, sizeof(name)) == 0) - { - return JS_NewString(context, name); - } - } - else - { - if (uv_ip4_name((struct sockaddr_in*)&addr, name, sizeof(name)) == 0) - { - return JS_NewString(context, name); - } - } - } - return JS_UNDEFINED; -} - -static JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - if (socket->_tls) - { - char buffer[128 * 1024]; - int result = tf_tls_session_get_peer_certificate(socket->_tls, buffer, sizeof(buffer)); - if (result > 0) - { - return tf_util_new_uint8_array(tf_task_get_context(socket->_task), (const uint8_t*)buffer, sizeof(buffer)); - } - } - return JS_UNDEFINED; -} - -static JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - return JS_NewBool(context, socket->_noDelay); -} - -static JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - socket_t* socket = JS_GetOpaque(this_val, _classId); - int result = JS_ToBool(context, argv[0]); - socket->_noDelay = result > 0; - uv_tcp_nodelay(&socket->_socket, result > 0 ? 1 : 0); - return JS_UNDEFINED; -} - -static JSValue _sockets_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - JSValue array = JS_NewArray(context); - for (int i = 0; i < _sockets_count; i++) - { - socket_t* s = _sockets[i]; - JSValue entry = JS_NewObject(context); - JS_SetPropertyStr(context, entry, "peer", JS_NewString(context, s->_peerName)); - JS_SetPropertyStr(context, entry, "listening", JS_NewBool(context, s->_listening)); - JS_SetPropertyStr(context, entry, "connected", JS_NewBool(context, s->_connected)); - JS_SetPropertyStr(context, entry, "tls", JS_NewBool(context, s->_tls != NULL)); - JS_SetPropertyStr(context, entry, "age_seconds", JS_NewFloat64(context, (uv_now(tf_task_get_loop(s->_task)) - s->created_ms) / 1000.0)); - JS_SetPropertyUint32(context, array, i, entry); - } - return array; -} - -static void _socket_resetTimeout(socket_t* socket) -{ - if (socket->timeout_ms && socket->_active == 0) - { - uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0); - } -} - -static void _socket_pauseTimeout(socket_t* socket) -{ - if (socket->_active++ == 1) - { - uv_timer_stop(&socket->_timer); - } -} - -static void _socket_resumeTimeout(socket_t* socket) -{ - if (--socket->_active == 0 && socket->timeout_ms > 0) - { - uv_timer_start(&socket->_timer, _socket_timeout, socket->timeout_ms, 0); - } -} diff --git a/src/socket.js.h b/src/socket.js.h deleted file mode 100644 index 1416d906..00000000 --- a/src/socket.js.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/** -** \defgroup socket_js Socket Interface -** Exposes network sockets to script. -** @{ -*/ - -#include "quickjs.h" - -/** -** Register the socket script interface. -** @param context The JS context. -** @return The Socket constructor. -*/ -JSValue tf_socket_register(JSContext* context); - -/** -** Get the number of active socket objects. -** @return The count. -*/ -int tf_socket_get_count(); - -/** -** Get the number of connected socket objects. -** @return the count. -*/ -int tf_socket_get_open_count(); - -/** @} */ diff --git a/src/task.c b/src/task.c index 326be208..643f95ed 100644 --- a/src/task.c +++ b/src/task.c @@ -8,7 +8,6 @@ #include "mem.h" #include "packetstream.h" #include "serialize.h" -#include "socket.js.h" #include "ssb.db.h" #include "ssb.h" #include "ssb.js.h" @@ -826,9 +825,6 @@ static JSValue _tf_task_getStats(JSContext* context, JSValueConst this_val, int JS_SetPropertyStr(context, result, "tls_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tls_malloc_size() / total_memory)); JS_SetPropertyStr(context, result, "tf_malloc_percent", JS_NewFloat64(context, 100.0 * tf_mem_get_tf_malloc_size() / total_memory)); - JS_SetPropertyStr(context, result, "socket_count", JS_NewInt32(context, tf_socket_get_count())); - JS_SetPropertyStr(context, result, "socket_open_count", JS_NewInt32(context, tf_socket_get_open_count())); - if (task->_ssb) { tf_ssb_stats_t ssb_stats = { 0 }; @@ -1665,8 +1661,6 @@ void tf_task_activate(tf_task_t* task) sqlite3_open(task->_db_path, &task->_db); JS_SetPropertyStr(context, global, "Task", tf_taskstub_register(context)); - JS_SetPropertyStr(context, global, "Socket", tf_socket_register(context)); - JS_SetPropertyStr(context, global, "TlsContext", tf_tls_context_register(context)); tf_file_register(context); tf_database_register(context); diff --git a/src/tests.c b/src/tests.c index 9b8a68ad..18d14b54 100644 --- a/src/tests.c +++ b/src/tests.c @@ -549,93 +549,6 @@ static void _test_float(const tf_test_options_t* options) unlink("out/child.js"); } -static void _test_socket(const tf_test_options_t* options) -{ - _write_file("out/test.js", - "'use strict';\n" - "\n" - "var s = new Socket();\n" - "print('connecting');\n" - "print('before connect', s.isConnected);\n" - "s.onError(function(e) {\n" - " print(e);\n" - "});\n" - "print('noDelay', s.noDelay);\n" - "s.noDelay = true;\n" - "s.connect('www.unprompted.com', 80).then(function() {\n" - " print('connected', 'www.unprompted.com', 80, s.isConnected);\n" - " print(s.peerName);\n" - " s.read(function(data) {\n" - " print('read', data ? data.length : null);\n" - " });\n" - " s.write('GET / HTTP/1.0\\r\\n\\r\\n');\n" - "}).then(function(e) {\n" - " print('closed 1');\n" - "});\n" - "\n" - "var s2 = new Socket();\n" - "print('connecting');\n" - "print('before connect', s2.isConnected);\n" - "s2.onError(function(e) {\n" - " print('error');\n" - " print(e);\n" - "});\n" - "print('noDelay', s2.noDelay);\n" - "s2.noDelay = true;\n" - "s2.connect('www.unprompted.com', 443).then(function() {\n" - " print('connected', 'www.unprompted.com', 443);\n" - " s2.read(function(data) {\n" - " print('read', data ? data.length : null);\n" - " });\n" - " return s2.startTls();\n" - "}).then(function() {\n" - " print('ready');\n" - " print(s2.peerName);\n" - " s2.write('GET / HTTP/1.0\\r\\nConnection: close\\r\\n\\r\\n').then(function() {\n" - " s2.shutdown();\n" - " });\n" - "}).catch(function(e) {\n" - " print('caught');\n" - " print(e);\n" - "});\n" - "var s3 = new Socket();\n" - "print('connecting s3');\n" - "print('before connect', s3.isConnected);\n" - "s3.onError(function(e) {\n" - " print('error');\n" - " print(e);\n" - "});\n" - "print('noDelay', s3.noDelay);\n" - "s3.noDelay = true;\n" - "s3.connect('0.0.0.0', 443).then(function() {\n" - " print('connected', '0.0.0.0', 443);\n" - " s3.read(function(data) {\n" - " print('read', data ? data.length : null);\n" - " });\n" - " return s3.startTls();\n" - "}).then(function() {\n" - " print('ready');\n" - " print(s3.peerName);\n" - " s3.write('GET / HTTP/1.0\\r\\nConnection: close\\r\\n\\r\\n').then(function() {\n" - " s3.shutdown();\n" - " });\n" - "}).catch(function(e) {\n" - " print('caught');\n" - " print(e);\n" - "});\n"); - - char command[256]; - unlink("out/test_db0.sqlite"); - snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path); - tf_printf("%s\n", command); - int result = system(command); - tf_printf("returned %d\n", WEXITSTATUS(result)); - assert(WIFEXITED(result)); - assert(WEXITSTATUS(result) == 0); - - unlink("out/test.js"); -} - static void _test_file(const tf_test_options_t* options) { _write_file("out/test.js", @@ -1065,7 +978,6 @@ void tf_tests(const tf_test_options_t* options) _tf_test_run(options, "icu", _test_icu, false); _tf_test_run(options, "uint8array", _test_uint8array, false); _tf_test_run(options, "float", _test_float, false); - _tf_test_run(options, "socket", _test_socket, false); _tf_test_run(options, "file", _test_file, false); _tf_test_run(options, "b64", _test_b64, false); _tf_test_run(options, "rooms", tf_ssb_test_rooms, false); diff --git a/src/tlscontext.js.c b/src/tlscontext.js.c deleted file mode 100644 index 5e47f77f..00000000 --- a/src/tlscontext.js.c +++ /dev/null @@ -1,105 +0,0 @@ -#include "tlscontext.js.h" - -#include "log.h" -#include "mem.h" -#include "task.h" -#include "tls.h" - -#include -#include - -static JSClassID _classId; -static int _count; - -typedef struct _tf_tls_context_t -{ - tf_tls_context_t* context; - tf_task_t* task; - JSValue object; -} tf_tls_context_t; - -static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); -static void _tls_context_finalizer(JSRuntime* runtime, JSValue value); - -static JSValue _tls_context_set_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId); - const char* value = JS_ToCString(context, argv[0]); - tf_tls_context_set_certificate(tls->context, value); - JS_FreeCString(context, value); - return JS_UNDEFINED; -} - -static JSValue _tls_context_set_private_key(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId); - const char* value = JS_ToCString(context, argv[0]); - tf_tls_context_set_private_key(tls->context, value); - JS_FreeCString(context, value); - return JS_UNDEFINED; -} - -static JSValue _tls_context_add_trusted_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_tls_context_t* tls = JS_GetOpaque(this_val, _classId); - const char* value = JS_ToCString(context, argv[0]); - tf_tls_context_add_trusted_certificate(tls->context, value); - JS_FreeCString(context, value); - return JS_UNDEFINED; -} - -JSValue tf_tls_context_register(JSContext* context) -{ - JS_NewClassID(&_classId); - JSClassDef def = { - .class_name = "TlsContext", - .finalizer = _tls_context_finalizer, - }; - if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0) - { - fprintf(stderr, "Failed to register TlsContext.\n"); - } - return JS_NewCFunction2(context, _tls_context_create, "TlsContext", 0, JS_CFUNC_constructor, 0); -} - -tf_tls_context_t* tf_tls_context_get(JSValue value) -{ - tf_tls_context_t* tls = JS_GetOpaque(value, _classId); - return tls ? tls->context : NULL; -} - -int tf_tls_context_get_count() -{ - return _count; -} - -static JSValue _tls_context_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - tf_tls_context_t* tls = tf_malloc(sizeof(tf_tls_context_t)); - memset(tls, 0, sizeof(*tls)); - - ++_count; - tls->object = JS_NewObjectClass(context, _classId); - JS_SetOpaque(tls->object, tls); - - JS_SetPropertyStr(context, tls->object, "setCertificate", JS_NewCFunction(context, _tls_context_set_certificate, "setCertificate", 1)); - JS_SetPropertyStr(context, tls->object, "setPrivateKey", JS_NewCFunction(context, _tls_context_set_private_key, "setPrivateKey", 1)); - JS_SetPropertyStr(context, tls->object, "addTrustedCertificate", JS_NewCFunction(context, _tls_context_add_trusted_certificate, "addTrustedCertificate", 1)); - - tls->context = tf_tls_context_create(); - tls->task = tf_task_get(context); - - return tls->object; -} - -static void _tls_context_finalizer(JSRuntime* runtime, JSValue value) -{ - tf_tls_context_t* tls = JS_GetOpaque(value, _classId); - if (tls->context) - { - tf_tls_context_destroy(tls->context); - tls->context = NULL; - } - --_count; - tf_free(tls); -} diff --git a/src/tlscontext.js.h b/src/tlscontext.js.h deleted file mode 100644 index 18ae113d..00000000 --- a/src/tlscontext.js.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -/** -** \defgroup tls_js TLS Interface -** Exposes \ref tls to JS. -** @{ -*/ - -#include "quickjs.h" - -/** -** A TLS context instance. -*/ -typedef struct _tf_tls_context_t tf_tls_context_t; - -/** -** Register TLS script interface. -** @param context The TLS context. -** @return the TlsContext constructor. -*/ -JSValue tf_tls_context_register(JSContext* context); - -/** -** Get a TLS context instance from its JS object. -** @param value A TlsContext JS object. -** @return The corresponding instance. -*/ -tf_tls_context_t* tf_tls_context_get(JSValue value); - -/** -** Get the number of active TLS context instances. -** @return The number of TlsContext objects created that have not been -** finalized. -*/ -int tf_tls_context_get_count(); - -/** @} */ diff --git a/src/util.js.c b/src/util.js.c index 28d654de..67623b90 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -253,66 +253,6 @@ bool tf_util_report_error(JSContext* context, JSValue value) return is_error; } -static JSValue _util_parseHttpResponse(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) -{ - JSValue result = JS_UNDEFINED; - int status = 0; - int minor_version = 0; - const char* message = NULL; - size_t message_length = 0; - struct phr_header headers[100]; - size_t header_count = sizeof(headers) / sizeof(*headers); - int previous_length = 0; - JS_ToInt32(context, &previous_length, argv[1]); - - JSValue buffer = JS_UNDEFINED; - size_t length; - uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[0]); - if (!array) - { - size_t offset; - size_t element_size; - buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size); - if (!JS_IsException(buffer)) - { - array = tf_util_try_get_array_buffer(context, &length, buffer); - } - } - - if (array) - { - int parse_result = phr_parse_response((const char*)array, length, &minor_version, &status, &message, &message_length, headers, &header_count, previous_length); - if (parse_result > 0) - { - result = JS_NewObject(context); - JS_SetPropertyStr(context, result, "bytes_parsed", JS_NewInt32(context, parse_result)); - JS_SetPropertyStr(context, result, "minor_version", JS_NewInt32(context, minor_version)); - JS_SetPropertyStr(context, result, "status", JS_NewInt32(context, status)); - JS_SetPropertyStr(context, result, "message", JS_NewStringLen(context, message, message_length)); - JSValue header_object = JS_NewObject(context); - for (int i = 0; i < (int)header_count; i++) - { - char name[256]; - snprintf(name, sizeof(name), "%.*s", (int)headers[i].name_len, headers[i].name); - JS_SetPropertyStr(context, header_object, name, JS_NewStringLen(context, headers[i].value, headers[i].value_len)); - } - JS_SetPropertyStr(context, result, "headers", header_object); - } - else - { - result = JS_NewInt32(context, parse_result); - } - } - else - { - result = JS_ThrowTypeError(context, "Could not convert argument to array."); - } - - JS_FreeValue(context, buffer); - - return result; -} - static const char* k_kind_name[] = { [k_kind_bool] = "bool", [k_kind_int] = "int", @@ -359,10 +299,6 @@ static const setting_t k_settings[] = { .type = "integer", .description = "Blobs older than this will be automatically deleted.", .default_value = { .kind = k_kind_int, .int_value = TF_IS_MOBILE ? (int)(1.0f * 365 * 24 * 60 * 60) : -1 } }, - { .name = "fetch_hosts", - .type = "string", - .description = "Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.", - .default_value = { .kind = k_kind_string, .string_value = NULL } }, { .name = "http_redirect", .type = "string", .description = "If connecting by HTTP and HTTPS is configured, Location header prefix (ie, \"http://example.com\")", @@ -523,7 +459,6 @@ void tf_util_register(JSContext* context) JS_SetPropertyStr(context, global, "bip39Words", JS_NewCFunction(context, _util_bip39_words, "bip39Words", 1)); JS_SetPropertyStr(context, global, "bip39Bytes", JS_NewCFunction(context, _util_bip39_bytes, "bip39Bytes", 1)); JS_SetPropertyStr(context, global, "print", JS_NewCFunction(context, _util_print, "print", 1)); - JS_SetPropertyStr(context, global, "parseHttpResponse", JS_NewCFunction(context, _util_parseHttpResponse, "parseHttpResponse", 2)); JS_SetPropertyStr(context, global, "defaultGlobalSettings", JS_NewCFunction(context, _util_defaultGlobalSettings, "defaultGlobalSettings", 2)); JS_FreeValue(context, global); }