Create html browser class and basic websocket server

This commit is contained in:
Robert Fewell 2024-01-11 11:51:10 +00:00
parent 1d94ec316f
commit d62bd35ce3
9 changed files with 2000 additions and 3 deletions

View File

@ -5,6 +5,10 @@ set (html_HEADERS
gnc-html-p.h
gnc-html-factory.h
gnc-html-extras.h
gnc-html-browser-p.h
gnc-html-browser.h
gnc-ws-server.h
gnc-ws-protocol.h
)
# Command to generate the swig-gnc-html.c wrapper file
@ -17,6 +21,9 @@ set (html_SOURCES
gnc-html.c
gnc-html-history.c
gnc-html-factory.c
gnc-html-browser.c
gnc-ws-server.c
gnc-ws-protocol.c
)
set (gnc_html_SCHEME html.scm)

View File

@ -0,0 +1,40 @@
/********************************************************************
* gnc-html-browser-p.h -- display html with gnc special tags *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef GNC_HTML_BROWSER_P_H
#define GNC_HTML_BROWSER_P_H
#include "gnc-html-p.h"
struct _GncHtmlBrowserPrivate
{
struct _GncHtmlPrivate base;
GtkWidget *web_view;
gchar* html_string; /* html string being displayed */
GncWsServer *gws;
const gchar *file_name;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
/********************************************************************
* gnc-html-browser.h -- display html with gnc special tags *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef GNC_HTML_BROWSER_H
#define GNC_HTML_BROWSER_H
#include <glib-object.h>
#include "gnc-html.h"
G_BEGIN_DECLS
#define GNC_TYPE_HTML_BROWSER (gnc_html_browser_get_type())
#define GNC_HTML_BROWSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNC_TYPE_HTML_BROWSER, GncHtmlBrowser))
#define GNC_HTML_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNC_TYPE_HTML_BROWSER, GncHtmlBrowserClass))
#define GNC_IS_HTML_BROWSER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNC_TYPE_HTML_BROWSER))
#define GNC_IS_HTML_BROWSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GNC_TYPE_HTML_BROWSER))
#define GNC_HTML_BROWSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GNC_TYPE_HTML_BROWSER, GncHtmlBrowserClass))
typedef struct _GncHtmlBrowser GncHtmlBrowser;
typedef struct _GncHtmlBrowserClass GncHtmlBrowserClass;
typedef struct _GncHtmlBrowserPrivate GncHtmlBrowserPrivate;
/** Key for saving the PDF-export directory in the print settings */
#define GNC_GTK_PRINT_SETTINGS_EXPORT_DIR "gnc-pdf-export-directory"
struct _GncHtmlBrowser
{
GncHtml parent_instance;
/*< private >*/
GncHtmlBrowserPrivate* priv;
};
struct _GncHtmlBrowserClass
{
GncHtmlClass parent_class;
};
GType gnc_html_browser_get_type (void);
GncHtml* gnc_html_browser_new (void);
G_END_DECLS
#endif

View File

@ -26,6 +26,7 @@
#include <gtk/gtk.h>
#include "gnc-html.h"
#include "gnc-html-browser.h"
#include "qoflog.h"
#include "gnc-engine.h"
@ -34,13 +35,13 @@
/* indicates the debugging module that this .o belongs to. */
G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_HTML;
GncHtml* gnc_html_factory_create_html( void )
GncHtml* gnc_html_factory_create_html (void)
{
return gnc_html_webkit_new();
return gnc_html_browser_new ();
}
gboolean
gnc_html_engine_supports_css( void )
gnc_html_engine_supports_css (void)
{
return TRUE;
}

View File

