=?UTF-8?q?=5BCache=20V2=201/1=5D=20grl-net=3A=20Use=20libsoup=20cache?=
- From: "Juan A. Suarez Romero" <jasuarez igalia com>
- To: grilo-list gnome org
- Subject: [Cache V2 1/1] grl-net: Use libsoup cache
- Date: Fri, 11 Mar 2011 10:55:59 +0100
If libsoup version is newer enough, use the cache feature it provides.
Thus, it will cache the content to speed up network access.
Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>
---
configure.ac | 4 +
libs/net/Makefile.am | 6 +
libs/net/grl-net-wc.c | 384 ++++++++++++++++++++++++++++++++++++++++++++++++-
libs/net/grl-net-wc.h | 11 ++-
libs/net/wc-test.c | 2 +-
5 files changed, 399 insertions(+), 8 deletions(-)
diff --git a/configure.ac b/configure.ac
index c307620..8a91bd2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -117,6 +117,9 @@ PKG_CHECK_MODULES(NET, libsoup-2.4,
HAVE_LIBSOUP=yes,
HAVE_LIBSOUP=no)
+PKG_CHECK_MODULES(NETCACHE, libsoup-2.4 >= 2.33.4,
+ HAVE_CACHED_LIBSOUP=yes,
+ HAVE_CACHED_LIBSOUP=no)
AC_ARG_ENABLE([grl_net],
AS_HELP_STRING([--enable-grl-net],
[Enable Grilo Net library (default: auto)]),
@@ -134,6 +137,7 @@ AC_ARG_ENABLE([grl_net],
])
AM_CONDITIONAL(BUILD_GRILO_NET, test "x$HAVE_LIBSOUP" = "xyes")
+AM_CONDITIONAL(BUILD_GRILO_NET_WITH_CACHE, test "x$HAVE_CACHED_LIBSOUP" = "xyes")
# ----------------------------------------------------------
# DEBUG SUPPORT
diff --git a/libs/net/Makefile.am b/libs/net/Makefile.am
index d7542fa..b43cc9a 100644
--- a/libs/net/Makefile.am
+++ b/libs/net/Makefile.am
@@ -5,6 +5,7 @@
#
# Copyright (C) 2010 Igalia S.L. All rights reserved.
+
lib_LTLIBRARIES = libgrlnet-@GRL_MAJORMINOR@.la
noinst_PROGRAMS = wc-test
@@ -20,6 +21,11 @@ libgrlnet_@GRL_MAJORMINOR@_la_CFLAGS = \
$(DEPS_CFLAGS) \
$(NET_CFLAGS)
+if BUILD_GRILO_NET_WITH_CACHE
+libgrlnet_@GRL_MAJORMINOR@_la_CFLAGS += \
+ -DLIBSOUP_WITH_CACHE
+endif
+
libgrlnet_@GRL_MAJORMINOR@_la_LIBADD = \
$(top_builddir)/src/lib@GRL_NAME@.la \
$(DEPS_LIBS) \
diff --git a/libs/net/grl-net-wc.c b/libs/net/grl-net-wc.c
index 0b6253f..4effa78 100644
--- a/libs/net/grl-net-wc.c
+++ b/libs/net/grl-net-wc.c
@@ -4,6 +4,7 @@
* Contact: Iago Toral Quiroga <itoral igalia com>
*
* Authors: Víctor M. Jáquez L. <vjaquez igalia com>
+ * Juan A. Suarez Romero <jasuarez igalia com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -35,8 +36,18 @@
#include "config.h"
#endif
+#include <string.h>
#include <libsoup/soup.h>
+#ifdef LIBSOUP_WITH_CACHE
+/* Using the cache feature requires to use the unstable API */
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
+#define BUFFER_SIZE (50*1024)
+#include <libsoup/soup-cache.h>
+#include <libsoup/soup-requester.h>
+#include <libsoup/soup-request-http.h>
+#endif
+
#include <grilo.h>
#include "grl-net-wc.h"
@@ -47,6 +58,9 @@ enum {
PROP_0,
PROP_LOG_LEVEL,
PROP_THROTTLING,
+ PROP_IDENTIFIER,
+ PROP_CACHE,
+ PROP_CACHE_SIZE,
};
#define GRL_NET_WC_GET_PRIVATE(object) \
@@ -57,8 +71,16 @@ enum {
typedef struct _RequestClosure RequestClosure;
struct _GrlNetWcPrivate {
+ gchar *identifier;
SoupSession *session;
SoupLoggerLogLevel log_level;
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ SoupRequester *requester;
+ SoupCache *cache;
+ gboolean use_cache;
+ guint cache_size;
+ gchar *previous_data;
+#endif
guint throttling;
GTimeVal last_request;
GQueue *pending; /* closure queue for delayed requests */
@@ -72,6 +94,15 @@ struct _RequestClosure {
guint source_id;
};
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+typedef struct {
+ SoupRequest *request;
+ gchar *buffer;
+ gsize length;
+ gsize offset;
+} RequestResult;
+#endif
+
GQuark
grl_net_wc_error_quark (void)
{
@@ -90,6 +121,10 @@ static void grl_net_wc_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+static void cache_down (GrlNetWc *self);
+#endif
+
static void
grl_net_wc_class_init (GrlNetWcClass *klass)
{
@@ -130,6 +165,49 @@ grl_net_wc_class_init (GrlNetWcClass *klass)
0, G_MAXUINT, 0,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
+ /**
+ * GrlNetWc::identifier
+ *
+ * An unique identifier for GrlNet. This identifier is used when cache is
+ * supported. Cached content in a subdirectory named "identifier".
+ */
+ g_object_class_install_property (g_klass,
+ PROP_IDENTIFIER,
+ g_param_spec_string ("identifier",
+ "GrlNet identifier",
+ "Unique identifier for GrlNet",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * GrlNetWc::cache
+ *
+ * %TRUE if cache must be used. %FALSE otherwise.
+ */
+ g_object_class_install_property (g_klass,
+ PROP_CACHE,
+ g_param_spec_boolean ("cache",
+ "Use cache",
+ "Use cache",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * GrlNetWc::cache-size
+ *
+ * Maximum size of cache, in Mb. Default value is 10Mb.
+ */
+ g_object_class_install_property (g_klass,
+ PROP_CACHE_SIZE,
+ g_param_spec_uint ("cache-size",
+ "Cache size",
+ "Size of cache in Mb",
+ 0, G_MAXUINT, 10,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -141,6 +219,11 @@ grl_net_wc_init (GrlNetWc *wc)
wc->priv->session = soup_session_async_new ();
wc->priv->pending = g_queue_new ();
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ wc->priv->requester = soup_requester_new();
+ soup_session_add_feature (wc->priv->session,
+ SOUP_SESSION_FEATURE (wc->priv->requester));
+#endif
}
static void
@@ -150,6 +233,12 @@ grl_net_wc_finalize (GObject *object)
wc = GRL_NET_WC (object);
grl_net_wc_flush_delayed_requests (wc);
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ cache_down (wc);
+ g_free (wc->priv->identifier);
+ g_free (wc->priv->previous_data);
+ g_object_unref (wc->priv->requester);
+#endif
g_queue_free (wc->priv->pending);
g_object_unref (wc->priv->session);
@@ -173,6 +262,15 @@ grl_net_wc_set_property (GObject *object,
case PROP_THROTTLING:
grl_net_wc_set_throttling (wc, g_value_get_uint (value));
break;
+ case PROP_IDENTIFIER:
+ grl_net_wc_set_identifier (wc, g_value_get_string (value));
+ break;
+ case PROP_CACHE:
+ grl_net_wc_set_cache (wc, g_value_get_boolean (value));
+ break;
+ case PROP_CACHE_SIZE:
+ grl_net_wc_set_cache_size (wc, g_value_get_uint (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (wc, propid, pspec);
}
@@ -195,6 +293,15 @@ grl_net_wc_get_property (GObject *object,
case PROP_THROTTLING:
g_value_set_uint (value, wc->priv->throttling);
break;
+ case PROP_IDENTIFIER:
+ g_value_set_string (value, wc->priv->identifier);
+ break;
+ case PROP_CACHE:
+ g_value_set_boolean (value, wc->priv->use_cache);
+ break;
+ case PROP_CACHE_SIZE:
+ g_value_set_uint (value, wc->priv->cache_size);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (wc, propid, pspec);
}
@@ -261,6 +368,88 @@ parse_error (guint status,
}
}
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+static void
+read_async_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GSimpleAsyncResult *result;
+ RequestResult *rr;
+ SoupMessage *msg;
+ GError *error = NULL;
+ gsize to_read;
+ gssize s;
+
+ result = G_SIMPLE_ASYNC_RESULT (user_data);
+ rr = g_simple_async_result_get_op_res_gpointer (result);
+
+ s = g_input_stream_read_finish (G_INPUT_STREAM (source), res, &error);
+ if (s > 0) {
+ /* Continue reading */
+ rr->offset += s;
+ to_read = rr->length - rr->offset;
+ if (!to_read) {
+ /* Buffer is not enough; we need to assign more space */
+ rr->length *= 2;
+ rr->buffer = g_renew (gchar, rr->buffer, rr->length);
+ to_read = rr->length - rr->offset;
+ }
+ g_input_stream_read_async (G_INPUT_STREAM (source), rr->buffer + rr->offset, to_read, G_PRIORITY_DEFAULT, NULL, read_async_cb, user_data);
+ return;
+ }
+
+ /* Put the end of string */
+ rr->buffer[rr->offset] = '\0';
+
+ g_input_stream_close (G_INPUT_STREAM (source), NULL, NULL);
+
+ if (error) {
+ if (error->code == G_IO_ERROR_CANCELLED) {
+ g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+ GRL_NET_WC_ERROR_CANCELLED,
+ "Operation was cancelled");
+ } else {
+ g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
+ GRL_NET_WC_ERROR_UNAVAILABLE,
+ "Data not available");
+ }
+ g_error_free (error);
+
+ g_simple_async_result_complete (result);
+ return;
+ }
+
+ msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+
+ if (msg) {
+ if (msg->status_code != SOUP_STATUS_OK) {
+ parse_error (msg->status_code,
+ msg->reason_phrase,
+ msg->response_body->data,
+ G_SIMPLE_ASYNC_RESULT (user_data));
+ g_object_unref (msg);
+ }
+ }
+
+ g_simple_async_result_complete (result);
+}
+
+static void
+reply_cb (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
+ RequestResult *rr = g_simple_async_result_get_op_res_gpointer (result);
+
+ GInputStream *in = soup_request_send_finish (rr->request, res, NULL);
+ rr->length = soup_request_get_content_length (rr->request) + 1;
+ if (rr->length == 1) {
+ rr->length = BUFFER_SIZE;
+ }
+ rr->buffer = g_new (gchar, rr->length);
+ g_input_stream_read_async (in, rr->buffer, rr->length, G_PRIORITY_DEFAULT, NULL, read_async_cb, user_data);
+}
+
+#else
+
static void
reply_cb (SoupSession *session,
SoupMessage *msg,
@@ -299,6 +488,25 @@ message_cancel_cb (GCancellable *cancellable,
msg, SOUP_STATUS_CANCELLED);
}
+#endif
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+static void
+get_url_now (GrlNetWc *self,
+ const char *url,
+ GAsyncResult *result,
+ GCancellable *cancellable)
+{
+ RequestResult *rr = g_slice_new0 (RequestResult);
+
+ rr->request = soup_requester_request (self->priv->requester, url, NULL);
+ g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result),
+ rr,
+ NULL);
+ soup_request_send_async (rr->request, cancellable, reply_cb, result);
+}
+
+#else
static void
get_url_now (GrlNetWc *self,
@@ -348,6 +556,7 @@ get_url_now (GrlNetWc *self,
reply_cb,
result);
}
+#endif
static gboolean
get_url_delayed (gpointer user_data)
@@ -405,16 +614,49 @@ get_url (GrlNetWc *self,
}
}
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+static void
+cache_down (GrlNetWc *self)
+{
+ soup_session_remove_feature (self->priv->session,
+ SOUP_SESSION_FEATURE (self->priv->cache));
+ soup_cache_clear (self->priv->cache);
+ g_object_unref (self->priv->cache);
+ self->priv->cache = NULL;
+}
+
+static void
+cache_up (GrlNetWc *self)
+{
+ gchar *cache_dir;
+
+ cache_dir = g_build_filename (g_get_user_cache_dir (),
+ g_get_prgname (),
+ self->priv->identifier,
+ NULL);
+ self->priv->cache = soup_cache_new (cache_dir, SOUP_CACHE_SINGLE_USER);
+ grl_net_wc_set_cache_size (self, self->priv->cache_size);
+ soup_session_add_feature (self->priv->session,
+ SOUP_SESSION_FEATURE (self->priv->cache));
+ g_free (cache_dir);
+}
+#endif
+
/**
* grl_net_wc_new:
+ * @identifier: an identifier
+ *
+ * Creates a new #GrlNetWc with unique @identifier.
*
* Returns: a new allocated instance of #GrlNetWc. Do g_object_unref() after
* use it.
*/
GrlNetWc *
-grl_net_wc_new (void)
+grl_net_wc_new (const gchar *identifier)
{
- return g_object_new (GRL_TYPE_NET_WC, NULL);
+ return g_object_new (GRL_TYPE_NET_WC,
+ "identifier", identifier,
+ NULL);
}
/**
@@ -471,7 +713,6 @@ grl_net_wc_request_finish (GrlNetWc *self,
GError **error)
{
GSimpleAsyncResult *res = G_SIMPLE_ASYNC_RESULT (result);
- SoupMessage *msg;
gboolean ret = TRUE;
g_warn_if_fail (g_simple_async_result_get_source_tag (res) ==
@@ -482,15 +723,40 @@ grl_net_wc_request_finish (GrlNetWc *self,
goto end_func;
}
- msg = (SoupMessage *) g_simple_async_result_get_op_res_gpointer (res);
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ RequestResult *rr = g_simple_async_result_get_op_res_gpointer (res);
- if (content != NULL)
- *content = (gchar *) msg->response_body->data;
+ if (self->priv->previous_data) {
+ g_free (self->priv->previous_data);
+ }
+ self->priv->previous_data = rr->buffer;
+
+ if (content) {
+ *content = self->priv->previous_data;
+ } else {
+ g_free (rr->buffer);
+ }
+
+ if (length) {
+ *length = rr->offset;
+ }
+
+#else
+ SoupMessage *msg = (SoupMessage *) g_simple_async_result_get_op_res_gpointer (res);
+
+ if (content != NULL)
+ *content = g_strdup ((gchar *) msg->response_body->data);
if (length != NULL)
*length = (gsize) msg->response_body->length;
+#endif
end_func:
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ g_object_unref (rr->request);
+ g_slice_free (RequestResult, rr);
+#endif
+
g_object_unref (res);
return ret;
}
@@ -552,6 +818,112 @@ grl_net_wc_set_throttling (GrlNetWc *self,
}
/**
+ * grl_net_wc_set_identifier:
+ * @self: a #GrlNetWc instance
+ * @identifier: new identifier
+ *
+ * Changes the identifier for this instance.
+ *
+ * If cache is supported, gets rid of previous cache and create a new one.
+ **/
+void
+grl_net_wc_set_identifier (GrlNetWc *self,
+ const gchar *identifier)
+{
+ g_return_if_fail (GRL_IS_NET_WC (self));
+
+ if (g_strcmp0 (self->priv->identifier, identifier) == 0) {
+ return;
+ }
+
+ if (self->priv->identifier) {
+ g_free (self->priv->identifier);
+ }
+
+ self->priv->identifier = g_strdup (identifier);
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ if (!self->priv->use_cache) {
+ return;
+ }
+
+ /* Get rid of old cache */
+ if (self->priv->cache) {
+ cache_down (self);
+ }
+
+ /* Create a new cache */
+ if (self->priv->identifier) {
+ cache_up (self);
+ }
+#endif
+}
+
+/**
+ * grl_net_wc_set_cache:
+ * @self: a #GrlNetWc instance
+ * @use_cache: if cache must be used or not
+ *
+ * Sets if cache must be used. Note that this will only work if caching is
+ * supporting. If sets %TRUE, a new cache will be created. If sets to %FALSE,
+ * current cache is clean and removed.
+ **/
+void
+grl_net_wc_set_cache (GrlNetWc *self,
+ gboolean use_cache)
+{
+ g_return_if_fail (GRL_IS_NET_WC (self));
+
+ if (self->priv->use_cache == use_cache) {
+ return;
+ }
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ if (self->priv->cache) {
+ cache_down (self);
+ }
+
+ self->priv->use_cache = use_cache;
+ if (use_cache && self->priv->identifier) {
+ cache_up (self);
+ }
+
+#else
+
+ GRL_WARNING ("Cache not supported");
+ wc->priv->cache = FALSE;
+#endif
+}
+
+/**
+ * grl_net_wc_set_cache_size:
+ * @self: a #GrlNetWc instance
+ * @cache_size: size of cache (in Mb)
+ *
+ * Sets the new maximum size of cache, in Megabytes. Default value is 10. Using
+ * 0 means no cache will be done.
+ **/
+void
+grl_net_wc_set_cache_size (GrlNetWc *self,
+ guint cache_size)
+{
+ g_return_if_fail (GRL_IS_NET_WC (self));
+
+ if (self->priv->cache_size == cache_size) {
+ return;
+ }
+
+ self->priv->cache_size = cache_size;
+
+#ifdef LIBSOUP_USE_UNSTABLE_REQUEST_API
+ if (self->priv->cache) {
+ guint size_in_bytes = cache_size * 1024 * 1024;
+ soup_cache_set_max_size (self->priv->cache, size_in_bytes);
+ }
+#endif
+}
+
+/**
* grl_net_wc_flush_delayed_requests:
* @self: a #GrlNetWc instance
*
diff --git a/libs/net/grl-net-wc.h b/libs/net/grl-net-wc.h
index 71dfd72..5d1ad85 100644
--- a/libs/net/grl-net-wc.h
+++ b/libs/net/grl-net-wc.h
@@ -109,7 +109,7 @@ GType grl_net_wc_get_type (void) G_GNUC_CONST;
GQuark grl_net_wc_error_quark (void) G_GNUC_CONST;
-GrlNetWc *grl_net_wc_new (void);
+GrlNetWc *grl_net_wc_new (const gchar *identifier);
void grl_net_wc_request_async (GrlNetWc *self,
const char *uri,
@@ -129,6 +129,15 @@ void grl_net_wc_set_log_level (GrlNetWc *self,
void grl_net_wc_set_throttling (GrlNetWc *self,
guint throttling);
+void grl_net_wc_set_identifier (GrlNetWc *self,
+ const gchar *identifier);
+
+void grl_net_wc_set_cache (GrlNetWc *self,
+ gboolean use_cache);
+
+void grl_net_wc_set_cache_size (GrlNetWc *self,
+ guint cache_size);
+
void grl_net_wc_flush_delayed_requests (GrlNetWc *self);
G_END_DECLS
diff --git a/libs/net/wc-test.c b/libs/net/wc-test.c
index 6313c67..8edbdba 100644
--- a/libs/net/wc-test.c
+++ b/libs/net/wc-test.c
@@ -76,7 +76,7 @@ main (int argc, const char **argv)
if (argc == 2)
uri = argv[1];
- wc = grl_net_wc_new ();
+ wc = grl_net_wc_new (NULL);
g_object_set (wc, "loglevel", 1, NULL);
--
1.7.1
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]