remove curl and openssl dependencies
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Successful in 1m44s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped

This commit is contained in:
2025-05-24 00:07:37 -05:00
parent f754d91e14
commit 1248b94244
6 changed files with 394 additions and 321 deletions

View File

@@ -65,6 +65,22 @@ endif
cmake = import('cmake') cmake = import('cmake')
mbedtls_opts = cmake.subproject_options()
mbedtls_opts.add_cmake_defines({
'ENABLE_PROGRAMS': 'OFF', # Disable Mbed TLS programs
'ENABLE_TESTING': 'OFF', # Disable Mbed TLS tests
'CMAKE_BUILD_TYPE': 'Release', # Optimize for release
'MBEDTLS_FATAL_WARNINGS': 'ON', # Treat warnings as errors
'USE_STATIC_MBEDTLS_LIBRARY': 'ON',# Build static libraries
'USE_SHARED_MBEDTLS_LIBRARY': 'OFF'# Disable shared libraries
})
mbedtls_proj = cmake.subproject('mbedtls', options: mbedtls_opts)
deps += [
mbedtls_proj.dependency('mbedtls'),
mbedtls_proj.dependency('mbedx509'),
mbedtls_proj.dependency('mbedcrypto')
]
sdl3_opts = cmake.subproject_options() sdl3_opts = cmake.subproject_options()
sdl3_opts.add_cmake_defines({ sdl3_opts.add_cmake_defines({
'SDL_STATIC': 'ON', 'SDL_STATIC': 'ON',
@@ -116,7 +132,6 @@ else
deps += sdl3_proj.dependency('SDL3-static') deps += sdl3_proj.dependency('SDL3-static')
endif endif
quickjs_opts = [] quickjs_opts = []
quickjs_opts += 'default_library=static' quickjs_opts += 'default_library=static'
@@ -156,76 +171,6 @@ src += [
'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c' 'thirdparty/quirc/identify.c', 'thirdparty/quirc/version_db.c'
] ]
curl_opts = [
'http=enabled',
'ssl=enabled',
'openssl=enabled',
'schannel=disabled',
'secure-transport=disabled',
'dict=disabled',
'file=disabled',
'ftp=disabled',
'gopher=disabled',
'imap=disabled',
'ldap=disabled',
'ldaps=disabled',
'mqtt=disabled',
'pop3=disabled',
'rtmp=disabled',
'rtsp=disabled',
'smb=disabled',
'smtp=disabled',
'telnet=disabled',
'tftp=disabled',
'alt-svc=disabled',
'asynchdns=disabled',
'aws=disabled',
'basic-auth=disabled',
'bearer-auth=disabled',
'bindlocal=disabled',
'brotli=disabled',
'cookies=disabled',
'digest-auth=disabled',
'doh=disabled',
'form-api=disabled',
'getoptions=disabled',
'gsasl=disabled',
'gss-api=disabled',
'headers-api=disabled',
'hsts=disabled',
'http2=disabled',
'idn=disabled',
'kerberos-auth=disabled',
'libcurl-option=disabled',
'libz=disabled',
'mime=disabled',
'negotiate-auth=disabled',
'netrc=disabled',
'ntlm=disabled',
'parsedate=disabled',
'progress-meter=disabled',
'proxy=disabled',
'psl=disabled',
'sha512_256=disabled',
'shuffle-dns=disabled',
'socketpair=disabled',
'tls-srp=disabled',
'unixsockets=disabled',
'verbose-strings=disabled',
'zstd=disabled',
'debug=disabled',
'curldebug=false',
'libuv=disabled',
'tests=disabled',
'unittests=disabled',
'default_library=static'
]
curl_proj = subproject('curl', default_options: curl_opts)
deps += dependency('libcurl')
deps += dependency('zlib', static: true)
deps += dependency('openssl', static:true)
imsrc = [ imsrc = [
'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp', 'GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp',
'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp', 'imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp',

View File

@@ -15,12 +15,4 @@ This function enqueues an HTTP GET request for the specified URL. It supports bo
- An error if memory allocation or CURL initialization fails. - An error if memory allocation or CURL initialization fails.
` `
http.poll[prosperon.DOC] = `Process pending HTTP requests and invoke callbacks.
This function checks for I/O activity on all enqueued HTTP requests and processes any that have completed or received data. For completed requests, it invokes the 'callback' function (if provided) with the result. For streaming requests, it invokes the 'data' function (if provided) as data arrives. This function must be called repeatedly to drive the asynchronous request system.
:return: undefined
:throws: An error if CURL multi-handle processing fails.
`
return http return http

View File

@@ -1,269 +1,368 @@
#include "quickjs.h" #include "quickjs.h"
#include <curl/curl.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/error.h>
// -----------------------------------------------------------------------------
// HTTP request structure
// -----------------------------------------------------------------------------
typedef struct { typedef struct {
char url[512]; // URL for the request char *data;
JSContext *ctx; // JS context for callbacks size_t size;
JSValue callback; // Completion callback (optional) size_t capacity;
JSValue on_data; // Streaming data callback (optional) } buffer_t;
char *response; // Buffer for non-streaming mode
size_t size; // Size of response buffer
CURL *curl; // CURL easy handle
int done; // Request completion flag
int curl_result; // CURL result code
} HttpRequest;
// ----------------------------------------------------------------------------- static void buffer_init(buffer_t *buf) {
// Global data buf->data = NULL;
// ----------------------------------------------------------------------------- buf->size = 0;
static CURLM *g_curl_multi = NULL; // CURL multi-handle for async I/O buf->capacity = 0;
// -----------------------------------------------------------------------------
// Libcurl write callback for streaming or buffering
// -----------------------------------------------------------------------------
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
HttpRequest *req = (HttpRequest *)userp;
// Streaming mode: call onData if provided
if (JS_IsFunction(req->ctx, req->on_data)) {
JSValue chunk = JS_NewStringLen(req->ctx, contents, realsize);
JSValue ret = JS_Call(req->ctx, req->on_data, JS_UNDEFINED, 1, &chunk);
JS_FreeValue(req->ctx, chunk);
JS_FreeValue(req->ctx, ret); // Ignore return value
return realsize;
}
// Non-streaming mode: buffer the response
char *ptr = realloc(req->response, req->size + realsize + 1);
if (!ptr) {
return 0; // Out of memory, tell CURL to abort
}
req->response = ptr;
memcpy(&(req->response[req->size]), contents, realsize);
req->size += realsize;
req->response[req->size] = '\0'; // Null-terminate
return realsize;
} }
// ----------------------------------------------------------------------------- static int buffer_append(buffer_t *buf, const void *data, size_t len) {
// JS function: http.fetch(url, options) if (buf->size + len > buf->capacity) {
// - Enqueues an async HTTP request with optional streaming size_t new_capacity = buf->capacity ? buf->capacity * 2 : 4096;
// ----------------------------------------------------------------------------- while (new_capacity < buf->size + len) {
static JSValue js_http_fetch(JSContext *ctx, JSValueConst this_val, new_capacity *= 2;
int argc, JSValueConst *argv) }
{ char *new_data = realloc(buf->data, new_capacity);
if (argc < 2 || !JS_IsString(argv[0])) { if (!new_data) return -1;
return JS_ThrowTypeError(ctx, "fetch expects a URL string and an options object or callback"); buf->data = new_data;
buf->capacity = new_capacity;
} }
memcpy(buf->data + buf->size, data, len);
buf->size += len;
return 0;
}
// Get URL static void buffer_free(buffer_t *buf) {
free(buf->data);
buf->data = NULL;
buf->size = 0;
buf->capacity = 0;
}
// Parse URL into components
static int parse_url(const char *url, char **host, char **port, char **path, int *use_ssl) {
*host = NULL;
*port = NULL;
*path = NULL;
*use_ssl = 0;
const char *p = url;
// Parse scheme
if (strncmp(p, "https://", 8) == 0) {
*use_ssl = 1;
p += 8;
} else if (strncmp(p, "http://", 7) == 0) {
*use_ssl = 0;
p += 7;
} else {
return -1; // Invalid scheme
}
// Find host end
const char *host_start = p;
const char *host_end = strchr(p, '/');
const char *port_start = strchr(p, ':');
if (port_start && (!host_end || port_start < host_end)) {
// Has explicit port
*host = strndup(host_start, port_start - host_start);
port_start++;
if (host_end) {
*port = strndup(port_start, host_end - port_start);
} else {
*port = strdup(port_start);
}
} else {
// No explicit port
if (host_end) {
*host = strndup(host_start, host_end - host_start);
} else {
*host = strdup(host_start);
}
*port = strdup(*use_ssl ? "443" : "80");
}
// Path
if (host_end) {
*path = strdup(host_end);
} else {
*path = strdup("/");
}
return 0;
}
// Perform HTTP request over plain socket
static int http_request(const char *host, const char *port, const char *request, size_t request_len, buffer_t *response) {
mbedtls_net_context server_fd;
int ret;
mbedtls_net_init(&server_fd);
// Connect to server
if ((ret = mbedtls_net_connect(&server_fd, host, port, MBEDTLS_NET_PROTO_TCP)) != 0) {
mbedtls_net_free(&server_fd);
return ret;
}
// Send request
size_t written = 0;
while (written < request_len) {
ret = mbedtls_net_send(&server_fd, (unsigned char *)request + written, request_len - written);
if (ret < 0) {
mbedtls_net_free(&server_fd);
return ret;
}
written += ret;
}
// Read response
unsigned char buf[4096];
do {
ret = mbedtls_net_recv(&server_fd, buf, sizeof(buf));
if (ret > 0) {
if (buffer_append(response, buf, ret) < 0) {
mbedtls_net_free(&server_fd);
return -1;
}
}
} while (ret > 0);
mbedtls_net_free(&server_fd);
return (ret == 0 || ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) ? 0 : ret;
}
// Perform HTTPS request over SSL
static int https_request(const char *host, const char *port, const char *request, size_t request_len, buffer_t *response) {
mbedtls_net_context server_fd;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
const char *pers = "qjs_https_client";
int ret;
// Initialize structures
mbedtls_net_init(&server_fd);
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
// Seed RNG
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)pers, strlen(pers))) != 0) {
goto exit;
}
// Connect to server
if ((ret = mbedtls_net_connect(&server_fd, host, port, MBEDTLS_NET_PROTO_TCP)) != 0) {
goto exit;
}
// Configure SSL
if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
goto exit;
}
mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
goto exit;
}
if ((ret = mbedtls_ssl_set_hostname(&ssl, host)) != 0) {
goto exit;
}
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
// Handshake
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
goto exit;
}
}
// Send request
size_t written = 0;
while (written < request_len) {
ret = mbedtls_ssl_write(&ssl, (const unsigned char *)request + written, request_len - written);
if (ret < 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_WANT_READ) {
goto exit;
}
} else {
written += ret;
}
}
// Read response
unsigned char buf[4096];
do {
ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
if (ret > 0) {
if (buffer_append(response, buf, ret) < 0) {
ret = -1;
goto exit;
}
}
} while (ret > 0);
if (ret < 0 && ret != MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY &&
ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
goto exit;
}
ret = 0;
exit:
mbedtls_ssl_close_notify(&ssl);
mbedtls_net_free(&server_fd);
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}
// Extract body from HTTP response
static char *extract_body(const char *response, size_t response_len, size_t *body_len) {
const char *body_start = strstr(response, "\r\n\r\n");
if (!body_start) {
return NULL;
}
body_start += 4;
*body_len = response_len - (body_start - response);
return (char *)body_start;
}
// JS function: fetch(url, options)
static JSValue js_fetch(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
if (argc < 1 || !JS_IsString(argv[0])) {
return JS_ThrowTypeError(ctx, "fetch expects a URL string");
}
const char *url = JS_ToCString(ctx, argv[0]); const char *url = JS_ToCString(ctx, argv[0]);
if (!url) { if (!url) {
return JS_ThrowTypeError(ctx, "Invalid URL"); return JS_ThrowTypeError(ctx, "Invalid URL");
} }
// Allocate request object char *host = NULL;
HttpRequest *req = calloc(1, sizeof(*req)); char *port = NULL;
if (!req) { char *path = NULL;
int use_ssl = 0;
buffer_t request_buf;
buffer_t response_buf;
JSValue result = JS_EXCEPTION;
buffer_init(&request_buf);
buffer_init(&response_buf);
// Parse URL
if (parse_url(url, &host, &port, &path, &use_ssl) < 0) {
JS_FreeCString(ctx, url); JS_FreeCString(ctx, url);
return JS_ThrowInternalError(ctx, "Failed to allocate memory"); return JS_ThrowTypeError(ctx, "Invalid URL format");
} }
strncpy(req->url, url, sizeof(req->url) - 1);
req->ctx = ctx; // Build request
req->callback = JS_NULL; buffer_append(&request_buf, "GET ", 4);
req->on_data = JS_NULL; buffer_append(&request_buf, path, strlen(path));
buffer_append(&request_buf, " HTTP/1.1\r\n", 11);
// Parse second argument: callback or options object buffer_append(&request_buf, "Host: ", 6);
if (JS_IsFunction(ctx, argv[1])) { buffer_append(&request_buf, host, strlen(host));
req->callback = JS_DupValue(ctx, argv[1]); buffer_append(&request_buf, "\r\n", 2);
} else if (JS_IsObject(argv[1])) {
JSValue callback = JS_GetPropertyStr(ctx, argv[1], "callback"); // Add headers from options if provided
JSValue on_data = JS_GetPropertyStr(ctx, argv[1], "on_data"); if (argc >= 2 && JS_IsObject(argv[1])) {
if (JS_IsFunction(ctx, callback)) { JSValue headers = JS_GetPropertyStr(ctx, argv[1], "headers");
req->callback = JS_DupValue(ctx, callback); if (JS_IsObject(headers)) {
} JSPropertyEnum *tab;
if (JS_IsFunction(ctx, on_data)) { uint32_t len;
req->on_data = JS_DupValue(ctx, on_data); if (JS_GetOwnPropertyNames(ctx, &tab, &len, headers, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) == 0) {
} for (uint32_t i = 0; i < len; i++) {
JS_FreeValue(ctx, callback); JSValue key = JS_AtomToString(ctx, tab[i].atom);
JS_FreeValue(ctx, on_data); JSValue val = JS_GetProperty(ctx, headers, tab[i].atom);
} else {
JS_FreeCString(ctx, url);
free(req);
return JS_ThrowTypeError(ctx, "Second argument must be a callback or options object");
}
JS_FreeCString(ctx, url);
// Initialize CURL easy handle
req->curl = curl_easy_init();
if (!req->curl) {
JS_FreeValue(ctx, req->callback);
JS_FreeValue(ctx, req->on_data);
free(req);
return JS_ThrowInternalError(ctx, "Failed to create CURL handle");
}
// Set CURL options
curl_easy_setopt(req->curl, CURLOPT_URL, req->url);
curl_easy_setopt(req->curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(req->curl, CURLOPT_WRITEDATA, req);
curl_easy_setopt(req->curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(req->curl, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(req->curl, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(req->curl, CURLOPT_USERAGENT, "prosperon");
curl_easy_setopt(req->curl, CURLOPT_PRIVATE, req);
// Add to multi-handle
if (!g_curl_multi) {
curl_easy_cleanup(req->curl);
JS_FreeValue(ctx, req->callback);
JS_FreeValue(ctx, req->on_data);
free(req);
return JS_ThrowInternalError(ctx, "CURL multi-handle not initialized");
}
CURLMcode mc = curl_multi_add_handle(g_curl_multi, req->curl);
if (mc != CURLM_OK) {
curl_easy_cleanup(req->curl);
JS_FreeValue(ctx, req->callback);
JS_FreeValue(ctx, req->on_data);
free(req);
return JS_ThrowInternalError(ctx, "curl_multi_add_handle failed: %s", curl_multi_strerror(mc));
}
return JS_UNDEFINED;
}
// -----------------------------------------------------------------------------
// JS function: http.poll()
// - Checks for I/O and completed requests, invoking callbacks as needed
// -----------------------------------------------------------------------------
static JSValue js_http_poll(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
if (!g_curl_multi) {
return JS_UNDEFINED;
}
// Perform pending transfers
int still_running = 0;
CURLMcode mc = curl_multi_perform(g_curl_multi, &still_running);
if (mc != CURLM_OK) {
return JS_ThrowInternalError(ctx, "curl_multi_perform failed: %s", curl_multi_strerror(mc));
}
// Check for completed requests
CURLMsg *msg;
int msgq = 0;
while ((msg = curl_multi_info_read(g_curl_multi, &msgq))) {
if (msg->msg == CURLMSG_DONE) {
CURL *easy = msg->easy_handle;
HttpRequest *req = NULL;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, (char **)&req);
char *ct = NULL;
curl_easy_getinfo(easy, CURLINFO_CONTENT_TYPE, &ct);
// Remove from multi-handle
curl_multi_remove_handle(g_curl_multi, easy);
// Mark as done
req->curl_result = msg->data.result;
req->done = 1;
// Call completion callback if provided
if (JS_IsFunction(req->ctx, req->callback)) {
JSValue arg = JS_NewObject(req->ctx);
if (req->curl_result == CURLE_OK) {
JS_SetPropertyStr(req->ctx, arg, "data",
JS_NewArrayBufferCopy(req->ctx, req->response, req->size));
JS_SetPropertyStr(req->ctx, arg, "error", JS_UNDEFINED);
if (ct) JS_SetPropertyStr(req->ctx, arg, "type", JS_NewString(req->ctx, ct)); const char *key_str = JS_ToCString(ctx, key);
} else { const char *val_str = JS_ToCString(ctx, val);
JS_DefinePropertyValueStr(req->ctx, arg, "data", JS_NULL, JS_PROP_C_W_E);
const char *err_str = curl_easy_strerror(req->curl_result); if (key_str && val_str) {
JS_DefinePropertyValueStr(req->ctx, arg, "error", buffer_append(&request_buf, key_str, strlen(key_str));
JS_NewString(req->ctx, err_str ? err_str : "Unknown error"), buffer_append(&request_buf, ": ", 2);
JS_PROP_C_W_E); buffer_append(&request_buf, val_str, strlen(val_str));
buffer_append(&request_buf, "\r\n", 2);
}
JS_FreeCString(ctx, key_str);
JS_FreeCString(ctx, val_str);
JS_FreeValue(ctx, key);
JS_FreeValue(ctx, val);
} }
JSValue ret = JS_Call(req->ctx, req->callback, JS_UNDEFINED, 1, &arg); js_free(ctx, tab);
JS_FreeValue(req->ctx, arg);
JS_FreeValue(req->ctx, ret);
} }
// Cleanup
JS_FreeValue(req->ctx, req->callback);
JS_FreeValue(req->ctx, req->on_data);
curl_easy_cleanup(req->curl);
free(req->response);
free(req);
} }
JS_FreeValue(ctx, headers);
} }
return JS_UNDEFINED; buffer_append(&request_buf, "Connection: close\r\n\r\n", 21);
// Perform request
int ret;
if (use_ssl) {
ret = https_request(host, port, request_buf.data, request_buf.size, &response_buf);
} else {
ret = http_request(host, port, request_buf.data, request_buf.size, &response_buf);
}
if (ret == 0 && response_buf.data) {
// Extract body
size_t body_len;
char *body = extract_body(response_buf.data, response_buf.size, &body_len);
if (body) {
// Return body as ArrayBuffer
result = JS_NewArrayBufferCopy(ctx, (uint8_t *)body, body_len);
} else {
result = JS_ThrowInternalError(ctx, "Failed to parse HTTP response");
}
} else {
char error_buf[256];
mbedtls_strerror(ret, error_buf, sizeof(error_buf));
result = JS_ThrowInternalError(ctx, "Request failed: %s", error_buf);
}
// Cleanup
JS_FreeCString(ctx, url);
free(host);
free(port);
free(path);
buffer_free(&request_buf);
buffer_free(&response_buf);
return result;
} }
// ----------------------------------------------------------------------------- #define countof(a) (sizeof(a)/sizeof(*(a)))
// Module initialization
// ----------------------------------------------------------------------------- // Module exports
static const JSCFunctionListEntry js_http_funcs[] = { static const JSCFunctionListEntry js_http_funcs[] = {
JS_CFUNC_DEF("fetch", 2, js_http_fetch), JS_CFUNC_DEF("fetch", 2, js_fetch),
JS_CFUNC_DEF("poll", 0, js_http_poll),
}; };
JSValue js_http_use(JSContext *ctx) JSValue js_http_use(JSContext *js)
{ {
// Initialize CURL globally (once per process) JSValue exp = JS_NewObject(js);
static int s_curl_init_done = 0; JS_SetPropertyFunctionList(js, exp, js_http_funcs, countof(js_http_funcs));
if (!s_curl_init_done) { return exp;
s_curl_init_done = 1;
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
return JS_ThrowInternalError(ctx, "Failed to initialize CURL");
}
}
// Initialize multi-handle (once per module)
if (!g_curl_multi) {
g_curl_multi = curl_multi_init();
if (!g_curl_multi) {
return JS_ThrowInternalError(ctx, "Failed to initialize CURL multi-handle");
}
}
// Export fetch and poll functions
JSValue export_obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, export_obj, js_http_funcs,
sizeof(js_http_funcs) / sizeof(JSCFunctionListEntry));
return export_obj;
}
static int js_http_init(JSContext *ctx, JSModuleDef *m)
{
JS_SetModuleExport(ctx, m, "default", js_http_use(ctx));
return 0;
}
#ifdef JS_SHARED_LIBRARY
#define JS_INIT_MODULE js_init_module
#else
#define JS_INIT_MODULE js_init_module_http
#endif
JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
JSModuleDef *m = JS_NewCModule(ctx, module_name, js_http_init);
if (!m) {
return NULL;
}
JS_AddModuleExport(ctx, m, "default");
return m;
} }