@ -0,0 +1,338 @@
/********************************************************************
* gnc-ws-protocol.c -- basic websocket server protocol *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
********************************************************************/
#include <config.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "gnc-ws-protocol.h"
#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
#define WS_HS_ACCEPT "HTTP/1.1 101 Switching Protocols\r\n"\
"Upgrade: websocket\r\n"\
"Connection: Upgrade\r\n"\
"Sec-WebSocket-Accept: "
#ifdef skip
static void
print_hex (const gchar *s)
{
while (*s)
g_printf("%02x", (unsigned int) *s++);
g_printf("\n");
}
#endif
#ifdef skip
static void
print_bytes (GBytes *bytes_in)
{
gsize length;
const guint8 *array = g_bytes_get_data (bytes_in, &length);
GString *printable;
guint i = 0;
printable = g_string_new ("[");
while (i < length)
{
if (i > 0)
g_string_append_c (printable, ' ');
g_string_append_printf (printable, "%02x", array[i++]);
}
g_string_append_c (printable, ']');
gchar *test = g_string_free (printable, FALSE);
g_print("Print bytes: '%s' %" G_GUINT64_FORMAT "\n", test, length);
g_free (test);
}
#endif
static gchar*
find_header_text (gchar **header, const gchar *find_text)
{
gchar *ret_text = NULL;
guint parts = g_strv_length (header);
for (guint i = 0; i < parts; i++)
{
if (g_strstr_len (header[i], -1, find_text))
{
ret_text = g_strdup (header[i]);
break;
}
}
return ret_text;
}
#define BUFFER_LEN 200 // only dealing with max message of 125
static GBytes *
make_frame (guint8 opcode, const gchar *message)
{
guint8 first_byte = (0x80 | opcode);
guint8 msg_len = strlen (message);
if (msg_len > 125)
{
g_print("## Not supporting messages lonmger than 125 characters ##\n");
return NULL;
}
guint8 header[2];
header[0] = first_byte;
header[1] = msg_len;
static guint8 array_buf[BUFFER_LEN];
memset (array_buf, 0, BUFFER_LEN);
memcpy (array_buf, header, 2);
memcpy (array_buf + 2, message, msg_len);
GBytes *test = g_bytes_new (array_buf, msg_len + 2);
return test;
}
GBytes *
gnc_ws_send_message (const gchar *message)
{
return make_frame (OPCODE_TEXT, message);
}
static gboolean
parse_frame (GBytes *bytes_in, gint *opcode, GBytes **bytes_out)
{
gsize length;
const guint8 *array = g_bytes_get_data (bytes_in, &length);
const guint8 first_byte = array[0];
const guint8 second_byte = array[1];
gboolean fin = (first_byte >> 7) & 1;
gboolean rsv1 = (first_byte >> 6) & 1;
gboolean rsv2 = (first_byte >> 5) & 1;
gboolean rsv3 = (first_byte >> 4) & 1;
*opcode = first_byte & 0xf;
if (rsv1 || rsv2 || rsv3)
{
g_print("WebSocketError: Received frame with non-zero reserved bits\n");
return FALSE;
}
if (fin == 0 && *opcode == OPCODE_CONTINUATION)
{
g_print("WebSocketError: Received new fragment frame with non-zero opcode\n");
return FALSE;
}
gboolean has_mask = (second_byte >> 7) & 1;
guint8 payload_len = second_byte & 0x7f;
gint offset = 2;
if ((*opcode > 0x7) && (payload_len > 125))
{
g_print("WebSocketError: Control frame payload cannot be larger than 125 bytes\n");
return FALSE;
}
if (payload_len > 125)
{
g_print("## Payload length greater than 125 bytes, not suported ##\n");
return FALSE;
}
if (has_mask)
{
guint8 masks[4];
gint i = 0;
masks[0] = array[offset];
masks[1] = array[offset + 1];
masks[2] = array[offset + 2];
masks[3] = array[offset + 3];
offset = offset + 4;
guint8 mypayload[length];
while ((i + offset) < length)
{
guint8 data = array[i + offset] ^ masks[i % 4];
mypayload[i] = data;
i++;
}
*bytes_out = g_bytes_new (mypayload, length - offset);
}
else
*bytes_out = g_bytes_new_from_bytes (bytes_in, offset, length - offset);
return TRUE;
}
gchar *
gnc_ws_parse_bytes_in (GBytes *bytes_in, gint *opcode_out)
{
GBytes *bytes_out = NULL;
gint opcode;
gboolean ok = FALSE;
ok = parse_frame (bytes_in, &opcode, &bytes_out);
if (!ok)
{
g_print("## Parsing bytes in failed ##\n");
return NULL;
}
*opcode_out = opcode;
if (opcode == OPCODE_TEXT)
{
gsize length;
const guint8 *array = g_bytes_get_data (bytes_out, &length);
GString *data_out = g_string_new (NULL);
gint i = 0;
while (i < length)
{
guint8 data = array[i];
g_string_append_printf (data_out, "%c", data);
i++;
}
return g_string_free (data_out, FALSE);
}
return NULL;
}
gchar *
gnc_ws_make_handshake (const gchar *message, gchar **id)
{
gchar **message_split = g_strsplit (message, "\r\n", -1);
gchar *message_out = NULL;
gchar *key = NULL;
gchar *message_test = find_header_text (message_split, "GET");
if (!message_test)
{
g_print(" Error: No Get\n");
return NULL;
}
else
{
gchar *ptr = g_strrstr (message_test, " ");
if (ptr)
*id = g_utf8_substring (message_test, 5, ptr - message_test);
}
g_free (message_test);
message_test = find_header_text (message_split, "Upgrade");
if (!message_test)
{
g_print(" Error: No upgrade hdr\n");
return NULL;
}
g_free (message_test);
message_test = find_header_text (message_split, "Sec-WebSocket-Version");
if (message_test)
{
if (!g_strstr_len (message_test , -1, "13"))
{
g_print(" Error: Unsupported version\n");
g_free (message_test);
return NULL;
}
}
else
{
g_print(" Error: Did not find websocket version\n");
return NULL;
}
g_free (message_test);
message_test = find_header_text (message_split, "Sec-WebSocket-Key");
if (message_test)
{
gchar *ptr = g_strstr_len (message_test , -1, ":");
if (ptr)
{
key = g_strstrip (g_strdup (ptr + 1));
gsize out_len;
guchar *decoded = g_base64_decode (key, &out_len);
if (out_len != 16)
{
g_print(" Error: Key wrong length\n");
g_free (message_test);
return NULL;
}
g_free (decoded);
}
else
{
g_print(" Error: Key not found\n");
g_free (message_test);
return NULL;
}
}
else
{
g_print(" Error: Key not found\n");
g_free (message_test);
return NULL;
}
g_free (message_test);
guint8 buf[100];
gsize bsize = 100;
GChecksum *cs = g_checksum_new (G_CHECKSUM_SHA1);
gchar *digest = g_strconcat (key, WS_KEY, NULL);
g_checksum_update (cs, (guint8*)digest, -1);
g_checksum_get_digest (cs, buf, &bsize);
gchar *str = g_base64_encode (buf, bsize);
message_out = g_strconcat (WS_HS_ACCEPT, str, "\r\n\r\n", NULL);
g_checksum_free (cs);
g_strfreev (message_split);
g_free (key);
return message_out;
}

