iopsys-feed/quickjs-websocket/src/lws-client.c
2021-12-01 16:22:58 +01:00

958 lines
30 KiB
C

/*
* 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 <stdint.h>
#include <string.h>
#include <quickjs/quickjs.h>
#include <quickjs/quickjs-libc.h>
#include <libwebsockets.h>
#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;
}