From 30bb30b20f65a7112065b18b8da5aee82a76307f Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Wed, 13 Mar 2024 22:25:28 -0700 Subject: [PATCH] [wasm64] Fix library_websocket.js Update `test_websocket_send.c` such that it checks that both text and binary messages are received. Also, delete `websocket.c` which I think was supposed to be removed as part of #7672 (where it was renamed to test_websocket_send.c). Fixes: #21515 --- src/generated_struct_info32.json | 16 +++ src/generated_struct_info64.json | 16 +++ src/library_websocket.js | 55 ++++---- src/struct_info.json | 18 +++ test/websocket/test_websocket_send.c | 196 +++++++++++++-------------- test/websocket/websocket.c | 72 ---------- 6 files changed, 170 insertions(+), 203 deletions(-) delete mode 100644 test/websocket/websocket.c diff --git a/src/generated_struct_info32.json b/src/generated_struct_info32.json index 14e1132df998..7eb3d9def4d1 100644 --- a/src/generated_struct_info32.json +++ b/src/generated_struct_info32.json @@ -627,6 +627,22 @@ "renderViaOffscreenBackBuffer": 52, "stencil": 8 }, + "EmscriptenWebSocketCloseEvent": { + "__size__": 524, + "code": 8, + "reason": 10, + "wasClean": 4 + }, + "EmscriptenWebSocketCreateAttributes": { + "__size__": 12, + "protocols": 4 + }, + "EmscriptenWebSocketMessageEvent": { + "__size__": 16, + "data": 4, + "isText": 12, + "numBytes": 8 + }, "EmscriptenWheelEvent": { "__size__": 104, "deltaMode": 96, diff --git a/src/generated_struct_info64.json b/src/generated_struct_info64.json index e6e6f133a358..b61bed8e15bf 100644 --- a/src/generated_struct_info64.json +++ b/src/generated_struct_info64.json @@ -627,6 +627,22 @@ "renderViaOffscreenBackBuffer": 52, "stencil": 8 }, + "EmscriptenWebSocketCloseEvent": { + "__size__": 524, + "code": 8, + "reason": 10, + "wasClean": 4 + }, + "EmscriptenWebSocketCreateAttributes": { + "__size__": 24, + "protocols": 8 + }, + "EmscriptenWebSocketMessageEvent": { + "__size__": 24, + "data": 8, + "isText": 20, + "numBytes": 16 + }, "EmscriptenWheelEvent": { "__size__": 104, "deltaMode": 96, diff --git a/src/library_websocket.js b/src/library_websocket.js index be2edcd3f8e6..f481862a5e49 100644 --- a/src/library_websocket.js +++ b/src/library_websocket.js @@ -7,7 +7,14 @@ var LibraryWebSocket = { $WS: { sockets: [null], - socketEvent: null + socketEvent: null, + getSocketEvent(socketId) { + // Singleton event pointer. Use EmscriptenWebSocketCloseEvent, which is + // the largest event struct + this.socketEvent ||= _malloc({{{ C_STRUCTS.EmscriptenWebSocketCloseEvent.__size__ }}}); + {{{ makeSetValue('this.socketEvent', 0, 'socketId', 'u32') }}}; + return this.socketEvent; + }, }, emscripten_websocket_get_ready_state__deps: ['$WS'], @@ -136,9 +143,7 @@ var LibraryWebSocket = { // TODO: // if (thread == {{{ cDefs.EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD }}} || // (thread == _pthread_self()) return emscripten_websocket_set_onopen_callback_on_calling_thread(socketId, userData, callbackFunc); - - WS.socketEvent ||= _malloc(1024); // TODO: sizeof(EmscriptenWebSocketCloseEvent), which is the largest event struct - + var eventPtr = WS.getSocketEvent(socketId); var socket = WS.sockets[socketId]; if (!socket) { #if WEBSOCKET_DEBUG @@ -154,8 +159,7 @@ var LibraryWebSocket = { #if WEBSOCKET_DEBUG dbg(`websocket event "open": socketId=${socketId},userData=${userData},callbackFunc=${callbackFunc})`); #endif - HEAPU32[WS.socketEvent>>2] = socketId; - {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, WS.socketEvent, userData); + {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, eventPtr, userData); } return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, @@ -163,8 +167,7 @@ var LibraryWebSocket = { emscripten_websocket_set_onerror_callback_on_thread__deps: ['$WS'], emscripten_websocket_set_onerror_callback_on_thread__proxy: 'sync', emscripten_websocket_set_onerror_callback_on_thread: (socketId, userData, callbackFunc, thread) => { - WS.socketEvent ||= _malloc(1024); // TODO: sizeof(EmscriptenWebSocketCloseEvent), which is the largest event struct - + var eventPtr = WS.getSocketEvent(socketId); var socket = WS.sockets[socketId]; if (!socket) { #if WEBSOCKET_DEBUG @@ -180,8 +183,7 @@ var LibraryWebSocket = { #if WEBSOCKET_DEBUG dbg(`websocket event "error": socketId=${socketId},userData=${userData},callbackFunc=${callbackFunc})`); #endif - HEAPU32[WS.socketEvent>>2] = socketId; - {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, WS.socketEvent, userData); + {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, eventPtr, userData); } return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, @@ -189,8 +191,7 @@ var LibraryWebSocket = { emscripten_websocket_set_onclose_callback_on_thread__deps: ['$WS', '$stringToUTF8'], emscripten_websocket_set_onclose_callback_on_thread__proxy: 'sync', emscripten_websocket_set_onclose_callback_on_thread: (socketId, userData, callbackFunc, thread) => { - WS.socketEvent ||= _malloc(1024); // TODO: sizeof(EmscriptenWebSocketCloseEvent), which is the largest event struct - + var eventPtr = WS.getSocketEvent(socketId); var socket = WS.sockets[socketId]; if (!socket) { #if WEBSOCKET_DEBUG @@ -206,11 +207,10 @@ var LibraryWebSocket = { #if WEBSOCKET_DEBUG dbg(`websocket event "close": socketId=${socketId},userData=${userData},callbackFunc=${callbackFunc})`); #endif - HEAPU32[WS.socketEvent>>2] = socketId; - HEAPU32[(WS.socketEvent+4)>>2] = e.wasClean; - HEAPU32[(WS.socketEvent+8)>>2] = e.code; - stringToUTF8(e.reason, WS.socketEvent+10, 512); - {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, WS.socketEvent, userData); + {{{ makeSetValue('eventPtr', C_STRUCTS.EmscriptenWebSocketCloseEvent.wasClean, 'e.wasClean', 'i32') }}}, + {{{ makeSetValue('eventPtr', C_STRUCTS.EmscriptenWebSocketCloseEvent.wasClean, 'e.code', 'i16') }}}, + stringToUTF8(e.reason, eventPtr + {{{ C_STRUCTS.EmscriptenWebSocketCloseEvent.reason }}}, 512); + {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, eventPtr, userData); } return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; }, @@ -218,8 +218,7 @@ var LibraryWebSocket = { emscripten_websocket_set_onmessage_callback_on_thread__deps: ['$WS', '$stringToNewUTF8', 'malloc', 'free'], emscripten_websocket_set_onmessage_callback_on_thread__proxy: 'sync', emscripten_websocket_set_onmessage_callback_on_thread: (socketId, userData, callbackFunc, thread) => { - WS.socketEvent ||= _malloc(1024); // TODO: sizeof(EmscriptenWebSocketCloseEvent), which is the largest event struct - + var eventPtr = WS.getSocketEvent(socketId); var socket = WS.sockets[socketId]; if (!socket) { #if WEBSOCKET_DEBUG @@ -235,15 +234,14 @@ var LibraryWebSocket = { #if WEBSOCKET_DEBUG == 2 dbg(`websocket event "message": socketId=${socketId},userData=${userData},callbackFunc=${callbackFunc})`); #endif - HEAPU32[WS.socketEvent>>2] = socketId; - if (typeof e.data == 'string') { + var isText = typeof e.data == 'string'; + if (isText) { var buf = stringToNewUTF8(e.data); var len = lengthBytesUTF8(e.data)+1; #if WEBSOCKET_DEBUG var s = (e.data.length < 256) ? e.data : (e.data.substr(0, 256) + ` (${e.data.length-256} more characters)`); dbg(`WebSocket onmessage, received data: "${e.data}", ${e.data.length} chars, ${len} bytes encoded as UTF-8: "${s}"`); #endif - HEAPU32[(WS.socketEvent+12)>>2] = 1; // text data } else { var len = e.data.byteLength; var buf = _malloc(len); @@ -258,11 +256,11 @@ var LibraryWebSocket = { dbg(s); #endif - HEAPU32[(WS.socketEvent+12)>>2] = 0; // binary data } - HEAPU32[(WS.socketEvent+4)>>2] = buf; - HEAPU32[(WS.socketEvent+8)>>2] = len; - {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, WS.socketEvent, userData); + {{{ makeSetValue('eventPtr', C_STRUCTS.EmscriptenWebSocketMessageEvent.data, 'buf', '*') }}}, + {{{ makeSetValue('eventPtr', C_STRUCTS.EmscriptenWebSocketMessageEvent.numBytes, 'len', 'i32') }}}, + {{{ makeSetValue('eventPtr', C_STRUCTS.EmscriptenWebSocketMessageEvent.isText, 'isText', 'i32') }}}, + {{{ makeDynCall('iipp', 'callbackFunc') }}}(0/*TODO*/, eventPtr, userData); _free(buf); } return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}}; @@ -284,9 +282,8 @@ var LibraryWebSocket = { return {{{ cDefs.EMSCRIPTEN_RESULT_INVALID_PARAM }}}; } - var createAttrs = createAttributes>>2; - var url = UTF8ToString(HEAP32[createAttrs]); - var protocols = HEAP32[createAttrs+1]; + var url = UTF8ToString({{{ makeGetValue('createAttributes', 0, '*') }}}); + var protocols = {{{ makeGetValue('createAttributes', C_STRUCTS.EmscriptenWebSocketCreateAttributes.protocols, '*') }}} // TODO: Add support for createOnMainThread==false; currently all WebSocket connections are created on the main thread. // var createOnMainThread = HEAP32[createAttrs+2]; diff --git a/src/struct_info.json b/src/struct_info.json index 5262b797e93e..a01e76abb42a 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1144,6 +1144,24 @@ ] } }, + { + "file": "emscripten/websocket.h", + "structs": { + "EmscriptenWebSocketCloseEvent": [ + "wasClean", + "code", + "reason" + ], + "EmscriptenWebSocketMessageEvent": [ + "data", + "numBytes", + "isText" + ], + "EmscriptenWebSocketCreateAttributes": [ + "protocols" + ] + } + }, { "file": "AL/al.h", "defines": [ diff --git a/test/websocket/test_websocket_send.c b/test/websocket/test_websocket_send.c index 35a5623895ab..35bb9f11d875 100644 --- a/test/websocket/test_websocket_send.c +++ b/test/websocket/test_websocket_send.c @@ -3,117 +3,109 @@ #include #include -EM_BOOL WebSocketOpen(int eventType, const EmscriptenWebSocketOpenEvent *e, void *userData) -{ - printf("open(eventType=%d, userData=%ld)\n", eventType, (long)userData); +EM_BOOL WebSocketOpen(int eventType, const EmscriptenWebSocketOpenEvent *e, void *userData) { + printf("open(eventType=%d, userData=%p)\n", eventType, userData); - emscripten_websocket_send_utf8_text(e->socket, "hello on the other side"); + emscripten_websocket_send_utf8_text(e->socket, "hello on the other side"); - char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - emscripten_websocket_send_binary(e->socket, data, sizeof(data)); + char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + emscripten_websocket_send_binary(e->socket, data, sizeof(data)); - return 0; + return 0; } -EM_BOOL WebSocketClose(int eventType, const EmscriptenWebSocketCloseEvent *e, void *userData) -{ - printf("close(eventType=%d, wasClean=%d, code=%d, reason=%s, userData=%ld)\n", eventType, e->wasClean, e->code, e->reason, (long)userData); - return 0; +EM_BOOL WebSocketClose(int eventType, const EmscriptenWebSocketCloseEvent *e, void *userData) { + printf("close(eventType=%d, wasClean=%d, code=%d, reason=%s, userData=%p)\n", eventType, e->wasClean, e->code, e->reason, userData); + return 0; } -EM_BOOL WebSocketError(int eventType, const EmscriptenWebSocketErrorEvent *e, void *userData) -{ - printf("error(eventType=%d, userData=%ld)\n", eventType, (long)userData); - return 0; +EM_BOOL WebSocketError(int eventType, const EmscriptenWebSocketErrorEvent *e, void *userData) { + printf("error(eventType=%d, userData=%p)\n", eventType, userData); + return 0; } -static int passed = 0; - -EM_BOOL WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, void *userData) -{ - printf("message(eventType=%d, userData=%ld, data=%p, numBytes=%d, isText=%d)\n", eventType, (long)userData, e->data, e->numBytes, e->isText); - if (e->isText) - { - printf("text data: \"%s\"\n", e->data); - assert(strcmp((const char*)e->data, "hello on the other side") == 0); - } - else - { - printf("binary data:"); - for(int i = 0; i < e->numBytes; ++i) - { - printf(" %02X", e->data[i]); - assert(e->data[i] == i); - } - printf("\n"); - - emscripten_websocket_close(e->socket, 0, 0); - emscripten_websocket_delete(e->socket); - emscripten_force_exit(0); - } - return 0; +EM_BOOL WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, void *userData) { + printf("message(eventType=%d, userData=%p data=%p, numBytes=%d, isText=%d)\n", eventType, userData, e->data, e->numBytes, e->isText); + static int text_received = 0; + if (e->isText) { + printf("text data: \"%s\"\n", e->data); + assert(strcmp((const char*)e->data, "hello on the other side") == 0); + text_received = 1; + return 0; + } + + // We expect to receive the text message beofre the binary one + assert(text_received); + printf("binary data:"); + for (int i = 0; i < e->numBytes; ++i) { + printf(" %02X", e->data[i]); + assert(e->data[i] == i); + } + printf("\n"); + + emscripten_websocket_close(e->socket, 0, 0); + emscripten_websocket_delete(e->socket); + emscripten_force_exit(0); + return 0; } -int main() -{ - if (!emscripten_websocket_is_supported()) - { - printf("WebSockets are not supported, cannot continue!\n"); - exit(1); - } - - EmscriptenWebSocketCreateAttributes attr; - emscripten_websocket_init_create_attributes(&attr); - - const char *url = "ws://localhost:8089/"; - attr.url = url; - attr.protocols = "binary,base64"; // We don't really use a special protocol on the server backend in this test, but check that it can be passed. - - EMSCRIPTEN_WEBSOCKET_T socket = emscripten_websocket_new(&attr); - if (socket <= 0) - { - printf("WebSocket creation failed, error code %d!\n", (EMSCRIPTEN_RESULT)socket); - exit(1); - } - - // URL: - int urlLength = 0; - EMSCRIPTEN_RESULT res = emscripten_websocket_get_url_length(socket, &urlLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - assert(urlLength == strlen(url)+1); - - char *url2 = malloc(urlLength); - res = emscripten_websocket_get_url(socket, url2, urlLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - printf("url: %s, verified: %s, length: %d\n", url, url2, urlLength); - assert(!strcmp(url, url2)); - - // Protocol: - int protocolLength = 0; - res = emscripten_websocket_get_protocol_length(socket, &protocolLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - assert(protocolLength == 1); // Null byte - - char *protocol = malloc(protocolLength); - res = emscripten_websocket_get_protocol(socket, protocol, protocolLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - assert(!strcmp(protocol, "")); // We don't really use a special protocol on the server backend in this test, but test that it comes out as an empty string at least. - - // Extensions: - int extensionsLength = 0; - res = emscripten_websocket_get_extensions_length(socket, &extensionsLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - assert(extensionsLength == 1); // Null byte - - char *extensions = malloc(extensionsLength); - res = emscripten_websocket_get_extensions(socket, extensions, extensionsLength); - assert(res == EMSCRIPTEN_RESULT_SUCCESS); - assert(!strcmp(extensions, "")); // We don't really use any extensions on the server backend in this test, but test that it comes out as an empty string at least. - - emscripten_websocket_set_onopen_callback(socket, (void*)42, WebSocketOpen); - emscripten_websocket_set_onclose_callback(socket, (void*)43, WebSocketClose); - emscripten_websocket_set_onerror_callback(socket, (void*)44, WebSocketError); - emscripten_websocket_set_onmessage_callback(socket, (void*)45, WebSocketMessage); - emscripten_exit_with_live_runtime(); - return 0; +int main() { + assert(emscripten_websocket_is_supported()); + + EmscriptenWebSocketCreateAttributes attr; + emscripten_websocket_init_create_attributes(&attr); + + const char *url = "ws://localhost:8089/"; + attr.url = url; + // We don't really use a special protocol on the server backend in this test, + // but check that it can be passed. + attr.protocols = "binary,base64"; + + EMSCRIPTEN_WEBSOCKET_T socket = emscripten_websocket_new(&attr); + assert(socket >= 0); + + // URL: + int urlLength = 0; + EMSCRIPTEN_RESULT res = emscripten_websocket_get_url_length(socket, &urlLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(urlLength == strlen(url)+1); + + char *url2 = malloc(urlLength); + res = emscripten_websocket_get_url(socket, url2, urlLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + printf("url: %s, verified: %s, length: %d\n", url, url2, urlLength); + assert(!strcmp(url, url2)); + + // Protocol: + int protocolLength = 0; + res = emscripten_websocket_get_protocol_length(socket, &protocolLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(protocolLength == 1); // Null byte + + char *protocol = malloc(protocolLength); + res = emscripten_websocket_get_protocol(socket, protocol, protocolLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + // We don't really use a special protocol on the server backend in this test, + // but test that it comes out as an empty string at least. + assert(!strcmp(protocol, "")); + + // Extensions: + int extensionsLength = 0; + res = emscripten_websocket_get_extensions_length(socket, &extensionsLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + assert(extensionsLength == 1); // Null byte + + char *extensions = malloc(extensionsLength); + res = emscripten_websocket_get_extensions(socket, extensions, extensionsLength); + assert(res == EMSCRIPTEN_RESULT_SUCCESS); + // We don't really use any extensions on the server backend in this test, but + // test that it comes out as an empty string at least. + assert(!strcmp(extensions, "")); + + emscripten_websocket_set_onopen_callback(socket, (void*)0x42, WebSocketOpen); + emscripten_websocket_set_onclose_callback(socket, (void*)0x43, WebSocketClose); + emscripten_websocket_set_onerror_callback(socket, (void*)0x44, WebSocketError); + emscripten_websocket_set_onmessage_callback(socket, (void*)0x45, WebSocketMessage); + emscripten_exit_with_live_runtime(); + return 0; } diff --git a/test/websocket/websocket.c b/test/websocket/websocket.c deleted file mode 100644 index 96f08bc2dd6b..000000000000 --- a/test/websocket/websocket.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include - -EM_BOOL WebSocketOpen(int eventType, const EmscriptenWebSocketOpenEvent *e, void *userData) -{ - printf("open(eventType=%d, userData=%ld)\n", eventType, (long)userData); - - emscripten_websocket_send_utf8_text(e->socket, "hello on the other side"); - - char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - emscripten_websocket_send_binary(e->socket, data, sizeof(data)); - - emscripten_websocket_close(e->socket, 0, 0); - return 0; -} - -EM_BOOL WebSocketClose(int eventType, const EmscriptenWebSocketCloseEvent *e, void *userData) -{ - printf("close(eventType=%d, wasClean=%d, code=%d, reason=%s, userData=%ld)\n", eventType, e->wasClean, e->code, e->reason, (long)userData); - return 0; -} - -EM_BOOL WebSocketError(int eventType, const EmscriptenWebSocketErrorEvent *e, void *userData) -{ - printf("error(eventType=%d, userData=%ld)\n", eventType, (long)userData); - return 0; -} - -EM_BOOL WebSocketMessage(int eventType, const EmscriptenWebSocketMessageEvent *e, void *userData) -{ - printf("message(eventType=%d, userData=%ld, data=%p, numBytes=%d, isText=%d)\n", eventType, (long)userData, e->data, e->numBytes, e->isText); - if (e->isText) - printf("text data: \"%s\"\n", e->data); - else - { - printf("binary data:"); - for(int i = 0; i < e->numBytes; ++i) - printf(" %02X", e->data[i]); - printf("\n"); - - emscripten_websocket_delete(e->socket); - exit(0); - } - return 0; -} - -int main() -{ - if (!emscripten_websocket_is_supported()) - { - printf("WebSockets are not supported, cannot continue!\n"); - exit(1); - } - - EmscriptenWebSocketCreateAttributes attr; - emscripten_websocket_init_create_attributes(&attr); - - attr.url = "ws://localhost:8080"; - - EMSCRIPTEN_WEBSOCKET_T socket = emscripten_websocket_new(&attr); - if (socket <= 0) - { - printf("WebSocket creation failed, error code %d!\n", (EMSCRIPTEN_RESULT)socket); - exit(1); - } - - emscripten_websocket_set_onopen_callback(socket, (void*)42, WebSocketOpen); - emscripten_websocket_set_onclose_callback(socket, (void*)43, WebSocketClose); - emscripten_websocket_set_onerror_callback(socket, (void*)44, WebSocketError); - emscripten_websocket_set_onmessage_callback(socket, (void*)45, WebSocketMessage); -}