diff --git a/meson.build b/meson.build index 5b4ec879..bcf7296d 100644 --- a/meson.build +++ b/meson.build @@ -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 += [ diff --git a/source/cell.c b/source/cell.c index fd0c273a..b04df5a4 100644 --- a/source/cell.c +++ b/source/cell.c @@ -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; -} diff --git a/source/jsffi.c b/source/jsffi.c index 4c43f6f2..d700d92a 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -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)); diff --git a/source/qjs_socket.c b/source/qjs_socket.c index 5f881e21..6d4a3704 100644 --- a/source/qjs_socket.c +++ b/source/qjs_socket.c @@ -13,104 +13,15 @@ #include #include -// 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)); @@ -708,4 +593,4 @@ JSValue js_socket_use(JSContext *js) { JS_SetPropertyStr(js, mod, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR)); return mod; -} \ No newline at end of file +} diff --git a/tests/http.ce b/tests/http.ce index b21e8ead..93521b09 100644 --- a/tests/http.ce +++ b/tests/http.ce @@ -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") - log.console(b2.length) - var text2 = text(b2) - log.console(text(b2)) - $_.stop() - send(arg[0], {test:'http', result:'pass'}) + 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() } catch (e) { - log.console("dictionary error:", e) + log.console("dictionary error:", e) } \ No newline at end of file diff --git a/tests/httpget.ce b/tests/httpget.ce new file mode 100644 index 00000000..7635cae8 --- /dev/null +++ b/tests/httpget.ce @@ -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()