soup WebSocket server asserts when a client is closing the connection
Hello,
I'm using the libsoup websocket server and recently updated my libsoup from 2.64.2 to 2.68.3 due to a bug that has been fixed with https://github.com/GNOME/libsoup/commit/35f1bac5ff9ec694e64b65e51f0e7a3226aa3aaf
Since then I have a new problem, which I better explain using this simple example code:
static void websocket_client_message_cb(SoupWebsocketConnection *ws, SoupWebsocketDataType type, GBytes *message, gpointer user_data) {
(void) ws; (void) type; (void) message; (void) user_data;
g_message("%s", __func__);
}
static void websocket_client_closed_cb(SoupWebsocketConnection *ws, gpointer user_data) {
(void) user_data;
g_object_unref(ws);
g_message("%s", __func__);
}
static void websocket_client_error_cb(SoupWebsocketConnection *ws, GError *error, gpointer user_data)'{
(void) ws; (void) error; (void) user_data;
g_message("%s", __func__);
}
static void websocket_client_connect_cb(SoupServer *server, SoupWebsocketConnection *ws,
const char *path, SoupClientContext *client, gpointer user_data) {
(void) server; (void) client; (void) user_data; (void) path;
g_object_ref(ws);
g_signal_connect(ws, "message", G_CALLBACK(websocket_client_message_cb), NULL);
g_signal_connect(ws, "closed", G_CALLBACK(websocket_client_closed_cb), NULL);
g_signal_connect(ws, "error", G_CALLBACK(websocket_client_error_cb), NULL);
g_message("%s", __func__);
}
int main(int argc, char* argv[]) {
GMainLoop *mainloop = g_main_loop_new(0, FALSE);
/* play here */
GError *error = NULL;
SoupServer *server = soup_server_new(NULL, NULL);
soup_server_listen_all(server, 33333, 0, &error);
g_assert_no_error(error);
soup_server_add_websocket_handler(server, "/", NULL, NULL, websocket_client_connect_cb, NULL, NULL);
g_message("ws server started");
/* run mainloop */
g_main_loop_run(mainloop);
}
This starts a simple websocket server with 1 handler and registers the usual signal callbacks. The new problem is, every time a websocket client disconnects from this server, I run into this g_assert (with G_MESSAGES_DEBUG=all):
(gdb) run
Starting program: /home/root/main2
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/libthread_db.so.1".
** Message: 15:18:58.822: ws server started
** Message: 15:19:07.256: websocket_client_connect_cb
(process:12326): libsoup-DEBUG: 15:19:08.896: stopping input source
(process:12326): libsoup-DEBUG: 15:19:08.896: received control frame 8 with 0 payload
(process:12326): libsoup-DEBUG: 15:19:08.897: responding to close request
(process:12326): libsoup-DEBUG: 15:19:08.897: sent frame
(process:12326): libsoup-DEBUG: 15:19:08.897: closing io stream
[New Thread 0x76823460 (LWP 12329)]
(process:12326): libsoup-DEBUG: 15:19:08.901: queued 8 frame of len 4
(process:12326): libsoup-DEBUG: 15:19:08.902: waiting 5 seconds for peer to close io
(process:12326): libsoup-DEBUG: 15:19:08.902: closed: completed io stream close
** Message: 15:19:08.903: websocket_client_closed_cb
**
libsoup:ERROR:../libsoup-2.68.3/libsoup/soup-websocket-connection.c:1445:soup_websocket_connection_finalize: assertion failed: (!pv->input_source)
Bail out! libsoup:ERROR:../libsoup-2.68.3/libsoup/soup-websocket-connection.c:1445:soup_websocket_connection_finalize: assertion failed: (!pv->input_source)
Thread 1 "main2" received signal SIGABRT, Aborted.
__libc_do_syscall () at libc-do-syscall.S:49
49 libc-do-syscall.S: No such file or directory.
(gdb) bt
#0 0x76c80c46 in __libc_do_syscall () at libc-do-syscall.S:49
#1 0x76c8dfa4 in __libc_signal_restore_set (set=0x7efff758) at ../sysdeps/unix/sysv/linux/internal-signals.h:84
#2 0x76c8dfa4 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:48
#3 0x76c806e6 in __GI_abort () at abort.c:79
#4 0x76f57bde in g_assertion_message
(domain=domain@entry=0x76ef2f1c "libsoup", file=file@entry=0x76ef93b8 "../libsoup-2.68.3/libsoup/soup-websocket-connection.c", line=line@entry=1445, func=func@entry=0x76efde48 <__FUNCTION__.3022
#5 0x76f57c50 in g_assertion_message_expr
(domain=0x76ef2f1c "libsoup", file=0x76ef93b8 "../libsoup-2.68.3/libsoup/soup-websocket-connection.c", line=line@entry=1445, func=0x76efde48 <__FUNCTION__.30297> "soup_websocket_connection_fina8
#6 0x76eea218 in soup_websocket_connection_finalize (object=0x2c098 [SoupWebsocketConnection]) at ../libsoup-2.68.3/libsoup/soup-websocket-connection.c:1445
#7 0x76d68528 in g_object_unref (_object=<optimized out>) at ../glib-2.62.4/gobject/gobject.c:3382
#8 0x76d68528 in g_object_unref (_object=0x2c098) at ../glib-2.62.4/gobject/gobject.c:3274
#9 0x76eea8c0 in on_iostream_closed (source=<optimized out>, result=<optimized out>, user_data=0x2c098) at ../libsoup-2.68.3/libsoup/soup-websocket-connection.c:297
#10 0x76e0bbec in g_task_return_now (task=0x2e400 [GTask]) at ../glib-2.62.4/gio/gtask.c:1212
#11 0x76e0c456 in g_task_return (task=0x2e400 [GTask], type=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1281
#12 0x76e0ca88 in g_task_return (type=G_TASK_RETURN_SUCCESS, task=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1797
#13 0x76e0ca88 in g_task_return_boolean (task=<optimized out>, result=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1801
#14 0x76decf84 in async_ready_close_callback_wrapper (source_object=<optimized out>, res=0x2e478, user_data=0x2e400) at ../glib-2.62.4/gio/giostream.c:455
#15 0x76e0bbec in g_task_return_now (task=0x2e478 [GTask]) at ../glib-2.62.4/gio/gtask.c:1212
#16 0x76e0c456 in g_task_return (task=0x2e478 [GTask], type=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1281
#17 0x76e0ca88 in g_task_return (type=G_TASK_RETURN_SUCCESS, task=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1797
#18 0x76e0ca88 in g_task_return_boolean (task=<optimized out>, result=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1801
#19 0x76ed0f34 in close_async_complete (object=0xc268 [GTcpConnection], result=0x2e4f0, user_data=0x2e478) at ../libsoup-2.68.3/libsoup/soup-io-stream.c:147
#20 0x76e0bbec in g_task_return_now (task=0x2e4f0 [GTask]) at ../glib-2.62.4/gio/gtask.c:1212
#21 0x76e0c456 in g_task_return (task=0x2e4f0 [GTask], type=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1281
#22 0x76e0ca88 in g_task_return (type=G_TASK_RETURN_SUCCESS, task=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1797
--Type <RET> for more, q to quit, c to continue without paging--
#23 0x76e0ca88 in g_task_return_boolean (task=<optimized out>, result=<optimized out>) at ../glib-2.62.4/gio/gtask.c:1801
#24 0x76decf84 in async_ready_close_callback_wrapper (source_object=<optimized out>, res=0x2e568, user_data=0x2e4f0) at ../glib-2.62.4/gio/giostream.c:455
#25 0x76e0bbec in g_task_return_now (task=0x2e568 [GTask]) at ../glib-2.62.4/gio/gtask.c:1212
#26 0x76e0bc20 in complete_in_idle_cb (task=0x2e568) at ../glib-2.62.4/gio/gtask.c:1226
#27 0x76f3d08a in g_main_dispatch (context=0x11f78) at ../glib-2.62.4/glib/gmain.c:3179
#28 0x76f3d08a in g_main_context_dispatch (context=context@entry=0x11f78) at ../glib-2.62.4/glib/gmain.c:3844
#29 0x76f3d2f8 in g_main_context_iterate (context=0x11f78, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib-2.62.4/glib/gmain.c:3917
#30 0x76f3d534 in g_main_loop_run (loop=0x13430) at ../glib-2.62.4/glib/gmain.c:4111
#31 0x00008c7c in main (argc=1, argv=0x7efffd44) at main2.c:112
(gdb)
This assertion is new and did not happen before the commit I mentioned at the beginning. I do NOT run into this g_assert if I'm either not using g_object_unref(ws) in function websocket_client_closed_cb or delay it by 1s. But I HAVE to use g_object_ref(ws) in function websocket_client_connect_cb otherwise the SoupWebsocketConnection *ws would not survive.
At last, I used valgrind on my example code and found out that it's pretty leaky w/o g_object_unref(ws).
The corresponding mails can be found here: https://mail.gnome.org/archives/libsoup-list/2020-January/msg00000.html
With best regards