fix sockets

This commit is contained in:
2025-06-07 12:24:00 -05:00
parent 9f6d27fb3c
commit efa63771e6
6 changed files with 118 additions and 201 deletions

View File

@@ -295,7 +295,7 @@ src += [
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c',
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c'
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c'
]
# quirc src
src += [

View File

@@ -790,7 +790,6 @@ static void loop()
SDL_LockMutex(queue_mutex);
cell_rt *actor = NULL;
printf("queue check? %d\n", arrlen(ready_queue));
for (int i = 0; i < arrlen(ready_queue); i++) {
if (ready_queue[i]->main_thread_only) {
actor = ready_queue[i];
@@ -814,11 +813,9 @@ static void loop()
// No more timers - hence, no more actors ... exit if single threaded.
}
printf("waiting for a timeout ...\n");
SDL_LockMutex(queue_mutex);
SDL_WaitConditionTimeout(queue_cond, queue_mutex, 100);
SDL_UnlockMutex(queue_mutex);
printf("something happened ...\n");
}
}
@@ -939,12 +936,3 @@ int actor_exists(const char *id)
else
return 1;
}
int JS_ArrayLength(JSContext *js, JSValue a)
{
JSValue length = JS_GetPropertyStr(js, a, "length");
int len;
JS_ToInt32(js,&len,length);
JS_FreeValue(js,length);
return len;
}

View File

