gvfs r1101 - in trunk: . client daemon
- From: gicmo svn gnome org
- To: svn-commits-list gnome org
- Subject: gvfs r1101 - in trunk: . client daemon
- Date: Fri, 11 Jan 2008 18:59:53 +0000 (GMT)
Author: gicmo
Date: Fri Jan 11 18:59:53 2008
New Revision: 1101
URL: http://svn.gnome.org/viewvc/gvfs?rev=1101&view=rev
Log:
2008-01-11 Christian Kellner <gicmo gnome org>
* client/gdaemonvfs.c:
Fix the double use of a counter variable inside nested loops.
* client/Makefile.am:
* client/httpuri.c:
UriMapper added, which is needed for plain http method.
* configure.ac:
Check for libsoup version >= 2.2.104
* daemon/gvfsbackendhttp.c:
Initial implementation of plain http methods.
* daemon/Makefile.am:
* daemon/http.mount.in:
Added.
* daemon/soup-input-stream.c:
* daemon/soup-input-stream.h:
Import of Dan Winship's libsoup based streaming classes.
Added:
trunk/client/httpuri.c
trunk/daemon/http.mount.in
trunk/daemon/soup-input-stream.c
trunk/daemon/soup-input-stream.h
Modified:
trunk/ChangeLog
trunk/client/Makefile.am
trunk/client/gdaemonvfs.c
trunk/configure.ac
trunk/daemon/Makefile.am
trunk/daemon/gvfsbackendhttp.c
Modified: trunk/client/Makefile.am
==============================================================================
--- trunk/client/Makefile.am (original)
+++ trunk/client/Makefile.am Fri Jan 11 18:59:53 2008
@@ -20,6 +20,7 @@
URI_PARSER_SOURCES = \
smburi.c \
+ httpuri.c \
$(NULL)
vfssources = \
Modified: trunk/client/gdaemonvfs.c
==============================================================================
--- trunk/client/gdaemonvfs.c (original)
+++ trunk/client/gdaemonvfs.c Fri Jan 11 18:59:53 2008
@@ -209,18 +209,20 @@
vfs->to_uri_hash = g_hash_table_new (g_str_hash, g_str_equal);
mappers = g_type_children (G_VFS_TYPE_URI_MAPPER, &n_mappers);
+
for (i = 0; i < n_mappers; i++)
{
+ int j;
mapper = g_object_new (mappers[i], NULL);
schemes = g_vfs_uri_mapper_get_handled_schemes (mapper);
- for (i = 0; schemes != NULL && schemes[i] != NULL; i++)
- g_hash_table_insert (vfs->from_uri_hash, (char *)schemes[i], mapper);
+ for (j = 0; schemes != NULL && schemes[j] != NULL; j++)
+ g_hash_table_insert (vfs->from_uri_hash, (char *)schemes[j], mapper);
mount_types = g_vfs_uri_mapper_get_handled_mount_types (mapper);
- for (i = 0; mount_types != NULL && mount_types[i] != NULL; i++)
- g_hash_table_insert (vfs->to_uri_hash, (char *)mount_types[i], mapper);
+ for (j = 0; mount_types != NULL && mount_types[j] != NULL; j++)
+ g_hash_table_insert (vfs->to_uri_hash, (char *)mount_types[j], mapper);
}
/* The above should have ref:ed the modules anyway */
@@ -780,6 +782,7 @@
/* Module API */
void g_vfs_uri_mapper_smb_register (GIOModule *module);
+void g_vfs_uri_mapper_http_register (GIOModule *module);
void
g_io_module_load (GIOModule *module)
@@ -789,6 +792,7 @@
g_vfs_uri_mapper_register (module);
g_vfs_uri_mapper_smb_register (module);
+ g_vfs_uri_mapper_http_register (module);
}
void
Added: trunk/client/httpuri.c
==============================================================================
--- (empty file)
+++ trunk/client/httpuri.c Fri Jan 11 18:59:53 2008
@@ -0,0 +1,137 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo gnome org>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <stdlib.h> /* atoi */
+
+#include <gio/gio.h>
+#include <gvfsurimapper.h>
+#include <gvfsuriutils.h>
+
+typedef struct _GVfsUriMapperHttp GVfsUriMapperHttp;
+typedef struct _GVfsUriMapperHttpClass GVfsUriMapperHttpClass;
+
+struct _GVfsUriMapperHttp
+{
+ GVfsUriMapper parent;
+};
+
+struct _GVfsUriMapperHttpClass
+{
+ GVfsUriMapperClass parent_class;
+};
+
+GType g_vfs_uri_mapper_http_get_type (void);
+void g_vfs_uri_mapper_http_register (GIOModule *module);
+
+G_DEFINE_DYNAMIC_TYPE (GVfsUriMapperHttp, g_vfs_uri_mapper_http, G_VFS_TYPE_URI_MAPPER)
+
+static void
+g_vfs_uri_mapper_http_init (GVfsUriMapperHttp *vfs)
+{
+}
+
+static const char * const *
+http_get_handled_schemes (GVfsUriMapper *mapper)
+{
+ static const char *schemes[] = {
+ "http",
+ "https",
+ NULL
+ };
+ return schemes;
+}
+
+static GVfsUriMountInfo *
+http_from_uri (GVfsUriMapper *mapper,
+ const char *uri_str)
+{
+ GVfsUriMountInfo *info;
+
+ info = g_vfs_uri_mount_info_new ("http");
+ info->path = g_strdup ("/");
+
+ g_vfs_uri_mount_info_set (info, "uri", uri_str);
+
+ return info;
+}
+
+static const char * const *
+http_get_handled_mount_types (GVfsUriMapper *mapper)
+{
+ static const char *types[] = {
+ "http",
+ NULL
+ };
+ return types;
+}
+
+static char *
+http_to_uri (GVfsUriMapper *mapper,
+ GVfsUriMountInfo *info,
+ gboolean allow_utf8)
+{
+ return g_strdup (g_vfs_uri_mount_info_get (info, "uri"));
+}
+
+static const char *
+http_to_uri_scheme (GVfsUriMapper *mapper,
+ GVfsUriMountInfo *info)
+{
+ const gchar *uri = g_vfs_uri_mount_info_get (info, "uri");
+
+ if (g_ascii_strncasecmp (uri, "https", 5) == 0)
+ return "https";
+ else if (g_ascii_strncasecmp (uri, "http", 4) == 0)
+ return "http";
+ else
+ return NULL;
+}
+
+static void
+g_vfs_uri_mapper_http_class_finalize (GVfsUriMapperHttpClass *klass)
+{
+}
+
+static void
+g_vfs_uri_mapper_http_class_init (GVfsUriMapperHttpClass *class)
+{
+ GObjectClass *object_class;
+ GVfsUriMapperClass *mapper_class;
+
+ object_class = (GObjectClass *) class;
+
+ mapper_class = G_VFS_URI_MAPPER_CLASS (class);
+ mapper_class->get_handled_schemes = http_get_handled_schemes;
+ mapper_class->from_uri = http_from_uri;
+ mapper_class->get_handled_mount_types = http_get_handled_mount_types;
+ mapper_class->to_uri = http_to_uri;
+ mapper_class->to_uri_scheme = http_to_uri_scheme;
+}
+
+void
+g_vfs_uri_mapper_http_register (GIOModule *module)
+{
+ g_vfs_uri_mapper_http_register_type (G_TYPE_MODULE (module));
+}
Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Fri Jan 11 18:59:53 2008
@@ -51,7 +51,7 @@
DBUS_SERVICE_DIR=$with_dbus_service_dir
AC_SUBST(DBUS_SERVICE_DIR)
-PKG_CHECK_MODULES(HTTP, libsoup-2.2)
+PKG_CHECK_MODULES(HTTP, libsoup-2.2 >= 2.2.104)
AC_SUBST(HTTP_CFLAGS)
AC_SUBST(HTTP_LIBS)
Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am (original)
+++ trunk/daemon/Makefile.am Fri Jan 11 18:59:53 2008
@@ -32,8 +32,8 @@
libexec_PROGRAMS=gvfsd gvfsd-ftp gvfsd-sftp gvfsd-http gvfsd-trash gvfsd-computer gvfsd-localtest
-mount_in_files = ftp.mount.in sftp.mount.in trash.mount.in computer.mount.in localtest.mount.in
-mount_DATA = ftp.mount sftp.mount trash.mount computer.mount localtest.mount
+mount_in_files = ftp.mount.in sftp.mount.in http.mount.in trash.mount.in computer.mount.in localtest.mount.in
+mount_DATA = ftp.mount sftp.mount trash.mount http.mount computer.mount localtest.mount
if HAVE_SAMBA
mount_in_files += smb.mount.in smb-browse.mount.in
@@ -221,6 +221,7 @@
gvfsd_cdda_LDADD = $(libraries) $(CDDA_LIBS)
gvfsd_http_SOURCES = \
+ soup-input-stream.c soup-input-stream.h \
gvfsbackendhttp.c gvfsbackendhttp.h \
daemon-main.c daemon-main.h \
daemon-main-generic.c
Modified: trunk/daemon/gvfsbackendhttp.c
==============================================================================
--- trunk/daemon/gvfsbackendhttp.c (original)
+++ trunk/daemon/gvfsbackendhttp.c Fri Jan 11 18:59:53 2008
@@ -50,11 +50,14 @@
#include "gvfsjobenumerate.h"
#include "gvfsdaemonprotocol.h"
+#include "soup-input-stream.h"
+
struct _GVfsBackendHttp
{
GVfsBackend parent_instance;
+ SoupUri *mount_base;
SoupSession *session;
};
@@ -76,12 +79,244 @@
{
}
+static gboolean
+try_mount (GVfsBackend *backend,
+ GVfsJobMount *job,
+ GMountSpec *mount_spec,
+ GMountSource *mount_source,
+ gboolean is_automount)
+{
+ GVfsBackendHttp *op_backend;
+ const char *uri_str;
+ SoupUri *uri;
+
+ op_backend = G_VFS_BACKEND_HTTP (backend);
+
+ uri = NULL;
+ uri_str = g_mount_spec_get (mount_spec, "uri");
+
+ if (uri_str)
+ uri = soup_uri_new (uri_str);
+
+ g_print ("+ try_mount: %s\n", uri_str ? uri_str : "(null)");
+
+ if (uri == NULL)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid mount spec"));
+ return TRUE;
+ }
+
+ op_backend->mount_base = uri;
+
+ op_backend->session = soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT,
+ g_main_context_default (),
+ NULL);
+ g_vfs_job_succeeded (G_VFS_JOB (job));
+ return TRUE;
+}
+
+/* *** open_read () *** */
+static void
+open_for_read_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GInputStream *stream;
+ GVfsJob *job;
+ gboolean res;
+ gboolean can_seek;
+ GError *error;
+
+ stream = G_INPUT_STREAM (source_object);
+ error = NULL;
+ job = G_VFS_JOB (user_data);
+
+ res = soup_input_stream_send_finish (stream,
+ result,
+ &error);
+
+ if (res == FALSE)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ error->domain,
+ error->code,
+ error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ can_seek = G_IS_SEEKABLE (stream) && g_seekable_can_seek (G_SEEKABLE (stream));
+
+ g_vfs_job_open_for_read_set_can_seek (G_VFS_JOB_OPEN_FOR_READ (job), can_seek);
+ g_vfs_job_open_for_read_set_handle (G_VFS_JOB_OPEN_FOR_READ (job), stream);
+ g_vfs_job_succeeded (job);
+}
+
+static gboolean
+try_open_for_read (GVfsBackend *backend,
+ GVfsJobOpenForRead *job,
+ const char *filename)
+{
+ GVfsBackendHttp *op_backend;
+ GInputStream *stream;
+ SoupUri *uri;
+ SoupMessage *msg;
+
+ op_backend = G_VFS_BACKEND_HTTP (backend);
+ uri = soup_uri_new_with_base (op_backend->mount_base, filename);
+
+ msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ soup_message_add_header (msg->request_headers, "User-Agent", "gvfs/" VERSION);
+
+ stream = soup_input_stream_new (op_backend->session, msg);
+ g_object_unref (msg);
+
+ soup_input_stream_send_async (stream,
+ G_PRIORITY_DEFAULT,
+ G_VFS_JOB (job)->cancellable,
+ open_for_read_ready,
+ job);
+ return TRUE;
+}
+
+/* *** read () *** */
+static void
+read_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GInputStream *stream;
+ GVfsJob *job;
+ GError *error;
+ gssize nread;
+
+ stream = G_INPUT_STREAM (source_object);
+ error = NULL;
+ job = G_VFS_JOB (user_data);
+
+ nread = g_input_stream_read_finish (stream, result, &error);
+
+ if (nread < 0)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ error->domain,
+ error->code,
+ error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ g_vfs_job_read_set_size (G_VFS_JOB_READ (job), nread);
+ g_vfs_job_succeeded (job);
+
+}
+
+static gboolean
+try_read (GVfsBackend *backend,
+ GVfsJobRead *job,
+ GVfsBackendHandle handle,
+ char *buffer,
+ gsize bytes_requested)
+{
+ GVfsBackendHttp *op_backend;
+ GInputStream *stream;
+
+ op_backend = G_VFS_BACKEND_HTTP (backend);
+ stream = G_INPUT_STREAM (handle);
+
+ g_input_stream_read_async (stream,
+ buffer,
+ bytes_requested,
+ G_PRIORITY_DEFAULT,
+ G_VFS_JOB (job)->cancellable,
+ read_ready,
+ job);
+ return TRUE;
+}
+
+/* *** read_close () *** */
+static void
+close_read_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GInputStream *stream;
+ GVfsJob *job;
+ GError *error;
+ gboolean res;
+
+ job = G_VFS_JOB (user_data);
+ stream = G_INPUT_STREAM (source_object);
+ res = g_input_stream_close_finish (stream,
+ result,
+ &error);
+ if (res == FALSE)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ error->domain,
+ error->code,
+ error->message);
+
+ g_error_free (error);
+ }
+ else
+ g_vfs_job_succeeded (job);
+
+ g_object_unref (stream);
+}
+
+static gboolean
+try_close_read (GVfsBackend *backend,
+ GVfsJobCloseRead *job,
+ GVfsBackendHandle handle)
+{
+ GVfsBackendHttp *op_backend;
+ GInputStream *stream;
+
+ op_backend = G_VFS_BACKEND_HTTP (backend);
+ stream = G_INPUT_STREAM (handle);
+
+ g_input_stream_close_async (stream,
+ G_PRIORITY_DEFAULT,
+ G_VFS_JOB (job)->cancellable,
+ close_read_ready,
+ job);
+
+ return TRUE;
+}
+
+/* *** query_info () *** */
+static gboolean
+try_query_info (GVfsBackend *backend,
+ GVfsJobQueryInfo *job,
+ const char *filename,
+ GFileQueryInfoFlags flags,
+ GFileInfo *info,
+ GFileAttributeMatcher *attribute_matcher)
+{
+
+ return TRUE;
+}
+
+
static void
g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- GVfsBackendClass *backend_class = G_VFS_BACKEND_CLASS (klass);
+ GVfsBackendClass *backend_class;
- gobject_class->finalize = g_vfs_backend_http_finalize;
+ gobject_class->finalize = g_vfs_backend_http_finalize;
+
+ backend_class = G_VFS_BACKEND_CLASS (klass);
+
+ backend_class->try_mount = try_mount;
+ backend_class->try_open_for_read = try_open_for_read;
+ backend_class->try_read = try_read;
+ backend_class->try_close_read = try_close_read;
+ backend_class->try_query_info = try_query_info;
}
Added: trunk/daemon/http.mount.in
==============================================================================
--- (empty file)
+++ trunk/daemon/http.mount.in Fri Jan 11 18:59:53 2008
@@ -0,0 +1,5 @@
+[Mount]
+Type=http
+Exec= libexecdir@/gvfsd-http
+AutoMount=true
+DBusName=org.gtk.vfs.mountpoint.http
Added: trunk/daemon/soup-input-stream.c
==============================================================================
--- (empty file)
+++ trunk/daemon/soup-input-stream.c Fri Jan 11 18:59:53 2008
@@ -0,0 +1,786 @@
+/* soup-input-stream.c, based on gsocketinputstream.c
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libsoup/soup.h>
+
+#include "soup-input-stream.h"
+
+G_DEFINE_TYPE (SoupInputStream, soup_input_stream, G_TYPE_INPUT_STREAM);
+
+typedef void (*SoupInputStreamCallback) (GInputStream *);
+
+typedef struct {
+ SoupSession *session;
+ GMainContext *async_context;
+ SoupMessage *msg;
+ gboolean got_headers;
+
+ GCancellable *cancellable;
+ GSource *cancel_watch;
+ SoupInputStreamCallback got_headers_cb;
+ SoupInputStreamCallback got_chunk_cb;
+ SoupInputStreamCallback finished_cb;
+ SoupInputStreamCallback cancelled_cb;
+
+ guchar *leftover_buffer;
+ gsize leftover_bufsize, leftover_offset;
+
+ guchar *caller_buffer;
+ gsize caller_bufsize, caller_nread;
+ GAsyncReadyCallback outstanding_callback;
+ GSimpleAsyncResult *result;
+
+} SoupInputStreamPrivate;
+#define SOUP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_INPUT_STREAM, SoupInputStreamPrivate))
+
+
+static gssize soup_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean soup_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+static void soup_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gssize soup_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+static void soup_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer data);
+static gboolean soup_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+
+static void soup_input_stream_got_headers (SoupMessage *msg, gpointer stream);
+static void soup_input_stream_got_chunk (SoupMessage *msg, gpointer stream);
+static void soup_input_stream_finished (SoupMessage *msg, gpointer stream);
+
+static void
+soup_input_stream_finalize (GObject *object)
+{
+ SoupInputStream *stream = SOUP_INPUT_STREAM (object);
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_object_unref (priv->session);
+
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_input_stream_got_headers), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_input_stream_got_chunk), stream);
+ g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (soup_input_stream_finished), stream);
+ g_object_unref (priv->msg);
+
+ if (priv->leftover_buffer)
+ g_free (priv->leftover_buffer);
+
+ if (G_OBJECT_CLASS (soup_input_stream_parent_class)->finalize)
+ (*G_OBJECT_CLASS (soup_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+soup_input_stream_class_init (SoupInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (SoupInputStreamPrivate));
+
+ gobject_class->finalize = soup_input_stream_finalize;
+
+ stream_class->read_fn = soup_input_stream_read;
+ stream_class->close_fn = soup_input_stream_close;
+ stream_class->read_async = soup_input_stream_read_async;
+ stream_class->read_finish = soup_input_stream_read_finish;
+ stream_class->close_async = soup_input_stream_close_async;
+ stream_class->close_finish = soup_input_stream_close_finish;
+}
+
+static void
+soup_input_stream_init (SoupInputStream *stream)
+{
+ ;
+}
+
+/**
+ * soup_input_stream_new:
+ * @session: the #SoupSession to use
+ * @msg: the #SoupMessage whose response will be streamed
+ *
+ * Prepares to send @msg over @session, and returns a #GInputStream
+ * that can be used to read the response.
+ *
+ * @msg may not be sent until the first read call; if you need to look
+ * at the status code or response headers before reading the body, you
+ * can use soup_input_stream_send() or soup_input_stream_send_async()
+ * to force the message to be sent and the response headers read.
+ *
+ * If @msg gets a non-2xx result, the first read (or send) will return
+ * an error with type %SOUP_INPUT_STREAM_HTTP_ERROR.
+ *
+ * Internally, #SoupInputStream is implemented using asynchronous I/O,
+ * so if you are using the synchronous API (eg,
+ * g_input_stream_read()), you should create a new #GMainContext and
+ * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If
+ * you don't, then synchronous #GInputStream calls will cause the main
+ * loop to be run recursively.) The async #GInputStream API works fine
+ * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset.
+ *
+ * Returns: a new #GInputStream.
+ **/
+GInputStream *
+soup_input_stream_new (SoupSession *session, SoupMessage *msg)
+{
+ SoupInputStream *stream;
+ SoupInputStreamPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SESSION_ASYNC (session), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ stream = g_object_new (SOUP_TYPE_INPUT_STREAM, NULL);
+ priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->session = g_object_ref (session);
+ priv->async_context = soup_session_get_async_context (session);
+ priv->msg = g_object_ref (msg);
+
+ g_signal_connect (msg, "got_headers",
+ G_CALLBACK (soup_input_stream_got_headers), stream);
+ g_signal_connect (msg, "got_chunk",
+ G_CALLBACK (soup_input_stream_got_chunk), stream);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (soup_input_stream_finished), stream);
+
+ /* Add an extra ref since soup_session_queue_message steals one */
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, NULL, NULL);
+
+ return G_INPUT_STREAM (stream);
+}
+
+static void
+soup_input_stream_got_headers (SoupMessage *msg, gpointer stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ /* If the status is unsuccessful, we just ignore the signal and let
+ * libsoup keep going (eventually either it will requeue the request
+ * (after handling authentication/redirection), or else the
+ * "finished" handler will run).
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ priv->got_headers = TRUE;
+ if (!priv->caller_buffer)
+ {
+ /* Not ready to read the body yet */
+ soup_message_io_pause (msg);
+ }
+
+ if (priv->got_headers_cb)
+ priv->got_headers_cb (stream);
+}
+
+static void
+soup_input_stream_got_chunk (SoupMessage *msg, gpointer stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+ gchar *chunk = msg->response.body;
+ gsize chunk_size = msg->response.length;
+
+ /* We only pay attention to the chunk if it's part of a successful
+ * response.
+ */
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ return;
+
+ /* Sanity check */
+ if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0)
+ g_warning ("soup_input_stream_got_chunk called again before previous chunk was processed");
+
+ /* Copy what we can into priv->caller_buffer */
+ if (priv->caller_bufsize - priv->caller_nread > 0)
+ {
+ gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread);
+
+ memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread);
+ priv->caller_nread += nread;
+ chunk += nread;
+ chunk_size -= nread;
+ }
+
+ if (chunk_size > 0)
+ {
+ /* Copy the rest into priv->leftover_buffer. If there's already
+ * some data there, realloc and append. Otherwise just copy.
+ */
+ if (priv->leftover_bufsize)
+ {
+ priv->leftover_buffer = g_realloc (priv->leftover_buffer,
+ priv->leftover_bufsize + chunk_size);
+ memcpy (priv->leftover_buffer + priv->leftover_bufsize,
+ chunk, chunk_size);
+ priv->leftover_bufsize += chunk_size;
+ }
+ else
+ {
+ priv->leftover_bufsize = chunk_size;
+ priv->leftover_buffer = g_memdup (chunk, chunk_size);
+ priv->leftover_offset = 0;
+ }
+ }
+
+ soup_message_io_pause (msg);
+ if (priv->got_chunk_cb)
+ priv->got_chunk_cb (stream);
+}
+
+static void
+soup_input_stream_finished (SoupMessage *msg, gpointer stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->finished_cb)
+ priv->finished_cb (stream);
+}
+
+static gboolean
+soup_input_stream_cancelled (GIOChannel *chan, GIOCondition condition,
+ gpointer stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ priv->cancel_watch = NULL;
+
+ soup_message_io_pause (priv->msg);
+ if (priv->cancelled_cb)
+ priv->cancelled_cb (stream);
+
+ return FALSE;
+}
+
+static void
+soup_input_stream_prepare_for_io (GInputStream *stream,
+ GCancellable *cancellable,
+ guchar *buffer,
+ gsize count)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+ int cancel_fd;
+
+ priv->cancellable = cancellable;
+ cancel_fd = g_cancellable_get_fd (cancellable);
+ if (cancel_fd != -1)
+ {
+ GIOChannel *chan = g_io_channel_unix_new (cancel_fd);
+ priv->cancel_watch = soup_add_io_watch (priv->async_context, chan,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ soup_input_stream_cancelled,
+ stream);
+ g_io_channel_unref (chan);
+ }
+
+ priv->caller_buffer = buffer;
+ priv->caller_bufsize = count;
+ priv->caller_nread = 0;
+
+ if (priv->msg->status == SOUP_MESSAGE_STATUS_RUNNING)
+ soup_message_io_unpause (priv->msg);
+}
+
+static void
+soup_input_stream_done_io (GInputStream *stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->cancel_watch)
+ {
+ g_source_destroy (priv->cancel_watch);
+ priv->cancel_watch = NULL;
+ }
+ priv->cancellable = NULL;
+
+ priv->caller_buffer = NULL;
+ priv->caller_bufsize = 0;
+}
+
+static gboolean
+set_error_if_http_failed (SoupMessage *msg, GError **error)
+{
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ {
+ g_set_error (error, SOUP_HTTP_ERROR,
+ msg->status_code, "%s", msg->reason_phrase);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gsize
+read_from_leftover (SoupInputStreamPrivate *priv,
+ gpointer buffer, gsize bufsize)
+{
+ gsize nread;
+
+ if (priv->leftover_bufsize - priv->leftover_offset <= bufsize)
+ {
+ nread = priv->leftover_bufsize - priv->leftover_offset;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+
+ g_free (priv->leftover_buffer);
+ priv->leftover_buffer = NULL;
+ priv->leftover_bufsize = priv->leftover_offset = 0;
+ }
+ else
+ {
+ nread = bufsize;
+ memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread);
+ priv->leftover_offset += nread;
+ }
+ return nread;
+}
+
+/* This does the work of soup_input_stream_send(), assuming that the
+ * GInputStream pending flag has already been set. It is also used by
+ * soup_input_stream_send_async() in some circumstances.
+ */
+static gboolean
+soup_input_stream_send_internal (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ soup_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ while (priv->msg->status != SOUP_MESSAGE_STATUS_FINISHED &&
+ !priv->got_headers &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ soup_input_stream_done_io (stream);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * soup_input_stream_send:
+ * @stream: a #SoupInputStream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Synchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after soup_input_stream_new()
+ * and before the first g_input_stream_read() if you want to check the
+ * HTTP status code before you start reading.
+ *
+ * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if
+ * not.
+ **/
+gboolean
+soup_input_stream_send (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupInputStreamPrivate *priv;
+ gboolean result;
+
+ g_return_val_if_fail (SOUP_IS_INPUT_STREAM (stream), FALSE);
+ priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (g_input_stream_has_pending (stream))
+ {
+ /* FIXME: should get this error message from gio */
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+ "Stream has outstanding operation");
+ return FALSE;
+ }
+
+ g_input_stream_set_pending (stream, TRUE);
+ result = soup_input_stream_send_internal (stream, cancellable, error);
+ g_input_stream_set_pending (stream, FALSE);
+
+ return result;
+}
+
+static gssize
+soup_input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->msg->status == SOUP_MESSAGE_STATUS_FINISHED)
+ return 0;
+
+ /* If there is data leftover from a previous read, return it. */
+ if (priv->leftover_bufsize)
+ return read_from_leftover (priv, buffer, count);
+
+ /* No leftover data, accept one chunk from the network */
+ soup_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+ while (priv->msg->status != SOUP_MESSAGE_STATUS_FINISHED &&
+ priv->caller_nread == 0 &&
+ !g_cancellable_is_cancelled (cancellable))
+ g_main_context_iteration (priv->async_context, TRUE);
+ soup_input_stream_done_io (stream);
+
+ if (priv->caller_nread > 0)
+ return priv->caller_nread;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+ else if (set_error_if_http_failed (priv->msg, error))
+ return -1;
+ else
+ return 0;
+}
+
+static gboolean
+soup_input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (priv->msg->status != SOUP_MESSAGE_STATUS_FINISHED)
+ {
+ soup_message_set_status (priv->msg, SOUP_STATUS_CANCELLED);
+ soup_session_cancel_message (priv->session, priv->msg);
+ }
+
+ return TRUE;
+}
+
+static void
+wrapper_callback (GObject *source_object, GAsyncResult *res,
+ gpointer user_data)
+{
+ GInputStream *stream = G_INPUT_STREAM (source_object);
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_input_stream_set_pending (stream, FALSE);
+ if (priv->outstanding_callback)
+ (*priv->outstanding_callback) (source_object, res, user_data);
+ priv->outstanding_callback = NULL;
+ g_object_unref (stream);
+}
+
+static void
+send_async_thread (GSimpleAsyncResult *res,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+ gboolean success;
+
+ success = soup_input_stream_send_internal (G_INPUT_STREAM (object),
+ cancellable, &error);
+ g_simple_async_result_set_op_res_gboolean (res, success);
+ if (error)
+ {
+ g_simple_async_result_set_from_error (res, error);
+ g_error_free (error);
+ }
+}
+
+static void
+soup_input_stream_send_async_in_thread (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+ soup_input_stream_send_async_in_thread);
+ g_simple_async_result_run_in_thread (res, send_async_thread,
+ io_priority, cancellable);
+ g_object_unref (res);
+}
+
+static void
+send_async_finished (GInputStream *stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+
+ priv->got_headers_cb = NULL;
+ priv->finished_cb = NULL;
+ soup_input_stream_done_io (stream);
+
+ result = priv->result;
+ priv->result = NULL;
+ g_simple_async_result_set_op_res_gboolean (result, SOUP_STATUS_IS_SUCCESSFUL (priv->msg->status_code));
+ g_simple_async_result_complete (result);
+}
+
+static void
+soup_input_stream_send_async_internal (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_object_ref (stream);
+ priv->outstanding_callback = callback;
+
+ /* If the session uses the default GMainContext, then we can do
+ * async I/O directly. But if it has its own main context, it's
+ * easier to just run it in another thread.
+ */
+ if (soup_session_get_async_context (priv->session))
+ {
+ soup_input_stream_send_async_in_thread (stream, io_priority, cancellable,
+ wrapper_callback, user_data);
+ return;
+ }
+
+ priv->got_headers_cb = send_async_finished;
+ priv->finished_cb = send_async_finished;
+
+ soup_input_stream_prepare_for_io (stream, cancellable, NULL, 0);
+ priv->result = g_simple_async_result_new (G_OBJECT (stream),
+ wrapper_callback, user_data,
+ soup_input_stream_send_async);
+}
+
+/**
+ * soup_input_stream_send_async:
+ * @stream: a #SoupInputStream
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously sends the HTTP request associated with @stream, and
+ * reads the response headers. Call this after soup_input_stream_new()
+ * and before the first g_input_stream_read_async() if you want to
+ * check the HTTP status code before you start reading.
+ **/
+void
+soup_input_stream_send_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ g_return_if_fail (SOUP_IS_INPUT_STREAM (stream));
+ priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+
+ if (g_input_stream_has_pending (stream))
+ {
+ /* FIXME: should get this error message from gio */
+ g_simple_async_report_error_in_idle (G_OBJECT (stream),
+ callback,
+ user_data,
+ G_IO_ERROR, G_IO_ERROR_PENDING,
+ "Stream has outstanding operation");
+ return;
+ }
+ g_input_stream_set_pending (stream, TRUE);
+ soup_input_stream_send_async_internal (stream, io_priority, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_input_stream_send_finish:
+ * @stream: a #SoupInputStream
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to
+ * ignore.
+ *
+ * Finishes a soup_input_stream_send_async() operation.
+ *
+ * Return value: %TRUE if the message was sent successfully and
+ * received a successful status code, %FALSE if not.
+ **/
+gboolean
+soup_input_stream_send_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_input_stream_send_async, FALSE);
+
+ return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+static void
+read_async_done (GInputStream *stream)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+ GError *error = NULL;
+
+ result = priv->result;
+ priv->result = NULL;
+
+ if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) ||
+ set_error_if_http_failed (priv->msg, &error))
+ {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+ else
+ g_simple_async_result_set_op_res_gssize (result, priv->caller_nread);
+
+ priv->got_chunk_cb = NULL;
+ priv->finished_cb = NULL;
+ priv->cancelled_cb = NULL;
+ soup_input_stream_done_io (stream);
+
+ g_simple_async_result_complete (result);
+}
+
+static void
+soup_input_stream_read_async (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream);
+ GSimpleAsyncResult *result;
+
+ /* If the session uses the default GMainContext, then we can do
+ * async I/O directly. But if it has its own main context, we fall
+ * back to the async-via-sync-in-another-thread implementation.
+ */
+ if (soup_session_get_async_context (priv->session))
+ {
+ G_INPUT_STREAM_CLASS (soup_input_stream_parent_class)->
+ read_async (stream, buffer, count, io_priority,
+ cancellable, callback, user_data);
+ return;
+ }
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ soup_input_stream_read_async);
+
+ if (priv->msg->status == SOUP_MESSAGE_STATUS_FINISHED)
+ {
+ g_simple_async_result_set_op_res_gssize (result, 0);
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ if (priv->leftover_bufsize)
+ {
+ gsize nread = read_from_leftover (priv, buffer, count);
+ g_simple_async_result_set_op_res_gssize (result, nread);
+ g_simple_async_result_complete_in_idle (result);
+ return;
+ }
+
+ priv->result = result;
+
+ priv->got_chunk_cb = read_async_done;
+ priv->finished_cb = read_async_done;
+ priv->cancelled_cb = read_async_done;
+ soup_input_stream_prepare_for_io (stream, cancellable, buffer, count);
+}
+
+static gssize
+soup_input_stream_read_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == soup_input_stream_read_async, -1);
+
+ return g_simple_async_result_get_op_res_gssize (simple);
+}
+
+static void
+soup_input_stream_close_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ gboolean success;
+ GError *error = NULL;
+
+ result = g_simple_async_result_new (G_OBJECT (stream),
+ callback, user_data,
+ soup_input_stream_close_async);
+ success = soup_input_stream_close (stream, cancellable, &error);
+ g_simple_async_result_set_op_res_gboolean (result, success);
+ if (error)
+ {
+ g_simple_async_result_set_from_error (result, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete_in_idle (result);
+}
+
+static gboolean
+soup_input_stream_close_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error)
+{
+ /* Failures handled in generic close_finish code */
+ return TRUE;
+}
+
+GQuark
+soup_http_error_quark (void)
+{
+ static GQuark error;
+ if (!error)
+ error = g_quark_from_static_string ("soup_http_error_quark");
+ return error;
+}
Added: trunk/daemon/soup-input-stream.h
==============================================================================
--- (empty file)
+++ trunk/daemon/soup-input-stream.h Fri Jan 11 18:59:53 2008
@@ -0,0 +1,78 @@
+/* Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SOUP_INPUT_STREAM_H__
+#define __SOUP_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_INPUT_STREAM (soup_input_stream_get_type ())
+#define SOUP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SOUP_TYPE_INPUT_STREAM, SoupInputStream))
+#define SOUP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SOUP_TYPE_INPUT_STREAM, SoupInputStreamClass))
+#define SOUP_IS_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SOUP_TYPE_INPUT_STREAM))
+#define SOUP_IS_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SOUP_TYPE_INPUT_STREAM))
+#define SOUP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SOUP_TYPE_INPUT_STREAM, SoupInputStreamClass))
+
+typedef struct SoupInputStream SoupInputStream;
+typedef struct SoupInputStreamClass SoupInputStreamClass;
+
+struct SoupInputStream
+{
+ GInputStream parent;
+
+};
+
+struct SoupInputStreamClass
+{
+ GInputStreamClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_g_reserved1) (void);
+ void (*_g_reserved2) (void);
+ void (*_g_reserved3) (void);
+ void (*_g_reserved4) (void);
+ void (*_g_reserved5) (void);
+};
+
+GType soup_input_stream_get_type (void) G_GNUC_CONST;
+
+GInputStream *soup_input_stream_new (SoupSession *session,
+ SoupMessage *msg);
+
+gboolean soup_input_stream_send (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+
+void soup_input_stream_send_async (GInputStream *stream,
+ int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_input_stream_send_finish (GInputStream *stream,
+ GAsyncResult *result,
+ GError **error);
+
+#define SOUP_HTTP_ERROR soup_http_error_quark()
+GQuark soup_http_error_quark (void);
+
+G_END_DECLS
+
+#endif /* __SOUP_INPUT_STREAM_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]