View File

@ -0,0 +1,45 @@
/********************************************************************
* gnc-ws-protocol.h -- basic websocket server protocol *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
********************************************************************/
#ifndef WS_PROTOCOL_H
#define WS_PROTOCOL_H
#include <config.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#define OPCODE_CONTINUATION 0x0
#define OPCODE_TEXT 0x1
#define OPCODE_BINARY 0x2
#define OPCODE_CLOSE 0x8
#define OPCODE_PING 0x9
#define OPCODE_PONG 0xa
gchar *gnc_ws_make_handshake (const gchar *message, gchar **id);
gchar *gnc_ws_parse_bytes_in (GBytes *bytes_in, gint *opcode);
GBytes *gnc_ws_send_message (const gchar *message);
#endif

View File

@ -0,0 +1,399 @@
/********************************************************************
* gnc-ws-server.c -- basic websocket server *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
********************************************************************/
#include <config.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "gnc-ws-server.h"
#include "gnc-ws-protocol.h"
#define GNC_WS_SERVER_PATH "gnc-ws-server-path"
G_DEFINE_TYPE (GncWsServer, gnc_ws_server, G_TYPE_OBJECT)
#define PORT 8080
#define BLOCK_SIZE 1024
static GncWsServer *gws = NULL;
/* Signal codes */
enum
{
OPEN,
MESSAGE,
CLOSE,
LAST_SIGNAL
};
static guint ws_server_signals [LAST_SIGNAL] = { 0 };
static void
gnc_ws_server_init (GncWsServer *self)
{
g_print("%s called\n",__FUNCTION__);
}
static void
gnc_ws_server_dispose (GObject *object)
{
g_print("%s called\n",__FUNCTION__);
g_return_if_fail (object != NULL);
GncWsServer *gws = (GncWsServer*)object;
g_print("ws service active %d\n", g_socket_service_is_active (gws->service));
if (gws->incoming_id != 0)
g_signal_handler_disconnect (G_OBJECT(gws->service), gws->incoming_id);
gws->incoming_id = 0;
g_socket_service_stop (gws->service);
g_print(" ws service active %d\n", g_socket_service_is_active (gws->service));
G_OBJECT_CLASS(gnc_ws_server_parent_class)->dispose (object);
}
static void
gnc_ws_server_finalize (GObject *object)
{
g_print("%s called\n",__FUNCTION__);
gws = NULL;
G_OBJECT_CLASS(gnc_ws_server_parent_class)->finalize (object);
}
static void
gnc_ws_server_class_init (GncWsServerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
g_print("%s called\n",__FUNCTION__);
gobject_class->dispose = gnc_ws_server_dispose;
gobject_class->finalize = gnc_ws_server_finalize;
ws_server_signals [OPEN] =
g_signal_new ("open",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
ws_server_signals [MESSAGE] =
g_signal_new ("message",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
2,
G_TYPE_STRING,
G_TYPE_STRING);
ws_server_signals [CLOSE] =
g_signal_new ("close",
G_OBJECT_CLASS_TYPE(gobject_class),
G_SIGNAL_RUN_FIRST,
0,
NULL,
NULL,
NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
}
static gboolean
send_message_bytes (GSocketConnection *connection, GBytes *bytes)
{
gboolean ret = TRUE;
GError *error = NULL;
GOutputStream *ostream = g_io_stream_get_output_stream (G_IO_STREAM(connection));
gssize data_out = g_output_stream_write_bytes (ostream, bytes, NULL, &error);
#ifdef G_OS_WIN32
g_print("%s called, connection %p, data out sent %d\n",__FUNCTION__, connection, data_out);
#else
g_print("%s called, connection %p, data out sent %ld\n",__FUNCTION__, connection, data_out);
#endif
if (error != NULL)
{
g_error ("%s", error->message);
g_print("send error '%s'\n", error->message);
g_clear_error (&error);
ret = FALSE;
}
return ret;
}
struct ConnHashData
{
GSocketConnection *connection;
const gchar *id;
};
static gboolean
connection_hash_table_find (gpointer key, gpointer value, gpointer user_data)
{
struct ConnHashData *data = user_data;
if (g_str_has_suffix (data->id, value))
{
data->connection = key;
return TRUE;
}
return FALSE;
}
void
gnc_ws_server_send_message (GncWsServer *gws, const gchar *id, const gchar *message)
{
struct ConnHashData *data = g_new (struct ConnHashData, 1);
data->id = id;
data->connection = NULL;
g_print("%s called, id '%s', message '%s'\n",__FUNCTION__, id, message);
g_hash_table_foreach (gws->connections_hash,
(GHFunc)connection_hash_table_find,
data);
if (data->connection)
{
GBytes *bytes = gnc_ws_send_message (message);
send_message_bytes (data->connection, bytes);
g_bytes_unref (bytes);
}
g_free (data);
}
static gboolean
send_handshake_message (GSocketConnection *connection, const gchar *message)
{
gboolean ret = TRUE;
GError *error = NULL;
GOutputStream *ostream = g_io_stream_get_output_stream (G_IO_STREAM(connection));
#ifdef G_OS_WIN32
g_print("%s called, connection %p, msg len %d\n",__FUNCTION__, connection, strlen (message));
#else
g_print("%s called, connection %p, msg len %ld\n",__FUNCTION__, connection, strlen (message));
#endif
g_output_stream_write (ostream,
message,
strlen (message),
NULL,
&error);
if (error != NULL)
{
g_error ("%s", error->message);
g_print("send error '%s'\n", error->message);
g_clear_error (&error);
ret = FALSE;
}
return ret;
}
struct ConnData
{
GncWsServer *gws;
GSocketConnection *connection;
char message[BLOCK_SIZE];
};
static void
bytes_ready_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GInputStream *istream = G_INPUT_STREAM(source_object);
GError *error = NULL;
struct ConnData *data = user_data;
GncWsServer *gws = data->gws;
GBytes *bytes_in = g_input_stream_read_bytes_finish (istream, res, &error);
g_print("%s called, istream %p\n",__FUNCTION__, istream);
if (error != NULL)
{
g_error ("%s", error->message);
g_print("bytes service error is: %s\n", error->message);
g_clear_error (&error);
return;
}
if (g_bytes_get_size (bytes_in) != 0)
{
gint opcode;
gchar *message = gnc_ws_parse_bytes_in (bytes_in, &opcode);
if ((opcode == OPCODE_TEXT) && message)
{
gchar *id = g_hash_table_lookup (gws->connections_hash, data->connection);
if (id)
g_signal_emit_by_name (data->gws, "message", id, message);
else
g_print("Error: Lookup Error\n");
}
g_free (message);
g_bytes_unref (bytes_in);
if (opcode == OPCODE_CLOSE)
{
gchar *id = g_hash_table_lookup (gws->connections_hash, data->connection);
if (id)
g_signal_emit_by_name (data->gws, "close", id);
else
g_print("Error: Lookup Error\n");
g_hash_table_remove (gws->connections_hash, data->connection);
return;
}
g_input_stream_read_bytes_async (istream, 8192, G_PRIORITY_DEFAULT,
NULL, bytes_ready_cb, user_data);
}
}
static void
message_ready_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GInputStream *istream = G_INPUT_STREAM(source_object);
GError *error = NULL;
struct ConnData *data = user_data;
int count;
count = g_input_stream_read_finish (istream, res, &error);
g_print("%s called, istream %p, count %d\n",__FUNCTION__, istream, count);
if (count == -1)
{
g_print ("Error: receiving message\n");
if (error != NULL)
{
g_print("Error: incoming stream error: %s\n", error->message);
g_clear_error (&error);
return;
}
}
gchar *id = NULL;
gchar *handshake_message = gnc_ws_make_handshake (data->message, &id);
if (handshake_message)
{
if (send_handshake_message (data->connection, handshake_message))
g_input_stream_read_bytes_async (istream, 8192, G_PRIORITY_DEFAULT,
NULL, bytes_ready_cb, user_data);
}
if (id)
{
g_hash_table_insert (gws->connections_hash, data->connection, g_strdup(id));
g_signal_emit_by_name (data->gws, "open", id);
}
g_free (handshake_message);
g_free (id);
// g_object_unref (G_SOCKET_CONNECTION(data->connection));
// g_free (data);
}
static gboolean
incoming_callback (GSocketService *service,
GSocketConnection *connection,
GObject *source_object,
gpointer user_data)
{
GncWsServer *gws = user_data;
GInputStream *istream = g_io_stream_get_input_stream (G_IO_STREAM(connection));
struct ConnData *data = g_new (struct ConnData, 1);
g_print("%s called, connection %p\n",__FUNCTION__, connection);
data->connection = g_object_ref (connection);
data->gws = gws;
g_input_stream_read_async (istream, data->message, sizeof (data->message),
G_PRIORITY_DEFAULT, NULL, message_ready_cb, data);
return FALSE;
}
GncWsServer *
gnc_ws_server_new (void)
{
g_print("%s called\n",__FUNCTION__);
if (gws)
return gws;
gws = g_object_new (GNC_TYPE_WS_SERVER, NULL);
g_print(" ws gws %p\n", gws);
gws->service = g_socket_service_new ();
GError *error = NULL;
gboolean ret = g_socket_listener_add_inet_port (G_SOCKET_LISTENER(gws->service),
PORT, NULL, &error);
if (ret && error != NULL)
{
g_error ("%s", error->message);
g_print("service error is: %s\n", error->message);
g_clear_error (&error);
return NULL;
}
gws->connections_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, g_free);
g_socket_service_start (gws->service);
gws->incoming_id = g_signal_connect (gws->service, "incoming",
G_CALLBACK(incoming_callback), gws);
g_print(" ws service active %d\n", g_socket_service_is_active (gws->service));
return gws;
}

