From bbf8f65d97ac9b20fa75ffa77402c4f5661baac0 Mon Sep 17 00:00:00 2001 From: d3x0r Date: Thu, 23 Jan 2025 20:31:43 +0000 Subject: [PATCH] Handle race condition between handling open and getting first event message. --- .../client/html5.websocket.client.c | 3 +- .../html5.websocket/html5.websocket.common.c | 1 + .../html5.websocket/html5.websocket.common.h | 10 ++--- .../html5.websocket/server/html5.websocket.c | 38 +++++++++++-------- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/netlib/html5.websocket/client/html5.websocket.client.c b/src/netlib/html5.websocket/client/html5.websocket.client.c index 76300798d..c82dbce1b 100644 --- a/src/netlib/html5.websocket/client/html5.websocket.client.c +++ b/src/netlib/html5.websocket/client/html5.websocket.client.c @@ -152,6 +152,7 @@ static void CPROC WebSocketClientReceive( PCLIENT pc, POINTER buffer, size_t len //lprintf( "reply is %d", result ); if( (int)result == 101 ) { + websock->input_state.flags.initial_handshake_done = 1; websock->flags.connected = 1; { PTEXT content = GetHttpContent( websock->pHttpState ); @@ -355,7 +356,7 @@ static void WebSocketClose_( WebSocketClient wsc, int code, const char *reason ) } if( websock->Magic == 0x20130912 ) { // struct html5_web_socket struct html5_web_socket *serverSock = (struct html5_web_socket*)websock; - if( serverSock->flags.initial_handshake_done ) { + if( serverSock->input_state.flags.initial_handshake_done ) { //lprintf( "Send server side close with no payload." ); SendWebSocketMessage( &serverSock->input_state, 8, 1, serverSock->input_state.flags.expect_masking, (const uint8_t*)buf, buflen ); serverSock->input_state.flags.closed = 1; diff --git a/src/netlib/html5.websocket/html5.websocket.common.c b/src/netlib/html5.websocket/html5.websocket.common.c index 630df9ded..357c7bbbc 100644 --- a/src/netlib/html5.websocket/html5.websocket.common.c +++ b/src/netlib/html5.websocket/html5.websocket.common.c @@ -365,6 +365,7 @@ void ProcessWebSockProtocol( WebSocketInputState websock, const uint8_t* msg, si /// single packet, final... //LogBinary( websock->fragment_collection, websock->fragment_collection_length ); if( websock->on_event ) { + while( !websock->flags.initial_handshake_done || websock->flags.in_open_event ) Relinquish(); #ifndef __NO_WEBSOCK_COMPRESSION__ if( websock->flags.deflate && ( websock->RSV1 & 0x40 ) ) { int r; diff --git a/src/netlib/html5.websocket/html5.websocket.common.h b/src/netlib/html5.websocket/html5.websocket.common.h index 2a2cbf87a..e35f7c941 100644 --- a/src/netlib/html5.websocket/html5.websocket.common.h +++ b/src/netlib/html5.websocket/html5.websocket.common.h @@ -34,7 +34,9 @@ struct web_socket_input_state BIT_FIELD use_ssl : 1; BIT_FIELD want_close : 1; // schedule to close (moved from client; client only) BIT_FIELD pipe : 1; - } flags; + BIT_FIELD initial_handshake_done : 1; + BIT_FIELD in_open_event : 1; + } volatile flags; uint32_t last_reception; // (last message tick) for automatic ping/keep alive/idle death #ifndef __NO_WEBSOCK_COMPRESSION__ @@ -100,14 +102,12 @@ struct html5_web_socket { uint32_t Magic; // this value must be 0x20130912 struct web_socket_flags { - BIT_FIELD initial_handshake_done : 1; BIT_FIELD rfc6455 : 1; BIT_FIELD accepted : 1; BIT_FIELD http_request_only : 1; - BIT_FIELD in_open_event : 1; // set when sent to client, which can write and close before return; no further read must be done. BIT_FIELD closed : 1; // was already closed (during in_read_event) BIT_FIELD skip_read : 1; - } flags; + } volatile flags; HTTPState http_state; PCLIENT pc; POINTER buffer; @@ -124,7 +124,7 @@ struct web_socket_client { BIT_FIELD connected : 1; // if not connected, then parse data as http, otherwise process as websock protocol. //BIT_FIELD use_ssl : 1; - } flags; + } volatile flags; PCLIENT pc; CTEXTSTR host; diff --git a/src/netlib/html5.websocket/server/html5.websocket.c b/src/netlib/html5.websocket/server/html5.websocket.c index b30c4c92c..f385deb3e 100644 --- a/src/netlib/html5.websocket/server/html5.websocket.c +++ b/src/netlib/html5.websocket/server/html5.websocket.c @@ -197,14 +197,14 @@ static void HandleData( HTML5WebSocket socket, CPOINTER buffer, size_t length ) void ResetWebsocketRequestHandler( PCLIENT pc ) { HTML5WebSocket socket = (HTML5WebSocket)GetNetworkLong( pc, 0 ); if( !socket ) return; // closing/closed.... - socket->flags.initial_handshake_done = 0; + socket->input_state.flags.initial_handshake_done = 0; socket->flags.http_request_only = 0; EndHttp( socket->http_state ); } void ResetWebsocketPipeRequestHandler( HTML5WebSocket socket ) { if( !socket ) return; // closing/closed.... - socket->flags.initial_handshake_done = 0; + socket->input_state.flags.initial_handshake_done = 0; socket->flags.http_request_only = 0; EndHttp( socket->http_state ); } @@ -223,7 +223,7 @@ uintptr_t WebSocketPipeGetServerData( HTML5WebSocket socket ) { static void CPROC destroyHttpState( HTML5WebSocket socket, PCLIENT pc_client ) { //HTML5WebSocket socket = (HTML5WebSocket)GetNetworkLong( pc_client, 0 ); - if( socket->flags.in_open_event ) { + if( socket->input_state.flags.in_open_event ) { socket->flags.closed = 1; return; } @@ -269,16 +269,16 @@ static void CPROC read_complete_process_data( HTML5WebSocket socket ) { || !StrCaseStr( GetText( value ), "upgrade" ) || !TextLike( value2, "websocket" ) ) { //lprintf( "request is not an upgrade for websocket." ); - socket->flags.initial_handshake_done = 1; + socket->input_state.flags.initial_handshake_done = 1; socket->flags.http_request_only = 1; - socket->flags.in_open_event = 1; + socket->input_state.flags.in_open_event = 1; if( socket->input_state.on_request ) { if( socket->Magic == 0x20240310 ) socket->input_state.on_request( (PCLIENT)socket, socket->input_state.psv_on ); else socket->input_state.on_request( socket->pc, socket->input_state.psv_on ); } else { - socket->flags.in_open_event = 0; + socket->input_state.flags.in_open_event = 0; if( socket->pc ) RemoveClient( socket->pc ); else if( socket->input_state.do_close ) @@ -286,7 +286,7 @@ static void CPROC read_complete_process_data( HTML5WebSocket socket ) { return; } - socket->flags.in_open_event = 0; + socket->input_state.flags.in_open_event = 0; if( socket->flags.closed ) { destroyHttpState( socket, NULL ); return; @@ -488,7 +488,7 @@ static void CPROC read_complete_process_data( HTML5WebSocket socket ) { SendTCP( socket->pc, GetText( value ), GetTextSize( value ) ); //lprintf( "Sent http reply." ); VarTextDestroy( &pvt_output ); - socket->flags.in_open_event = 1; + socket->input_state.flags.in_open_event = 1; if( socket->input_state.on_open ) { if( socket->Magic == 0x20240310 ) { @@ -497,7 +497,7 @@ static void CPROC read_complete_process_data( HTML5WebSocket socket ) { socket->input_state.psv_open = socket->input_state.on_open( socket->pc, socket->input_state.psv_on ); } } - socket->flags.in_open_event = 0; + socket->input_state.flags.in_open_event = 0; if( socket->flags.closed ) { destroyHttpState( socket, NULL ); return; @@ -512,7 +512,7 @@ static void CPROC read_complete_process_data( HTML5WebSocket socket ) { } // keep this until close, application might want resource and/or headers from this. //EndHttp( socket->http_state ); - socket->flags.initial_handshake_done = 1; + socket->input_state.flags.initial_handshake_done = 1; } break; } @@ -527,8 +527,8 @@ void WebSocketWrite( HTML5WebSocket socket, CPOINTER buffer, size_t length ) if( buffer ) { CTEXTSTR tmp = (CTEXTSTR)buffer; - if( !( socket->flags.initial_handshake_done - || socket->flags.in_open_event ) + if( !( socket->input_state.flags.initial_handshake_done + || socket->input_state.flags.in_open_event ) || socket->flags.http_request_only ) { if( AddHttpData( socket->http_state, tmp, length ) ) @@ -608,7 +608,11 @@ static void CPROC connected( PCLIENT pc_server, PCLIENT pc_new ) MemSet( socket, 0, sizeof( struct html5_web_socket ) ); socket->Magic = 0x20130912; socket->pc = pc_new; +#ifdef __cplusplus + MemCpy( &socket->input_state, &server_socket->input_state, sizeof( socket->input_state ) ); // clone callback methods and config flags ) +#else socket->input_state = server_socket->input_state; // clone callback methods and config flags +#endif socket->input_state.close_code = 1006; socket->input_state.close_reason = StrDup( "Because I don't Like You?"); socket->input_state.psvSender = (uintptr_t)pc_new; @@ -670,7 +674,11 @@ HTML5WebSocket WebSocketPipeConnect( HTML5WebSocket pipe, uintptr_t psvNew ) { MemSet( socket, 0, sizeof( struct html5_web_socket ) ); socket->Magic = 0x20240310; socket->pc = NULL; +#ifdef __cplusplus + MemCpy( &socket->input_state, &server_socket->input_state, sizeof( socket->input_state ) ); // clone callback methods and config flags ) +#else socket->input_state = server_socket->input_state; // clone callback methods and config flags +#endif socket->input_state.psvSender = psvNew; // this new socket gets a http state. socket->http_state = CreateHttpState( &socket->pc ); // start a new http state collector @@ -881,7 +889,7 @@ void WebSocketPipeAccept( HTML5WebSocket socket, char *protocols, int yesno ) { SendTCP( socket->pc, GetText( value ), GetTextSize( value ) ); //lprintf( "Sent http reply." ); VarTextDestroy( &pvt_output ); - socket->flags.in_open_event = 1; + socket->input_state.flags.in_open_event = 1; if( socket->input_state.on_open ) { //lprintf( "Doing open event too (this will be in the self-JS thread)"); @@ -891,12 +899,12 @@ void WebSocketPipeAccept( HTML5WebSocket socket, char *protocols, int yesno ) { socket->input_state.psv_open = socket->input_state.on_open( socket->pc, socket->input_state.psv_on ); } } - socket->flags.in_open_event = 0; + socket->input_state.flags.in_open_event = 0; if( socket->flags.closed ) { destroyHttpState( socket, NULL ); return; } - socket->flags.initial_handshake_done = 1; + socket->input_state.flags.initial_handshake_done = 1; } }