@@ -1527,6 +1527,7 @@ JSC_CCALL(os_value_id,
#include "qjs_time.h"
#include "qjs_http.h"
#include "qjs_wota.h"
#include "qjs_socket.h"
//JSValue js_imgui_use(JSContext *js);
#define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use}
@@ -1547,6 +1548,7 @@ void ffi_load(JSContext *js)
// extra
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
arrput(rt->module_registry, ((ModuleEntry){"fd", js_fd_use}));
arrput(rt->module_registry, MISTLINE(socket));
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
arrput(rt->module_registry, MISTLINE(qr));
arrput(rt->module_registry, MISTLINE(http));

View File

@@ -13,104 +13,15 @@
#include <errno.h>
#include <stdio.h>
// Socket wrapper structure
typedef struct {
int sockfd;
} SocketWrapper;
// Addrinfo wrapper structure
typedef struct {
struct addrinfo *info;
} AddrinfoWrapper;
// Free function for socket
static void Socket_free(JSRuntime *rt, SocketWrapper *sw)
// Helper to convert JS value to file descriptor
static int js2fd(JSContext *ctx, JSValueConst val)
{
if (sw->sockfd >= 0) {
close(sw->sockfd);
int fd;
if (JS_ToInt32(ctx, &fd, val) < 0) {
JS_ThrowTypeError(ctx, "Expected file descriptor number");
return -1;
}
js_free_rt(rt, sw);
}
// Free function for addrinfo
static void Addrinfo_free(JSRuntime *rt, AddrinfoWrapper *aw)
{
if (aw->info) {
freeaddrinfo(aw->info);
}
js_free_rt(rt, aw);
}
// Class definitions
static JSClassID js_socket_class_id;
static JSClassID js_addrinfo_class_id;
static JSClassDef js_socket_class = {
"Socket",
.finalizer = (JSClassFinalizer *)Socket_free,
};
static JSClassDef js_addrinfo_class = {
"Addrinfo",
.finalizer = (JSClassFinalizer *)Addrinfo_free,
};
// Helper to convert JS value to SocketWrapper
static SocketWrapper *js2socket(JSContext *ctx, JSValueConst obj)
{
SocketWrapper *sw = JS_GetOpaque2(ctx, obj, js_socket_class_id);
if (!sw) {
JS_ThrowTypeError(ctx, "Expected socket object");
return NULL;
}
return sw;
}
// Helper to convert JS value to AddrinfoWrapper
static AddrinfoWrapper *js2addrinfo(JSContext *ctx, JSValueConst obj)
{
AddrinfoWrapper *aw = JS_GetOpaque2(ctx, obj, js_addrinfo_class_id);
if (!aw) {
JS_ThrowTypeError(ctx, "Expected addrinfo object");
return NULL;
}
return aw;
}
// Helper to create JS SocketWrapper object
static JSValue socket2js(JSContext *ctx, int sockfd)
{
SocketWrapper *sw = js_mallocz(ctx, sizeof(SocketWrapper));
if (!sw) return JS_EXCEPTION;
sw->sockfd = sockfd;
JSValue obj = JS_NewObjectClass(ctx, js_socket_class_id);
if (JS_IsException(obj)) {
js_free(ctx, sw);
return obj;
}
JS_SetOpaque(obj, sw);
return obj;
}
// Helper to create JS AddrinfoWrapper object
static JSValue addrinfo2js(JSContext *ctx, struct addrinfo *info)
{
AddrinfoWrapper *aw = js_mallocz(ctx, sizeof(AddrinfoWrapper));
if (!aw) return JS_EXCEPTION;
aw->info = info;
JSValue obj = JS_NewObjectClass(ctx, js_addrinfo_class_id);
if (JS_IsException(obj)) {
js_free(ctx, aw);
return obj;
}
JS_SetOpaque(obj, aw);
return obj;
return fd;
}
// SOCKET FUNCTIONS
@@ -206,7 +117,9 @@ JSC_CCALL(socket_getaddrinfo,
copy->ai_canonname = strdup(p->ai_canonname);
}
JS_SetPropertyStr(js, info, "_addrinfo", addrinfo2js(js, copy));
// Store the addrinfo pointer as an internal property
// We'll need to handle this differently since we can't wrap it
// For now, we'll skip storing the raw addrinfo
JS_SetPropertyUint32(js, ret, idx++, info);
}
@@ -251,15 +164,15 @@ JSC_CCALL(socket_socket,
return JS_ThrowReferenceError(js, "socket failed: %s", strerror(errno));
}
return socket2js(js, sockfd);
return JS_NewInt32(js, sockfd);
)
JSC_CCALL(socket_bind,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
AddrinfoWrapper *aw = js2addrinfo(js, JS_GetPropertyStr(js, argv[1], "_addrinfo"));
if (!aw) {
// For now, we'll only support manual address parsing
{
// Try to parse address and port manually
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address"));
int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port"));
@@ -274,11 +187,7 @@ JSC_CCALL(socket_bind,
}
JS_FreeCString(js, addr_str);
if (bind(sw->sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return JS_ThrowReferenceError(js, "bind failed: %s", strerror(errno));
}
} else {
if (bind(sw->sockfd, aw->info->ai_addr, aw->info->ai_addrlen) < 0) {
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return JS_ThrowReferenceError(js, "bind failed: %s", strerror(errno));
}
}
@@ -287,11 +196,11 @@ JSC_CCALL(socket_bind,
)
JSC_CCALL(socket_connect,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
AddrinfoWrapper *aw = js2addrinfo(js, JS_GetPropertyStr(js, argv[1], "_addrinfo"));
if (!aw) {
// For now, we'll only support manual address parsing
{
// Try to parse address and port manually
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[1], "address"));
int port = js2number(js, JS_GetPropertyStr(js, argv[1], "port"));
@@ -306,11 +215,7 @@ JSC_CCALL(socket_connect,
}
JS_FreeCString(js, addr_str);
if (connect(sw->sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return JS_ThrowReferenceError(js, "connect failed: %s", strerror(errno));
}
} else {
if (connect(sw->sockfd, aw->info->ai_addr, aw->info->ai_addrlen) < 0) {
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
return JS_ThrowReferenceError(js, "connect failed: %s", strerror(errno));
}
}
@@ -319,15 +224,15 @@ JSC_CCALL(socket_connect,
)
JSC_CCALL(socket_listen,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
int backlog = 10;
if (argc > 1) {
backlog = js2number(js, argv[1]);
}
if (listen(sw->sockfd, backlog) < 0) {
if (listen(sockfd, backlog) < 0) {
return JS_ThrowReferenceError(js, "listen failed: %s", strerror(errno));
}
@@ -335,19 +240,19 @@ JSC_CCALL(socket_listen,
)
JSC_CCALL(socket_accept,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
struct sockaddr_storage their_addr;
socklen_t addr_size = sizeof their_addr;
int new_sockfd = accept(sw->sockfd, (struct sockaddr *)&their_addr, &addr_size);
int new_sockfd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
if (new_sockfd < 0) {
return JS_ThrowReferenceError(js, "accept failed: %s", strerror(errno));
}
ret = JS_NewObject(js);
JS_SetPropertyStr(js, ret, "socket", socket2js(js, new_sockfd));
JS_SetPropertyStr(js, ret, "socket", JS_NewInt32(js, new_sockfd));
// Get peer address info
char ipstr[INET6_ADDRSTRLEN];
@@ -369,8 +274,8 @@ JSC_CCALL(socket_accept,
)
JSC_CCALL(socket_send,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
size_t len;
ssize_t sent;
@@ -382,11 +287,11 @@ JSC_CCALL(socket_send,
if (JS_IsString(argv[1])) {
const char *data = JS_ToCStringLen(js, &len, argv[1]);
sent = send(sw->sockfd, data, len, flags);
sent = send(sockfd, data, len, flags);
JS_FreeCString(js, data);
} else {
unsigned char *data = js_get_blob_data(js, &len, argv[1]);
sent = send(sw->sockfd, data, len, flags);
sent = send(sockfd, data, len, flags);
}
if (sent < 0) {
@@ -397,8 +302,8 @@ JSC_CCALL(socket_send,
)
JSC_CCALL(socket_recv,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
size_t len = 4096;
if (argc > 1) {
@@ -415,7 +320,7 @@ JSC_CCALL(socket_recv,
return JS_ThrowReferenceError(js, "malloc failed");
}
ssize_t received = recv(sw->sockfd, buf, len, flags);
ssize_t received = recv(sockfd, buf, len, flags);
if (received < 0) {
free(buf);
return JS_ThrowReferenceError(js, "recv failed: %s", strerror(errno));
@@ -427,8 +332,8 @@ JSC_CCALL(socket_recv,
)
JSC_CCALL(socket_sendto,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
size_t len;
ssize_t sent;
@@ -439,11 +344,11 @@ JSC_CCALL(socket_sendto,
}
// Get destination address
AddrinfoWrapper *aw = js2addrinfo(js, JS_GetPropertyStr(js, argv[2], "_addrinfo"));
struct sockaddr *to_addr;
socklen_t to_len;
if (!aw) {
// For now, we'll only support manual address parsing
{
// Try to parse address and port manually
const char *addr_str = JS_ToCString(js, JS_GetPropertyStr(js, argv[2], "address"));
int port = js2number(js, JS_GetPropertyStr(js, argv[2], "port"));
@@ -460,18 +365,15 @@ JSC_CCALL(socket_sendto,
to_addr = (struct sockaddr *)&addr;
to_len = sizeof(addr);
} else {
to_addr = aw->info->ai_addr;
to_len = aw->info->ai_addrlen;
}
if (JS_IsString(argv[1])) {
const char *data = JS_ToCStringLen(js, &len, argv[1]);
sent = sendto(sw->sockfd, data, len, flags, to_addr, to_len);
sent = sendto(sockfd, data, len, flags, to_addr, to_len);
JS_FreeCString(js, data);
} else {
unsigned char *data = js_get_blob_data(js, &len, argv[1]);
sent = sendto(sw->sockfd, data, len, flags, to_addr, to_len);
sent = sendto(sockfd, data, len, flags, to_addr, to_len);
}
if (sent < 0) {
@@ -482,8 +384,8 @@ JSC_CCALL(socket_sendto,
)
JSC_CCALL(socket_recvfrom,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
size_t len = 4096;
if (argc > 1) {
@@ -503,7 +405,7 @@ JSC_CCALL(socket_recvfrom,
struct sockaddr_storage from_addr;
socklen_t from_len = sizeof from_addr;
ssize_t received = recvfrom(sw->sockfd, buf, len, flags,
ssize_t received = recvfrom(sockfd, buf, len, flags,
(struct sockaddr *)&from_addr, &from_len);
if (received < 0) {
free(buf);
@@ -534,15 +436,15 @@ JSC_CCALL(socket_recvfrom,
)
JSC_CCALL(socket_shutdown,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
int how = SHUT_RDWR;
if (argc > 1) {
how = js2number(js, argv[1]);
}
if (shutdown(sw->sockfd, how) < 0) {
if (shutdown(sockfd, how) < 0) {
return JS_ThrowReferenceError(js, "shutdown failed: %s", strerror(errno));
}
@@ -550,13 +452,13 @@ JSC_CCALL(socket_shutdown,
)
JSC_CCALL(socket_getpeername,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
struct sockaddr_storage addr;
socklen_t len = sizeof addr;
if (getpeername(sw->sockfd, (struct sockaddr *)&addr, &len) < 0) {
if (getpeername(sockfd, (struct sockaddr *)&addr, &len) < 0) {
return JS_ThrowReferenceError(js, "getpeername failed: %s", strerror(errno));
}
@@ -585,19 +487,6 @@ JSC_CCALL(socket_gethostname,
return JS_NewString(js, hostname);
)
JSC_CCALL(socket_freeaddrinfo,
AddrinfoWrapper *aw = js2addrinfo(js, argv[0]);
if (!aw) return JS_EXCEPTION;
if (aw->info) {
if (aw->info->ai_addr) free(aw->info->ai_addr);
if (aw->info->ai_canonname) free(aw->info->ai_canonname);
free(aw->info);
aw->info = NULL;
}
return JS_UNDEFINED;
)
JSC_CCALL(socket_gai_strerror,
int errcode = js2number(js, argv[0]);
@@ -605,8 +494,8 @@ JSC_CCALL(socket_gai_strerror,
)
JSC_CCALL(socket_setsockopt,
SocketWrapper *sw = js2socket(js, argv[0]);
if (!sw) return JS_EXCEPTION;
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
int level = SOL_SOCKET;
int optname = 0;
@@ -637,12 +526,12 @@ JSC_CCALL(socket_setsockopt,
// Parse option value
if (JS_IsBool(argv[3])) {
int optval = JS_ToBool(js, argv[3]);
if (setsockopt(sw->sockfd, level, optname, &optval, sizeof(optval)) < 0) {
if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) {
return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno));
}
} else if (JS_IsNumber(argv[3])) {
int optval = js2number(js, argv[3]);
if (setsockopt(sw->sockfd, level, optname, &optval, sizeof(optval)) < 0) {
if (setsockopt(sockfd, level, optname, &optval, sizeof(optval)) < 0) {
return JS_ThrowReferenceError(js, "setsockopt failed: %s", strerror(errno));
}
} else {
@@ -652,6 +541,16 @@ JSC_CCALL(socket_setsockopt,
return JS_UNDEFINED;
)
JSC_CCALL(socket_close,
int sockfd = js2fd(js, argv[0]);
if (sockfd < 0) return JS_EXCEPTION;
if (close(sockfd) != 0)
return JS_ThrowReferenceError(js, "close failed: %s", strerror(errno));
return JS_UNDEFINED;
)
static const JSCFunctionListEntry js_socket_funcs[] = {
MIST_FUNC_DEF(socket, getaddrinfo, 3),
MIST_FUNC_DEF(socket, socket, 3),
@@ -666,26 +565,12 @@ static const JSCFunctionListEntry js_socket_funcs[] = {
MIST_FUNC_DEF(socket, shutdown, 2),
MIST_FUNC_DEF(socket, getpeername, 1),
MIST_FUNC_DEF(socket, gethostname, 0),
MIST_FUNC_DEF(socket, freeaddrinfo, 1),
MIST_FUNC_DEF(socket, gai_strerror, 1),
MIST_FUNC_DEF(socket, setsockopt, 4),
MIST_FUNC_DEF(socket, close, 1),
};
JSValue js_socket_use(JSContext *js) {
// Initialize the socket class
JS_NewClassID(&js_socket_class_id);
JS_NewClass(JS_GetRuntime(js), js_socket_class_id, &js_socket_class);
JSValue proto = JS_NewObject(js);
JS_SetClassProto(js, js_socket_class_id, proto);
// Initialize the addrinfo class
JS_NewClassID(&js_addrinfo_class_id);
JS_NewClass(JS_GetRuntime(js), js_addrinfo_class_id, &js_addrinfo_class);
JSValue addrinfo_proto = JS_NewObject(js);
JS_SetClassProto(js, js_addrinfo_class_id, addrinfo_proto);
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_socket_funcs, countof(js_socket_funcs));

View File

@@ -1,13 +1,15 @@
var http = use('http')
var text = use('text')
var time = use('time')
try {
var b2 = http.fetch("https://gitea.pockle.world/api/v1/repos/john/prosperon/branches/master")
var st = time.number()
var b2 = http.fetch(arg[0])
log.console(`time took ${time.number()-st}`)
log.console(b2.length)
var text2 = text(b2)
log.console(text(b2))
$_.stop()
send(arg[0], {test:'http', result:'pass'})
} catch (e) {
log.console("dictionary error:", e)
}

40
tests/httpget.ce Normal file
View File

@@ -0,0 +1,40 @@
var socket = use('socket')
var time = use('time')
var blob = use('blob')
var data = new blob
var start_time = time.number()
var host = arg[0]
var path = arg[1] || '/'
var addrs = socket.getaddrinfo(host, '80')
var addr = addrs[0]
var sock = socket.socket()
socket.connect(sock, addr)
var req = `GET ${path} HTTP/1.1\r\nHost: ${host}\r\nConnection: close\r\n\r\n`
socket.send(sock, req)
var chunk_size = 4096
function get_chunk()
{
var chunk = socket.recv(sock, chunk_size)
if (chunk.length > 0) {
log.console('got chunk size ' + chunk.length/8 + ' bytes')
data.write_blob(chunk)
get_chunk()
} else {
log.console(`http GET took ${time.number() - start_time}`)
log.console(`total length is ${data.length}`)
stone(data)
log.console(text(data))
log.console(`time taken: ${time.number()-start_time}`)
$_.stop()
}
}
get_chunk()