[Spice-devel] [PATCH spice-gtk 3/3] webdav: use a pipe to connect to server

Marc-André Lureau marcandre.lureau at redhat.com
Thu Feb 12 05:12:41 PST 2015


Instead of listening on TCP sockets, and proxying connections there,
make the webdav server accept new connections from stream. The streams
are user-space GIOStream pipe, one side is connected to the Spice webdav
channel muxer/demuxer, the other side is a SoupSocket client.

This makes the server not exposed any local public access, avoid the
need for server threads, or proxying the connections through system
sockets.
---
 gtk/channel-webdav.c | 85 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 64 insertions(+), 21 deletions(-)

diff --git a/gtk/channel-webdav.c b/gtk/channel-webdav.c
index 8cf53cf..18cc502 100644
--- a/gtk/channel-webdav.c
+++ b/gtk/channel-webdav.c
@@ -24,6 +24,7 @@
 #include "spice-marshal.h"
 #include "glib-compat.h"
 #include "vmcstream.h"
+#include "giopipe.h"
 
 /**
  * SECTION:channel-webdav
@@ -70,6 +71,7 @@ static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 
 struct _OutputQueue {
     GOutputStream *output;
+    GMemoryInputStream *memory;
     gboolean flushing;
     guint idle_id;
     GQueue *queue;
@@ -100,6 +102,7 @@ static void output_queue_free(OutputQueue *queue)
 
     g_queue_free_full(queue->queue, g_free);
     g_clear_object(&queue->output);
+    g_clear_object(&queue->memory);
     if (queue->idle_id)
         g_source_remove(queue->idle_id);
     g_free(queue);
@@ -185,8 +188,7 @@ typedef struct Client
 {
     guint refs;
     SpiceWebdavChannel *self;
-    GIOStream *conn;
-    OutputQueue *output;
+    GIOStream *pipe;
     gint64 id;
     GCancellable *cancellable;
 
@@ -204,9 +206,8 @@ client_unref(Client *client)
         return;
 
     g_free(client->mux.buf);
-    output_queue_free(client->output);
 
-    g_object_unref(client->conn);
+    g_object_unref(client->pipe);
     g_object_unref(client->cancellable);
 
     g_free(client);
@@ -289,7 +290,7 @@ static void client_start_read(SpiceWebdavChannel *self, Client *client)
 {
     GInputStream *input;
 
-    input = g_io_stream_get_input_stream(G_IO_STREAM(client->conn));
+    input = g_io_stream_get_input_stream(G_IO_STREAM(client->pipe));
     g_input_stream_read_async(input, client->mux.buf, MAX_MUX_SIZE,
                               G_PRIORITY_DEFAULT, client->cancellable, server_reply_cb,
                               client_ref(client));
@@ -297,43 +298,70 @@ static void client_start_read(SpiceWebdavChannel *self, Client *client)
 
 static void start_demux(SpiceWebdavChannel *self);
 
-static void pushed_client_cb(OutputQueue *q, gpointer user_data)
+static void demux_to_client_finish(SpiceWebdavChannel *self,
+                                   Client *client, gssize size)
 {
-    Client *client = user_data;
-    SpiceWebdavChannel *self = client->self;
     SpiceWebdavChannelPrivate *c = self->priv;
 
+    if (size <= 0) {
+        remove_client(self, client);
+    }
+
     c->demuxing = FALSE;
     start_demux(self);
 }
 
+static void demux_to_client_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    Client *client = user_data;
+    SpiceWebdavChannelPrivate *c = client->self->priv;
+    GError *error = NULL;
+    gssize size;
+
+    size = g_output_stream_write_finish(G_OUTPUT_STREAM(source), result, &error);
+
+    if (error) {
+        CHANNEL_DEBUG(client->self, "write failed: %s", error->message);
+        g_clear_error(&error);
+    }
+
+    g_warn_if_fail(size == c->demux.size);
+    demux_to_client_finish(client->self, client, size);
+}
+
 static void demux_to_client(SpiceWebdavChannel *self,
-                           Client *client)
+                            Client *client)
 {
     SpiceWebdavChannelPrivate *c = self->priv;
     gssize size = c->demux.size;
 
     CHANNEL_DEBUG(self, "pushing %"G_GSSIZE_FORMAT" to client %p", size, client);
 
-    if (size != 0) {
-        output_queue_push(client->output, (guint8 *)c->demux.buf, size,
-                          (GFunc)pushed_client_cb, client);
+    if (size > 0) {
+        g_output_stream_write_async(g_io_stream_get_output_stream(client->pipe),
+                                    c->demux.buf, size, G_PRIORITY_DEFAULT,
+                                    c->cancellable, demux_to_client_cb, client);
+        return;
     } else {
-        remove_client(self, client);
-        c->demuxing = FALSE;
-        start_demux(self);
+        demux_to_client_finish(self, client, size);
     }
 }
 
 static void start_client(SpiceWebdavChannel *self)
 {
     SpiceWebdavChannelPrivate *c = self->priv;
-    GOutputStream *output;
     Client *client;
+    GIOStream *peer = NULL;
+    SpiceSession *session;
+    SoupServer *server;
+    SoupSocket *socket;
+    GError *error = NULL;
+
+    session = spice_channel_get_session(SPICE_CHANNEL(self));
+    server = phodav_server_get_soup_server(spice_session_get_webdav_server(session));
 
     CHANNEL_DEBUG(self, "starting client %" G_GINT64_FORMAT, c->demux.client);
 
-    /* FIXME: connect to server */
     client = g_new0(Client, 1);
     client->refs = 1;
     client->id = c->demux.client;
@@ -341,15 +369,30 @@ static void start_client(SpiceWebdavChannel *self)
     client->mux.id = GINT64_TO_LE(client->id);
     client->mux.buf = g_malloc0(MAX_MUX_SIZE);
     client->cancellable = g_cancellable_new();
+    spice_make_pipe(&client->pipe, &peer);
 
-    output = g_buffered_output_stream_new(g_io_stream_get_output_stream(G_IO_STREAM(client->conn)));
-    client->output = output_queue_new(output);
-    g_object_unref(output);
+    socket = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+                             "giostream", peer,
+                             NULL);
+    if (!socket)
+        goto fail;
+
+    soup_server_add_connection(server, socket);
+    g_object_unref(socket);
 
-    client_start_read(self, client);
     g_hash_table_insert(c->clients, &client->id, client);
 
+    client_start_read(self, client);
     demux_to_client(self, client);
+    return;
+
+fail:
+    if (error)
+        CHANNEL_DEBUG(self, "failed to start client: %s", error->message);
+
+    g_clear_object(&peer);
+    g_clear_error(&error);
+    client_unref(client);
 }
 
 static void data_read_cb(GObject *source_object,
-- 
2.1.0



More information about the Spice-devel mailing list