/* * Copyright (c) 2020 Genexis B.V. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #define countof(x) (sizeof(x) / sizeof((x)[0])) #define CDEF(name) JS_PROP_INT32_DEF(#name, name, JS_PROP_CONFIGURABLE) #if LWS_LIBRARY_VERSION_NUMBER < 3002000 #define MAX_WAIT 1000 #else #define MAX_WAIT INT32_MAX #ifndef LWS_WITH_EXTERNAL_POLL #error "LWS_WITH_EXTERNAL_POLL is needed for LWS versions >= 3.2.0" #endif #if LWS_LIBRARY_VERSION_NUMBER < 4001002 #error "External poll is broken for 3.2.0 <= LWS version < 4.1.2" #endif #endif #define WSI_DATA_USE_OBJECT (1 << 0) #define WSI_DATA_USE_LINKED (1 << 1) typedef struct js_lws_wsi_data { struct js_lws_wsi_data *next; struct lws *wsi; JSValue object; JSValue context; JSValue user; uint8_t in_use; } js_lws_wsi_data_t; typedef struct { struct lws_context *context; JSContext *ctx; JSValue callback; js_lws_wsi_data_t *wsi_list; } js_lws_context_data_t; static JSClassID js_lws_context_class_id; static JSClassID js_lws_wsi_class_id; static void free_wsi_data_rt(JSRuntime *rt, js_lws_wsi_data_t *data) { JS_FreeValueRT(rt, data->object); JS_FreeValueRT(rt, data->context); JS_FreeValueRT(rt, data->user); js_free_rt(rt, data); } static void unlink_wsi_rt(JSRuntime *rt, js_lws_context_data_t *data, js_lws_wsi_data_t *wsi_data) { js_lws_wsi_data_t **p; for (p = &data->wsi_list; *p; p = &(*p)->next) { if (*p == wsi_data) { *p = (*p)->next; break; } } wsi_data->next = NULL; wsi_data->wsi = NULL; JS_FreeValueRT(rt, wsi_data->object); wsi_data->object = JS_UNDEFINED; wsi_data->in_use &= ~WSI_DATA_USE_LINKED; if (wsi_data->in_use == 0) free_wsi_data_rt(rt, wsi_data); } static void unlink_wsi(JSContext *ctx, js_lws_context_data_t *data, js_lws_wsi_data_t *wsi_data) { unlink_wsi_rt(JS_GetRuntime(ctx), data, wsi_data); } static JSValue convert_pollargs(JSContext *ctx, const struct lws_pollargs *pa) { JSValue obj; if (pa == NULL) return JS_NULL; obj = JS_NewObject(ctx); if (JS_IsException(obj)) return obj; if (JS_SetPropertyStr(ctx, obj, "fd", JS_NewInt32(ctx, pa->fd)) < 0 || JS_SetPropertyStr(ctx, obj, "events", JS_NewInt32(ctx, pa->events)) < 0 || JS_SetPropertyStr(ctx, obj, "prev_events", JS_NewInt32(ctx, pa->prev_events)) < 0) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } return obj; } static JSValue convert_close_payload(JSContext *ctx, uint8_t *payload, size_t len) { JSValue array; uint16_t status; JSValue reason; if (payload == NULL || len < 2) return JS_NULL; array = JS_NewArray(ctx); if (JS_IsException(array)) return array; status = (payload[0] << 8) | payload[1]; if (JS_SetPropertyUint32(ctx, array, 0, JS_NewInt32(ctx, status)) < 0) { JS_FreeValue(ctx, array); return JS_EXCEPTION; } reason = JS_NewStringLen(ctx, (const char *)(payload + 2), len - 2); if (JS_IsException(reason) || JS_SetPropertyUint32(ctx, array, 1, reason) < 0) { JS_FreeValue(ctx, array); return JS_EXCEPTION; } return array; } static int client_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { js_lws_context_data_t *data = lws_context_user(lws_get_context(wsi)); js_lws_wsi_data_t *wsi_data = lws_wsi_user(wsi); JSContext *ctx; JSValue args[3]; JSValue ret; int32_t ret_int; int i; if (data == NULL || data->ctx == NULL || JS_IsUndefined(data->callback)) return 0; ctx = data->ctx; args[0] = wsi_data ? JS_DupValue(ctx, wsi_data->object) : JS_NULL; args[1] = JS_NewInt32(ctx, reason); args[2] = JS_NULL; switch (reason) { case LWS_CALLBACK_ADD_POLL_FD: case LWS_CALLBACK_DEL_POLL_FD: case LWS_CALLBACK_CHANGE_MODE_POLL_FD: args[2] = convert_pollargs(ctx, in); break; case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: if (in) args[2] = JS_NewStringLen(ctx, in, len); break; case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: args[2] = convert_close_payload(ctx, in, len); break; case LWS_CALLBACK_RECEIVE: case LWS_CALLBACK_CLIENT_RECEIVE: if (in) args[2] = JS_NewArrayBufferCopy(ctx, in, len); break; case LWS_CALLBACK_WSI_DESTROY: if (wsi_data) unlink_wsi(ctx, data, wsi_data); break; default: break; } if (JS_IsException(args[2])) ret = JS_EXCEPTION; else ret = JS_Call(ctx, data->callback, JS_UNDEFINED, countof(args), args); if (JS_IsException(ret) || JS_ToInt32(ctx, &ret_int, ret) < 0) { js_std_dump_error(ctx); ret_int = -1; } JS_FreeValue(ctx, ret); for (i = 0; i < countof(args); i++) { JS_FreeValue(ctx, args[i]); } return ret_int; } static const struct lws_protocols client_protocols[] = { { "lws-client", client_callback, 0, 0, 0, NULL, 0 }, { NULL, NULL, 0, 0, 0, NULL, 0 } }; static JSValue js_decode_utf8(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { size_t size; uint8_t *ptr = JS_GetArrayBuffer(ctx, &size, argv[0]); if (ptr == NULL) return JS_EXCEPTION; return JS_NewStringLen(ctx, (const char *)ptr, size); } static JSValue js_lws_set_log_level(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int32_t level; if (JS_ToInt32(ctx, &level, argv[0]) < 0) return JS_EXCEPTION; lws_set_log_level(level, NULL); return JS_UNDEFINED; } static JSValue js_lws_create_context(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int secure; JSValue obj; js_lws_context_data_t *data; struct lws_context_creation_info info; struct lws_context *context; if (!JS_IsFunction(ctx, argv[0])) return JS_ThrowTypeError(ctx, "not a function"); secure = JS_ToBool(ctx, argv[1]); if (secure < 0) return JS_EXCEPTION; obj = JS_NewObjectClass(ctx, js_lws_context_class_id); if (JS_IsException(obj)) return obj; data = js_mallocz(ctx, sizeof(js_lws_context_data_t)); if (data == NULL) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } data->callback = JS_UNDEFINED; memset(&info, 0, sizeof(info)); info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = client_protocols; info.gid = -1; info.uid = -1; info.options = secure ? LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT : 0; info.user = data; context = lws_create_context(&info); if (context == NULL) { JS_FreeValue(ctx, obj); js_free(ctx, data); return JS_ThrowOutOfMemory(ctx); } data->context = context; data->ctx = JS_DupContext(ctx); data->callback = JS_DupValue(ctx, argv[0]); JS_SetOpaque(obj, data); return obj; } static void js_lws_context_finalizer(JSRuntime *rt, JSValue val) { js_lws_context_data_t *data = JS_GetOpaque(val, js_lws_context_class_id); if (data) { JS_FreeContext(data->ctx); data->ctx = NULL; JS_FreeValueRT(rt, data->callback); data->callback = JS_UNDEFINED; lws_context_destroy(data->context); while (data->wsi_list) { unlink_wsi_rt(rt, data, data->wsi_list); } js_free_rt(rt, data); } } static void js_lws_context_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) { js_lws_context_data_t *data = JS_GetOpaque(val, js_lws_context_class_id); if (data) { js_lws_wsi_data_t *wd; mark_func(rt, (JSGCObjectHeader *)data->ctx); JS_MarkValue(rt, data->callback, mark_func); for (wd = data->wsi_list; wd; wd = wd->next) { JS_MarkValue(rt, wd->object, mark_func); } } } static JSValue js_lws_context_get_connections(JSContext *ctx, JSValueConst this_val) { js_lws_context_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_context_class_id); int32_t connections = 0; js_lws_wsi_data_t *wd; if (data == NULL) return JS_EXCEPTION; for (wd = data->wsi_list; wd; wd = wd->next) { connections++; } return JS_NewInt32(ctx, connections); } static JSValue js_lws_client_connect(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_context_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_context_class_id); const char *address = NULL; int32_t port; int secure; const char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; JSValue obj = JS_UNDEFINED, ret = JS_EXCEPTION; js_lws_wsi_data_t *wsi_data; struct lws_client_connect_info info; if (data == NULL) return JS_EXCEPTION; address = JS_ToCString(ctx, argv[0]); if (address == NULL) goto exception; if (JS_ToInt32(ctx, &port, argv[1]) < 0) goto exception; if (port < 1 || port > 65535) { JS_ThrowRangeError(ctx, "port must be between 1 and 65535"); goto exception; } secure = JS_ToBool(ctx, argv[2]); if (secure < 0) goto exception; path = JS_ToCString(ctx, argv[3]); if (path == NULL) goto exception; host = JS_ToCString(ctx, argv[4]); if (host == NULL) goto exception; if (!JS_IsUndefined(argv[5]) && !JS_IsNull(argv[5])) { origin = JS_ToCString(ctx, argv[5]); if (origin == NULL) goto exception; } if (!JS_IsUndefined(argv[6]) && !JS_IsNull(argv[6])) { protocol = JS_ToCString(ctx, argv[6]); if (protocol == NULL) goto exception; } obj = JS_NewObjectClass(ctx, js_lws_wsi_class_id); if (JS_IsException(obj)) goto exception; wsi_data = js_mallocz(ctx, sizeof(js_lws_wsi_data_t)); if (wsi_data == NULL) goto exception; wsi_data->next = data->wsi_list; wsi_data->object = JS_DupValue(ctx, obj); wsi_data->context = JS_DupValue(ctx, this_val); wsi_data->user = JS_DupValue(ctx, argv[7]); wsi_data->in_use = WSI_DATA_USE_OBJECT | WSI_DATA_USE_LINKED; data->wsi_list = wsi_data; JS_SetOpaque(obj, wsi_data); memset(&info, 0, sizeof(info)); info.context = data->context; info.address = address; info.port = port; info.ssl_connection = secure ? LCCSCF_USE_SSL : 0; info.local_protocol_name = "lws-client"; info.path = path; info.host = host; info.origin = origin; info.protocol = protocol; info.ietf_version_or_minus_one = -1; info.userdata = wsi_data; info.pwsi = &wsi_data->wsi; lws_client_connect_via_info(&info); if (wsi_data->wsi) { ret = JS_DupValue(ctx, obj); } else { unlink_wsi(ctx, data, wsi_data); JS_ThrowReferenceError(ctx, "cannot connect to [%s]:%d (%s)", address, port, secure ? "wss" : "ws"); } exception: JS_FreeCString(ctx, address); JS_FreeCString(ctx, path); JS_FreeCString(ctx, host); JS_FreeCString(ctx, origin); JS_FreeCString(ctx, protocol); JS_FreeValue(ctx, obj); return ret; } static JSValue js_lws_service_fd(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_context_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_context_class_id); int32_t fd, events, revents; struct lws_pollfd pfd; if (data == NULL) return JS_EXCEPTION; if (JS_ToInt32(ctx, &fd, argv[0]) < 0) return JS_EXCEPTION; if (JS_ToInt32(ctx, &events, argv[1]) < 0) return JS_EXCEPTION; if (JS_ToInt32(ctx, &revents, argv[2]) < 0) return JS_EXCEPTION; pfd.fd = fd; pfd.events = events; pfd.revents = revents; lws_service_fd(data->context, &pfd); return JS_NewInt32(ctx, lws_service_adjust_timeout(data->context, MAX_WAIT, 0)); } static JSValue js_lws_service_periodic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_context_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_context_class_id); int timeout; if (data == NULL) return JS_EXCEPTION; #if LWS_LIBRARY_VERSION_NUMBER < 3002000 lws_service_fd(data->context, NULL); #endif timeout = lws_service_adjust_timeout(data->context, MAX_WAIT, 0); if (timeout == 0) { lws_service(data->context, -1); timeout = lws_service_adjust_timeout(data->context, MAX_WAIT, 0); } return JS_NewInt32(ctx, timeout); } static void js_lws_wsi_finalizer(JSRuntime *rt, JSValue val) { js_lws_wsi_data_t *data = JS_GetOpaque(val, js_lws_wsi_class_id); if (data) { JS_FreeValueRT(rt, data->context); data->context = JS_UNDEFINED; JS_FreeValueRT(rt, data->user); data->user = JS_UNDEFINED; data->in_use &= ~WSI_DATA_USE_OBJECT; if (data->in_use == 0) free_wsi_data_rt(rt, data); } } static void js_lws_wsi_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) { js_lws_wsi_data_t *data = JS_GetOpaque(val, js_lws_wsi_class_id); if (data) { JS_MarkValue(rt, data->context, mark_func); JS_MarkValue(rt, data->user, mark_func); } } static JSValue js_lws_wsi_get_context(JSContext *ctx, JSValueConst this_val) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; return JS_DupValue(ctx, data->context); } static JSValue js_lws_wsi_get_user(JSContext *ctx, JSValueConst this_val) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; return JS_DupValue(ctx, data->user); } static JSValue js_lws_wsi_get_hdr(JSContext *ctx, JSValueConst this_val, int magic) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); int len; char *str; JSValue ret; if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); len = lws_hdr_total_length(data->wsi, magic); if (len < 0) return JS_ThrowReferenceError(ctx, "HTTP headers unavailable"); len++; str = js_mallocz(ctx, len); if (str == NULL) return JS_EXCEPTION; len = lws_hdr_copy(data->wsi, str, len, magic); if (len < 0) ret = JS_ThrowReferenceError(ctx, "HTTP headers unavailable"); else ret = JS_NewStringLen(ctx, str, len); js_free(ctx, str); return ret; } static JSValue js_lws_is_final_fragment(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); return JS_NewBool(ctx, lws_is_final_fragment(data->wsi)); } static JSValue js_lws_is_first_fragment(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); return JS_NewBool(ctx, lws_is_first_fragment(data->wsi)); } static JSValue js_lws_frame_is_binary(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); return JS_NewBool(ctx, lws_frame_is_binary(data->wsi)); } static JSValue js_lws_callback_on_writable(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); lws_callback_on_writable(data->wsi); return JS_UNDEFINED; } static JSValue js_lws_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); const char *str = NULL; const uint8_t *ptr; uint8_t *buf; size_t size; enum lws_write_protocol protocol; int ret; if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); if (JS_IsString(argv[0])) { str = JS_ToCStringLen(ctx, &size, argv[0]); if (str == NULL) return JS_EXCEPTION; ptr = (const uint8_t *)str; protocol = LWS_WRITE_TEXT; } else { ptr = JS_GetArrayBuffer(ctx, &size, argv[0]); if (ptr == NULL) return JS_EXCEPTION; protocol = LWS_WRITE_BINARY; } buf = js_malloc(ctx, LWS_PRE + size); if (buf) memcpy(buf + LWS_PRE, ptr, size); if (str) JS_FreeCString(ctx, str); if (buf == NULL) return JS_EXCEPTION; ret = lws_write(data->wsi, buf + LWS_PRE, size, protocol); js_free(ctx, buf); if (ret < 0) return JS_ThrowTypeError(ctx, "WSI not writable"); return JS_UNDEFINED; } static JSValue js_lws_close_reason(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { js_lws_wsi_data_t *data = JS_GetOpaque2(ctx, this_val, js_lws_wsi_class_id); int32_t status; const char *reason = NULL; size_t len = 0; if (data == NULL) return JS_EXCEPTION; if (data->wsi == NULL) return JS_ThrowTypeError(ctx, "defunct WSI"); if (JS_ToInt32(ctx, &status, argv[0]) < 0) return JS_EXCEPTION; if (status < 0 || status > 65535) return JS_ThrowRangeError(ctx, "status must be between 0 and 65535"); if (!JS_IsUndefined(argv[1])) { reason = JS_ToCStringLen(ctx, &len, argv[1]); if (reason == NULL) return JS_EXCEPTION; if (len > 123) { JS_FreeCString(ctx, reason); return JS_ThrowTypeError(ctx, "reason too long (%zu > 123)", len); } } lws_close_reason(data->wsi, status, (uint8_t *)reason, len); if (reason) JS_FreeCString(ctx, reason); return JS_UNDEFINED; } static const JSCFunctionListEntry js_lws_funcs[] = { CDEF(LLL_ERR), CDEF(LLL_WARN), CDEF(LLL_NOTICE), CDEF(LLL_INFO), CDEF(LLL_DEBUG), CDEF(LLL_PARSER), CDEF(LLL_HEADER), CDEF(LLL_EXT), CDEF(LLL_CLIENT), CDEF(LLL_LATENCY), CDEF(LLL_USER), CDEF(LLL_THREAD), CDEF(LLL_COUNT), CDEF(LWS_CALLBACK_PROTOCOL_INIT), CDEF(LWS_CALLBACK_PROTOCOL_DESTROY), CDEF(LWS_CALLBACK_WSI_CREATE), CDEF(LWS_CALLBACK_WSI_DESTROY), CDEF(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS), CDEF(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS), CDEF(LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION), CDEF(LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY), CDEF(LWS_CALLBACK_SSL_INFO), CDEF(LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION), CDEF(LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED), CDEF(LWS_CALLBACK_HTTP), CDEF(LWS_CALLBACK_HTTP_BODY), CDEF(LWS_CALLBACK_HTTP_BODY_COMPLETION), CDEF(LWS_CALLBACK_HTTP_FILE_COMPLETION), CDEF(LWS_CALLBACK_HTTP_WRITEABLE), CDEF(LWS_CALLBACK_CLOSED_HTTP), CDEF(LWS_CALLBACK_FILTER_HTTP_CONNECTION), CDEF(LWS_CALLBACK_ADD_HEADERS), CDEF(LWS_CALLBACK_CHECK_ACCESS_RIGHTS), CDEF(LWS_CALLBACK_PROCESS_HTML), CDEF(LWS_CALLBACK_HTTP_BIND_PROTOCOL), CDEF(LWS_CALLBACK_HTTP_DROP_PROTOCOL), CDEF(LWS_CALLBACK_HTTP_CONFIRM_UPGRADE), CDEF(LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP), CDEF(LWS_CALLBACK_CLOSED_CLIENT_HTTP), CDEF(LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ), CDEF(LWS_CALLBACK_RECEIVE_CLIENT_HTTP), CDEF(LWS_CALLBACK_COMPLETED_CLIENT_HTTP), CDEF(LWS_CALLBACK_CLIENT_HTTP_WRITEABLE), CDEF(LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL), CDEF(LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL), CDEF(LWS_CALLBACK_ESTABLISHED), CDEF(LWS_CALLBACK_CLOSED), CDEF(LWS_CALLBACK_SERVER_WRITEABLE), CDEF(LWS_CALLBACK_RECEIVE), CDEF(LWS_CALLBACK_RECEIVE_PONG), CDEF(LWS_CALLBACK_WS_PEER_INITIATED_CLOSE), CDEF(LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION), CDEF(LWS_CALLBACK_CONFIRM_EXTENSION_OKAY), CDEF(LWS_CALLBACK_WS_SERVER_BIND_PROTOCOL), CDEF(LWS_CALLBACK_WS_SERVER_DROP_PROTOCOL), CDEF(LWS_CALLBACK_CLIENT_CONNECTION_ERROR), CDEF(LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH), CDEF(LWS_CALLBACK_CLIENT_ESTABLISHED), CDEF(LWS_CALLBACK_CLIENT_CLOSED), CDEF(LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER), CDEF(LWS_CALLBACK_CLIENT_RECEIVE), CDEF(LWS_CALLBACK_CLIENT_RECEIVE_PONG), CDEF(LWS_CALLBACK_CLIENT_WRITEABLE), CDEF(LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED), CDEF(LWS_CALLBACK_WS_EXT_DEFAULTS), CDEF(LWS_CALLBACK_FILTER_NETWORK_CONNECTION), CDEF(LWS_CALLBACK_WS_CLIENT_BIND_PROTOCOL), CDEF(LWS_CALLBACK_WS_CLIENT_DROP_PROTOCOL), CDEF(LWS_CALLBACK_GET_THREAD_ID), CDEF(LWS_CALLBACK_ADD_POLL_FD), CDEF(LWS_CALLBACK_DEL_POLL_FD), CDEF(LWS_CALLBACK_CHANGE_MODE_POLL_FD), CDEF(LWS_CALLBACK_LOCK_POLL), CDEF(LWS_CALLBACK_UNLOCK_POLL), CDEF(LWS_CALLBACK_CGI), CDEF(LWS_CALLBACK_CGI_TERMINATED), CDEF(LWS_CALLBACK_CGI_STDIN_DATA), CDEF(LWS_CALLBACK_CGI_STDIN_COMPLETED), CDEF(LWS_CALLBACK_CGI_PROCESS_ATTACH), CDEF(LWS_CALLBACK_SESSION_INFO), CDEF(LWS_CALLBACK_GS_EVENT), CDEF(LWS_CALLBACK_HTTP_PMO), CDEF(LWS_CALLBACK_RAW_RX), CDEF(LWS_CALLBACK_RAW_CLOSE), CDEF(LWS_CALLBACK_RAW_WRITEABLE), CDEF(LWS_CALLBACK_RAW_ADOPT), CDEF(LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL), CDEF(LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL), CDEF(LWS_CALLBACK_RAW_ADOPT_FILE), CDEF(LWS_CALLBACK_RAW_RX_FILE), CDEF(LWS_CALLBACK_RAW_WRITEABLE_FILE), CDEF(LWS_CALLBACK_RAW_CLOSE_FILE), CDEF(LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL), CDEF(LWS_CALLBACK_RAW_FILE_DROP_PROTOCOL), CDEF(LWS_CALLBACK_TIMER), CDEF(LWS_CALLBACK_EVENT_WAIT_CANCELLED), CDEF(LWS_CALLBACK_CHILD_CLOSING), CDEF(LWS_CALLBACK_VHOST_CERT_AGING), CDEF(LWS_CALLBACK_VHOST_CERT_UPDATE), CDEF(LWS_CALLBACK_USER), CDEF(LWS_POLLHUP), CDEF(LWS_POLLIN), CDEF(LWS_POLLOUT), JS_CFUNC_DEF("decode_utf8", 1, js_decode_utf8), JS_CFUNC_DEF("set_log_level", 1, js_lws_set_log_level), JS_CFUNC_DEF("create_context", 2, js_lws_create_context), }; static const JSClassDef js_lws_context_class = { "Context", .finalizer = js_lws_context_finalizer, .gc_mark = js_lws_context_mark, }; static const JSCFunctionListEntry js_lws_context_proto_funcs[] = { JS_CGETSET_DEF("connections", js_lws_context_get_connections, NULL), JS_CFUNC_DEF("client_connect", 8, js_lws_client_connect), JS_CFUNC_DEF("service_fd", 3, js_lws_service_fd), JS_CFUNC_DEF("service_periodic", 0, js_lws_service_periodic), }; static const JSClassDef js_lws_wsi_class = { "WSI", .finalizer = js_lws_wsi_finalizer, .gc_mark = js_lws_wsi_mark, }; #define HDRGET(name) JS_CGETSET_MAGIC_DEF(#name, js_lws_wsi_get_hdr, NULL, name) static const JSCFunctionListEntry js_lws_wsi_proto_funcs[] = { JS_CGETSET_DEF("context", js_lws_wsi_get_context, NULL), JS_CGETSET_DEF("user", js_lws_wsi_get_user, NULL), HDRGET(WSI_TOKEN_GET_URI), HDRGET(WSI_TOKEN_POST_URI), HDRGET(WSI_TOKEN_OPTIONS_URI), HDRGET(WSI_TOKEN_HOST), HDRGET(WSI_TOKEN_CONNECTION), HDRGET(WSI_TOKEN_UPGRADE), HDRGET(WSI_TOKEN_ORIGIN), HDRGET(WSI_TOKEN_DRAFT), HDRGET(WSI_TOKEN_CHALLENGE), HDRGET(WSI_TOKEN_EXTENSIONS), HDRGET(WSI_TOKEN_KEY1), HDRGET(WSI_TOKEN_KEY2), HDRGET(WSI_TOKEN_PROTOCOL), HDRGET(WSI_TOKEN_ACCEPT), HDRGET(WSI_TOKEN_NONCE), HDRGET(WSI_TOKEN_HTTP), HDRGET(WSI_TOKEN_HTTP2_SETTINGS), HDRGET(WSI_TOKEN_HTTP_ACCEPT), HDRGET(WSI_TOKEN_HTTP_AC_REQUEST_HEADERS), HDRGET(WSI_TOKEN_HTTP_IF_MODIFIED_SINCE), HDRGET(WSI_TOKEN_HTTP_IF_NONE_MATCH), HDRGET(WSI_TOKEN_HTTP_ACCEPT_ENCODING), HDRGET(WSI_TOKEN_HTTP_ACCEPT_LANGUAGE), HDRGET(WSI_TOKEN_HTTP_PRAGMA), HDRGET(WSI_TOKEN_HTTP_CACHE_CONTROL), HDRGET(WSI_TOKEN_HTTP_AUTHORIZATION), HDRGET(WSI_TOKEN_HTTP_COOKIE), HDRGET(WSI_TOKEN_HTTP_CONTENT_LENGTH), HDRGET(WSI_TOKEN_HTTP_CONTENT_TYPE), HDRGET(WSI_TOKEN_HTTP_DATE), HDRGET(WSI_TOKEN_HTTP_RANGE), HDRGET(WSI_TOKEN_HTTP_REFERER), HDRGET(WSI_TOKEN_KEY), HDRGET(WSI_TOKEN_VERSION), HDRGET(WSI_TOKEN_SWORIGIN), HDRGET(WSI_TOKEN_HTTP_COLON_AUTHORITY), HDRGET(WSI_TOKEN_HTTP_COLON_METHOD), HDRGET(WSI_TOKEN_HTTP_COLON_PATH), HDRGET(WSI_TOKEN_HTTP_COLON_SCHEME), HDRGET(WSI_TOKEN_HTTP_COLON_STATUS), HDRGET(WSI_TOKEN_HTTP_ACCEPT_CHARSET), HDRGET(WSI_TOKEN_HTTP_ACCEPT_RANGES), HDRGET(WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN), HDRGET(WSI_TOKEN_HTTP_AGE), HDRGET(WSI_TOKEN_HTTP_ALLOW), HDRGET(WSI_TOKEN_HTTP_CONTENT_DISPOSITION), HDRGET(WSI_TOKEN_HTTP_CONTENT_ENCODING), HDRGET(WSI_TOKEN_HTTP_CONTENT_LANGUAGE), HDRGET(WSI_TOKEN_HTTP_CONTENT_LOCATION), HDRGET(WSI_TOKEN_HTTP_CONTENT_RANGE), HDRGET(WSI_TOKEN_HTTP_ETAG), HDRGET(WSI_TOKEN_HTTP_EXPECT), HDRGET(WSI_TOKEN_HTTP_EXPIRES), HDRGET(WSI_TOKEN_HTTP_FROM), HDRGET(WSI_TOKEN_HTTP_IF_MATCH), HDRGET(WSI_TOKEN_HTTP_IF_RANGE), HDRGET(WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE), HDRGET(WSI_TOKEN_HTTP_LAST_MODIFIED), HDRGET(WSI_TOKEN_HTTP_LINK), HDRGET(WSI_TOKEN_HTTP_LOCATION), HDRGET(WSI_TOKEN_HTTP_MAX_FORWARDS), HDRGET(WSI_TOKEN_HTTP_PROXY_AUTHENTICATE), HDRGET(WSI_TOKEN_HTTP_PROXY_AUTHORIZATION), HDRGET(WSI_TOKEN_HTTP_REFRESH), HDRGET(WSI_TOKEN_HTTP_RETRY_AFTER), HDRGET(WSI_TOKEN_HTTP_SERVER), HDRGET(WSI_TOKEN_HTTP_SET_COOKIE), HDRGET(WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY), HDRGET(WSI_TOKEN_HTTP_TRANSFER_ENCODING), HDRGET(WSI_TOKEN_HTTP_USER_AGENT), HDRGET(WSI_TOKEN_HTTP_VARY), HDRGET(WSI_TOKEN_HTTP_VIA), HDRGET(WSI_TOKEN_HTTP_WWW_AUTHENTICATE), HDRGET(WSI_TOKEN_PATCH_URI), HDRGET(WSI_TOKEN_PUT_URI), HDRGET(WSI_TOKEN_DELETE_URI), HDRGET(WSI_TOKEN_HTTP_URI_ARGS), HDRGET(WSI_TOKEN_PROXY), HDRGET(WSI_TOKEN_HTTP_X_REAL_IP), HDRGET(WSI_TOKEN_HTTP1_0), HDRGET(WSI_TOKEN_X_FORWARDED_FOR), HDRGET(WSI_TOKEN_CONNECT), HDRGET(WSI_TOKEN_HEAD_URI), HDRGET(WSI_TOKEN_TE), HDRGET(WSI_TOKEN_REPLAY_NONCE), HDRGET(WSI_TOKEN_COLON_PROTOCOL), HDRGET(WSI_TOKEN_X_AUTH_TOKEN), JS_CFUNC_DEF("is_final_fragment", 0, js_lws_is_final_fragment), JS_CFUNC_DEF("is_first_fragment", 0, js_lws_is_first_fragment), JS_CFUNC_DEF("frame_is_binary", 0, js_lws_frame_is_binary), JS_CFUNC_DEF("callback_on_writable", 0, js_lws_callback_on_writable), JS_CFUNC_DEF("write", 1, js_lws_write), JS_CFUNC_DEF("close_reason", 2, js_lws_close_reason), }; static int js_lws_init(JSContext *ctx, JSModuleDef *m) { JSValue proto; JS_NewClassID(&js_lws_context_class_id); JS_NewClass(JS_GetRuntime(ctx), js_lws_context_class_id, &js_lws_context_class); proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, js_lws_context_proto_funcs, countof(js_lws_context_proto_funcs)); JS_SetClassProto(ctx, js_lws_context_class_id, proto); JS_NewClassID(&js_lws_wsi_class_id); JS_NewClass(JS_GetRuntime(ctx), js_lws_wsi_class_id, &js_lws_wsi_class); proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, js_lws_wsi_proto_funcs, countof(js_lws_wsi_proto_funcs)); JS_SetClassProto(ctx, js_lws_wsi_class_id, proto); return JS_SetModuleExportList(ctx, m, js_lws_funcs, countof(js_lws_funcs)); } JSModuleDef *js_init_module(JSContext *ctx, const char *module_name) { JSModuleDef *m = JS_NewCModule(ctx, module_name, js_lws_init); if (m == NULL) return NULL; JS_AddModuleExportList(ctx, m, js_lws_funcs, countof(js_lws_funcs)); return m; }