diff options
| author | Dan Winship <danw@gnome.org> | 2012-12-18 12:59:03 (GMT) |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@redhat.com> | 2012-12-18 13:21:55 (GMT) |
| commit | cac808508f1fcdb3c9c00cdb36ba6053a1f5dbbb (patch) | |
| tree | 0aee32f6f209a613623aae064afb7062fc72eb92 | |
| parent | 333d9db1d52e61f690bd68bcd70c4affe446724a (diff) | |
| download | gvfs-cac808508f1fcdb3c9c00cdb36ba6053a1f5dbbb.zip gvfs-cac808508f1fcdb3c9c00cdb36ba6053a1f5dbbb.tar.xz | |
http: replace SoupInputStream with SoupRequest
Replace the hacky SoupInputStream with a new GVfsHttpInputStream that
is a wrapper around SoupRequest. (We need a wrapper stream rather than
just using SoupRequest directly because we want the stream here to be
seekable, which requires cancelling and re-sending the HTTP request
and getting a new underlying stream.)
The http and dav backends still use both a sync and an async
SoupSession, even though this is no longer necessary, since changing
this would require a lot of rewriting of code that currently works.
https://bugzilla.gnome.org/show_bug.cgi?id=687757
Signed-off-by: Tomas Bzatek <tbzatek@redhat.com>
| -rw-r--r-- | daemon/Makefile.am | 4 | ||||
| -rw-r--r-- | daemon/gvfsbackenddav.c | 3 | ||||
| -rw-r--r-- | daemon/gvfsbackendhttp.c | 50 | ||||
| -rw-r--r-- | daemon/gvfshttpinputstream.c | 586 | ||||
| -rw-r--r-- | daemon/gvfshttpinputstream.h | 77 | ||||
| -rw-r--r-- | daemon/soup-input-stream.c | 930 | ||||
| -rw-r--r-- | daemon/soup-input-stream.h | 80 | ||||
| -rw-r--r-- | daemon/soup-output-stream.c | 1 |
8 files changed, 697 insertions, 1034 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 7d6e6af..52684fc 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -449,7 +449,7 @@ gvfsd_gphoto2_LDADD = $(libraries) $(GPHOTO2_LIBS) $(HAL_LIBS) endif gvfsd_http_SOURCES = \ - soup-input-stream.c soup-input-stream.h \ + gvfshttpinputstream.c gvfshttpinputstream.h \ gvfsbackendhttp.c gvfsbackendhttp.h \ daemon-main.c daemon-main.h \ daemon-main-generic.c @@ -465,7 +465,7 @@ gvfsd_http_CPPFLAGS = \ gvfsd_http_LDADD = $(libraries) $(HTTP_LIBS) gvfsd_dav_SOURCES = \ - soup-input-stream.c soup-input-stream.h \ + gvfshttpinputstream.c gvfshttpinputstream.h \ soup-output-stream.c soup-output-stream.h \ gvfsbackendhttp.c gvfsbackendhttp.h \ gvfsbackenddav.c gvfsbackenddav.h \ diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c index 02e4234..e5fc4c5 100644 --- a/daemon/gvfsbackenddav.c +++ b/daemon/gvfsbackenddav.c @@ -58,7 +58,6 @@ #include "gvfsjobenumerate.h" #include "gvfsdaemonprotocol.h" -#include "soup-input-stream.h" #include "soup-output-stream.h" #ifdef HAVE_AVAHI @@ -1954,7 +1953,7 @@ do_mount (GVfsBackend *backend, g_object_unref (msg_opts); g_object_unref (msg_stat); - /* also auth the workaround async session we need for SoupInputStream */ + /* also auth the async session */ g_signal_connect (G_VFS_BACKEND_HTTP (backend)->session_async, "authenticate", G_CALLBACK (soup_authenticate_from_data), data); diff --git a/daemon/gvfsbackendhttp.c b/daemon/gvfsbackendhttp.c index a9d69dd..1c0f370 100644 --- a/daemon/gvfsbackendhttp.c +++ b/daemon/gvfsbackendhttp.c @@ -33,8 +33,12 @@ #include <glib/gi18n.h> #include <gio/gio.h> +#define LIBSOUP_USE_UNSTABLE_REQUEST_API #include <libsoup/soup-gnome.h> +#include <libsoup/soup-requester.h> + #include "gvfsbackendhttp.h" +#include "gvfshttpinputstream.h" #include "gvfsjobopenforread.h" #include "gvfsjobread.h" #include "gvfsjobseekread.h" @@ -49,9 +53,6 @@ #include "gvfsdaemonprotocol.h" #include "gvfsdaemonutils.h" -#include "soup-input-stream.h" - - G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND) static void @@ -118,6 +119,10 @@ g_vfs_backend_http_init (GVfsBackendHttp *backend) soup_session_add_feature (backend->session_async, content_decoder); g_object_unref (content_decoder); + /* Request API */ + soup_session_add_feature_by_type (backend->session, SOUP_TYPE_REQUESTER); + soup_session_add_feature_by_type (backend->session_async, SOUP_TYPE_REQUESTER); + /* Logging */ debug = g_getenv ("GVFS_HTTP_DEBUG"); if (debug) @@ -337,6 +342,7 @@ open_for_read_ready (GObject *source_object, { GInputStream *stream; GVfsJob *job; + SoupMessage *msg; gboolean res; gboolean can_seek; GError *error; @@ -345,9 +351,9 @@ open_for_read_ready (GObject *source_object, error = NULL; job = G_VFS_JOB (user_data); - res = soup_input_stream_send_finish (stream, - result, - &error); + res = g_vfs_http_input_stream_send_finish (stream, + result, + &error); if (res == FALSE) { g_vfs_job_failed_literal (G_VFS_JOB (job), @@ -360,6 +366,18 @@ open_for_read_ready (GObject *source_object, return; } + msg = g_vfs_http_input_stream_get_message (stream); + if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + { + g_vfs_job_failed_from_http_status (G_VFS_JOB (job), + msg->status_code, + msg->reason_phrase); + g_object_unref (msg); + g_object_unref (stream); + return; + } + g_object_unref (msg); + 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); @@ -387,22 +405,16 @@ http_backend_open_for_read (GVfsBackend *backend, { GVfsBackendHttp *op_backend; GInputStream *stream; - SoupMessage *msg; op_backend = G_VFS_BACKEND_HTTP (backend); - msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); - - soup_message_body_set_accumulate (msg->response_body, FALSE); - - stream = soup_input_stream_new (op_backend->session_async, msg); - g_object_unref (msg); + stream = g_vfs_http_input_stream_new (op_backend->session_async, uri); - soup_input_stream_send_async (stream, - G_PRIORITY_DEFAULT, - job->cancellable, - open_for_read_ready, - job); + g_vfs_http_input_stream_send_async (stream, + G_PRIORITY_DEFAULT, + job->cancellable, + open_for_read_ready, + job); } /* *** read () *** */ @@ -690,7 +702,7 @@ try_query_info_on_read (GVfsBackend *backend, GFileInfo *info, GFileAttributeMatcher *attribute_matcher) { - SoupMessage *msg = soup_input_stream_get_message (G_INPUT_STREAM (handle)); + SoupMessage *msg = g_vfs_http_input_stream_get_message (G_INPUT_STREAM (handle)); file_info_from_message (msg, info, attribute_matcher); g_object_unref (msg); diff --git a/daemon/gvfshttpinputstream.c b/daemon/gvfshttpinputstream.c new file mode 100644 index 0000000..35d9c64 --- /dev/null +++ b/daemon/gvfshttpinputstream.c @@ -0,0 +1,586 @@ +/* gvfshttpinputstream.c: seekable wrapper around SoupRequestHTTP + * + * Copyright (C) 2006, 2007, 2012 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <config.h> + +#include <string.h> + +#include <glib.h> +#include <gio/gio.h> + +#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#include <libsoup/soup.h> +#include <libsoup/soup-requester.h> +#include <libsoup/soup-request-http.h> + +#include "gvfshttpinputstream.h" + +static void g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); + +G_DEFINE_TYPE_WITH_CODE (GVfsHttpInputStream, g_vfs_http_input_stream, G_TYPE_INPUT_STREAM, + G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, + g_vfs_http_input_stream_seekable_iface_init)) + +typedef struct { + SoupURI *uri; + SoupRequester *requester; + SoupRequest *req; + SoupMessage *msg; + GInputStream *stream; + + char *range; + goffset offset; + +} GVfsHttpInputStreamPrivate; +#define G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), G_VFS_TYPE_HTTP_INPUT_STREAM, GVfsHttpInputStreamPrivate)) + +static void +g_vfs_http_input_stream_init (GVfsHttpInputStream *stream) +{ + ; +} + +static void +g_vfs_http_input_stream_finalize (GObject *object) +{ + GVfsHttpInputStream *stream = G_VFS_HTTP_INPUT_STREAM (object); + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + g_clear_pointer (&priv->uri, soup_uri_free); + g_clear_object (&priv->requester); + g_clear_object (&priv->req); + g_clear_object (&priv->msg); + g_clear_object (&priv->stream); + g_free (priv->range); + + G_OBJECT_CLASS (g_vfs_http_input_stream_parent_class)->finalize (object); +} + +/** + * g_vfs_http_input_stream_new: + * @session: a #SoupSession + * @uri: a #SoupURI + * + * Prepares to send a GET request for @uri on @session, and returns a + * #GInputStream that can be used to read the response. + * + * The request will 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 g_vfs_http_input_stream_send() or + * g_vfs_http_input_stream_send_async() to force the message to be + * sent and the response headers read. + * + * Returns: a new #GInputStream. + **/ +GInputStream * +g_vfs_http_input_stream_new (SoupSession *session, + SoupURI *uri) +{ + GVfsHttpInputStream *stream; + GVfsHttpInputStreamPrivate *priv; + + stream = g_object_new (G_VFS_TYPE_HTTP_INPUT_STREAM, NULL); + priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + priv->requester = (SoupRequester *)soup_session_get_feature (session, SOUP_TYPE_REQUESTER); + g_object_ref (priv->requester); + priv->uri = soup_uri_copy (uri); + + return G_INPUT_STREAM (stream); +} + +static SoupRequest * +g_vfs_http_input_stream_ensure_request (GInputStream *stream) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + if (!priv->req) + { + GError *error = NULL; + + priv->req = soup_requester_request_uri (priv->requester, priv->uri, &error); + g_assert_no_error (error); + priv->msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (priv->req)); + priv->offset = 0; + + if (priv->range) + soup_message_headers_replace (priv->msg->request_headers, "Range", priv->range); + } + + return priv->req; +} + +/** + * g_vfs_http_input_stream_send: + * @stream: a #GVfsHttpInputStream + * @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 g_vfs_http_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 was successfully sent, %FALSE if not + **/ +gboolean +g_vfs_http_input_stream_send (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GVfsHttpInputStreamPrivate *priv; + + g_return_val_if_fail (G_VFS_IS_HTTP_INPUT_STREAM (stream), FALSE); + priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + if (priv->stream) + return TRUE; + + if (!g_input_stream_set_pending (stream, error)) + return FALSE; + g_vfs_http_input_stream_ensure_request (stream); + priv->stream = soup_request_send (priv->req, cancellable, error); + g_input_stream_clear_pending (stream); + + return priv->stream != NULL; +} + +static gssize +g_vfs_http_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + gssize nread; + + if (!priv->stream) + { + g_vfs_http_input_stream_ensure_request (stream); + priv->stream = soup_request_send (priv->req, cancellable, error); + if (!priv->stream) + return -1; + } + + nread = g_input_stream_read (priv->stream, buffer, count, cancellable, error); + if (nread > 0) + priv->offset += nread; + return nread; +} + +static gboolean +g_vfs_http_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + if (priv->stream) + { + if (!g_input_stream_close (priv->stream, cancellable, error)) + return FALSE; + g_clear_object (&priv->stream); + } + + return TRUE; +} + +static void +send_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GInputStream *http_stream = g_task_get_source_object (task); + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (http_stream); + GError *error = NULL; + + g_input_stream_clear_pending (http_stream); + + priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error); + if (priv->stream) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +/** + * g_vfs_http_input_stream_send_async: + * @stream: a #GVfsHttpInputStream + * @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 g_vfs_http_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 +g_vfs_http_input_stream_send_async (GInputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVfsHttpInputStreamPrivate *priv; + GError *error = NULL; + GTask *task; + + g_return_if_fail (G_VFS_IS_HTTP_INPUT_STREAM (stream)); + priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + task = g_task_new (stream, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + + if (priv->stream) + { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + if (!g_input_stream_set_pending (stream, &error)) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_vfs_http_input_stream_ensure_request (stream); + soup_request_send_async (priv->req, cancellable, + send_callback, task); +} + +/** + * g_vfs_http_input_stream_send_finish: + * @stream: a #GVfsHttpInputStream + * @result: a #GAsyncResult. + * @error: a #GError location to store the error occuring, or %NULL to + * ignore. + * + * Finishes a g_vfs_http_input_stream_send_async() operation. + * + * Return value: %TRUE if the message was sent successfully, %FALSE if + * not. + **/ +gboolean +g_vfs_http_input_stream_send_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +read_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GInputStream *vfsstream = g_task_get_source_object (task); + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (vfsstream); + GError *error = NULL; + gssize nread; + + nread = g_input_stream_read_finish (G_INPUT_STREAM (object), result, &error); + if (nread >= 0) + { + priv->offset += nread; + g_task_return_int (task, nread); + } + else + g_task_return_error (task, error); + g_object_unref (task); +} + +typedef struct { + gpointer buffer; + gsize count; +} ReadAfterSendData; + +static void +read_send_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GInputStream *vfsstream = g_task_get_source_object (task); + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (vfsstream); + ReadAfterSendData *rasd = g_task_get_task_data (task); + GError *error = NULL; + + if (!soup_request_send_finish (SOUP_REQUEST (object), result, &error)) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + if (!SOUP_STATUS_IS_SUCCESSFUL (priv->msg->status_code)) + { + g_task_return_new_error (task, + SOUP_HTTP_ERROR, + priv->msg->status_code, + "%s", priv->msg->reason_phrase); + g_object_unref (task); + return; + } + + g_input_stream_read_async (priv->stream, rasd->buffer, rasd->count, + g_task_get_priority (task), + g_task_get_cancellable (task), + read_callback, task); +} + +static void +g_vfs_http_input_stream_read_async (GInputStream *stream, + void *buffer, + gsize count, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + GError *error = NULL; + GTask *task; + + task = g_task_new (stream, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + + if (!priv->stream) + { + ReadAfterSendData *rasd; + + rasd = g_new (ReadAfterSendData, 1); + rasd->buffer = buffer; + rasd->count = count; + g_task_set_task_data (task, rasd, g_free); + + g_vfs_http_input_stream_ensure_request (stream); + soup_request_send_async (priv->req, cancellable, + read_send_callback, task); + return; + } + + g_input_stream_read_async (priv->stream, buffer, count, io_priority, + cancellable, read_callback, task); +} + +static gssize +g_vfs_http_input_stream_read_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, stream), -1); + + return g_task_propagate_int (G_TASK (result), error); +} + +static void +close_callback (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GTask *task = user_data; + GError *error = NULL; + + if (g_input_stream_close_finish (G_INPUT_STREAM (object), result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +static void +g_vfs_http_input_stream_close_async (GInputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + GError *error = NULL; + GTask *task; + + task = g_task_new (stream, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + + if (priv->stream == NULL) + { + g_task_return_boolean (task, TRUE); + return; + } + + g_input_stream_close_async (priv->stream, io_priority, + cancellable, close_callback, task); +} + +static gboolean +g_vfs_http_input_stream_close_finish (GInputStream *stream, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, stream), -1); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static goffset +g_vfs_http_input_stream_tell (GSeekable *seekable) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); + + return priv->offset; +} + +static gboolean +g_vfs_http_input_stream_can_seek (GSeekable *seekable) +{ + return TRUE; +} + +static gboolean +g_vfs_http_input_stream_seek (GSeekable *seekable, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) +{ + GInputStream *stream = G_INPUT_STREAM (seekable); + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); + + if (type == G_SEEK_END && priv->msg) + { + goffset content_length = soup_message_headers_get_content_length (priv->msg->response_headers); + + if (content_length) + { + type = G_SEEK_SET; + offset = content_length - offset; + } + } + + if (type == G_SEEK_END) + { + /* We could send "bytes=-offset", but since we don't know the + * Content-Length, we wouldn't be able to answer a tell() + * properly after that. We could maybe find the Content-Length + * by doing a HEAD... but that would require blocking. + */ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "G_SEEK_END not supported"); + return FALSE; + } + + if (!g_input_stream_set_pending (stream, error)) + return FALSE; + + if (priv->stream) + { + if (!g_input_stream_close (priv->stream, NULL, error)) + return FALSE; + g_clear_object (&priv->stream); + } + + g_clear_pointer (&priv->range, g_free); + + switch (type) + { + case G_SEEK_CUR: + offset += priv->offset; + /* fall through */ + + case G_SEEK_SET: + priv->range = g_strdup_printf ("bytes=%"G_GUINT64_FORMAT"-", (guint64)offset); + priv->offset = offset; + break; + + case G_SEEK_END: + g_return_val_if_reached (FALSE); + break; + + default: + g_return_val_if_reached (FALSE); + } + + g_input_stream_clear_pending (stream); + return TRUE; +} + +static gboolean +g_vfs_http_input_stream_can_truncate (GSeekable *seekable) +{ + return FALSE; +} + +static gboolean +g_vfs_http_input_stream_truncate (GSeekable *seekable, + goffset offset, + GCancellable *cancellable, + GError **error) +{ + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Truncate not allowed on input stream"); + return FALSE; +} + +SoupMessage * +g_vfs_http_input_stream_get_message (GInputStream *stream) +{ + GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM_GET_PRIVATE (stream); + + g_vfs_http_input_stream_ensure_request (stream); + return g_object_ref (priv->msg); +} + + +static void +g_vfs_http_input_stream_class_init (GVfsHttpInputStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GVfsHttpInputStreamPrivate)); + + gobject_class->finalize = g_vfs_http_input_stream_finalize; + + stream_class->read_fn = g_vfs_http_input_stream_read; + stream_class->close_fn = g_vfs_http_input_stream_close; + stream_class->read_async = g_vfs_http_input_stream_read_async; + stream_class->read_finish = g_vfs_http_input_stream_read_finish; + stream_class->close_async = g_vfs_http_input_stream_close_async; + stream_class->close_finish = g_vfs_http_input_stream_close_finish; +} + +static void +g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface) +{ + seekable_iface->tell = g_vfs_http_input_stream_tell; + seekable_iface->can_seek = g_vfs_http_input_stream_can_seek; + seekable_iface->seek = g_vfs_http_input_stream_seek; + seekable_iface->can_truncate = g_vfs_http_input_stream_can_truncate; + seekable_iface->truncate_fn = g_vfs_http_input_stream_truncate; +} diff --git a/daemon/gvfshttpinputstream.h b/daemon/gvfshttpinputstream.h new file mode 100644 index 0000000..dbd06d9 --- /dev/null +++ b/daemon/gvfshttpinputstream.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2006, 2007, 2012 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __G_VFS_HTTP_INPUT_STREAM_H__ +#define __G_VFS_HTTP_INPUT_STREAM_H__ + +#include <gio/gio.h> +#include <libsoup/soup-types.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_HTTP_INPUT_STREAM (g_vfs_http_input_stream_get_type ()) +#define G_VFS_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_HTTP_INPUT_STREAM, GVfsHttpInputStream)) +#define G_VFS_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_HTTP_INPUT_STREAM, GVfsHttpInputStreamClass)) +#define G_VFS_IS_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_HTTP_INPUT_STREAM)) +#define G_VFS_IS_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_HTTP_INPUT_STREAM)) +#define G_VFS_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_HTTP_INPUT_STREAM, GVfsHttpInputStreamClass)) + +typedef struct GVfsHttpInputStream GVfsHttpInputStream; +typedef struct GVfsHttpInputStreamClass GVfsHttpInputStreamClass; + +struct GVfsHttpInputStream +{ + GInputStream parent; + +}; + +struct GVfsHttpInputStreamClass +{ + 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 g_vfs_http_input_stream_get_type (void) G_GNUC_CONST; + +GInputStream *g_vfs_http_input_stream_new (SoupSession *session, + SoupURI *uri); + +gboolean g_vfs_http_input_stream_send (GInputStream *stream, + GCancellable *cancellable, + GError **error); + +void g_vfs_http_input_stream_send_async (GInputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_vfs_http_input_stream_send_finish (GInputStream *stream, + GAsyncResult *result, + GError **error); + +SoupMessage *g_vfs_http_input_stream_get_message (GInputStream *stream); + +G_END_DECLS + +#endif /* __G_VFS_HTTP_INPUT_STREAM_H__ */ diff --git a/daemon/soup-input-stream.c b/daemon/soup-input-stream.c deleted file mode 100644 index 18d576d..0000000 --- a/daemon/soup-input-stream.c +++ /dev/null @@ -1,930 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <config.h> - -#include <string.h> - -#include <glib.h> -#include <gio/gio.h> - -#include <libsoup/soup.h> - -#include "soup-input-stream.h" - -static void soup_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); - -G_DEFINE_TYPE_WITH_CODE (SoupInputStream, soup_input_stream, G_TYPE_INPUT_STREAM, - G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, - soup_input_stream_seekable_iface_init)) - -typedef void (*SoupInputStreamCallback) (GInputStream *); - -typedef struct { - SoupSession *session; - GMainContext *async_context; - SoupMessage *msg; - gboolean got_headers, finished; - goffset offset; - - 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 goffset soup_input_stream_tell (GSeekable *seekable); - -static gboolean soup_input_stream_can_seek (GSeekable *seekable); -static gboolean soup_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error); - -static gboolean soup_input_stream_can_truncate (GSeekable *seekable); -static gboolean soup_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error); - -static void soup_input_stream_got_headers (SoupMessage *msg, gpointer stream); -static void soup_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, 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); - 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_seekable_iface_init (GSeekableIface *seekable_iface) -{ - seekable_iface->tell = soup_input_stream_tell; - seekable_iface->can_seek = soup_input_stream_can_seek; - seekable_iface->seek = soup_input_stream_seek; - seekable_iface->can_truncate = soup_input_stream_can_truncate; - seekable_iface->truncate_fn = soup_input_stream_truncate; -} - -static void -soup_input_stream_init (SoupInputStream *stream) -{ - ; -} - -static void -soup_input_stream_queue_message (SoupInputStream *stream) -{ - SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->got_headers = priv->finished = FALSE; - - /* Add an extra ref since soup_session_queue_message steals one */ - g_object_ref (priv->msg); - soup_session_queue_message (priv->session, priv->msg, NULL, NULL); -} - -/** - * 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_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); - - soup_input_stream_queue_message (stream); - 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_session_pause_message (priv->session, msg); - } - - if (priv->got_headers_cb) - priv->got_headers_cb (stream); -} - -static void -soup_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer, - gpointer stream) -{ - SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream); - const gchar *chunk = chunk_buffer->data; - gsize chunk_size = chunk_buffer->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; - priv->offset += 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_session_pause_message (priv->session, 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); - - priv->finished = TRUE; - - 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_session_pause_message (priv->session, 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->got_headers) - soup_session_unpause_message (priv->session, 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; - g_cancellable_release_fd (priv->cancellable); - } - 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_literal (error, SOUP_HTTP_ERROR, - msg->status_code, 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; - } - - priv->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->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) -{ - gboolean result; - - g_return_val_if_fail (SOUP_IS_INPUT_STREAM (stream), FALSE); - - if (!g_input_stream_set_pending (stream, error)) - return FALSE; - result = soup_input_stream_send_internal (stream, cancellable, error); - g_input_stream_clear_pending (stream); - - 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->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->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->finished) - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - - 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_clear_pending (stream); - 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; - GError *error = NULL; - - if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) - set_error_if_http_failed (priv->msg, &error); - - 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, error == NULL); - if (error) - { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } - g_simple_async_result_complete (result); - g_object_unref (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) -{ - GError *error = NULL; - - g_return_if_fail (SOUP_IS_INPUT_STREAM (stream)); - - if (!g_input_stream_set_pending (stream, &error)) - { - g_simple_async_report_gerror_in_idle (G_OBJECT (stream), - callback, - user_data, - error); - g_error_free (error); - return; - } - 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); - - if (g_simple_async_result_propagate_error (simple, error)) - return 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); - g_object_unref (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->finished) - { - g_simple_async_result_set_op_res_gssize (result, 0); - g_simple_async_result_complete_in_idle (result); - g_object_unref (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); - g_object_unref (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); - g_object_unref (result); -} - -static gboolean -soup_input_stream_close_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - /* Failures handled in generic close_finish code */ - return TRUE; -} - -static goffset -soup_input_stream_tell (GSeekable *seekable) -{ - SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (seekable); - - return priv->offset; -} - -static gboolean -soup_input_stream_can_seek (GSeekable *seekable) -{ - return TRUE; -} - -extern void soup_message_io_cleanup (SoupMessage *msg); - -static gboolean -soup_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error) -{ - GInputStream *stream = G_INPUT_STREAM (seekable); - SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (seekable); - char *range; - - if (type == G_SEEK_END) - { - /* FIXME: we could send "bytes=-offset", but unless we know the - * Content-Length, we wouldn't be able to answer a tell() properly. - * We could find the Content-Length by doing a HEAD... - */ - - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "G_SEEK_END not currently supported"); - return FALSE; - } - - if (!g_input_stream_set_pending (stream, error)) - return FALSE; - - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - soup_message_io_cleanup (priv->msg); - - switch (type) - { - case G_SEEK_CUR: - offset += priv->offset; - /* fall through */ - - case G_SEEK_SET: - range = g_strdup_printf ("bytes=%"G_GUINT64_FORMAT"-", (guint64)offset); - priv->offset = offset; - break; - - case G_SEEK_END: - range = NULL; /* keep compilers happy */ - g_return_val_if_reached (FALSE); - break; - - default: - g_return_val_if_reached (FALSE); - } - - soup_message_headers_remove (priv->msg->request_headers, "Range"); - soup_message_headers_append (priv->msg->request_headers, "Range", range); - g_free (range); - - soup_input_stream_queue_message (SOUP_INPUT_STREAM (stream)); - - g_input_stream_clear_pending (stream); - return TRUE; -} - -static gboolean -soup_input_stream_can_truncate (GSeekable *seekable) -{ - return FALSE; -} - -static gboolean -soup_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error) -{ - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "Truncate not allowed on input stream"); - return FALSE; -} - -SoupMessage * -soup_input_stream_get_message (GInputStream *stream) -{ - SoupInputStreamPrivate *priv = SOUP_INPUT_STREAM_GET_PRIVATE (stream); - return priv->msg ? g_object_ref (priv->msg) : NULL; -} - -GQuark -soup_http_error_quark (void) -{ - static GQuark error; - if (!error) - error = g_quark_from_static_string ("soup_http_error_quark"); - return error; -} diff --git a/daemon/soup-input-stream.h b/daemon/soup-input-stream.h deleted file mode 100644 index 1f98f9a..0000000 --- a/daemon/soup-input-stream.h +++ /dev/null @@ -1,80 +0,0 @@ -/* 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., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, 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); - -SoupMessage *soup_input_stream_get_message (GInputStream *stream); - -#define SOUP_HTTP_ERROR soup_http_error_quark() -GQuark soup_http_error_quark (void); - -G_END_DECLS - -#endif /* __SOUP_INPUT_STREAM_H__ */ diff --git a/daemon/soup-output-stream.c b/daemon/soup-output-stream.c index 47109e7..0231776 100644 --- a/daemon/soup-output-stream.c +++ b/daemon/soup-output-stream.c @@ -28,7 +28,6 @@ #include <libsoup/soup.h> #include "soup-output-stream.h" -#include "soup-input-stream.h" G_DEFINE_TYPE (SoupOutputStream, soup_output_stream, G_TYPE_OUTPUT_STREAM) |