View File

@ -0,0 +1,57 @@
/********************************************************************
* gnc-ws-server.h -- basic websocket server *
* Copyright (C) 2024 Bob Fewell *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef WS_SERVER_H
#define WS_SERVER_H
#include <config.h>
#define GNC_TYPE_WS_SERVER (gnc_ws_server_get_type ())
#define GNC_WS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNC_TYPE_WS_SERVER, GncWsServer))
#define GNC_WS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNC_TYPE_WS_SERVER, GncWsServerClass))
#define GNC_IS_WS_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNC_TYPE_WS_SERVER))
#define GNC_IS_WS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), GNC_TYPE_WS_SERVER))
#define GNC_WS_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GNC_TYPE_WS_SERVER, GncWsServerClass))
typedef struct _GncWsServer GncWsServer;
typedef struct _GncWsServerClass GncWsServerClass;
struct _GncWsServer
{
GObject parent;
GSocketService *service;
gulong incoming_id;
GHashTable *connections_hash;
};
struct _GncWsServerClass
{
GObjectClass parent;
};
GType gnc_ws_server_get_type (void) G_GNUC_CONST;
GncWsServer *gnc_ws_server_new (void);
void gnc_ws_server_send_message (GncWsServer *gws, const gchar *id, const gchar *message);
#endif