View File

@@ -193,6 +193,32 @@ JSC_CCALL(os_on,
rt->on_exception = JS_DupValue(js,argv[1]); rt->on_exception = JS_DupValue(js,argv[1]);
) )
JSC_CCALL(os_buffer2string,
if (argc < 1) {
return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer");
}
size_t len;
uint8_t *buf = JS_GetArrayBuffer(js, &len, argv[0]);
if (!buf) {
return JS_ThrowTypeError(js, "First argument must be an ArrayBuffer");
}
// Create a null-terminated string from the buffer
char *str = js_malloc(js, len + 1);
if (!str) {
return JS_ThrowInternalError(js, "Failed to allocate memory");
}
memcpy(str, buf, len);
str[len] = '\0';
JSValue result = JS_NewString(js, str);
js_free(js, str);
return result;
)
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \ #define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\ JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\
@@ -254,6 +280,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, on, 2), MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, rusage, 0), MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0), MIST_FUNC_DEF(os, mallinfo, 0),
MIST_FUNC_DEF(os, buffer2string, 1),
}; };
JSValue js_os_use(JSContext *js) { JSValue js_os_use(JSContext *js) {

4
subprojects/mbedtls.wrap Normal file
View File

@@ -0,0 +1,4 @@
[wrap-git]
url = https://github.com/Mbed-TLS/mbedtls.git
revision = v3.6.3.1
depth = 1

6
tests/https.js Normal file
View File

@@ -0,0 +1,6 @@
var http = use('http')
var os = use('os')
var res = http.fetch("https://dictionary.ink/find?word=palm")
console.log(os.buffer2string(res))